##// END OF EJS Templates
remove unused imports
Srinivas Reddy Thatiparthy -
Show More
@@ -1,378 +1,378 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Decorators for labeling test objects.
2 """Decorators for labeling test objects.
3
3
4 Decorators that merely return a modified version of the original function
4 Decorators that merely return a modified version of the original function
5 object are straightforward. Decorators that return a new function object need
5 object are straightforward. Decorators that return a new function object need
6 to use nose.tools.make_decorator(original_function)(decorator) in returning the
6 to use nose.tools.make_decorator(original_function)(decorator) in returning the
7 decorator, in order to preserve metadata such as function name, setup and
7 decorator, in order to preserve metadata such as function name, setup and
8 teardown functions and so on - see nose.tools for more information.
8 teardown functions and so on - see nose.tools for more information.
9
9
10 This module provides a set of useful decorators meant to be ready to use in
10 This module provides a set of useful decorators meant to be ready to use in
11 your own tests. See the bottom of the file for the ready-made ones, and if you
11 your own tests. See the bottom of the file for the ready-made ones, and if you
12 find yourself writing a new one that may be of generic use, add it here.
12 find yourself writing a new one that may be of generic use, add it here.
13
13
14 Included decorators:
14 Included decorators:
15
15
16
16
17 Lightweight testing that remains unittest-compatible.
17 Lightweight testing that remains unittest-compatible.
18
18
19 - An @as_unittest decorator can be used to tag any normal parameter-less
19 - An @as_unittest decorator can be used to tag any normal parameter-less
20 function as a unittest TestCase. Then, both nose and normal unittest will
20 function as a unittest TestCase. Then, both nose and normal unittest will
21 recognize it as such. This will make it easier to migrate away from Nose if
21 recognize it as such. This will make it easier to migrate away from Nose if
22 we ever need/want to while maintaining very lightweight tests.
22 we ever need/want to while maintaining very lightweight tests.
23
23
24 NOTE: This file contains IPython-specific decorators. Using the machinery in
24 NOTE: This file contains IPython-specific decorators. Using the machinery in
25 IPython.external.decorators, we import either numpy.testing.decorators if numpy is
25 IPython.external.decorators, we import either numpy.testing.decorators if numpy is
26 available, OR use equivalent code in IPython.external._decorators, which
26 available, OR use equivalent code in IPython.external._decorators, which
27 we've copied verbatim from numpy.
27 we've copied verbatim from numpy.
28
28
29 """
29 """
30
30
31 # Copyright (c) IPython Development Team.
31 # Copyright (c) IPython Development Team.
32 # Distributed under the terms of the Modified BSD License.
32 # Distributed under the terms of the Modified BSD License.
33
33
34 import sys
34 import sys
35 import os
35 import os
36 import tempfile
36 import tempfile
37 import unittest
37 import unittest
38 import warnings
38 import warnings
39 from importlib import import_module
39 from importlib import import_module
40
40
41 from decorator import decorator
41 from decorator import decorator
42
42
43 # Expose the unittest-driven decorators
43 # Expose the unittest-driven decorators
44 from .ipunittest import ipdoctest, ipdocstring
44 from .ipunittest import ipdoctest, ipdocstring
45
45
46 # Grab the numpy-specific decorators which we keep in a file that we
46 # Grab the numpy-specific decorators which we keep in a file that we
47 # occasionally update from upstream: decorators.py is a copy of
47 # occasionally update from upstream: decorators.py is a copy of
48 # numpy.testing.decorators, we expose all of it here.
48 # numpy.testing.decorators, we expose all of it here.
49 from IPython.external.decorators import *
49 from IPython.external.decorators import *
50
50
51 # For onlyif_cmd_exists decorator
51 # For onlyif_cmd_exists decorator
52 from IPython.utils.py3compat import which, PY2, PY3, PYPY
52 from IPython.utils.py3compat import which
53
53
54 #-----------------------------------------------------------------------------
54 #-----------------------------------------------------------------------------
55 # Classes and functions
55 # Classes and functions
56 #-----------------------------------------------------------------------------
56 #-----------------------------------------------------------------------------
57
57
58 # Simple example of the basic idea
58 # Simple example of the basic idea
59 def as_unittest(func):
59 def as_unittest(func):
60 """Decorator to make a simple function into a normal test via unittest."""
60 """Decorator to make a simple function into a normal test via unittest."""
61 class Tester(unittest.TestCase):
61 class Tester(unittest.TestCase):
62 def test(self):
62 def test(self):
63 func()
63 func()
64
64
65 Tester.__name__ = func.__name__
65 Tester.__name__ = func.__name__
66
66
67 return Tester
67 return Tester
68
68
69 # Utility functions
69 # Utility functions
70
70
71 def apply_wrapper(wrapper, func):
71 def apply_wrapper(wrapper, func):
72 """Apply a wrapper to a function for decoration.
72 """Apply a wrapper to a function for decoration.
73
73
74 This mixes Michele Simionato's decorator tool with nose's make_decorator,
74 This mixes Michele Simionato's decorator tool with nose's make_decorator,
75 to apply a wrapper in a decorator so that all nose attributes, as well as
75 to apply a wrapper in a decorator so that all nose attributes, as well as
76 function signature and other properties, survive the decoration cleanly.
76 function signature and other properties, survive the decoration cleanly.
77 This will ensure that wrapped functions can still be well introspected via
77 This will ensure that wrapped functions can still be well introspected via
78 IPython, for example.
78 IPython, for example.
79 """
79 """
80 warnings.warn("The function `apply_wrapper` is deprecated since IPython 4.0",
80 warnings.warn("The function `apply_wrapper` is deprecated since IPython 4.0",
81 DeprecationWarning, stacklevel=2)
81 DeprecationWarning, stacklevel=2)
82 import nose.tools
82 import nose.tools
83
83
84 return decorator(wrapper,nose.tools.make_decorator(func)(wrapper))
84 return decorator(wrapper,nose.tools.make_decorator(func)(wrapper))
85
85
86
86
87 def make_label_dec(label, ds=None):
87 def make_label_dec(label, ds=None):
88 """Factory function to create a decorator that applies one or more labels.
88 """Factory function to create a decorator that applies one or more labels.
89
89
90 Parameters
90 Parameters
91 ----------
91 ----------
92 label : string or sequence
92 label : string or sequence
93 One or more labels that will be applied by the decorator to the functions
93 One or more labels that will be applied by the decorator to the functions
94 it decorates. Labels are attributes of the decorated function with their
94 it decorates. Labels are attributes of the decorated function with their
95 value set to True.
95 value set to True.
96
96
97 ds : string
97 ds : string
98 An optional docstring for the resulting decorator. If not given, a
98 An optional docstring for the resulting decorator. If not given, a
99 default docstring is auto-generated.
99 default docstring is auto-generated.
100
100
101 Returns
101 Returns
102 -------
102 -------
103 A decorator.
103 A decorator.
104
104
105 Examples
105 Examples
106 --------
106 --------
107
107
108 A simple labeling decorator:
108 A simple labeling decorator:
109
109
110 >>> slow = make_label_dec('slow')
110 >>> slow = make_label_dec('slow')
111 >>> slow.__doc__
111 >>> slow.__doc__
112 "Labels a test as 'slow'."
112 "Labels a test as 'slow'."
113
113
114 And one that uses multiple labels and a custom docstring:
114 And one that uses multiple labels and a custom docstring:
115
115
116 >>> rare = make_label_dec(['slow','hard'],
116 >>> rare = make_label_dec(['slow','hard'],
117 ... "Mix labels 'slow' and 'hard' for rare tests.")
117 ... "Mix labels 'slow' and 'hard' for rare tests.")
118 >>> rare.__doc__
118 >>> rare.__doc__
119 "Mix labels 'slow' and 'hard' for rare tests."
119 "Mix labels 'slow' and 'hard' for rare tests."
120
120
121 Now, let's test using this one:
121 Now, let's test using this one:
122 >>> @rare
122 >>> @rare
123 ... def f(): pass
123 ... def f(): pass
124 ...
124 ...
125 >>>
125 >>>
126 >>> f.slow
126 >>> f.slow
127 True
127 True
128 >>> f.hard
128 >>> f.hard
129 True
129 True
130 """
130 """
131
131
132 warnings.warn("The function `make_label_dec` is deprecated since IPython 4.0",
132 warnings.warn("The function `make_label_dec` is deprecated since IPython 4.0",
133 DeprecationWarning, stacklevel=2)
133 DeprecationWarning, stacklevel=2)
134 if isinstance(label, str):
134 if isinstance(label, str):
135 labels = [label]
135 labels = [label]
136 else:
136 else:
137 labels = label
137 labels = label
138
138
139 # Validate that the given label(s) are OK for use in setattr() by doing a
139 # Validate that the given label(s) are OK for use in setattr() by doing a
140 # dry run on a dummy function.
140 # dry run on a dummy function.
141 tmp = lambda : None
141 tmp = lambda : None
142 for label in labels:
142 for label in labels:
143 setattr(tmp,label,True)
143 setattr(tmp,label,True)
144
144
145 # This is the actual decorator we'll return
145 # This is the actual decorator we'll return
146 def decor(f):
146 def decor(f):
147 for label in labels:
147 for label in labels:
148 setattr(f,label,True)
148 setattr(f,label,True)
149 return f
149 return f
150
150
151 # Apply the user's docstring, or autogenerate a basic one
151 # Apply the user's docstring, or autogenerate a basic one
152 if ds is None:
152 if ds is None:
153 ds = "Labels a test as %r." % label
153 ds = "Labels a test as %r." % label
154 decor.__doc__ = ds
154 decor.__doc__ = ds
155
155
156 return decor
156 return decor
157
157
158
158
159 # Inspired by numpy's skipif, but uses the full apply_wrapper utility to
159 # Inspired by numpy's skipif, but uses the full apply_wrapper utility to
160 # preserve function metadata better and allows the skip condition to be a
160 # preserve function metadata better and allows the skip condition to be a
161 # callable.
161 # callable.
162 def skipif(skip_condition, msg=None):
162 def skipif(skip_condition, msg=None):
163 ''' Make function raise SkipTest exception if skip_condition is true
163 ''' Make function raise SkipTest exception if skip_condition is true
164
164
165 Parameters
165 Parameters
166 ----------
166 ----------
167
167
168 skip_condition : bool or callable
168 skip_condition : bool or callable
169 Flag to determine whether to skip test. If the condition is a
169 Flag to determine whether to skip test. If the condition is a
170 callable, it is used at runtime to dynamically make the decision. This
170 callable, it is used at runtime to dynamically make the decision. This
171 is useful for tests that may require costly imports, to delay the cost
171 is useful for tests that may require costly imports, to delay the cost
172 until the test suite is actually executed.
172 until the test suite is actually executed.
173 msg : string
173 msg : string
174 Message to give on raising a SkipTest exception.
174 Message to give on raising a SkipTest exception.
175
175
176 Returns
176 Returns
177 -------
177 -------
178 decorator : function
178 decorator : function
179 Decorator, which, when applied to a function, causes SkipTest
179 Decorator, which, when applied to a function, causes SkipTest
180 to be raised when the skip_condition was True, and the function
180 to be raised when the skip_condition was True, and the function
181 to be called normally otherwise.
181 to be called normally otherwise.
182
182
183 Notes
183 Notes
184 -----
184 -----
185 You will see from the code that we had to further decorate the
185 You will see from the code that we had to further decorate the
186 decorator with the nose.tools.make_decorator function in order to
186 decorator with the nose.tools.make_decorator function in order to
187 transmit function name, and various other metadata.
187 transmit function name, and various other metadata.
188 '''
188 '''
189
189
190 def skip_decorator(f):
190 def skip_decorator(f):
191 # Local import to avoid a hard nose dependency and only incur the
191 # Local import to avoid a hard nose dependency and only incur the
192 # import time overhead at actual test-time.
192 # import time overhead at actual test-time.
193 import nose
193 import nose
194
194
195 # Allow for both boolean or callable skip conditions.
195 # Allow for both boolean or callable skip conditions.
196 if callable(skip_condition):
196 if callable(skip_condition):
197 skip_val = skip_condition
197 skip_val = skip_condition
198 else:
198 else:
199 skip_val = lambda : skip_condition
199 skip_val = lambda : skip_condition
200
200
201 def get_msg(func,msg=None):
201 def get_msg(func,msg=None):
202 """Skip message with information about function being skipped."""
202 """Skip message with information about function being skipped."""
203 if msg is None: out = 'Test skipped due to test condition.'
203 if msg is None: out = 'Test skipped due to test condition.'
204 else: out = msg
204 else: out = msg
205 return "Skipping test: %s. %s" % (func.__name__,out)
205 return "Skipping test: %s. %s" % (func.__name__,out)
206
206
207 # We need to define *two* skippers because Python doesn't allow both
207 # We need to define *two* skippers because Python doesn't allow both
208 # return with value and yield inside the same function.
208 # return with value and yield inside the same function.
209 def skipper_func(*args, **kwargs):
209 def skipper_func(*args, **kwargs):
210 """Skipper for normal test functions."""
210 """Skipper for normal test functions."""
211 if skip_val():
211 if skip_val():
212 raise nose.SkipTest(get_msg(f,msg))
212 raise nose.SkipTest(get_msg(f,msg))
213 else:
213 else:
214 return f(*args, **kwargs)
214 return f(*args, **kwargs)
215
215
216 def skipper_gen(*args, **kwargs):
216 def skipper_gen(*args, **kwargs):
217 """Skipper for test generators."""
217 """Skipper for test generators."""
218 if skip_val():
218 if skip_val():
219 raise nose.SkipTest(get_msg(f,msg))
219 raise nose.SkipTest(get_msg(f,msg))
220 else:
220 else:
221 for x in f(*args, **kwargs):
221 for x in f(*args, **kwargs):
222 yield x
222 yield x
223
223
224 # Choose the right skipper to use when building the actual generator.
224 # Choose the right skipper to use when building the actual generator.
225 if nose.util.isgenerator(f):
225 if nose.util.isgenerator(f):
226 skipper = skipper_gen
226 skipper = skipper_gen
227 else:
227 else:
228 skipper = skipper_func
228 skipper = skipper_func
229
229
230 return nose.tools.make_decorator(f)(skipper)
230 return nose.tools.make_decorator(f)(skipper)
231
231
232 return skip_decorator
232 return skip_decorator
233
233
234 # A version with the condition set to true, common case just to attach a message
234 # A version with the condition set to true, common case just to attach a message
235 # to a skip decorator
235 # to a skip decorator
236 def skip(msg=None):
236 def skip(msg=None):
237 """Decorator factory - mark a test function for skipping from test suite.
237 """Decorator factory - mark a test function for skipping from test suite.
238
238
239 Parameters
239 Parameters
240 ----------
240 ----------
241 msg : string
241 msg : string
242 Optional message to be added.
242 Optional message to be added.
243
243
244 Returns
244 Returns
245 -------
245 -------
246 decorator : function
246 decorator : function
247 Decorator, which, when applied to a function, causes SkipTest
247 Decorator, which, when applied to a function, causes SkipTest
248 to be raised, with the optional message added.
248 to be raised, with the optional message added.
249 """
249 """
250
250
251 return skipif(True,msg)
251 return skipif(True,msg)
252
252
253
253
254 def onlyif(condition, msg):
254 def onlyif(condition, msg):
255 """The reverse from skipif, see skipif for details."""
255 """The reverse from skipif, see skipif for details."""
256
256
257 if callable(condition):
257 if callable(condition):
258 skip_condition = lambda : not condition()
258 skip_condition = lambda : not condition()
259 else:
259 else:
260 skip_condition = lambda : not condition
260 skip_condition = lambda : not condition
261
261
262 return skipif(skip_condition, msg)
262 return skipif(skip_condition, msg)
263
263
264 #-----------------------------------------------------------------------------
264 #-----------------------------------------------------------------------------
265 # Utility functions for decorators
265 # Utility functions for decorators
266 def module_not_available(module):
266 def module_not_available(module):
267 """Can module be imported? Returns true if module does NOT import.
267 """Can module be imported? Returns true if module does NOT import.
268
268
269 This is used to make a decorator to skip tests that require module to be
269 This is used to make a decorator to skip tests that require module to be
270 available, but delay the 'import numpy' to test execution time.
270 available, but delay the 'import numpy' to test execution time.
271 """
271 """
272 try:
272 try:
273 mod = import_module(module)
273 mod = import_module(module)
274 mod_not_avail = False
274 mod_not_avail = False
275 except ImportError:
275 except ImportError:
276 mod_not_avail = True
276 mod_not_avail = True
277
277
278 return mod_not_avail
278 return mod_not_avail
279
279
280
280
281 def decorated_dummy(dec, name):
281 def decorated_dummy(dec, name):
282 """Return a dummy function decorated with dec, with the given name.
282 """Return a dummy function decorated with dec, with the given name.
283
283
284 Examples
284 Examples
285 --------
285 --------
286 import IPython.testing.decorators as dec
286 import IPython.testing.decorators as dec
287 setup = dec.decorated_dummy(dec.skip_if_no_x11, __name__)
287 setup = dec.decorated_dummy(dec.skip_if_no_x11, __name__)
288 """
288 """
289 warnings.warn("The function `decorated_dummy` is deprecated since IPython 4.0",
289 warnings.warn("The function `decorated_dummy` is deprecated since IPython 4.0",
290 DeprecationWarning, stacklevel=2)
290 DeprecationWarning, stacklevel=2)
291 dummy = lambda: None
291 dummy = lambda: None
292 dummy.__name__ = name
292 dummy.__name__ = name
293 return dec(dummy)
293 return dec(dummy)
294
294
295 #-----------------------------------------------------------------------------
295 #-----------------------------------------------------------------------------
296 # Decorators for public use
296 # Decorators for public use
297
297
298 # Decorators to skip certain tests on specific platforms.
298 # Decorators to skip certain tests on specific platforms.
299 skip_win32 = skipif(sys.platform == 'win32',
299 skip_win32 = skipif(sys.platform == 'win32',
300 "This test does not run under Windows")
300 "This test does not run under Windows")
301 skip_linux = skipif(sys.platform.startswith('linux'),
301 skip_linux = skipif(sys.platform.startswith('linux'),
302 "This test does not run under Linux")
302 "This test does not run under Linux")
303 skip_osx = skipif(sys.platform == 'darwin',"This test does not run under OS X")
303 skip_osx = skipif(sys.platform == 'darwin',"This test does not run under OS X")
304
304
305
305
306 # Decorators to skip tests if not on specific platforms.
306 # Decorators to skip tests if not on specific platforms.
307 skip_if_not_win32 = skipif(sys.platform != 'win32',
307 skip_if_not_win32 = skipif(sys.platform != 'win32',
308 "This test only runs under Windows")
308 "This test only runs under Windows")
309 skip_if_not_linux = skipif(not sys.platform.startswith('linux'),
309 skip_if_not_linux = skipif(not sys.platform.startswith('linux'),
310 "This test only runs under Linux")
310 "This test only runs under Linux")
311 skip_if_not_osx = skipif(sys.platform != 'darwin',
311 skip_if_not_osx = skipif(sys.platform != 'darwin',
312 "This test only runs under OSX")
312 "This test only runs under OSX")
313
313
314
314
315 _x11_skip_cond = (sys.platform not in ('darwin', 'win32') and
315 _x11_skip_cond = (sys.platform not in ('darwin', 'win32') and
316 os.environ.get('DISPLAY', '') == '')
316 os.environ.get('DISPLAY', '') == '')
317 _x11_skip_msg = "Skipped under *nix when X11/XOrg not available"
317 _x11_skip_msg = "Skipped under *nix when X11/XOrg not available"
318
318
319 skip_if_no_x11 = skipif(_x11_skip_cond, _x11_skip_msg)
319 skip_if_no_x11 = skipif(_x11_skip_cond, _x11_skip_msg)
320
320
321 # not a decorator itself, returns a dummy function to be used as setup
321 # not a decorator itself, returns a dummy function to be used as setup
322 def skip_file_no_x11(name):
322 def skip_file_no_x11(name):
323 warnings.warn("The function `skip_file_no_x11` is deprecated since IPython 4.0",
323 warnings.warn("The function `skip_file_no_x11` is deprecated since IPython 4.0",
324 DeprecationWarning, stacklevel=2)
324 DeprecationWarning, stacklevel=2)
325 return decorated_dummy(skip_if_no_x11, name) if _x11_skip_cond else None
325 return decorated_dummy(skip_if_no_x11, name) if _x11_skip_cond else None
326
326
327 # Other skip decorators
327 # Other skip decorators
328
328
329 # generic skip without module
329 # generic skip without module
330 skip_without = lambda mod: skipif(module_not_available(mod), "This test requires %s" % mod)
330 skip_without = lambda mod: skipif(module_not_available(mod), "This test requires %s" % mod)
331
331
332 skipif_not_numpy = skip_without('numpy')
332 skipif_not_numpy = skip_without('numpy')
333
333
334 skipif_not_matplotlib = skip_without('matplotlib')
334 skipif_not_matplotlib = skip_without('matplotlib')
335
335
336 skipif_not_sympy = skip_without('sympy')
336 skipif_not_sympy = skip_without('sympy')
337
337
338 skip_known_failure = knownfailureif(True,'This test is known to fail')
338 skip_known_failure = knownfailureif(True,'This test is known to fail')
339
339
340 # A null 'decorator', useful to make more readable code that needs to pick
340 # A null 'decorator', useful to make more readable code that needs to pick
341 # between different decorators based on OS or other conditions
341 # between different decorators based on OS or other conditions
342 null_deco = lambda f: f
342 null_deco = lambda f: f
343
343
344 # Some tests only run where we can use unicode paths. Note that we can't just
344 # Some tests only run where we can use unicode paths. Note that we can't just
345 # check os.path.supports_unicode_filenames, which is always False on Linux.
345 # check os.path.supports_unicode_filenames, which is always False on Linux.
346 try:
346 try:
347 f = tempfile.NamedTemporaryFile(prefix=u"tmp€")
347 f = tempfile.NamedTemporaryFile(prefix=u"tmp€")
348 except UnicodeEncodeError:
348 except UnicodeEncodeError:
349 unicode_paths = False
349 unicode_paths = False
350 else:
350 else:
351 unicode_paths = True
351 unicode_paths = True
352 f.close()
352 f.close()
353
353
354 onlyif_unicode_paths = onlyif(unicode_paths, ("This test is only applicable "
354 onlyif_unicode_paths = onlyif(unicode_paths, ("This test is only applicable "
355 "where we can use unicode in filenames."))
355 "where we can use unicode in filenames."))
356
356
357
357
358 def onlyif_cmds_exist(*commands):
358 def onlyif_cmds_exist(*commands):
359 """
359 """
360 Decorator to skip test when at least one of `commands` is not found.
360 Decorator to skip test when at least one of `commands` is not found.
361 """
361 """
362 for cmd in commands:
362 for cmd in commands:
363 if not which(cmd):
363 if not which(cmd):
364 return skip("This test runs only if command '{0}' "
364 return skip("This test runs only if command '{0}' "
365 "is installed".format(cmd))
365 "is installed".format(cmd))
366 return null_deco
366 return null_deco
367
367
368 def onlyif_any_cmd_exists(*commands):
368 def onlyif_any_cmd_exists(*commands):
369 """
369 """
370 Decorator to skip test unless at least one of `commands` is found.
370 Decorator to skip test unless at least one of `commands` is found.
371 """
371 """
372 warnings.warn("The function `onlyif_any_cmd_exists` is deprecated since IPython 4.0",
372 warnings.warn("The function `onlyif_any_cmd_exists` is deprecated since IPython 4.0",
373 DeprecationWarning, stacklevel=2)
373 DeprecationWarning, stacklevel=2)
374 for cmd in commands:
374 for cmd in commands:
375 if which(cmd):
375 if which(cmd):
376 return null_deco
376 return null_deco
377 return skip("This test runs only if one of the commands {0} "
377 return skip("This test runs only if one of the commands {0} "
378 "is installed".format(commands))
378 "is installed".format(commands))
General Comments 0
You need to be logged in to leave comments. Login now