[py-svn] py-trunk commit 52f5dfc7ef98: * properly expose and document runtest-protocol related Exceptions

commits-noreply at bitbucket.org commits-noreply at bitbucket.org
Tue Apr 27 21:16:51 CEST 2010


# HG changeset patch -- Bitbucket.org
# Project py-trunk
# URL http://bitbucket.org/hpk42/py-trunk/overview
# User holger krekel <holger at merlinux.eu>
# Date 1272395589 -7200
# Node ID 52f5dfc7ef98dd8d1cbca6314aae3fb83a360706
# Parent  d622f7f24d98556d71e37e395e2ed0c857ad72fb
* properly expose and document runtest-protocol related Exceptions
  and move all definitions to the runner plugin for now.

* also move EXIT codes to session.py, obsoleting outcome.py alltogether.

--- a/py/_plugin/pytest_pdb.py
+++ b/py/_plugin/pytest_pdb.py
@@ -3,7 +3,6 @@ interactive debugging with the Python De
 """
 import py
 import pdb, sys, linecache
-from py._test.outcome import Skipped
 
 def pytest_addoption(parser):
     group = parser.getgroup("general") 
@@ -17,7 +16,8 @@ def pytest_configure(config):
 
 class PdbInvoke:
     def pytest_runtest_makereport(self, item, call):
-        if call.excinfo and not call.excinfo.errisinstance(Skipped): 
+        if call.excinfo and not \
+           call.excinfo.errisinstance(py.test.exc.Skipped): 
             # play well with capturing, slightly hackish
             capman = item.config.pluginmanager.getplugin('capturemanager')
             capman.suspendcapture() 

--- a/py/_plugin/pytest_runner.py
+++ b/py/_plugin/pytest_runner.py
@@ -3,7 +3,23 @@ collect and run test items and create re
 """
 
 import py, sys
-from py._test.outcome import Skipped
+
+def pytest_namespace():
+    class exc:
+        """ namespace holding py.test runner exceptions. """
+        Skipped = Skipped
+        ExceptionFailure = ExceptionFailure
+        Failed = Failed
+        Exit = Exit
+
+    return {
+        'exc'          : exc, 
+        'raises'       : raises, 
+        'skip'         : skip,
+        'importorskip' : importorskip,
+        'fail'         : fail, 
+        'exit'         : exit, 
+    }
 
 #
 # pytest plugin hooks 
@@ -141,7 +157,7 @@ class ItemTestReport(BaseReport):
                 self.failed = True
                 shortrepr = "?"
                 longrepr = excinfo 
-            elif excinfo.errisinstance(Skipped):
+            elif excinfo.errisinstance(py.test.exc.Skipped):
                 self.skipped = True 
                 shortrepr = "s"
                 longrepr = self.item._repr_failure_py(excinfo)
@@ -180,7 +196,7 @@ class CollectReport(BaseReport):
             self.result = result 
         else:
             self.longrepr = self.collector._repr_failure_py(excinfo)
-            if excinfo.errisinstance(Skipped):
+            if excinfo.errisinstance(py.test.exc.Skipped):
                 self.skipped = True
                 self.reason = str(excinfo.value)
             else:
@@ -259,3 +275,125 @@ class SetupState(object):
             except Exception:
                 col._prepare_exc = sys.exc_info()
                 raise
+
+# =============================================================
+# Test OutcomeExceptions and helpers for creating them. 
+
+
+class OutcomeException(Exception): 
+    """ OutcomeException and its subclass instances indicate and 
+        contain info about test and collection outcomes. 
+    """ 
+    def __init__(self, msg=None, excinfo=None): 
+        self.msg = msg 
+        self.excinfo = excinfo
+
+    def __repr__(self):
+        if self.msg: 
+            return repr(self.msg) 
+        return "<%s instance>" %(self.__class__.__name__,)
+    __str__ = __repr__
+
+class Skipped(OutcomeException): 
+    # XXX slighly hackish: on 3k we fake to live in the builtins 
+    # in order to have Skipped exception printing shorter/nicer
+    __module__ = 'builtins'
+
+class Failed(OutcomeException): 
+    """ raised from an explicit call to py.test.fail() """
+
+class ExceptionFailure(Failed): 
+    """ raised by py.test.raises on an exception-assertion mismatch. """
+    def __init__(self, expr, expected, msg=None, excinfo=None): 
+        Failed.__init__(self, msg=msg, excinfo=excinfo) 
+        self.expr = expr 
+        self.expected = expected
+
+class Exit(KeyboardInterrupt):
+    """ raised by py.test.exit for immediate program exits without tracebacks and reporter/summary. """
+    def __init__(self, msg="unknown reason"):
+        self.msg = msg 
+        KeyboardInterrupt.__init__(self, msg)
+
+# exposed helper methods 
+
+def exit(msg): 
+    """ exit testing process as if KeyboardInterrupt was triggered. """ 
+    __tracebackhide__ = True
+    raise Exit(msg)
+
+def skip(msg=""):
+    """ skip an executing test with the given message.  Note: it's usually
+    better use the py.test.mark.skipif marker to declare a test to be
+    skipped under certain conditions like mismatching platforms or 
+    dependencies.  See the pytest_skipping plugin for details. 
+    """
+    __tracebackhide__ = True
+    raise Skipped(msg=msg) 
+
+def fail(msg=""):
+    """ explicitely fail an currently-executing test with the given Message. """
+    __tracebackhide__ = True
+    raise Failed(msg=msg) 
+
+def raises(ExpectedException, *args, **kwargs):
+    """ if args[0] is callable: raise AssertionError if calling it with 
+        the remaining arguments does not raise the expected exception.  
+        if args[0] is a string: raise AssertionError if executing the
+        the string in the calling scope does not raise expected exception. 
+        for examples:
+        x = 5
+        raises(TypeError, lambda x: x + 'hello', x=x)
+        raises(TypeError, "x + 'hello'")
+    """
+    __tracebackhide__ = True 
+    assert args
+    if isinstance(args[0], str):
+        code, = args
+        assert isinstance(code, str)
+        frame = sys._getframe(1)
+        loc = frame.f_locals.copy()
+        loc.update(kwargs)
+        #print "raises frame scope: %r" % frame.f_locals
+        try:
+            code = py.code.Source(code).compile()
+            py.builtin.exec_(code, frame.f_globals, loc)
+            # XXX didn'T mean f_globals == f_locals something special?
+            #     this is destroyed here ...
+        except ExpectedException:
+            return py.code.ExceptionInfo()
+    else:
+        func = args[0]
+        try:
+            func(*args[1:], **kwargs)
+        except ExpectedException:
+            return py.code.ExceptionInfo()
+        k = ", ".join(["%s=%r" % x for x in kwargs.items()])
+        if k:
+            k = ', ' + k
+        expr = '%s(%r%s)' %(getattr(func, '__name__', func), args, k)
+    raise ExceptionFailure(msg="DID NOT RAISE", 
+                           expr=args, expected=ExpectedException) 
+
+def importorskip(modname, minversion=None):
+    """ return imported module if it has a higher __version__ than the 
+    optionally specified 'minversion' - otherwise call py.test.skip() 
+    with a message detailing the mismatch. 
+    """
+    compile(modname, '', 'eval') # to catch syntaxerrors
+    try:
+        mod = __import__(modname, None, None, ['__doc__'])
+    except ImportError:
+        py.test.skip("could not import %r" %(modname,))
+    if minversion is None:
+        return mod
+    verattr = getattr(mod, '__version__', None)
+    if isinstance(minversion, str):
+        minver = minversion.split(".")
+    else:
+        minver = list(minversion)
+    if verattr is None or verattr.split(".") < minver:
+        py.test.skip("module %r has __version__ %r, required is: %r" %(
+                     modname, verattr, minversion))
+    return mod
+

--- a/testing/plugin/test_pytest_doctest.py
+++ b/testing/plugin/test_pytest_doctest.py
@@ -45,8 +45,6 @@ class TestDoctests:
         reprec.assertoutcome(failed=1)
 
     def test_doctest_unexpected_exception(self, testdir):
-        from py._test.outcome import Failed 
-
         p = testdir.maketxtfile("""
             >>> i = 0
             >>> i = 1 

--- a/py/__init__.py
+++ b/py/__init__.py
@@ -38,11 +38,6 @@ py.apipkg.initpkg(__name__, dict(
         # helpers for use from test functions or collectors
         '__onfirstaccess__' : '._test.config:onpytestaccess',
         '__doc__'           : '._test:__doc__',
-        'raises'            : '._test.outcome:raises',
-        'skip'              : '._test.outcome:skip',
-        'importorskip'      : '._test.outcome:importorskip',
-        'fail'              : '._test.outcome:fail',
-        'exit'              : '._test.outcome:exit',
         # configuration/initialization related test api
         'config'            : '._test.config:config_per_process',
         'ensuretemp'        : '._test.config:ensuretemp',

--- a/py/_test/outcome.py
+++ /dev/null
@@ -1,136 +0,0 @@
-"""
-    Test OutcomeExceptions and helpers for creating them. 
-    py.test.skip|fail|raises helper implementations 
-
-"""
-
-import py
-import sys
-
-class OutcomeException(Exception): 
-    """ OutcomeException and its subclass instances indicate and 
-        contain info about test and collection outcomes. 
-    """ 
-    def __init__(self, msg=None, excinfo=None): 
-        self.msg = msg 
-        self.excinfo = excinfo
-
-    def __repr__(self):
-        if self.msg: 
-            return repr(self.msg) 
-        return "<%s instance>" %(self.__class__.__name__,)
-    __str__ = __repr__
-
-class Passed(OutcomeException): 
-    pass
-
-class Skipped(OutcomeException): 
-    # XXX slighly hackish: on 3k we fake to live in the builtins 
-    # in order to have Skipped exception printing shorter/nicer
-    __module__ = 'builtins'
-
-class Failed(OutcomeException): 
-    pass
-
-class ExceptionFailure(Failed): 
-    def __init__(self, expr, expected, msg=None, excinfo=None): 
-        Failed.__init__(self, msg=msg, excinfo=excinfo) 
-        self.expr = expr 
-        self.expected = expected
-
-class Exit(KeyboardInterrupt):
-    """ for immediate program exits without tracebacks and reporter/summary. """
-    def __init__(self, msg="unknown reason"):
-        self.msg = msg 
-        KeyboardInterrupt.__init__(self, msg)
-
-# exposed helper methods 
-
-def exit(msg): 
-    """ exit testing process as if KeyboardInterrupt was triggered. """ 
-    __tracebackhide__ = True
-    raise Exit(msg)
-
-def skip(msg=""):
-    """ skip an executing test with the given message.  Note: it's usually
-    better use the py.test.mark.skipif marker to declare a test to be
-    skipped under certain conditions like mismatching platforms or 
-    dependencies.  See the pytest_skipping plugin for details. 
-    """
-    __tracebackhide__ = True
-    raise Skipped(msg=msg) 
-
-def fail(msg=""):
-    """ explicitely fail this executing test with the given Message. """
-    __tracebackhide__ = True
-    raise Failed(msg=msg) 
-
-def raises(ExpectedException, *args, **kwargs):
-    """ if args[0] is callable: raise AssertionError if calling it with 
-        the remaining arguments does not raise the expected exception.  
-        if args[0] is a string: raise AssertionError if executing the
-        the string in the calling scope does not raise expected exception. 
-        for examples:
-        x = 5
-        raises(TypeError, lambda x: x + 'hello', x=x)
-        raises(TypeError, "x + 'hello'")
-    """
-    __tracebackhide__ = True 
-    assert args
-    if isinstance(args[0], str):
-        code, = args
-        assert isinstance(code, str)
-        frame = sys._getframe(1)
-        loc = frame.f_locals.copy()
-        loc.update(kwargs)
-        #print "raises frame scope: %r" % frame.f_locals
-        try:
-            code = py.code.Source(code).compile()
-            py.builtin.exec_(code, frame.f_globals, loc)
-            # XXX didn'T mean f_globals == f_locals something special?
-            #     this is destroyed here ...
-        except ExpectedException:
-            return py.code.ExceptionInfo()
-    else:
-        func = args[0]
-        try:
-            func(*args[1:], **kwargs)
-        except ExpectedException:
-            return py.code.ExceptionInfo()
-        k = ", ".join(["%s=%r" % x for x in kwargs.items()])
-        if k:
-            k = ', ' + k
-        expr = '%s(%r%s)' %(getattr(func, '__name__', func), args, k)
-    raise ExceptionFailure(msg="DID NOT RAISE", 
-                           expr=args, expected=ExpectedException) 
-
-def importorskip(modname, minversion=None):
-    """ return imported module if it has a higher __version__ than the 
-    optionally specified 'minversion' - otherwise call py.test.skip() 
-    with a message detailing the mismatch. 
-    """
-    compile(modname, '', 'eval') # to catch syntaxerrors
-    try:
-        mod = __import__(modname, None, None, ['__doc__'])
-    except ImportError:
-        py.test.skip("could not import %r" %(modname,))
-    if minversion is None:
-        return mod
-    verattr = getattr(mod, '__version__', None)
-    if isinstance(minversion, str):
-        minver = minversion.split(".")
-    else:
-        minver = list(minversion)
-    if verattr is None or verattr.split(".") < minver:
-        py.test.skip("module %r has __version__ %r, required is: %r" %(
-                     modname, verattr, minversion))
-    return mod
-
-
-
-# exitcodes for the command line
-EXIT_OK = 0
-EXIT_TESTSFAILED = 1
-EXIT_INTERRUPTED = 2
-EXIT_INTERNALERROR = 3
-EXIT_NOHOSTS = 4

--- a/CHANGELOG
+++ b/CHANGELOG
@@ -9,6 +9,8 @@ Changes between 1.2.1 and 1.2.2 (release
 - added links to the new capturelog and coverage plugins 
 - (issue87) fix unboundlocal error in assertionold code 
 - (issue86) improve documentation for looponfailing
+- expose some internal test running exceptions under py.test.exc.*
+  and shift raises/importorskip etc. helper definitions to runner plugin .
 - ship distribute_setup.py version 0.6.10 
 
 

--- a/py/_test/pluginmanager.py
+++ b/py/_test/pluginmanager.py
@@ -4,7 +4,6 @@ managing loading and interacting with py
 import py
 import inspect
 from py._plugin import hookspec
-from py._test.outcome import Skipped
 
 default_plugins = (
     "default runner capture mark terminal skipping tmpdir monkeypatch "
@@ -139,7 +138,7 @@ class PluginManager(object):
             mod = importplugin(modname)
         except KeyboardInterrupt:
             raise
-        except Skipped:
+        except py.test.exc.Skipped:
             e = py.std.sys.exc_info()[1]
             self._hints.append("skipped plugin %r: %s" %((modname, e.msg)))
         else:

--- a/testing/test_config.py
+++ b/testing/test_config.py
@@ -75,13 +75,14 @@ class TestConfigAPI:
         py.test.raises(KeyError, 'config.getvalue("y", o)')
 
     def test_config_getvalueorskip(self, testdir):
-        from py._test.outcome import Skipped
         config = testdir.parseconfig()
-        py.test.raises(Skipped, "config.getvalueorskip('hello')")
+        py.test.raises(py.test.exc.Skipped, 
+            "config.getvalueorskip('hello')")
         verbose = config.getvalueorskip("verbose")
         assert verbose == config.option.verbose
         config.option.hello = None
-        py.test.raises(Skipped, "config.getvalueorskip('hello')")
+        py.test.raises(py.test.exc.Skipped, 
+            "config.getvalueorskip('hello')")
 
     def test_config_overwrite(self, testdir):
         o = testdir.tmpdir

--- a/testing/root/test_py_imports.py
+++ b/testing/root/test_py_imports.py
@@ -1,7 +1,6 @@
 import py
 import types
 import sys
-from py._test.outcome import Skipped
 
 def checksubpackage(name):
     obj = getattr(py, name)
@@ -52,7 +51,7 @@ def test_importall():
                 modpath = 'py.%s' % relpath
                 try:
                     check_import(modpath)
-                except Skipped:
+                except py.test.exc.Skipped:
                     pass
 
 def check_import(modpath):

--- a/testing/test_outcome.py
+++ b/testing/test_outcome.py
@@ -16,13 +16,12 @@ class TestRaises:
         py.test.raises(ValueError, int, 'hello')
 
     def test_raises_callable_no_exception(self):
-        from py._test.outcome import ExceptionFailure
         class A:
             def __call__(self):
                 pass
         try:
             py.test.raises(ValueError, A())
-        except ExceptionFailure:
+        except py.test.exc.ExceptionFailure:
             pass
 
 def test_pytest_exit():
@@ -41,23 +40,23 @@ def test_exception_printing_skip():
         assert s.startswith("Skipped")
 
 def test_importorskip():
-    from py._test.outcome import Skipped, importorskip
-    assert importorskip == py.test.importorskip
+    importorskip = py.test.importorskip
     try:
         sys = importorskip("sys")
         assert sys == py.std.sys
         #path = py.test.importorskip("os.path")
         #assert path == py.std.os.path
-        py.test.raises(Skipped, "py.test.importorskip('alskdj')")
+        py.test.raises(py.test.exc.Skipped, 
+            "py.test.importorskip('alskdj')")
         py.test.raises(SyntaxError, "py.test.importorskip('x y z')")
         py.test.raises(SyntaxError, "py.test.importorskip('x=y')")
         path = importorskip("py", minversion=".".join(py.__version__))
         mod = py.std.types.ModuleType("hello123")
         mod.__version__ = "1.3"
-        py.test.raises(Skipped, """
+        py.test.raises(py.test.exc.Skipped, """
             py.test.importorskip("hello123", minversion="5.0")
         """)
-    except Skipped:
+    except py.test.exc.Skipped:
         print(py.code.ExceptionInfo())
         py.test.fail("spurious skip")
 

--- a/testing/plugin/test_pytest_runner.py
+++ b/testing/plugin/test_pytest_runner.py
@@ -197,14 +197,13 @@ class BaseFunctionalTests:
         assert rep.when == "call"
 
     def test_exit_propagates(self, testdir):
-        from py._test.outcome import Exit
         try:
             testdir.runitem("""
-                from py._test.outcome import Exit
+                import py
                 def test_func():
-                    raise Exit()
+                    raise py.test.exc.Exit()
             """)
-        except Exit:
+        except py.test.exc.Exit:
             pass
         else: 
             py.test.fail("did not raise")
@@ -216,7 +215,6 @@ class TestExecutionNonForked(BaseFunctio
         return f
 
     def test_keyboardinterrupt_propagates(self, testdir):
-        from py._test.outcome import Exit
         try:
             testdir.runitem("""
                 def test_func():

--- a/py/_test/session.py
+++ b/py/_test/session.py
@@ -6,7 +6,13 @@
 """
 
 import py
-from py._test import outcome
+
+# exitcodes for the command line
+EXIT_OK = 0
+EXIT_TESTSFAILED = 1
+EXIT_INTERRUPTED = 2
+EXIT_INTERNALERROR = 3
+EXIT_NOHOSTS = 4
 
 # imports used for genitems()
 Item = py.test.collect.Item
@@ -96,21 +102,21 @@ class Session(object):
         """ main loop for running tests. """
         self.shouldstop = False 
         self.sessionstarts()
-        exitstatus = outcome.EXIT_OK
+        exitstatus = EXIT_OK
         try:
             self._mainloop(colitems)
             if self._testsfailed:
-                exitstatus = outcome.EXIT_TESTSFAILED
+                exitstatus = EXIT_TESTSFAILED
             self.sessionfinishes(exitstatus=exitstatus)
         except KeyboardInterrupt:
             excinfo = py.code.ExceptionInfo()
             self.config.hook.pytest_keyboard_interrupt(excinfo=excinfo)
-            exitstatus = outcome.EXIT_INTERRUPTED
+            exitstatus = EXIT_INTERRUPTED
         except:
             excinfo = py.code.ExceptionInfo()
             self.config.pluginmanager.notify_exception(excinfo)
-            exitstatus = outcome.EXIT_INTERNALERROR
-        if exitstatus in (outcome.EXIT_INTERNALERROR, outcome.EXIT_INTERRUPTED):
+            exitstatus = EXIT_INTERNALERROR
+        if exitstatus in (EXIT_INTERNALERROR, EXIT_INTERRUPTED):
             self.sessionfinishes(exitstatus=exitstatus)
         return exitstatus
 

--- a/testing/test_pycollect.py
+++ b/testing/test_pycollect.py
@@ -444,8 +444,7 @@ def test_modulecol_roundtrip(testdir):
 
 class TestTracebackCutting:
     def test_skip_simple(self):
-        from py._test.outcome import Skipped
-        excinfo = py.test.raises(Skipped, 'py.test.skip("xxx")')
+        excinfo = py.test.raises(py.test.exc.Skipped, 'py.test.skip("xxx")')
         assert excinfo.traceback[-1].frame.code.name == "skip"
         assert excinfo.traceback[-1].ishidden()
 

--- a/testing/test_deprecated_api.py
+++ b/testing/test_deprecated_api.py
@@ -1,6 +1,5 @@
 
 import py
-from py._test.outcome import Skipped
 
 class TestCollectDeprecated:
         
@@ -191,7 +190,7 @@ class TestDisabled:
         l = modcol.collect()
         assert len(l) == 1
         recwarn.clear()
-        py.test.raises(Skipped, "modcol.setup()")
+        py.test.raises(py.test.exc.Skipped, "modcol.setup()")
         recwarn.pop(DeprecationWarning)
 
     def test_disabled_class(self, recwarn, testdir):
@@ -208,7 +207,7 @@ class TestDisabled:
         l = modcol.collect()
         assert len(l) == 1
         recwarn.clear()
-        py.test.raises(Skipped, "modcol.setup()")
+        py.test.raises(py.test.exc.Skipped, "modcol.setup()")
         recwarn.pop(DeprecationWarning)
 
     def test_disabled_class_functional(self, testdir):



More information about the pytest-commit mailing list