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