1"""
2exception classes and constants handling test outcomes
3as well as functions creating them
4"""
5from __future__ import absolute_import, division, print_function
6import py
7import sys
8
9
10class OutcomeException(BaseException):
11    """ OutcomeException and its subclass instances indicate and
12        contain info about test and collection outcomes.
13    """
14    def __init__(self, msg=None, pytrace=True):
15        BaseException.__init__(self, msg)
16        self.msg = msg
17        self.pytrace = pytrace
18
19    def __repr__(self):
20        if self.msg:
21            val = self.msg
22            if isinstance(val, bytes):
23                val = py._builtin._totext(val, errors='replace')
24            return val
25        return "<%s instance>" % (self.__class__.__name__,)
26    __str__ = __repr__
27
28
29TEST_OUTCOME = (OutcomeException, Exception)
30
31
32class Skipped(OutcomeException):
33    # XXX hackish: on 3k we fake to live in the builtins
34    # in order to have Skipped exception printing shorter/nicer
35    __module__ = 'builtins'
36
37    def __init__(self, msg=None, pytrace=True, allow_module_level=False):
38        OutcomeException.__init__(self, msg=msg, pytrace=pytrace)
39        self.allow_module_level = allow_module_level
40
41
42class Failed(OutcomeException):
43    """ raised from an explicit call to pytest.fail() """
44    __module__ = 'builtins'
45
46
47class Exit(KeyboardInterrupt):
48    """ raised for immediate program exits (no tracebacks/summaries)"""
49    def __init__(self, msg="unknown reason"):
50        self.msg = msg
51        KeyboardInterrupt.__init__(self, msg)
52
53# exposed helper methods
54
55
56def exit(msg):
57    """ exit testing process as if KeyboardInterrupt was triggered. """
58    __tracebackhide__ = True
59    raise Exit(msg)
60
61
62exit.Exception = Exit
63
64
65def skip(msg="", **kwargs):
66    """ skip an executing test with the given message.  Note: it's usually
67    better to use the pytest.mark.skipif marker to declare a test to be
68    skipped under certain conditions like mismatching platforms or
69    dependencies.  See the pytest_skipping plugin for details.
70
71    :kwarg bool allow_module_level: allows this function to be called at
72        module level, skipping the rest of the module. Default to False.
73    """
74    __tracebackhide__ = True
75    allow_module_level = kwargs.pop('allow_module_level', False)
76    if kwargs:
77        keys = [k for k in kwargs.keys()]
78        raise TypeError('unexpected keyword arguments: {0}'.format(keys))
79    raise Skipped(msg=msg, allow_module_level=allow_module_level)
80
81
82skip.Exception = Skipped
83
84
85def fail(msg="", pytrace=True):
86    """ explicitly fail an currently-executing test with the given Message.
87
88    :arg pytrace: if false the msg represents the full failure information
89                  and no python traceback will be reported.
90    """
91    __tracebackhide__ = True
92    raise Failed(msg=msg, pytrace=pytrace)
93
94
95fail.Exception = Failed
96
97
98class XFailed(fail.Exception):
99    """ raised from an explicit call to pytest.xfail() """
100
101
102def xfail(reason=""):
103    """ xfail an executing test or setup functions with the given reason."""
104    __tracebackhide__ = True
105    raise XFailed(reason)
106
107
108xfail.Exception = XFailed
109
110
111def importorskip(modname, minversion=None):
112    """ return imported module if it has at least "minversion" as its
113    __version__ attribute.  If no minversion is specified the a skip
114    is only triggered if the module can not be imported.
115    """
116    import warnings
117    __tracebackhide__ = True
118    compile(modname, '', 'eval')  # to catch syntaxerrors
119    should_skip = False
120
121    with warnings.catch_warnings():
122        # make sure to ignore ImportWarnings that might happen because
123        # of existing directories with the same name we're trying to
124        # import but without a __init__.py file
125        warnings.simplefilter('ignore')
126        try:
127            __import__(modname)
128        except ImportError:
129            # Do not raise chained exception here(#1485)
130            should_skip = True
131    if should_skip:
132        raise Skipped("could not import %r" % (modname,), allow_module_level=True)
133    mod = sys.modules[modname]
134    if minversion is None:
135        return mod
136    verattr = getattr(mod, '__version__', None)
137    if minversion is not None:
138        try:
139            from pkg_resources import parse_version as pv
140        except ImportError:
141            raise Skipped("we have a required version for %r but can not import "
142                          "pkg_resources to parse version strings." % (modname,),
143                          allow_module_level=True)
144        if verattr is None or pv(verattr) < pv(minversion):
145            raise Skipped("module %r has __version__ %r, required is: %r" % (
146                          modname, verattr, minversion), allow_module_level=True)
147    return mod
148