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