* 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.

--HG--
branch : trunk
This commit is contained in:
holger krekel
2010-04-27 21:13:09 +02:00
parent 0d0a7b7fec
commit d5e463605e
14 changed files with 175 additions and 178 deletions
-5
View File
@@ -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',
+2 -2
View File
@@ -3,7 +3,6 @@ interactive debugging with the Python Debugger.
"""
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()
+141 -3
View File
@@ -3,7 +3,23 @@ collect and run test items and create reports.
"""
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
-136
View File
@@ -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
+1 -2
View File
@@ -4,7 +4,6 @@ managing loading and interacting with pytest plugins.
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:
+12 -6
View File
@@ -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