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