##// END OF EJS Templates
Remove bunch of deprecated and unused testing decorators
Nikita Kniazev -
Show More
@@ -1,342 +1,223
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 os
35 35 import shutil
36 36 import sys
37 37 import tempfile
38 38 import unittest
39 39 import warnings
40 40 from importlib import import_module
41 41
42 42 from decorator import decorator
43 43
44 44 # Expose the unittest-driven decorators
45 45 from .ipunittest import ipdoctest, ipdocstring
46 46
47 47 #-----------------------------------------------------------------------------
48 48 # Classes and functions
49 49 #-----------------------------------------------------------------------------
50 50
51 51 # Simple example of the basic idea
52 52 def as_unittest(func):
53 53 """Decorator to make a simple function into a normal test via unittest."""
54 54 class Tester(unittest.TestCase):
55 55 def test(self):
56 56 func()
57 57
58 58 Tester.__name__ = func.__name__
59 59
60 60 return Tester
61 61
62 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 65 def skip_iptest_but_not_pytest(f):
153 66 """
154 67 Warning this will make the test invisible to iptest.
155 68 """
156 69 import os
157 70
158 71 if os.environ.get("IPTEST_WORKING_DIR", None) is not None:
159 72 f.__test__ = False
160 73 return f
161 74
162 75
163 76 def skipif(skip_condition, msg=None):
164 77 """Make function raise SkipTest exception if skip_condition is true
165 78
166 79 Parameters
167 80 ----------
168 81
169 82 skip_condition : bool or callable
170 83 Flag to determine whether to skip test. If the condition is a
171 84 callable, it is used at runtime to dynamically make the decision. This
172 85 is useful for tests that may require costly imports, to delay the cost
173 86 until the test suite is actually executed.
174 87 msg : string
175 88 Message to give on raising a SkipTest exception.
176 89
177 90 Returns
178 91 -------
179 92 decorator : function
180 93 Decorator, which, when applied to a function, causes SkipTest
181 94 to be raised when the skip_condition was True, and the function
182 95 to be called normally otherwise.
183 96 """
184 97 if msg is None:
185 98 msg = "Test skipped due to test condition."
186 99
187 100 import pytest
188 101
189 102 assert isinstance(skip_condition, bool)
190 103 return pytest.mark.skipif(skip_condition, reason=msg)
191 104
192 105
193 106 # A version with the condition set to true, common case just to attach a message
194 107 # to a skip decorator
195 108 def skip(msg=None):
196 109 """Decorator factory - mark a test function for skipping from test suite.
197 110
198 111 Parameters
199 112 ----------
200 113 msg : string
201 114 Optional message to be added.
202 115
203 116 Returns
204 117 -------
205 118 decorator : function
206 119 Decorator, which, when applied to a function, causes SkipTest
207 120 to be raised, with the optional message added.
208 121 """
209 122 if msg and not isinstance(msg, str):
210 123 raise ValueError('invalid object passed to `@skip` decorator, did you '
211 124 'meant `@skip()` with brackets ?')
212 125 return skipif(True, msg)
213 126
214 127
215 128 def onlyif(condition, msg):
216 129 """The reverse from skipif, see skipif for details."""
217 130
218 131 return skipif(not condition, msg)
219 132
220 133 #-----------------------------------------------------------------------------
221 134 # Utility functions for decorators
222 135 def module_not_available(module):
223 136 """Can module be imported? Returns true if module does NOT import.
224 137
225 138 This is used to make a decorator to skip tests that require module to be
226 139 available, but delay the 'import numpy' to test execution time.
227 140 """
228 141 try:
229 142 mod = import_module(module)
230 143 mod_not_avail = False
231 144 except ImportError:
232 145 mod_not_avail = True
233 146
234 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 151 # Decorators for public use
253 152
254 153 # Decorators to skip certain tests on specific platforms.
255 154 skip_win32 = skipif(sys.platform == 'win32',
256 155 "This test does not run under Windows")
257 156 skip_linux = skipif(sys.platform.startswith('linux'),
258 157 "This test does not run under Linux")
259 158 skip_osx = skipif(sys.platform == 'darwin',"This test does not run under OS X")
260 159
261 160
262 161 # Decorators to skip tests if not on specific platforms.
263 162 skip_if_not_win32 = skipif(sys.platform != 'win32',
264 163 "This test only runs under Windows")
265 164 skip_if_not_linux = skipif(not sys.platform.startswith('linux'),
266 165 "This test only runs under Linux")
267 166 skip_if_not_osx = skipif(sys.platform != 'darwin',
268 167 "This test only runs under OSX")
269 168
270 169
271 170 _x11_skip_cond = (sys.platform not in ('darwin', 'win32') and
272 171 os.environ.get('DISPLAY', '') == '')
273 172 _x11_skip_msg = "Skipped under *nix when X11/XOrg not available"
274 173
275 174 skip_if_no_x11 = skipif(_x11_skip_cond, _x11_skip_msg)
276 175
277 176
278 177 # Decorators to skip certain tests on specific platform/python combinations
279 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 181 # Other skip decorators
289 182
290 183 # generic skip without module
291 184 skip_without = lambda mod: skipif(module_not_available(mod), "This test requires %s" % mod)
292 185
293 186 skipif_not_numpy = skip_without('numpy')
294 187
295 188 skipif_not_matplotlib = skip_without('matplotlib')
296 189
297 190 skipif_not_sympy = skip_without('sympy')
298 191
299 192 # A null 'decorator', useful to make more readable code that needs to pick
300 193 # between different decorators based on OS or other conditions
301 194 null_deco = lambda f: f
302 195
303 196 # Some tests only run where we can use unicode paths. Note that we can't just
304 197 # check os.path.supports_unicode_filenames, which is always False on Linux.
305 198 try:
306 199 f = tempfile.NamedTemporaryFile(prefix=u"tmp€")
307 200 except UnicodeEncodeError:
308 201 unicode_paths = False
309 202 else:
310 203 unicode_paths = True
311 204 f.close()
312 205
313 206 onlyif_unicode_paths = onlyif(unicode_paths, ("This test is only applicable "
314 207 "where we can use unicode in filenames."))
315 208
316 209
317 210 def onlyif_cmds_exist(*commands):
318 211 """
319 212 Decorator to skip test when at least one of `commands` is not found.
320 213 """
321 214 for cmd in commands:
322 215 reason = f"This test runs only if command '{cmd}' is installed"
323 216 if not shutil.which(cmd):
324 217 if os.environ.get("IPTEST_WORKING_DIR", None) is not None:
325 218 return skip(reason)
326 219 else:
327 220 import pytest
328 221
329 222 return pytest.mark.skip(reason=reason)
330 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