1"""
2Decorators for labeling and modifying behavior of test objects.
3
4Decorators that merely return a modified version of the original
5function object are straightforward. Decorators that return a new
6function object need to use
7::
8
9  nose.tools.make_decorator(original_function)(decorator)
10
11in returning the decorator, in order to preserve meta-data such as
12function name, setup and teardown functions and so on - see
13``nose.tools`` for more information.
14
15"""
16
17# IPython changes: make this work if numpy not available
18# Original code:
19try:
20    from ._numpy_testing_noseclasses import KnownFailureTest
21except:
22    pass
23
24# End IPython changes
25
26
27def skipif(skip_condition, msg=None):
28    """
29    Make function raise SkipTest exception if a given condition is true.
30
31    If the condition is a callable, it is used at runtime to dynamically
32    make the decision. This is useful for tests that may require costly
33    imports, to delay the cost until the test suite is actually executed.
34
35    Parameters
36    ----------
37    skip_condition : bool or callable
38        Flag to determine whether to skip the decorated test.
39    msg : str, optional
40        Message to give on raising a SkipTest exception. Default is None.
41
42    Returns
43    -------
44    decorator : function
45        Decorator which, when applied to a function, causes SkipTest
46        to be raised when `skip_condition` is True, and the function
47        to be called normally otherwise.
48
49    Notes
50    -----
51    The decorator itself is decorated with the ``nose.tools.make_decorator``
52    function in order to transmit function name, and various other metadata.
53
54    """
55
56    def skip_decorator(f):
57        # Local import to avoid a hard nose dependency and only incur the
58        # import time overhead at actual test-time.
59        import nose
60
61        # Allow for both boolean or callable skip conditions.
62        if callable(skip_condition):
63            skip_val = lambda : skip_condition()
64        else:
65            skip_val = lambda : skip_condition
66
67        def get_msg(func,msg=None):
68            """Skip message with information about function being skipped."""
69            if msg is None:
70                out = 'Test skipped due to test condition'
71            else:
72                out = '\n'+msg
73
74            return "Skipping test: %s%s" % (func.__name__,out)
75
76        # We need to define *two* skippers because Python doesn't allow both
77        # return with value and yield inside the same function.
78        def skipper_func(*args, **kwargs):
79            """Skipper for normal test functions."""
80            if skip_val():
81                raise nose.SkipTest(get_msg(f,msg))
82            else:
83                return f(*args, **kwargs)
84
85        def skipper_gen(*args, **kwargs):
86            """Skipper for test generators."""
87            if skip_val():
88                raise nose.SkipTest(get_msg(f,msg))
89            else:
90                for x in f(*args, **kwargs):
91                    yield x
92
93        # Choose the right skipper to use when building the actual decorator.
94        if nose.util.isgenerator(f):
95            skipper = skipper_gen
96        else:
97            skipper = skipper_func
98
99        return nose.tools.make_decorator(f)(skipper)
100
101    return skip_decorator
102
103def knownfailureif(fail_condition, msg=None):
104    """
105    Make function raise KnownFailureTest exception if given condition is true.
106
107    Parameters
108    ----------
109    fail_condition : bool
110        Flag to determine whether to mark the decorated test as a known
111        failure (if True) or not (if False).
112    msg : str, optional
113        Message to give on raising a KnownFailureTest exception.
114        Default is None.
115
116    Returns
117    -------
118    decorator : function
119        Decorator, which, when applied to a function, causes KnownFailureTest to
120        be raised when `fail_condition` is True and the test fails.
121
122    Notes
123    -----
124    The decorator itself is decorated with the ``nose.tools.make_decorator``
125    function in order to transmit function name, and various other metadata.
126
127    """
128    if msg is None:
129        msg = 'Test skipped due to known failure'
130
131    def knownfail_decorator(f):
132        # Local import to avoid a hard nose dependency and only incur the
133        # import time overhead at actual test-time.
134        import nose
135
136        def knownfailer(*args, **kwargs):
137            if fail_condition:
138                raise KnownFailureTest(msg)
139            else:
140                return f(*args, **kwargs)
141        return nose.tools.make_decorator(f)(knownfailer)
142
143    return knownfail_decorator
144