2015-09-19 07:03:05 +08:00
|
|
|
"""
|
|
|
|
exception classes and constants handling test outcomes
|
|
|
|
as well as functions creating them
|
|
|
|
"""
|
|
|
|
from __future__ import absolute_import, division, print_function
|
|
|
|
import py
|
|
|
|
import sys
|
|
|
|
|
|
|
|
|
|
|
|
class OutcomeException(BaseException):
|
|
|
|
""" OutcomeException and its subclass instances indicate and
|
|
|
|
contain info about test and collection outcomes.
|
|
|
|
"""
|
|
|
|
def __init__(self, msg=None, pytrace=True):
|
|
|
|
BaseException.__init__(self, msg)
|
|
|
|
self.msg = msg
|
|
|
|
self.pytrace = pytrace
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
if self.msg:
|
|
|
|
val = self.msg
|
|
|
|
if isinstance(val, bytes):
|
|
|
|
val = py._builtin._totext(val, errors='replace')
|
|
|
|
return val
|
2017-07-29 00:27:59 +08:00
|
|
|
return "<%s instance>" % (self.__class__.__name__,)
|
2015-09-19 07:03:05 +08:00
|
|
|
__str__ = __repr__
|
|
|
|
|
|
|
|
|
|
|
|
TEST_OUTCOME = (OutcomeException, Exception)
|
|
|
|
|
|
|
|
|
|
|
|
class Skipped(OutcomeException):
|
|
|
|
# XXX hackish: on 3k we fake to live in the builtins
|
|
|
|
# in order to have Skipped exception printing shorter/nicer
|
|
|
|
__module__ = 'builtins'
|
|
|
|
|
|
|
|
def __init__(self, msg=None, pytrace=True, allow_module_level=False):
|
|
|
|
OutcomeException.__init__(self, msg=msg, pytrace=pytrace)
|
|
|
|
self.allow_module_level = allow_module_level
|
|
|
|
|
|
|
|
|
|
|
|
class Failed(OutcomeException):
|
|
|
|
""" raised from an explicit call to pytest.fail() """
|
|
|
|
__module__ = 'builtins'
|
|
|
|
|
|
|
|
|
|
|
|
class Exit(KeyboardInterrupt):
|
|
|
|
""" raised for immediate program exits (no tracebacks/summaries)"""
|
|
|
|
def __init__(self, msg="unknown reason"):
|
|
|
|
self.msg = msg
|
|
|
|
KeyboardInterrupt.__init__(self, msg)
|
|
|
|
|
|
|
|
# exposed helper methods
|
|
|
|
|
2017-07-29 00:27:59 +08:00
|
|
|
|
2015-09-19 07:03:05 +08:00
|
|
|
def exit(msg):
|
|
|
|
""" exit testing process as if KeyboardInterrupt was triggered. """
|
|
|
|
__tracebackhide__ = True
|
|
|
|
raise Exit(msg)
|
|
|
|
|
|
|
|
|
|
|
|
exit.Exception = Exit
|
|
|
|
|
|
|
|
|
2017-10-02 05:38:29 +08:00
|
|
|
def skip(msg="", **kwargs):
|
2015-09-19 07:03:05 +08:00
|
|
|
""" skip an executing test with the given message. Note: it's usually
|
|
|
|
better to use the pytest.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.
|
2017-10-02 05:38:29 +08:00
|
|
|
|
|
|
|
:kwarg bool allow_module_level: allows this function to be called at
|
2017-10-03 08:24:52 +08:00
|
|
|
module level, skipping the rest of the module. Default to False.
|
2015-09-19 07:03:05 +08:00
|
|
|
"""
|
|
|
|
__tracebackhide__ = True
|
2017-10-02 05:38:29 +08:00
|
|
|
allow_module_level = kwargs.pop('allow_module_level', False)
|
|
|
|
if kwargs:
|
|
|
|
keys = [k for k in kwargs.keys()]
|
2018-05-18 05:31:16 +08:00
|
|
|
raise TypeError('unexpected keyword arguments: {}'.format(keys))
|
2017-10-02 05:38:29 +08:00
|
|
|
raise Skipped(msg=msg, allow_module_level=allow_module_level)
|
2015-09-19 07:03:05 +08:00
|
|
|
|
|
|
|
|
|
|
|
skip.Exception = Skipped
|
|
|
|
|
|
|
|
|
|
|
|
def fail(msg="", pytrace=True):
|
2018-05-13 18:06:09 +08:00
|
|
|
""" explicitly fail a currently-executing test with the given Message.
|
2015-09-19 07:03:05 +08:00
|
|
|
|
|
|
|
:arg pytrace: if false the msg represents the full failure information
|
|
|
|
and no python traceback will be reported.
|
|
|
|
"""
|
|
|
|
__tracebackhide__ = True
|
|
|
|
raise Failed(msg=msg, pytrace=pytrace)
|
|
|
|
|
|
|
|
|
|
|
|
fail.Exception = Failed
|
|
|
|
|
|
|
|
|
|
|
|
class XFailed(fail.Exception):
|
|
|
|
""" raised from an explicit call to pytest.xfail() """
|
|
|
|
|
|
|
|
|
|
|
|
def xfail(reason=""):
|
|
|
|
""" xfail an executing test or setup functions with the given reason."""
|
|
|
|
__tracebackhide__ = True
|
|
|
|
raise XFailed(reason)
|
|
|
|
|
|
|
|
|
|
|
|
xfail.Exception = XFailed
|
|
|
|
|
|
|
|
|
|
|
|
def importorskip(modname, minversion=None):
|
|
|
|
""" return imported module if it has at least "minversion" as its
|
|
|
|
__version__ attribute. If no minversion is specified the a skip
|
|
|
|
is only triggered if the module can not be imported.
|
|
|
|
"""
|
|
|
|
import warnings
|
|
|
|
__tracebackhide__ = True
|
2017-07-29 00:27:59 +08:00
|
|
|
compile(modname, '', 'eval') # to catch syntaxerrors
|
2015-09-19 07:03:05 +08:00
|
|
|
should_skip = False
|
|
|
|
|
|
|
|
with warnings.catch_warnings():
|
|
|
|
# make sure to ignore ImportWarnings that might happen because
|
|
|
|
# of existing directories with the same name we're trying to
|
|
|
|
# import but without a __init__.py file
|
|
|
|
warnings.simplefilter('ignore')
|
|
|
|
try:
|
|
|
|
__import__(modname)
|
|
|
|
except ImportError:
|
|
|
|
# Do not raise chained exception here(#1485)
|
|
|
|
should_skip = True
|
|
|
|
if should_skip:
|
2017-07-29 00:27:59 +08:00
|
|
|
raise Skipped("could not import %r" % (modname,), allow_module_level=True)
|
2015-09-19 07:03:05 +08:00
|
|
|
mod = sys.modules[modname]
|
|
|
|
if minversion is None:
|
|
|
|
return mod
|
|
|
|
verattr = getattr(mod, '__version__', None)
|
|
|
|
if minversion is not None:
|
|
|
|
try:
|
|
|
|
from pkg_resources import parse_version as pv
|
|
|
|
except ImportError:
|
|
|
|
raise Skipped("we have a required version for %r but can not import "
|
|
|
|
"pkg_resources to parse version strings." % (modname,),
|
|
|
|
allow_module_level=True)
|
|
|
|
if verattr is None or pv(verattr) < pv(minversion):
|
2017-07-29 00:27:59 +08:00
|
|
|
raise Skipped("module %r has __version__ %r, required is: %r" % (
|
2015-09-19 07:03:05 +08:00
|
|
|
modname, verattr, minversion), allow_module_level=True)
|
|
|
|
return mod
|