##// END OF EJS Templates
Remove bunch of deprecated and unused testing decorators
Nikita Kniazev -
Show More
@@ -1,342 +1,223
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 skip_iptest_but_not_pytest(f):
65 def skip_iptest_but_not_pytest(f):
153 """
66 """
154 Warning this will make the test invisible to iptest.
67 Warning this will make the test invisible to iptest.
155 """
68 """
156 import os
69 import os
157
70
158 if os.environ.get("IPTEST_WORKING_DIR", None) is not None:
71 if os.environ.get("IPTEST_WORKING_DIR", None) is not None:
159 f.__test__ = False
72 f.__test__ = False
160 return f
73 return f
161
74
162
75
163 def skipif(skip_condition, msg=None):
76 def skipif(skip_condition, msg=None):
164 """Make function raise SkipTest exception if skip_condition is true
77 """Make function raise SkipTest exception if skip_condition is true
165
78
166 Parameters
79 Parameters
167 ----------
80 ----------
168
81
169 skip_condition : bool or callable
82 skip_condition : bool or callable
170 Flag to determine whether to skip test. If the condition is a
83 Flag to determine whether to skip test. If the condition is a
171 callable, it is used at runtime to dynamically make the decision. This
84 callable, it is used at runtime to dynamically make the decision. This
172 is useful for tests that may require costly imports, to delay the cost
85 is useful for tests that may require costly imports, to delay the cost
173 until the test suite is actually executed.
86 until the test suite is actually executed.
174 msg : string
87 msg : string
175 Message to give on raising a SkipTest exception.
88 Message to give on raising a SkipTest exception.
176
89
177 Returns
90 Returns
178 -------
91 -------
179 decorator : function
92 decorator : function
180 Decorator, which, when applied to a function, causes SkipTest
93 Decorator, which, when applied to a function, causes SkipTest
181 to be raised when the skip_condition was True, and the function
94 to be raised when the skip_condition was True, and the function
182 to be called normally otherwise.
95 to be called normally otherwise.
183 """
96 """
184 if msg is None:
97 if msg is None:
185 msg = "Test skipped due to test condition."
98 msg = "Test skipped due to test condition."
186
99
187 import pytest
100 import pytest
188
101
189 assert isinstance(skip_condition, bool)
102 assert isinstance(skip_condition, bool)
190 return pytest.mark.skipif(skip_condition, reason=msg)
103 return pytest.mark.skipif(skip_condition, reason=msg)
191
104
192
105
193 # A version with the condition set to true, common case just to attach a message
106 # A version with the condition set to true, common case just to attach a message
194 # to a skip decorator
107 # to a skip decorator
195 def skip(msg=None):
108 def skip(msg=None):
196 """Decorator factory - mark a test function for skipping from test suite.
109 """Decorator factory - mark a test function for skipping from test suite.
197
110
198 Parameters
111 Parameters
199 ----------
112 ----------
200 msg : string
113 msg : string
201 Optional message to be added.
114 Optional message to be added.
202
115
203 Returns
116 Returns
204 -------
117 -------
205 decorator : function
118 decorator : function
206 Decorator, which, when applied to a function, causes SkipTest
119 Decorator, which, when applied to a function, causes SkipTest
207 to be raised, with the optional message added.
120 to be raised, with the optional message added.
208 """
121 """
209 if msg and not isinstance(msg, str):
122 if msg and not isinstance(msg, str):
210 raise ValueError('invalid object passed to `@skip` decorator, did you '
123 raise ValueError('invalid object passed to `@skip` decorator, did you '
211 'meant `@skip()` with brackets ?')
124 'meant `@skip()` with brackets ?')
212 return skipif(True, msg)
125 return skipif(True, msg)
213
126
214
127
215 def onlyif(condition, msg):
128 def onlyif(condition, msg):
216 """The reverse from skipif, see skipif for details."""
129 """The reverse from skipif, see skipif for details."""
217
130
218 return skipif(not condition, msg)
131 return skipif(not condition, msg)
219
132
220 #-----------------------------------------------------------------------------
133 #-----------------------------------------------------------------------------
221 # Utility functions for decorators
134 # Utility functions for decorators
222 def module_not_available(module):
135 def module_not_available(module):
223 """Can module be imported? Returns true if module does NOT import.
136 """Can module be imported? Returns true if module does NOT import.
224
137
225 This is used to make a decorator to skip tests that require module to be
138 This is used to make a decorator to skip tests that require module to be
226 available, but delay the 'import numpy' to test execution time.
139 available, but delay the 'import numpy' to test execution time.
227 """
140 """
228 try:
141 try:
229 mod = import_module(module)
142 mod = import_module(module)
230 mod_not_avail = False
143 mod_not_avail = False
231 except ImportError:
144 except ImportError:
232 mod_not_avail = True
145 mod_not_avail = True
233
146
234 return mod_not_avail
147 return mod_not_avail
235
148
236
149
237 def decorated_dummy(dec, name):
238 """Return a dummy function decorated with dec, with the given name.
239
240 Examples
241 --------
242 import IPython.testing.decorators as dec
243 setup = dec.decorated_dummy(dec.skip_if_no_x11, __name__)
244 """
245 warnings.warn("The function `decorated_dummy` is deprecated since IPython 4.0",
246 DeprecationWarning, stacklevel=2)
247 dummy = lambda: None
248 dummy.__name__ = name
249 return dec(dummy)
250
251 #-----------------------------------------------------------------------------
150 #-----------------------------------------------------------------------------
252 # Decorators for public use
151 # Decorators for public use
253
152
254 # Decorators to skip certain tests on specific platforms.
153 # Decorators to skip certain tests on specific platforms.
255 skip_win32 = skipif(sys.platform == 'win32',
154 skip_win32 = skipif(sys.platform == 'win32',
256 "This test does not run under Windows")
155 "This test does not run under Windows")
257 skip_linux = skipif(sys.platform.startswith('linux'),
156 skip_linux = skipif(sys.platform.startswith('linux'),
258 "This test does not run under Linux")
157 "This test does not run under Linux")
259 skip_osx = skipif(sys.platform == 'darwin',"This test does not run under OS X")
158 skip_osx = skipif(sys.platform == 'darwin',"This test does not run under OS X")
260
159
261
160
262 # Decorators to skip tests if not on specific platforms.
161 # Decorators to skip tests if not on specific platforms.
263 skip_if_not_win32 = skipif(sys.platform != 'win32',
162 skip_if_not_win32 = skipif(sys.platform != 'win32',
264 "This test only runs under Windows")
163 "This test only runs under Windows")
265 skip_if_not_linux = skipif(not sys.platform.startswith('linux'),
164 skip_if_not_linux = skipif(not sys.platform.startswith('linux'),
266 "This test only runs under Linux")
165 "This test only runs under Linux")
267 skip_if_not_osx = skipif(sys.platform != 'darwin',
166 skip_if_not_osx = skipif(sys.platform != 'darwin',
268 "This test only runs under OSX")
167 "This test only runs under OSX")
269
168
270
169
271 _x11_skip_cond = (sys.platform not in ('darwin', 'win32') and
170 _x11_skip_cond = (sys.platform not in ('darwin', 'win32') and
272 os.environ.get('DISPLAY', '') == '')
171 os.environ.get('DISPLAY', '') == '')
273 _x11_skip_msg = "Skipped under *nix when X11/XOrg not available"
172 _x11_skip_msg = "Skipped under *nix when X11/XOrg not available"
274
173
275 skip_if_no_x11 = skipif(_x11_skip_cond, _x11_skip_msg)
174 skip_if_no_x11 = skipif(_x11_skip_cond, _x11_skip_msg)
276
175
277
176
278 # Decorators to skip certain tests on specific platform/python combinations
177 # Decorators to skip certain tests on specific platform/python combinations
279 skip_win32_py38 = skipif(sys.version_info > (3,8) and os.name == 'nt')
178 skip_win32_py38 = skipif(sys.version_info > (3,8) and os.name == 'nt')
280
179
281
180
282 # not a decorator itself, returns a dummy function to be used as setup
283 def skip_file_no_x11(name):
284 warnings.warn("The function `skip_file_no_x11` is deprecated since IPython 4.0",
285 DeprecationWarning, stacklevel=2)
286 return decorated_dummy(skip_if_no_x11, name) if _x11_skip_cond else None
287
288 # Other skip decorators
181 # Other skip decorators
289
182
290 # generic skip without module
183 # generic skip without module
291 skip_without = lambda mod: skipif(module_not_available(mod), "This test requires %s" % mod)
184 skip_without = lambda mod: skipif(module_not_available(mod), "This test requires %s" % mod)
292
185
293 skipif_not_numpy = skip_without('numpy')
186 skipif_not_numpy = skip_without('numpy')
294
187
295 skipif_not_matplotlib = skip_without('matplotlib')
188 skipif_not_matplotlib = skip_without('matplotlib')
296
189
297 skipif_not_sympy = skip_without('sympy')
190 skipif_not_sympy = skip_without('sympy')
298
191
299 # A null 'decorator', useful to make more readable code that needs to pick
192 # A null 'decorator', useful to make more readable code that needs to pick
300 # between different decorators based on OS or other conditions
193 # between different decorators based on OS or other conditions
301 null_deco = lambda f: f
194 null_deco = lambda f: f
302
195
303 # Some tests only run where we can use unicode paths. Note that we can't just
196 # Some tests only run where we can use unicode paths. Note that we can't just
304 # check os.path.supports_unicode_filenames, which is always False on Linux.
197 # check os.path.supports_unicode_filenames, which is always False on Linux.
305 try:
198 try:
306 f = tempfile.NamedTemporaryFile(prefix=u"tmp€")
199 f = tempfile.NamedTemporaryFile(prefix=u"tmp€")
307 except UnicodeEncodeError:
200 except UnicodeEncodeError:
308 unicode_paths = False
201 unicode_paths = False
309 else:
202 else:
310 unicode_paths = True
203 unicode_paths = True
311 f.close()
204 f.close()
312
205
313 onlyif_unicode_paths = onlyif(unicode_paths, ("This test is only applicable "
206 onlyif_unicode_paths = onlyif(unicode_paths, ("This test is only applicable "
314 "where we can use unicode in filenames."))
207 "where we can use unicode in filenames."))
315
208
316
209
317 def onlyif_cmds_exist(*commands):
210 def onlyif_cmds_exist(*commands):
318 """
211 """
319 Decorator to skip test when at least one of `commands` is not found.
212 Decorator to skip test when at least one of `commands` is not found.
320 """
213 """
321 for cmd in commands:
214 for cmd in commands:
322 reason = f"This test runs only if command '{cmd}' is installed"
215 reason = f"This test runs only if command '{cmd}' is installed"
323 if not shutil.which(cmd):
216 if not shutil.which(cmd):
324 if os.environ.get("IPTEST_WORKING_DIR", None) is not None:
217 if os.environ.get("IPTEST_WORKING_DIR", None) is not None:
325 return skip(reason)
218 return skip(reason)
326 else:
219 else:
327 import pytest
220 import pytest
328
221
329 return pytest.mark.skip(reason=reason)
222 return pytest.mark.skip(reason=reason)
330 return null_deco
223 return null_deco
331
332 def onlyif_any_cmd_exists(*commands):
333 """
334 Decorator to skip test unless at least one of `commands` is found.
335 """
336 warnings.warn("The function `onlyif_any_cmd_exists` is deprecated since IPython 4.0",
337 DeprecationWarning, stacklevel=2)
338 for cmd in commands:
339 if shutil.which(cmd):
340 return null_deco
341 return skip("This test runs only if one of the commands {0} "
342 "is installed".format(commands))
General Comments 0
You need to be logged in to leave comments. Login now