_decorators.py
143 lines
| 4.5 KiB
| text/x-python
|
PythonLexer
Fernando Perez
|
r2369 | """ | ||
Decorators for labeling and modifying behavior of test objects. | ||||
Fernando Perez
|
r1420 | |||
Decorators that merely return a modified version of the original | ||||
Fernando Perez
|
r2369 | function object are straightforward. Decorators that return a new | ||
Fernando Perez
|
r1420 | function object need to use | ||
Fernando Perez
|
r2369 | :: | ||
nose.tools.make_decorator(original_function)(decorator) | ||||
in returning the decorator, in order to preserve meta-data such as | ||||
function name, setup and teardown functions and so on - see | ||||
``nose.tools`` for more information. | ||||
Fernando Perez
|
r1420 | |||
""" | ||||
Fernando Perez
|
r2369 | |||
Fernando Perez
|
r2406 | # IPython changes: make this work if numpy not available | ||
# Original code: | ||||
MinRK
|
r3692 | try: | ||
Thomas Kluyver
|
r13347 | from ._numpy_testing_noseclasses import KnownFailureTest | ||
MinRK
|
r3692 | except: | ||
pass | ||||
Paul Ivanov
|
r3511 | |||
Fernando Perez
|
r2406 | # End IPython changes | ||
Fernando Perez
|
r1420 | |||
Fernando Perez
|
r2369 | def skipif(skip_condition, msg=None): | ||
""" | ||||
Make function raise SkipTest exception if a given condition is true. | ||||
If the condition is a callable, it is used at runtime to dynamically | ||||
make the decision. This is useful for tests that may require costly | ||||
imports, to delay the cost until the test suite is actually executed. | ||||
Fernando Perez
|
r1420 | |||
Parameters | ||||
Fernando Perez
|
r1850 | ---------- | ||
Fernando Perez
|
r2369 | skip_condition : bool or callable | ||
Flag to determine whether to skip the decorated test. | ||||
msg : str, optional | ||||
Message to give on raising a SkipTest exception. Default is None. | ||||
Returns | ||||
------- | ||||
decorator : function | ||||
Decorator which, when applied to a function, causes SkipTest | ||||
to be raised when `skip_condition` is True, and the function | ||||
to be called normally otherwise. | ||||
Fernando Perez
|
r1420 | |||
Notes | ||||
----- | ||||
Fernando Perez
|
r2369 | The decorator itself is decorated with the ``nose.tools.make_decorator`` | ||
function in order to transmit function name, and various other metadata. | ||||
""" | ||||
Fernando Perez
|
r1420 | def skip_decorator(f): | ||
Fernando Perez
|
r2369 | # Local import to avoid a hard nose dependency and only incur the | ||
# import time overhead at actual test-time. | ||||
Fernando Perez
|
r1420 | import nose | ||
Fernando Perez
|
r2369 | |||
# Allow for both boolean or callable skip conditions. | ||||
if callable(skip_condition): | ||||
skip_val = lambda : skip_condition() | ||||
else: | ||||
skip_val = lambda : skip_condition | ||||
def get_msg(func,msg=None): | ||||
"""Skip message with information about function being skipped.""" | ||||
Bernardo B. Marques
|
r4872 | if msg is None: | ||
Fernando Perez
|
r2369 | out = 'Test skipped due to test condition' | ||
Bernardo B. Marques
|
r4872 | else: | ||
Fernando Perez
|
r2369 | out = '\n'+msg | ||
return "Skipping test: %s%s" % (func.__name__,out) | ||||
# We need to define *two* skippers because Python doesn't allow both | ||||
# return with value and yield inside the same function. | ||||
def skipper_func(*args, **kwargs): | ||||
"""Skipper for normal test functions.""" | ||||
if skip_val(): | ||||
raise nose.SkipTest(get_msg(f,msg)) | ||||
Fernando Perez
|
r1420 | else: | ||
return f(*args, **kwargs) | ||||
Fernando Perez
|
r2369 | |||
def skipper_gen(*args, **kwargs): | ||||
"""Skipper for test generators.""" | ||||
if skip_val(): | ||||
raise nose.SkipTest(get_msg(f,msg)) | ||||
else: | ||||
for x in f(*args, **kwargs): | ||||
yield x | ||||
# Choose the right skipper to use when building the actual decorator. | ||||
if nose.util.isgenerator(f): | ||||
skipper = skipper_gen | ||||
else: | ||||
skipper = skipper_func | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r1420 | return nose.tools.make_decorator(f)(skipper) | ||
Fernando Perez
|
r2369 | |||
Fernando Perez
|
r1420 | return skip_decorator | ||
Fernando Perez
|
r2369 | def knownfailureif(fail_condition, msg=None): | ||
""" | ||||
Make function raise KnownFailureTest exception if given condition is true. | ||||
Parameters | ||||
---------- | ||||
Matthias Bussonnier
|
r23405 | fail_condition : bool | ||
Fernando Perez
|
r2369 | Flag to determine whether to mark the decorated test as a known | ||
failure (if True) or not (if False). | ||||
msg : str, optional | ||||
Message to give on raising a KnownFailureTest exception. | ||||
Default is None. | ||||
Returns | ||||
------- | ||||
decorator : function | ||||
Matthias Bussonnier
|
r23405 | Decorator, which, when applied to a function, causes KnownFailureTest to | ||
be raised when `fail_condition` is True and the test fails. | ||||
Fernando Perez
|
r2369 | |||
Notes | ||||
----- | ||||
The decorator itself is decorated with the ``nose.tools.make_decorator`` | ||||
function in order to transmit function name, and various other metadata. | ||||
""" | ||||
if msg is None: | ||||
msg = 'Test skipped due to known failure' | ||||
def knownfail_decorator(f): | ||||
# Local import to avoid a hard nose dependency and only incur the | ||||
# import time overhead at actual test-time. | ||||
import nose | ||||
Matthias Bussonnier
|
r23405 | |||
Fernando Perez
|
r2369 | def knownfailer(*args, **kwargs): | ||
Matthias Bussonnier
|
r23405 | if fail_condition: | ||
Bradley M. Froehle
|
r7843 | raise KnownFailureTest(msg) | ||
Fernando Perez
|
r2369 | else: | ||
return f(*args, **kwargs) | ||||
return nose.tools.make_decorator(f)(knownfailer) | ||||
return knownfail_decorator | ||||