##// END OF EJS Templates
Merge pull request #13304 from Kojoley/remove-bunch-of-deprecated-and-unused-testing-decorators...
Matthias Bussonnier -
r27112:1ed2ee03 merge
parent child Browse files
Show More
@@ -1,331 +1,212 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 os
34 import os
35 import shutil
35 import shutil
36 import sys
36 import sys
37 import tempfile
37 import tempfile
38 import unittest
38 import unittest
39 import warnings
39 import warnings
40 from importlib import import_module
40 from importlib import import_module
41
41
42 from decorator import decorator
42 from decorator import decorator
43
43
44 # Expose the unittest-driven decorators
44 # Expose the unittest-driven decorators
45 from .ipunittest import ipdoctest, ipdocstring
45 from .ipunittest import ipdoctest, ipdocstring
46
46
47 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
48 # Classes and functions
48 # Classes and functions
49 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
50
50
51 # Simple example of the basic idea
51 # Simple example of the basic idea
52 def as_unittest(func):
52 def as_unittest(func):
53 """Decorator to make a simple function into a normal test via unittest."""
53 """Decorator to make a simple function into a normal test via unittest."""
54 class Tester(unittest.TestCase):
54 class Tester(unittest.TestCase):
55 def test(self):
55 def test(self):
56 func()
56 func()
57
57
58 Tester.__name__ = func.__name__
58 Tester.__name__ = func.__name__
59
59
60 return Tester
60 return Tester
61
61
62 # Utility functions
62 # Utility functions
63
63
64 def apply_wrapper(wrapper, func):
65 """Apply a wrapper to a function for decoration.
66
67 This mixes Michele Simionato's decorator tool with nose's make_decorator,
68 to apply a wrapper in a decorator so that all nose attributes, as well as
69 function signature and other properties, survive the decoration cleanly.
70 This will ensure that wrapped functions can still be well introspected via
71 IPython, for example.
72 """
73 warnings.warn("The function `apply_wrapper` is deprecated since IPython 4.0",
74 DeprecationWarning, stacklevel=2)
75 import nose.tools
76
77 return decorator(wrapper,nose.tools.make_decorator(func)(wrapper))
78
79
80 def make_label_dec(label, ds=None):
81 """Factory function to create a decorator that applies one or more labels.
82
83 Parameters
84 ----------
85 label : string or sequence
86 One or more labels that will be applied by the decorator to the functions
87 it decorates. Labels are attributes of the decorated function with their
88 value set to True.
89
90 ds : string
91 An optional docstring for the resulting decorator. If not given, a
92 default docstring is auto-generated.
93
94 Returns
95 -------
96 A decorator.
97
98 Examples
99 --------
100
101 A simple labeling decorator:
102
103 >>> slow = make_label_dec('slow')
104 >>> slow.__doc__
105 "Labels a test as 'slow'."
106
107 And one that uses multiple labels and a custom docstring:
108
109 >>> rare = make_label_dec(['slow','hard'],
110 ... "Mix labels 'slow' and 'hard' for rare tests.")
111 >>> rare.__doc__
112 "Mix labels 'slow' and 'hard' for rare tests."
113
114 Now, let's test using this one:
115 >>> @rare
116 ... def f(): pass
117 ...
118 >>>
119 >>> f.slow
120 True
121 >>> f.hard
122 True
123 """
124
125 warnings.warn("The function `make_label_dec` is deprecated since IPython 4.0",
126 DeprecationWarning, stacklevel=2)
127 if isinstance(label, str):
128 labels = [label]
129 else:
130 labels = label
131
132 # Validate that the given label(s) are OK for use in setattr() by doing a
133 # dry run on a dummy function.
134 tmp = lambda : None
135 for label in labels:
136 setattr(tmp,label,True)
137
138 # This is the actual decorator we'll return
139 def decor(f):
140 for label in labels:
141 setattr(f,label,True)
142 return f
143
144 # Apply the user's docstring, or autogenerate a basic one
145 if ds is None:
146 ds = "Labels a test as %r." % label
147 decor.__doc__ = ds
148
149 return decor
150
151
64
152 def skipif(skip_condition, msg=None):
65 def skipif(skip_condition, msg=None):
153 """Make function raise SkipTest exception if skip_condition is true
66 """Make function raise SkipTest exception if skip_condition is true
154
67
155 Parameters
68 Parameters
156 ----------
69 ----------
157
70
158 skip_condition : bool or callable
71 skip_condition : bool or callable
159 Flag to determine whether to skip test. If the condition is a
72 Flag to determine whether to skip test. If the condition is a
160 callable, it is used at runtime to dynamically make the decision. This
73 callable, it is used at runtime to dynamically make the decision. This
161 is useful for tests that may require costly imports, to delay the cost
74 is useful for tests that may require costly imports, to delay the cost
162 until the test suite is actually executed.
75 until the test suite is actually executed.
163 msg : string
76 msg : string
164 Message to give on raising a SkipTest exception.
77 Message to give on raising a SkipTest exception.
165
78
166 Returns
79 Returns
167 -------
80 -------
168 decorator : function
81 decorator : function
169 Decorator, which, when applied to a function, causes SkipTest
82 Decorator, which, when applied to a function, causes SkipTest
170 to be raised when the skip_condition was True, and the function
83 to be raised when the skip_condition was True, and the function
171 to be called normally otherwise.
84 to be called normally otherwise.
172 """
85 """
173 if msg is None:
86 if msg is None:
174 msg = "Test skipped due to test condition."
87 msg = "Test skipped due to test condition."
175
88
176 import pytest
89 import pytest
177
90
178 assert isinstance(skip_condition, bool)
91 assert isinstance(skip_condition, bool)
179 return pytest.mark.skipif(skip_condition, reason=msg)
92 return pytest.mark.skipif(skip_condition, reason=msg)
180
93
181
94
182 # A version with the condition set to true, common case just to attach a message
95 # A version with the condition set to true, common case just to attach a message
183 # to a skip decorator
96 # to a skip decorator
184 def skip(msg=None):
97 def skip(msg=None):
185 """Decorator factory - mark a test function for skipping from test suite.
98 """Decorator factory - mark a test function for skipping from test suite.
186
99
187 Parameters
100 Parameters
188 ----------
101 ----------
189 msg : string
102 msg : string
190 Optional message to be added.
103 Optional message to be added.
191
104
192 Returns
105 Returns
193 -------
106 -------
194 decorator : function
107 decorator : function
195 Decorator, which, when applied to a function, causes SkipTest
108 Decorator, which, when applied to a function, causes SkipTest
196 to be raised, with the optional message added.
109 to be raised, with the optional message added.
197 """
110 """
198 if msg and not isinstance(msg, str):
111 if msg and not isinstance(msg, str):
199 raise ValueError('invalid object passed to `@skip` decorator, did you '
112 raise ValueError('invalid object passed to `@skip` decorator, did you '
200 'meant `@skip()` with brackets ?')
113 'meant `@skip()` with brackets ?')
201 return skipif(True, msg)
114 return skipif(True, msg)
202
115
203
116
204 def onlyif(condition, msg):
117 def onlyif(condition, msg):
205 """The reverse from skipif, see skipif for details."""
118 """The reverse from skipif, see skipif for details."""
206
119
207 return skipif(not condition, msg)
120 return skipif(not condition, msg)
208
121
209 #-----------------------------------------------------------------------------
122 #-----------------------------------------------------------------------------
210 # Utility functions for decorators
123 # Utility functions for decorators
211 def module_not_available(module):
124 def module_not_available(module):
212 """Can module be imported? Returns true if module does NOT import.
125 """Can module be imported? Returns true if module does NOT import.
213
126
214 This is used to make a decorator to skip tests that require module to be
127 This is used to make a decorator to skip tests that require module to be
215 available, but delay the 'import numpy' to test execution time.
128 available, but delay the 'import numpy' to test execution time.
216 """
129 """
217 try:
130 try:
218 mod = import_module(module)
131 mod = import_module(module)
219 mod_not_avail = False
132 mod_not_avail = False
220 except ImportError:
133 except ImportError:
221 mod_not_avail = True
134 mod_not_avail = True
222
135
223 return mod_not_avail
136 return mod_not_avail
224
137
225
138
226 def decorated_dummy(dec, name):
227 """Return a dummy function decorated with dec, with the given name.
228
229 Examples
230 --------
231 import IPython.testing.decorators as dec
232 setup = dec.decorated_dummy(dec.skip_if_no_x11, __name__)
233 """
234 warnings.warn("The function `decorated_dummy` is deprecated since IPython 4.0",
235 DeprecationWarning, stacklevel=2)
236 dummy = lambda: None
237 dummy.__name__ = name
238 return dec(dummy)
239
240 #-----------------------------------------------------------------------------
139 #-----------------------------------------------------------------------------
241 # Decorators for public use
140 # Decorators for public use
242
141
243 # Decorators to skip certain tests on specific platforms.
142 # Decorators to skip certain tests on specific platforms.
244 skip_win32 = skipif(sys.platform == 'win32',
143 skip_win32 = skipif(sys.platform == 'win32',
245 "This test does not run under Windows")
144 "This test does not run under Windows")
246 skip_linux = skipif(sys.platform.startswith('linux'),
145 skip_linux = skipif(sys.platform.startswith('linux'),
247 "This test does not run under Linux")
146 "This test does not run under Linux")
248 skip_osx = skipif(sys.platform == 'darwin',"This test does not run under OS X")
147 skip_osx = skipif(sys.platform == 'darwin',"This test does not run under OS X")
249
148
250
149
251 # Decorators to skip tests if not on specific platforms.
150 # Decorators to skip tests if not on specific platforms.
252 skip_if_not_win32 = skipif(sys.platform != 'win32',
151 skip_if_not_win32 = skipif(sys.platform != 'win32',
253 "This test only runs under Windows")
152 "This test only runs under Windows")
254 skip_if_not_linux = skipif(not sys.platform.startswith('linux'),
153 skip_if_not_linux = skipif(not sys.platform.startswith('linux'),
255 "This test only runs under Linux")
154 "This test only runs under Linux")
256 skip_if_not_osx = skipif(sys.platform != 'darwin',
155 skip_if_not_osx = skipif(sys.platform != 'darwin',
257 "This test only runs under OSX")
156 "This test only runs under OSX")
258
157
259
158
260 _x11_skip_cond = (sys.platform not in ('darwin', 'win32') and
159 _x11_skip_cond = (sys.platform not in ('darwin', 'win32') and
261 os.environ.get('DISPLAY', '') == '')
160 os.environ.get('DISPLAY', '') == '')
262 _x11_skip_msg = "Skipped under *nix when X11/XOrg not available"
161 _x11_skip_msg = "Skipped under *nix when X11/XOrg not available"
263
162
264 skip_if_no_x11 = skipif(_x11_skip_cond, _x11_skip_msg)
163 skip_if_no_x11 = skipif(_x11_skip_cond, _x11_skip_msg)
265
164
266
165
267 # Decorators to skip certain tests on specific platform/python combinations
166 # Decorators to skip certain tests on specific platform/python combinations
268 skip_win32_py38 = skipif(sys.version_info > (3,8) and os.name == 'nt')
167 skip_win32_py38 = skipif(sys.version_info > (3,8) and os.name == 'nt')
269
168
270
169
271 # not a decorator itself, returns a dummy function to be used as setup
272 def skip_file_no_x11(name):
273 warnings.warn("The function `skip_file_no_x11` is deprecated since IPython 4.0",
274 DeprecationWarning, stacklevel=2)
275 return decorated_dummy(skip_if_no_x11, name) if _x11_skip_cond else None
276
277 # Other skip decorators
170 # Other skip decorators
278
171
279 # generic skip without module
172 # generic skip without module
280 skip_without = lambda mod: skipif(module_not_available(mod), "This test requires %s" % mod)
173 skip_without = lambda mod: skipif(module_not_available(mod), "This test requires %s" % mod)
281
174
282 skipif_not_numpy = skip_without('numpy')
175 skipif_not_numpy = skip_without('numpy')
283
176
284 skipif_not_matplotlib = skip_without('matplotlib')
177 skipif_not_matplotlib = skip_without('matplotlib')
285
178
286 skipif_not_sympy = skip_without('sympy')
179 skipif_not_sympy = skip_without('sympy')
287
180
288 # A null 'decorator', useful to make more readable code that needs to pick
181 # A null 'decorator', useful to make more readable code that needs to pick
289 # between different decorators based on OS or other conditions
182 # between different decorators based on OS or other conditions
290 null_deco = lambda f: f
183 null_deco = lambda f: f
291
184
292 # Some tests only run where we can use unicode paths. Note that we can't just
185 # Some tests only run where we can use unicode paths. Note that we can't just
293 # check os.path.supports_unicode_filenames, which is always False on Linux.
186 # check os.path.supports_unicode_filenames, which is always False on Linux.
294 try:
187 try:
295 f = tempfile.NamedTemporaryFile(prefix=u"tmp€")
188 f = tempfile.NamedTemporaryFile(prefix=u"tmp€")
296 except UnicodeEncodeError:
189 except UnicodeEncodeError:
297 unicode_paths = False
190 unicode_paths = False
298 else:
191 else:
299 unicode_paths = True
192 unicode_paths = True
300 f.close()
193 f.close()
301
194
302 onlyif_unicode_paths = onlyif(unicode_paths, ("This test is only applicable "
195 onlyif_unicode_paths = onlyif(unicode_paths, ("This test is only applicable "
303 "where we can use unicode in filenames."))
196 "where we can use unicode in filenames."))
304
197
305
198
306 def onlyif_cmds_exist(*commands):
199 def onlyif_cmds_exist(*commands):
307 """
200 """
308 Decorator to skip test when at least one of `commands` is not found.
201 Decorator to skip test when at least one of `commands` is not found.
309 """
202 """
310 for cmd in commands:
203 for cmd in commands:
311 reason = f"This test runs only if command '{cmd}' is installed"
204 reason = f"This test runs only if command '{cmd}' is installed"
312 if not shutil.which(cmd):
205 if not shutil.which(cmd):
313 if os.environ.get("IPTEST_WORKING_DIR", None) is not None:
206 if os.environ.get("IPTEST_WORKING_DIR", None) is not None:
314 return skip(reason)
207 return skip(reason)
315 else:
208 else:
316 import pytest
209 import pytest
317
210
318 return pytest.mark.skip(reason=reason)
211 return pytest.mark.skip(reason=reason)
319 return null_deco
212 return null_deco
320
321 def onlyif_any_cmd_exists(*commands):
322 """
323 Decorator to skip test unless at least one of `commands` is found.
324 """
325 warnings.warn("The function `onlyif_any_cmd_exists` is deprecated since IPython 4.0",
326 DeprecationWarning, stacklevel=2)
327 for cmd in commands:
328 if shutil.which(cmd):
329 return null_deco
330 return skip("This test runs only if one of the commands {0} "
331 "is installed".format(commands))
General Comments 0
You need to be logged in to leave comments. Login now