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