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