##// END OF EJS Templates
Merging from upstream trunk.
Merging from upstream trunk.

File last commit:

r2461:a97dcd31
r2516:6b6d4719 merge
Show More
decorators.py
324 lines | 10.7 KiB | text/x-python | PythonLexer
Fernando Perez
Checkpoint with more tests working....
r1420 """Decorators for labeling test objects.
Fernando Perez
Update decorators and test scripts.
r1848 Decorators that merely return a modified version of the original function
object are straightforward. Decorators that return a new function object need
to use nose.tools.make_decorator(original_function)(decorator) in returning the
decorator, in order to preserve metadata such as function name, setup and
teardown functions and so on - see nose.tools for more information.
Fernando Perez
Checkpoint with more tests working....
r1420
Fernando Perez
Add new decorators to skip os-specific tests....
r1721 This module provides a set of useful decorators meant to be ready to use in
your own tests. See the bottom of the file for the ready-made ones, and if you
find yourself writing a new one that may be of generic use, add it here.
Fernando Perez
Add new testing support machinery with better parametric tests....
r2368 Included decorators:
Lightweight testing that remains unittest-compatible.
- @parametric, for parametric test support that is vastly easier to use than
nose's for debugging. With ours, if a test fails, the stack under inspection
is that of the test and not that of the test framework.
- An @as_unittest decorator can be used to tag any normal parameter-less
function as a unittest TestCase. Then, both nose and normal unittest will
recognize it as such. This will make it easier to migrate away from Nose if
we ever need/want to while maintaining very lightweight tests.
Fernando Perez
Checkpoint with more tests working....
r1420 NOTE: This file contains IPython-specific decorators and imports the
numpy.testing.decorators file, which we've copied verbatim. Any of our own
code will be added at the bottom if we end up extending this.
Fernando Perez
Add new testing support machinery with better parametric tests....
r2368
Authors
-------
- Fernando Perez <Fernando.Perez@berkeley.edu>
Fernando Perez
Checkpoint with more tests working....
r1420 """
Fernando Perez
Add new testing support machinery with better parametric tests....
r2368 #-----------------------------------------------------------------------------
# Copyright (C) 2009-2010 The IPython Development Team
#
# Distributed under the terms of the BSD License. The full license is in
# the file COPYING, distributed as part of this software.
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------
Fernando Perez
Checkpoint with more tests working....
r1420 # Stdlib imports
import inspect
Fernando Perez
Add new decorators to skip os-specific tests....
r1721 import sys
Fernando Perez
Add new testing support machinery with better parametric tests....
r2368 import unittest
Fernando Perez
Checkpoint with more tests working....
r1420
# Third-party imports
Fernando Perez
Add new testing support machinery with better parametric tests....
r2368 # This is Michele Simionato's decorator module, kept verbatim.
Brian Granger
Updated Michele Simionato's decorator.py module to 3.1.2....
r2268 from IPython.external.decorator import decorator, update_wrapper
Fernando Perez
Checkpoint with more tests working....
r1420
Fernando Perez
Add new testing support machinery with better parametric tests....
r2368 # We already have python3-compliant code for parametric tests
if sys.version[0]=='2':
Fernando Perez
Added new Tee class, that works much like Unix's 'tee' command....
r2436 from _paramtestpy2 import parametric, ParametricTestCase
Fernando Perez
Add new testing support machinery with better parametric tests....
r2368 else:
Fernando Perez
Added new Tee class, that works much like Unix's 'tee' command....
r2436 from _paramtestpy3 import parametric, ParametricTestCase
Fernando Perez
Add new testing support machinery with better parametric tests....
r2368
Fernando Perez
Massive amount of work to improve the test suite, restores doctests....
r2414 # Expose the unittest-driven decorators
from ipunittest import ipdoctest, ipdocstring
Fernando Perez
Checkpoint with more tests working....
r1420 # Grab the numpy-specific decorators which we keep in a file that we
Fernando Perez
Add new testing support machinery with better parametric tests....
r2368 # occasionally update from upstream: decorators.py is a copy of
# numpy.testing.decorators, we expose all of it here.
from IPython.external.decorators import *
#-----------------------------------------------------------------------------
# Classes and functions
#-----------------------------------------------------------------------------
Fernando Perez
Checkpoint with more tests working....
r1420
Fernando Perez
Add new testing support machinery with better parametric tests....
r2368 # Simple example of the basic idea
def as_unittest(func):
"""Decorator to make a simple function into a normal test via unittest."""
class Tester(unittest.TestCase):
def test(self):
func()
Tester.__name__ = func.__name__
return Tester
Fernando Perez
Checkpoint with more tests working....
r1420
# Utility functions
def apply_wrapper(wrapper,func):
"""Apply a wrapper to a function for decoration.
This mixes Michele Simionato's decorator tool with nose's make_decorator,
to apply a wrapper in a decorator so that all nose attributes, as well as
function signature and other properties, survive the decoration cleanly.
This will ensure that wrapped functions can still be well introspected via
IPython, for example.
"""
import nose.tools
return decorator(wrapper,nose.tools.make_decorator(func)(wrapper))
def make_label_dec(label,ds=None):
"""Factory function to create a decorator that applies one or more labels.
Fernando Perez
Add new testing support machinery with better parametric tests....
r2368 Parameters
----------
Fernando Perez
Checkpoint with more tests working....
r1420 label : string or sequence
One or more labels that will be applied by the decorator to the functions
it decorates. Labels are attributes of the decorated function with their
value set to True.
ds : string
An optional docstring for the resulting decorator. If not given, a
default docstring is auto-generated.
Fernando Perez
Add new testing support machinery with better parametric tests....
r2368 Returns
-------
Fernando Perez
Checkpoint with more tests working....
r1420 A decorator.
Fernando Perez
Add new testing support machinery with better parametric tests....
r2368 Examples
--------
Fernando Perez
Checkpoint with more tests working....
r1420
A simple labeling decorator:
>>> slow = make_label_dec('slow')
>>> print slow.__doc__
Labels a test as 'slow'.
And one that uses multiple labels and a custom docstring:
>>> rare = make_label_dec(['slow','hard'],
... "Mix labels 'slow' and 'hard' for rare tests.")
>>> print rare.__doc__
Mix labels 'slow' and 'hard' for rare tests.
Now, let's test using this one:
>>> @rare
... def f(): pass
...
>>>
>>> f.slow
True
>>> f.hard
True
"""
if isinstance(label,basestring):
labels = [label]
else:
labels = label
# Validate that the given label(s) are OK for use in setattr() by doing a
# dry run on a dummy function.
tmp = lambda : None
for label in labels:
setattr(tmp,label,True)
# This is the actual decorator we'll return
def decor(f):
for label in labels:
setattr(f,label,True)
return f
# Apply the user's docstring, or autogenerate a basic one
if ds is None:
ds = "Labels a test as %r." % label
decor.__doc__ = ds
return decor
Fernando Perez
Update decorators and test scripts.
r1848 # Inspired by numpy's skipif, but uses the full apply_wrapper utility to
# preserve function metadata better and allows the skip condition to be a
# callable.
def skipif(skip_condition, msg=None):
''' Make function raise SkipTest exception if skip_condition is true
Parameters
Fernando Perez
Update docs for automatic API building.
r1850 ----------
Fernando Perez
Update decorators and test scripts.
r1848 skip_condition : bool or callable.
Brian Granger
Fixing misc testing related things.
r1960 Flag to determine whether to skip test. 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
Update decorators and test scripts.
r1848 msg : string
Message to give on raising a SkipTest exception
Returns
-------
decorator : function
Decorator, which, when applied to a function, causes SkipTest
to be raised when the skip_condition was True, and the function
to be called normally otherwise.
Notes
-----
You will see from the code that we had to further decorate the
decorator with the nose.tools.make_decorator function in order to
transmit function name, and various other metadata.
'''
def skip_decorator(f):
# Local import to avoid a hard nose dependency and only incur the
# import time overhead at actual test-time.
import nose
# Allow for both boolean or callable skip conditions.
if callable(skip_condition):
Fernando Perez
Add new @onlyif decorator, the reverse of @skipif....
r2452 skip_val = skip_condition
Fernando Perez
Update decorators and test scripts.
r1848 else:
skip_val = lambda : skip_condition
def get_msg(func,msg=None):
"""Skip message with information about function being skipped."""
if msg is None: out = 'Test skipped due to test condition.'
else: out = 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))
else:
return f(*args, **kwargs)
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 generator.
if nose.util.isgenerator(f):
skipper = skipper_gen
else:
skipper = skipper_func
return nose.tools.make_decorator(f)(skipper)
return skip_decorator
# A version with the condition set to true, common case just to attacha message
# to a skip decorator
def skip(msg=None):
"""Decorator factory - mark a test function for skipping from test suite.
Fernando Perez
Add new decorators to skip os-specific tests....
r1721
Fernando Perez
Add new testing support machinery with better parametric tests....
r2368 Parameters
----------
Fernando Perez
Add optional message to @skip test decorator.
r1560 msg : string
Optional message to be added.
Fernando Perez
Update decorators and test scripts.
r1848
Fernando Perez
Add new testing support machinery with better parametric tests....
r2368 Returns
-------
Fernando Perez
Update decorators and test scripts.
r1848 decorator : function
Decorator, which, when applied to a function, causes SkipTest
to be raised, with the optional message added.
Fernando Perez
Add optional message to @skip test decorator.
r1560 """
Fernando Perez
Checkpoint with more tests working....
r1420
Fernando Perez
Update decorators and test scripts.
r1848 return skipif(True,msg)
Gael Varoquaux
Fix tests when ipdoctest nose plugin is enable (Grrr, no isolation at...
r1505
Fernando Perez
Fix error in test decorator.
r1577
Fernando Perez
Add new @onlyif decorator, the reverse of @skipif....
r2452 def onlyif(condition, msg):
"""The reverse from skipif, see skipif for details."""
if callable(condition):
skip_condition = lambda : not condition()
else:
skip_condition = lambda : not condition
return skipif(skip_condition, msg)
Fernando Perez
Update decorators and test scripts.
r1848 #-----------------------------------------------------------------------------
# Utility functions for decorators
def numpy_not_available():
"""Can numpy be imported? Returns true if numpy does NOT import.
Fernando Perez
Fix error in test decorator.
r1577
Fernando Perez
Update decorators and test scripts.
r1848 This is used to make a decorator to skip tests that require numpy to be
available, but delay the 'import numpy' to test execution time.
"""
try:
import numpy
np_not_avail = False
except ImportError:
np_not_avail = True
return np_not_avail
#-----------------------------------------------------------------------------
# Decorators for public use
skip_doctest = make_label_dec('skip_doctest',
"""Decorator - mark a function or method for skipping its doctest.
Gael Varoquaux
Fix tests when ipdoctest nose plugin is enable (Grrr, no isolation at...
r1505
Fernando Perez
Update decorators and test scripts.
r1848 This decorator allows you to mark a function whose docstring you wish to
omit from testing, while preserving the docstring for introspection, help,
etc.""")
Fernando Perez
Add new decorators to skip os-specific tests....
r1721
# Decorators to skip certain tests on specific platforms.
Fernando Perez
Merging upstream changes from trunk (after fixing small conflicts).
r1872 skip_win32 = skipif(sys.platform == 'win32',
Fernando Perez
Update decorators and test scripts.
r1848 "This test does not run under Windows")
Fernando Perez
Merging upstream changes from trunk (after fixing small conflicts).
r1872 skip_linux = skipif(sys.platform == 'linux2',
"This test does not run under Linux")
skip_osx = skipif(sys.platform == 'darwin',"This test does not run under OS X")
Fernando Perez
Update decorators and test scripts.
r1848
Jorgen Stenarson
Moved skip decorator to testing and created similar ones for OSX and linux, create delete testdirs in module setup/teardown
r1803 # Decorators to skip tests if not on specific platforms.
Fernando Perez
Merging upstream changes from trunk (after fixing small conflicts).
r1872 skip_if_not_win32 = skipif(sys.platform != 'win32',
"This test only runs under Windows")
skip_if_not_linux = skipif(sys.platform != 'linux2',
"This test only runs under Linux")
skip_if_not_osx = skipif(sys.platform != 'darwin',
"This test only runs under OSX")
# Other skip decorators
Fernando Perez
Update decorators and test scripts.
r1848 skipif_not_numpy = skipif(numpy_not_available,"This test requires numpy")
skipknownfailure = skip('This test is known to fail')
Fernando Perez
Fixes for test suite in win32 when all dependencies (esp. Twisted) are...
r2461
# A null 'decorator', useful to make more readable code that needs to pick
# between different decorators based on OS or other conditions
null_deco = lambda f: f