##// END OF EJS Templates
Make check for pywin32 more portable....
Thomas Kluyver -
Show More
@@ -1,381 +1,381 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 Authors
30 30 -------
31 31
32 32 - Fernando Perez <Fernando.Perez@berkeley.edu>
33 33 """
34 34
35 35 #-----------------------------------------------------------------------------
36 36 # Copyright (C) 2009-2011 The IPython Development Team
37 37 #
38 38 # Distributed under the terms of the BSD License. The full license is in
39 39 # the file COPYING, distributed as part of this software.
40 40 #-----------------------------------------------------------------------------
41 41
42 42 #-----------------------------------------------------------------------------
43 43 # Imports
44 44 #-----------------------------------------------------------------------------
45 45
46 46 # Stdlib imports
47 47 import sys
48 48 import os
49 49 import tempfile
50 50 import unittest
51 51
52 52 # Third-party imports
53 53
54 54 # This is Michele Simionato's decorator module, kept verbatim.
55 55 from IPython.external.decorator import decorator
56 56
57 57 # Expose the unittest-driven decorators
58 58 from ipunittest import ipdoctest, ipdocstring
59 59
60 60 # Grab the numpy-specific decorators which we keep in a file that we
61 61 # occasionally update from upstream: decorators.py is a copy of
62 62 # numpy.testing.decorators, we expose all of it here.
63 63 from IPython.external.decorators import *
64 64
65 65 # For onlyif_cmd_exists decorator
66 66 from IPython.utils.process import is_cmd_found
67 67
68 68 #-----------------------------------------------------------------------------
69 69 # Classes and functions
70 70 #-----------------------------------------------------------------------------
71 71
72 72 # Simple example of the basic idea
73 73 def as_unittest(func):
74 74 """Decorator to make a simple function into a normal test via unittest."""
75 75 class Tester(unittest.TestCase):
76 76 def test(self):
77 77 func()
78 78
79 79 Tester.__name__ = func.__name__
80 80
81 81 return Tester
82 82
83 83 # Utility functions
84 84
85 85 def apply_wrapper(wrapper,func):
86 86 """Apply a wrapper to a function for decoration.
87 87
88 88 This mixes Michele Simionato's decorator tool with nose's make_decorator,
89 89 to apply a wrapper in a decorator so that all nose attributes, as well as
90 90 function signature and other properties, survive the decoration cleanly.
91 91 This will ensure that wrapped functions can still be well introspected via
92 92 IPython, for example.
93 93 """
94 94 import nose.tools
95 95
96 96 return decorator(wrapper,nose.tools.make_decorator(func)(wrapper))
97 97
98 98
99 99 def make_label_dec(label,ds=None):
100 100 """Factory function to create a decorator that applies one or more labels.
101 101
102 102 Parameters
103 103 ----------
104 104 label : string or sequence
105 105 One or more labels that will be applied by the decorator to the functions
106 106 it decorates. Labels are attributes of the decorated function with their
107 107 value set to True.
108 108
109 109 ds : string
110 110 An optional docstring for the resulting decorator. If not given, a
111 111 default docstring is auto-generated.
112 112
113 113 Returns
114 114 -------
115 115 A decorator.
116 116
117 117 Examples
118 118 --------
119 119
120 120 A simple labeling decorator:
121 121
122 122 >>> slow = make_label_dec('slow')
123 123 >>> slow.__doc__
124 124 "Labels a test as 'slow'."
125 125
126 126 And one that uses multiple labels and a custom docstring:
127 127
128 128 >>> rare = make_label_dec(['slow','hard'],
129 129 ... "Mix labels 'slow' and 'hard' for rare tests.")
130 130 >>> rare.__doc__
131 131 "Mix labels 'slow' and 'hard' for rare tests."
132 132
133 133 Now, let's test using this one:
134 134 >>> @rare
135 135 ... def f(): pass
136 136 ...
137 137 >>>
138 138 >>> f.slow
139 139 True
140 140 >>> f.hard
141 141 True
142 142 """
143 143
144 144 if isinstance(label,basestring):
145 145 labels = [label]
146 146 else:
147 147 labels = label
148 148
149 149 # Validate that the given label(s) are OK for use in setattr() by doing a
150 150 # dry run on a dummy function.
151 151 tmp = lambda : None
152 152 for label in labels:
153 153 setattr(tmp,label,True)
154 154
155 155 # This is the actual decorator we'll return
156 156 def decor(f):
157 157 for label in labels:
158 158 setattr(f,label,True)
159 159 return f
160 160
161 161 # Apply the user's docstring, or autogenerate a basic one
162 162 if ds is None:
163 163 ds = "Labels a test as %r." % label
164 164 decor.__doc__ = ds
165 165
166 166 return decor
167 167
168 168
169 169 # Inspired by numpy's skipif, but uses the full apply_wrapper utility to
170 170 # preserve function metadata better and allows the skip condition to be a
171 171 # callable.
172 172 def skipif(skip_condition, msg=None):
173 173 ''' Make function raise SkipTest exception if skip_condition is true
174 174
175 175 Parameters
176 176 ----------
177 177 skip_condition : bool or callable.
178 178 Flag to determine whether to skip test. If the condition is a
179 179 callable, it is used at runtime to dynamically make the decision. This
180 180 is useful for tests that may require costly imports, to delay the cost
181 181 until the test suite is actually executed.
182 182 msg : string
183 183 Message to give on raising a SkipTest exception
184 184
185 185 Returns
186 186 -------
187 187 decorator : function
188 188 Decorator, which, when applied to a function, causes SkipTest
189 189 to be raised when the skip_condition was True, and the function
190 190 to be called normally otherwise.
191 191
192 192 Notes
193 193 -----
194 194 You will see from the code that we had to further decorate the
195 195 decorator with the nose.tools.make_decorator function in order to
196 196 transmit function name, and various other metadata.
197 197 '''
198 198
199 199 def skip_decorator(f):
200 200 # Local import to avoid a hard nose dependency and only incur the
201 201 # import time overhead at actual test-time.
202 202 import nose
203 203
204 204 # Allow for both boolean or callable skip conditions.
205 205 if callable(skip_condition):
206 206 skip_val = skip_condition
207 207 else:
208 208 skip_val = lambda : skip_condition
209 209
210 210 def get_msg(func,msg=None):
211 211 """Skip message with information about function being skipped."""
212 212 if msg is None: out = 'Test skipped due to test condition.'
213 213 else: out = msg
214 214 return "Skipping test: %s. %s" % (func.__name__,out)
215 215
216 216 # We need to define *two* skippers because Python doesn't allow both
217 217 # return with value and yield inside the same function.
218 218 def skipper_func(*args, **kwargs):
219 219 """Skipper for normal test functions."""
220 220 if skip_val():
221 221 raise nose.SkipTest(get_msg(f,msg))
222 222 else:
223 223 return f(*args, **kwargs)
224 224
225 225 def skipper_gen(*args, **kwargs):
226 226 """Skipper for test generators."""
227 227 if skip_val():
228 228 raise nose.SkipTest(get_msg(f,msg))
229 229 else:
230 230 for x in f(*args, **kwargs):
231 231 yield x
232 232
233 233 # Choose the right skipper to use when building the actual generator.
234 234 if nose.util.isgenerator(f):
235 235 skipper = skipper_gen
236 236 else:
237 237 skipper = skipper_func
238 238
239 239 return nose.tools.make_decorator(f)(skipper)
240 240
241 241 return skip_decorator
242 242
243 243 # A version with the condition set to true, common case just to attach a message
244 244 # to a skip decorator
245 245 def skip(msg=None):
246 246 """Decorator factory - mark a test function for skipping from test suite.
247 247
248 248 Parameters
249 249 ----------
250 250 msg : string
251 251 Optional message to be added.
252 252
253 253 Returns
254 254 -------
255 255 decorator : function
256 256 Decorator, which, when applied to a function, causes SkipTest
257 257 to be raised, with the optional message added.
258 258 """
259 259
260 260 return skipif(True,msg)
261 261
262 262
263 263 def onlyif(condition, msg):
264 264 """The reverse from skipif, see skipif for details."""
265 265
266 266 if callable(condition):
267 267 skip_condition = lambda : not condition()
268 268 else:
269 269 skip_condition = lambda : not condition
270 270
271 271 return skipif(skip_condition, msg)
272 272
273 273 #-----------------------------------------------------------------------------
274 274 # Utility functions for decorators
275 275 def module_not_available(module):
276 276 """Can module be imported? Returns true if module does NOT import.
277 277
278 278 This is used to make a decorator to skip tests that require module to be
279 279 available, but delay the 'import numpy' to test execution time.
280 280 """
281 281 try:
282 282 mod = __import__(module)
283 283 mod_not_avail = False
284 284 except ImportError:
285 285 mod_not_avail = True
286 286
287 287 return mod_not_avail
288 288
289 289
290 290 def decorated_dummy(dec, name):
291 291 """Return a dummy function decorated with dec, with the given name.
292 292
293 293 Examples
294 294 --------
295 295 import IPython.testing.decorators as dec
296 296 setup = dec.decorated_dummy(dec.skip_if_no_x11, __name__)
297 297 """
298 298 dummy = lambda: None
299 299 dummy.__name__ = name
300 300 return dec(dummy)
301 301
302 302 #-----------------------------------------------------------------------------
303 303 # Decorators for public use
304 304
305 305 # Decorators to skip certain tests on specific platforms.
306 306 skip_win32 = skipif(sys.platform == 'win32',
307 307 "This test does not run under Windows")
308 308 skip_linux = skipif(sys.platform.startswith('linux'),
309 309 "This test does not run under Linux")
310 310 skip_osx = skipif(sys.platform == 'darwin',"This test does not run under OS X")
311 311
312 312
313 313 # Decorators to skip tests if not on specific platforms.
314 314 skip_if_not_win32 = skipif(sys.platform != 'win32',
315 315 "This test only runs under Windows")
316 316 skip_if_not_linux = skipif(not sys.platform.startswith('linux'),
317 317 "This test only runs under Linux")
318 318 skip_if_not_osx = skipif(sys.platform != 'darwin',
319 319 "This test only runs under OSX")
320 320
321 321
322 322 _x11_skip_cond = (sys.platform not in ('darwin', 'win32') and
323 323 os.environ.get('DISPLAY', '') == '')
324 324 _x11_skip_msg = "Skipped under *nix when X11/XOrg not available"
325 325
326 326 skip_if_no_x11 = skipif(_x11_skip_cond, _x11_skip_msg)
327 327
328 328 # not a decorator itself, returns a dummy function to be used as setup
329 329 def skip_file_no_x11(name):
330 330 return decorated_dummy(skip_if_no_x11, name) if _x11_skip_cond else None
331 331
332 332 # Other skip decorators
333 333
334 334 # generic skip without module
335 335 skip_without = lambda mod: skipif(module_not_available(mod), "This test requires %s" % mod)
336 336
337 337 skipif_not_numpy = skip_without('numpy')
338 338
339 339 skipif_not_matplotlib = skip_without('matplotlib')
340 340
341 341 skipif_not_sympy = skip_without('sympy')
342 342
343 343 skip_known_failure = knownfailureif(True,'This test is known to fail')
344 344
345 345 known_failure_py3 = knownfailureif(sys.version_info[0] >= 3,
346 346 'This test is known to fail on Python 3.')
347 347
348 348 # A null 'decorator', useful to make more readable code that needs to pick
349 349 # between different decorators based on OS or other conditions
350 350 null_deco = lambda f: f
351 351
352 352 # Some tests only run where we can use unicode paths. Note that we can't just
353 353 # check os.path.supports_unicode_filenames, which is always False on Linux.
354 354 try:
355 355 f = tempfile.NamedTemporaryFile(prefix=u"tmp€")
356 356 except UnicodeEncodeError:
357 357 unicode_paths = False
358 358 else:
359 359 unicode_paths = True
360 360 f.close()
361 361
362 362 onlyif_unicode_paths = onlyif(unicode_paths, ("This test is only applicable "
363 363 "where we can use unicode in filenames."))
364 364
365 365
366 366 def onlyif_cmds_exist(*commands):
367 367 """
368 368 Decorator to skip test when at least one of `commands` is not found.
369 369 """
370 370 for cmd in commands:
371 371 try:
372 372 if not is_cmd_found(cmd):
373 373 return skip("This test runs only if command '{0}' "
374 374 "is installed".format(cmd))
375 375 except ImportError as e:
376 376 # is_cmd_found uses pywin32 on windows, which might not be available
377 if sys.platform == 'win32' and 'pywin32' in e.message:
377 if sys.platform == 'win32' and 'pywin32' in str(e):
378 378 return skip("This test runs only if pywin32 and command '{0}' "
379 379 "is installed".format(cmd))
380 380 raise e
381 381 return null_deco
General Comments 0
You need to be logged in to leave comments. Login now