##// END OF EJS Templates
Add tests for IPython.lib.latextools
Takafumi Arakaki -
Show More
@@ -0,0 +1,119 b''
1 # encoding: utf-8
2 """Tests for IPython.utils.path.py"""
3
4 #-----------------------------------------------------------------------------
5 # Copyright (C) 2008-2011 The IPython Development Team
6 #
7 # Distributed under the terms of the BSD License. The full license is in
8 # the file COPYING, distributed as part of this software.
9 #-----------------------------------------------------------------------------
10
11 import nose.tools as nt
12
13 from IPython.lib import latextools
14 from IPython.testing.decorators import onlyif_cmds_exist
15 from IPython.testing.tools import monkeypatch
16 from IPython.utils.process import FindCmdError
17
18
19 def test_latex_to_png_dvipng_fails_when_no_cmd():
20 """
21 `latex_to_png_dvipng` should return None when there is no required command
22 """
23 for command in ['latex', 'dvipng']:
24 yield (check_latex_to_png_dvipng_fails_when_no_cmd, command)
25
26
27 def check_latex_to_png_dvipng_fails_when_no_cmd(command):
28 def mock_find_cmd(arg):
29 if arg == command:
30 raise FindCmdError
31
32 with monkeypatch(latextools, "find_cmd", mock_find_cmd):
33 nt.assert_equals(latextools.latex_to_png_dvipng("whatever", True),
34 None)
35
36
37 @onlyif_cmds_exist('latex', 'dvipng')
38 def test_latex_to_png_dvipng_runs():
39 """
40 Test that latex_to_png_dvipng just runs without error.
41 """
42 def mock_kpsewhich(filename):
43 nt.assert_equals(filename, "breqn.sty")
44 return None
45
46 for (s, wrap) in [("$$x^2$$", False), ("x^2", True)]:
47 yield (latextools.latex_to_png_dvipng, s, wrap)
48
49 with monkeypatch(latextools, "kpsewhich", mock_kpsewhich):
50 yield (latextools.latex_to_png_dvipng, s, wrap)
51
52
53 def test_genelatex_no_wrap():
54 """
55 Test genelatex with wrap=False.
56 """
57 def mock_kpsewhich(filename):
58 assert False, ("kpsewhich should not be called "
59 "(called with {0})".format(filename))
60
61 with monkeypatch(latextools, "kpsewhich", mock_kpsewhich):
62 nt.assert_equals(
63 '\n'.join(latextools.genelatex("body text", False)),
64 r'''\documentclass{article}
65 \usepackage{amsmath}
66 \usepackage{amsthm}
67 \usepackage{amssymb}
68 \usepackage{bm}
69 \pagestyle{empty}
70 \begin{document}
71 body text
72 \end{document}''')
73
74
75 def test_genelatex_wrap_with_breqn():
76 """
77 Test genelatex with wrap=True for the case breqn.sty is installed.
78 """
79 def mock_kpsewhich(filename):
80 nt.assert_equals(filename, "breqn.sty")
81 return "path/to/breqn.sty"
82
83 with monkeypatch(latextools, "kpsewhich", mock_kpsewhich):
84 nt.assert_equals(
85 '\n'.join(latextools.genelatex("x^2", True)),
86 r'''\documentclass{article}
87 \usepackage{amsmath}
88 \usepackage{amsthm}
89 \usepackage{amssymb}
90 \usepackage{bm}
91 \usepackage{breqn}
92 \pagestyle{empty}
93 \begin{document}
94 \begin{dmath*}
95 x^2
96 \end{dmath*}
97 \end{document}''')
98
99
100 def test_genelatex_wrap_without_breqn():
101 """
102 Test genelatex with wrap=True for the case breqn.sty is not installed.
103 """
104 def mock_kpsewhich(filename):
105 nt.assert_equals(filename, "breqn.sty")
106 return None
107
108 with monkeypatch(latextools, "kpsewhich", mock_kpsewhich):
109 nt.assert_equals(
110 '\n'.join(latextools.genelatex("x^2", True)),
111 r'''\documentclass{article}
112 \usepackage{amsmath}
113 \usepackage{amsthm}
114 \usepackage{amssymb}
115 \usepackage{bm}
116 \pagestyle{empty}
117 \begin{document}
118 $$x^2$$
119 \end{document}''')
@@ -1,344 +1,358 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 - @parametric, for parametric test support that is vastly easier to use than
19 - @parametric, for parametric test support that is vastly easier to use than
20 nose's for debugging. With ours, if a test fails, the stack under inspection
20 nose's for debugging. With ours, if a test fails, the stack under inspection
21 is that of the test and not that of the test framework.
21 is that of the test and not that of the test framework.
22
22
23 - An @as_unittest decorator can be used to tag any normal parameter-less
23 - An @as_unittest decorator can be used to tag any normal parameter-less
24 function as a unittest TestCase. Then, both nose and normal unittest will
24 function as a unittest TestCase. Then, both nose and normal unittest will
25 recognize it as such. This will make it easier to migrate away from Nose if
25 recognize it as such. This will make it easier to migrate away from Nose if
26 we ever need/want to while maintaining very lightweight tests.
26 we ever need/want to while maintaining very lightweight tests.
27
27
28 NOTE: This file contains IPython-specific decorators. Using the machinery in
28 NOTE: This file contains IPython-specific decorators. Using the machinery in
29 IPython.external.decorators, we import either numpy.testing.decorators if numpy is
29 IPython.external.decorators, we import either numpy.testing.decorators if numpy is
30 available, OR use equivalent code in IPython.external._decorators, which
30 available, OR use equivalent code in IPython.external._decorators, which
31 we've copied verbatim from numpy.
31 we've copied verbatim from numpy.
32
32
33 Authors
33 Authors
34 -------
34 -------
35
35
36 - Fernando Perez <Fernando.Perez@berkeley.edu>
36 - Fernando Perez <Fernando.Perez@berkeley.edu>
37 """
37 """
38
38
39 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
40 # Copyright (C) 2009-2011 The IPython Development Team
40 # Copyright (C) 2009-2011 The IPython Development Team
41 #
41 #
42 # Distributed under the terms of the BSD License. The full license is in
42 # Distributed under the terms of the BSD License. The full license is in
43 # the file COPYING, distributed as part of this software.
43 # the file COPYING, distributed as part of this software.
44 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
45
45
46 #-----------------------------------------------------------------------------
46 #-----------------------------------------------------------------------------
47 # Imports
47 # Imports
48 #-----------------------------------------------------------------------------
48 #-----------------------------------------------------------------------------
49
49
50 # Stdlib imports
50 # Stdlib imports
51 import inspect
51 import inspect
52 import sys
52 import sys
53 import tempfile
53 import tempfile
54 import unittest
54 import unittest
55
55
56 # Third-party imports
56 # Third-party imports
57
57
58 # This is Michele Simionato's decorator module, kept verbatim.
58 # This is Michele Simionato's decorator module, kept verbatim.
59 from IPython.external.decorator import decorator
59 from IPython.external.decorator import decorator
60
60
61 # We already have python3-compliant code for parametric tests
61 # We already have python3-compliant code for parametric tests
62 if sys.version[0]=='2':
62 if sys.version[0]=='2':
63 from _paramtestpy2 import parametric, ParametricTestCase
63 from _paramtestpy2 import parametric, ParametricTestCase
64 else:
64 else:
65 from _paramtestpy3 import parametric, ParametricTestCase
65 from _paramtestpy3 import parametric, ParametricTestCase
66
66
67 # Expose the unittest-driven decorators
67 # Expose the unittest-driven decorators
68 from ipunittest import ipdoctest, ipdocstring
68 from ipunittest import ipdoctest, ipdocstring
69
69
70 # Grab the numpy-specific decorators which we keep in a file that we
70 # Grab the numpy-specific decorators which we keep in a file that we
71 # occasionally update from upstream: decorators.py is a copy of
71 # occasionally update from upstream: decorators.py is a copy of
72 # numpy.testing.decorators, we expose all of it here.
72 # numpy.testing.decorators, we expose all of it here.
73 from IPython.external.decorators import *
73 from IPython.external.decorators import *
74
74
75 # For onlyif_cmd_exists decorator
76 from IPython.utils.process import is_cmd_found
77
75 #-----------------------------------------------------------------------------
78 #-----------------------------------------------------------------------------
76 # Classes and functions
79 # Classes and functions
77 #-----------------------------------------------------------------------------
80 #-----------------------------------------------------------------------------
78
81
79 # Simple example of the basic idea
82 # Simple example of the basic idea
80 def as_unittest(func):
83 def as_unittest(func):
81 """Decorator to make a simple function into a normal test via unittest."""
84 """Decorator to make a simple function into a normal test via unittest."""
82 class Tester(unittest.TestCase):
85 class Tester(unittest.TestCase):
83 def test(self):
86 def test(self):
84 func()
87 func()
85
88
86 Tester.__name__ = func.__name__
89 Tester.__name__ = func.__name__
87
90
88 return Tester
91 return Tester
89
92
90 # Utility functions
93 # Utility functions
91
94
92 def apply_wrapper(wrapper,func):
95 def apply_wrapper(wrapper,func):
93 """Apply a wrapper to a function for decoration.
96 """Apply a wrapper to a function for decoration.
94
97
95 This mixes Michele Simionato's decorator tool with nose's make_decorator,
98 This mixes Michele Simionato's decorator tool with nose's make_decorator,
96 to apply a wrapper in a decorator so that all nose attributes, as well as
99 to apply a wrapper in a decorator so that all nose attributes, as well as
97 function signature and other properties, survive the decoration cleanly.
100 function signature and other properties, survive the decoration cleanly.
98 This will ensure that wrapped functions can still be well introspected via
101 This will ensure that wrapped functions can still be well introspected via
99 IPython, for example.
102 IPython, for example.
100 """
103 """
101 import nose.tools
104 import nose.tools
102
105
103 return decorator(wrapper,nose.tools.make_decorator(func)(wrapper))
106 return decorator(wrapper,nose.tools.make_decorator(func)(wrapper))
104
107
105
108
106 def make_label_dec(label,ds=None):
109 def make_label_dec(label,ds=None):
107 """Factory function to create a decorator that applies one or more labels.
110 """Factory function to create a decorator that applies one or more labels.
108
111
109 Parameters
112 Parameters
110 ----------
113 ----------
111 label : string or sequence
114 label : string or sequence
112 One or more labels that will be applied by the decorator to the functions
115 One or more labels that will be applied by the decorator to the functions
113 it decorates. Labels are attributes of the decorated function with their
116 it decorates. Labels are attributes of the decorated function with their
114 value set to True.
117 value set to True.
115
118
116 ds : string
119 ds : string
117 An optional docstring for the resulting decorator. If not given, a
120 An optional docstring for the resulting decorator. If not given, a
118 default docstring is auto-generated.
121 default docstring is auto-generated.
119
122
120 Returns
123 Returns
121 -------
124 -------
122 A decorator.
125 A decorator.
123
126
124 Examples
127 Examples
125 --------
128 --------
126
129
127 A simple labeling decorator:
130 A simple labeling decorator:
128 >>> slow = make_label_dec('slow')
131 >>> slow = make_label_dec('slow')
129 >>> print slow.__doc__
132 >>> print slow.__doc__
130 Labels a test as 'slow'.
133 Labels a test as 'slow'.
131
134
132 And one that uses multiple labels and a custom docstring:
135 And one that uses multiple labels and a custom docstring:
133 >>> rare = make_label_dec(['slow','hard'],
136 >>> rare = make_label_dec(['slow','hard'],
134 ... "Mix labels 'slow' and 'hard' for rare tests.")
137 ... "Mix labels 'slow' and 'hard' for rare tests.")
135 >>> print rare.__doc__
138 >>> print rare.__doc__
136 Mix labels 'slow' and 'hard' for rare tests.
139 Mix labels 'slow' and 'hard' for rare tests.
137
140
138 Now, let's test using this one:
141 Now, let's test using this one:
139 >>> @rare
142 >>> @rare
140 ... def f(): pass
143 ... def f(): pass
141 ...
144 ...
142 >>>
145 >>>
143 >>> f.slow
146 >>> f.slow
144 True
147 True
145 >>> f.hard
148 >>> f.hard
146 True
149 True
147 """
150 """
148
151
149 if isinstance(label,basestring):
152 if isinstance(label,basestring):
150 labels = [label]
153 labels = [label]
151 else:
154 else:
152 labels = label
155 labels = label
153
156
154 # Validate that the given label(s) are OK for use in setattr() by doing a
157 # Validate that the given label(s) are OK for use in setattr() by doing a
155 # dry run on a dummy function.
158 # dry run on a dummy function.
156 tmp = lambda : None
159 tmp = lambda : None
157 for label in labels:
160 for label in labels:
158 setattr(tmp,label,True)
161 setattr(tmp,label,True)
159
162
160 # This is the actual decorator we'll return
163 # This is the actual decorator we'll return
161 def decor(f):
164 def decor(f):
162 for label in labels:
165 for label in labels:
163 setattr(f,label,True)
166 setattr(f,label,True)
164 return f
167 return f
165
168
166 # Apply the user's docstring, or autogenerate a basic one
169 # Apply the user's docstring, or autogenerate a basic one
167 if ds is None:
170 if ds is None:
168 ds = "Labels a test as %r." % label
171 ds = "Labels a test as %r." % label
169 decor.__doc__ = ds
172 decor.__doc__ = ds
170
173
171 return decor
174 return decor
172
175
173
176
174 # Inspired by numpy's skipif, but uses the full apply_wrapper utility to
177 # Inspired by numpy's skipif, but uses the full apply_wrapper utility to
175 # preserve function metadata better and allows the skip condition to be a
178 # preserve function metadata better and allows the skip condition to be a
176 # callable.
179 # callable.
177 def skipif(skip_condition, msg=None):
180 def skipif(skip_condition, msg=None):
178 ''' Make function raise SkipTest exception if skip_condition is true
181 ''' Make function raise SkipTest exception if skip_condition is true
179
182
180 Parameters
183 Parameters
181 ----------
184 ----------
182 skip_condition : bool or callable.
185 skip_condition : bool or callable.
183 Flag to determine whether to skip test. If the condition is a
186 Flag to determine whether to skip test. If the condition is a
184 callable, it is used at runtime to dynamically make the decision. This
187 callable, it is used at runtime to dynamically make the decision. This
185 is useful for tests that may require costly imports, to delay the cost
188 is useful for tests that may require costly imports, to delay the cost
186 until the test suite is actually executed.
189 until the test suite is actually executed.
187 msg : string
190 msg : string
188 Message to give on raising a SkipTest exception
191 Message to give on raising a SkipTest exception
189
192
190 Returns
193 Returns
191 -------
194 -------
192 decorator : function
195 decorator : function
193 Decorator, which, when applied to a function, causes SkipTest
196 Decorator, which, when applied to a function, causes SkipTest
194 to be raised when the skip_condition was True, and the function
197 to be raised when the skip_condition was True, and the function
195 to be called normally otherwise.
198 to be called normally otherwise.
196
199
197 Notes
200 Notes
198 -----
201 -----
199 You will see from the code that we had to further decorate the
202 You will see from the code that we had to further decorate the
200 decorator with the nose.tools.make_decorator function in order to
203 decorator with the nose.tools.make_decorator function in order to
201 transmit function name, and various other metadata.
204 transmit function name, and various other metadata.
202 '''
205 '''
203
206
204 def skip_decorator(f):
207 def skip_decorator(f):
205 # Local import to avoid a hard nose dependency and only incur the
208 # Local import to avoid a hard nose dependency and only incur the
206 # import time overhead at actual test-time.
209 # import time overhead at actual test-time.
207 import nose
210 import nose
208
211
209 # Allow for both boolean or callable skip conditions.
212 # Allow for both boolean or callable skip conditions.
210 if callable(skip_condition):
213 if callable(skip_condition):
211 skip_val = skip_condition
214 skip_val = skip_condition
212 else:
215 else:
213 skip_val = lambda : skip_condition
216 skip_val = lambda : skip_condition
214
217
215 def get_msg(func,msg=None):
218 def get_msg(func,msg=None):
216 """Skip message with information about function being skipped."""
219 """Skip message with information about function being skipped."""
217 if msg is None: out = 'Test skipped due to test condition.'
220 if msg is None: out = 'Test skipped due to test condition.'
218 else: out = msg
221 else: out = msg
219 return "Skipping test: %s. %s" % (func.__name__,out)
222 return "Skipping test: %s. %s" % (func.__name__,out)
220
223
221 # We need to define *two* skippers because Python doesn't allow both
224 # We need to define *two* skippers because Python doesn't allow both
222 # return with value and yield inside the same function.
225 # return with value and yield inside the same function.
223 def skipper_func(*args, **kwargs):
226 def skipper_func(*args, **kwargs):
224 """Skipper for normal test functions."""
227 """Skipper for normal test functions."""
225 if skip_val():
228 if skip_val():
226 raise nose.SkipTest(get_msg(f,msg))
229 raise nose.SkipTest(get_msg(f,msg))
227 else:
230 else:
228 return f(*args, **kwargs)
231 return f(*args, **kwargs)
229
232
230 def skipper_gen(*args, **kwargs):
233 def skipper_gen(*args, **kwargs):
231 """Skipper for test generators."""
234 """Skipper for test generators."""
232 if skip_val():
235 if skip_val():
233 raise nose.SkipTest(get_msg(f,msg))
236 raise nose.SkipTest(get_msg(f,msg))
234 else:
237 else:
235 for x in f(*args, **kwargs):
238 for x in f(*args, **kwargs):
236 yield x
239 yield x
237
240
238 # Choose the right skipper to use when building the actual generator.
241 # Choose the right skipper to use when building the actual generator.
239 if nose.util.isgenerator(f):
242 if nose.util.isgenerator(f):
240 skipper = skipper_gen
243 skipper = skipper_gen
241 else:
244 else:
242 skipper = skipper_func
245 skipper = skipper_func
243
246
244 return nose.tools.make_decorator(f)(skipper)
247 return nose.tools.make_decorator(f)(skipper)
245
248
246 return skip_decorator
249 return skip_decorator
247
250
248 # A version with the condition set to true, common case just to attach a message
251 # A version with the condition set to true, common case just to attach a message
249 # to a skip decorator
252 # to a skip decorator
250 def skip(msg=None):
253 def skip(msg=None):
251 """Decorator factory - mark a test function for skipping from test suite.
254 """Decorator factory - mark a test function for skipping from test suite.
252
255
253 Parameters
256 Parameters
254 ----------
257 ----------
255 msg : string
258 msg : string
256 Optional message to be added.
259 Optional message to be added.
257
260
258 Returns
261 Returns
259 -------
262 -------
260 decorator : function
263 decorator : function
261 Decorator, which, when applied to a function, causes SkipTest
264 Decorator, which, when applied to a function, causes SkipTest
262 to be raised, with the optional message added.
265 to be raised, with the optional message added.
263 """
266 """
264
267
265 return skipif(True,msg)
268 return skipif(True,msg)
266
269
267
270
268 def onlyif(condition, msg):
271 def onlyif(condition, msg):
269 """The reverse from skipif, see skipif for details."""
272 """The reverse from skipif, see skipif for details."""
270
273
271 if callable(condition):
274 if callable(condition):
272 skip_condition = lambda : not condition()
275 skip_condition = lambda : not condition()
273 else:
276 else:
274 skip_condition = lambda : not condition
277 skip_condition = lambda : not condition
275
278
276 return skipif(skip_condition, msg)
279 return skipif(skip_condition, msg)
277
280
278 #-----------------------------------------------------------------------------
281 #-----------------------------------------------------------------------------
279 # Utility functions for decorators
282 # Utility functions for decorators
280 def module_not_available(module):
283 def module_not_available(module):
281 """Can module be imported? Returns true if module does NOT import.
284 """Can module be imported? Returns true if module does NOT import.
282
285
283 This is used to make a decorator to skip tests that require module to be
286 This is used to make a decorator to skip tests that require module to be
284 available, but delay the 'import numpy' to test execution time.
287 available, but delay the 'import numpy' to test execution time.
285 """
288 """
286 try:
289 try:
287 mod = __import__(module)
290 mod = __import__(module)
288 mod_not_avail = False
291 mod_not_avail = False
289 except ImportError:
292 except ImportError:
290 mod_not_avail = True
293 mod_not_avail = True
291
294
292 return mod_not_avail
295 return mod_not_avail
293
296
294 #-----------------------------------------------------------------------------
297 #-----------------------------------------------------------------------------
295 # Decorators for public use
298 # Decorators for public use
296
299
297 # Decorators to skip certain tests on specific platforms.
300 # Decorators to skip certain tests on specific platforms.
298 skip_win32 = skipif(sys.platform == 'win32',
301 skip_win32 = skipif(sys.platform == 'win32',
299 "This test does not run under Windows")
302 "This test does not run under Windows")
300 skip_linux = skipif(sys.platform.startswith('linux'),
303 skip_linux = skipif(sys.platform.startswith('linux'),
301 "This test does not run under Linux")
304 "This test does not run under Linux")
302 skip_osx = skipif(sys.platform == 'darwin',"This test does not run under OS X")
305 skip_osx = skipif(sys.platform == 'darwin',"This test does not run under OS X")
303
306
304
307
305 # Decorators to skip tests if not on specific platforms.
308 # Decorators to skip tests if not on specific platforms.
306 skip_if_not_win32 = skipif(sys.platform != 'win32',
309 skip_if_not_win32 = skipif(sys.platform != 'win32',
307 "This test only runs under Windows")
310 "This test only runs under Windows")
308 skip_if_not_linux = skipif(not sys.platform.startswith('linux'),
311 skip_if_not_linux = skipif(not sys.platform.startswith('linux'),
309 "This test only runs under Linux")
312 "This test only runs under Linux")
310 skip_if_not_osx = skipif(sys.platform != 'darwin',
313 skip_if_not_osx = skipif(sys.platform != 'darwin',
311 "This test only runs under OSX")
314 "This test only runs under OSX")
312
315
313 # Other skip decorators
316 # Other skip decorators
314
317
315 # generic skip without module
318 # generic skip without module
316 skip_without = lambda mod: skipif(module_not_available(mod), "This test requires %s" % mod)
319 skip_without = lambda mod: skipif(module_not_available(mod), "This test requires %s" % mod)
317
320
318 skipif_not_numpy = skip_without('numpy')
321 skipif_not_numpy = skip_without('numpy')
319
322
320 skipif_not_matplotlib = skip_without('matplotlib')
323 skipif_not_matplotlib = skip_without('matplotlib')
321
324
322 skipif_not_sympy = skip_without('sympy')
325 skipif_not_sympy = skip_without('sympy')
323
326
324 skip_known_failure = knownfailureif(True,'This test is known to fail')
327 skip_known_failure = knownfailureif(True,'This test is known to fail')
325
328
326 known_failure_py3 = knownfailureif(sys.version_info[0] >= 3,
329 known_failure_py3 = knownfailureif(sys.version_info[0] >= 3,
327 'This test is known to fail on Python 3.')
330 'This test is known to fail on Python 3.')
328
331
329 # A null 'decorator', useful to make more readable code that needs to pick
332 # A null 'decorator', useful to make more readable code that needs to pick
330 # between different decorators based on OS or other conditions
333 # between different decorators based on OS or other conditions
331 null_deco = lambda f: f
334 null_deco = lambda f: f
332
335
333 # Some tests only run where we can use unicode paths. Note that we can't just
336 # Some tests only run where we can use unicode paths. Note that we can't just
334 # check os.path.supports_unicode_filenames, which is always False on Linux.
337 # check os.path.supports_unicode_filenames, which is always False on Linux.
335 try:
338 try:
336 f = tempfile.NamedTemporaryFile(prefix=u"tmp€")
339 f = tempfile.NamedTemporaryFile(prefix=u"tmp€")
337 except UnicodeEncodeError:
340 except UnicodeEncodeError:
338 unicode_paths = False
341 unicode_paths = False
339 else:
342 else:
340 unicode_paths = True
343 unicode_paths = True
341 f.close()
344 f.close()
342
345
343 onlyif_unicode_paths = onlyif(unicode_paths, ("This test is only applicable "
346 onlyif_unicode_paths = onlyif(unicode_paths, ("This test is only applicable "
344 "where we can use unicode in filenames."))
347 "where we can use unicode in filenames."))
348
349
350 def onlyif_cmds_exist(*commands):
351 """
352 Decorator to skip test when at least one of `commands` is not found.
353 """
354 for cmd in commands:
355 if not is_cmd_found(cmd):
356 return skip("This test runs only if command '{0}' "
357 "is installed".format(cmd))
358 return null_deco
@@ -1,392 +1,403 b''
1 """Generic testing tools.
1 """Generic testing tools.
2
2
3 In particular, this module exposes a set of top-level assert* functions that
3 In particular, this module exposes a set of top-level assert* functions that
4 can be used in place of nose.tools.assert* in method generators (the ones in
4 can be used in place of nose.tools.assert* in method generators (the ones in
5 nose can not, at least as of nose 0.10.4).
5 nose can not, at least as of nose 0.10.4).
6
6
7
7
8 Authors
8 Authors
9 -------
9 -------
10 - Fernando Perez <Fernando.Perez@berkeley.edu>
10 - Fernando Perez <Fernando.Perez@berkeley.edu>
11 """
11 """
12
12
13 from __future__ import absolute_import
13 from __future__ import absolute_import
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Copyright (C) 2009-2011 The IPython Development Team
16 # Copyright (C) 2009-2011 The IPython Development Team
17 #
17 #
18 # Distributed under the terms of the BSD License. The full license is in
18 # Distributed under the terms of the BSD License. The full license is in
19 # the file COPYING, distributed as part of this software.
19 # the file COPYING, distributed as part of this software.
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23 # Imports
23 # Imports
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25
25
26 import os
26 import os
27 import re
27 import re
28 import sys
28 import sys
29 import tempfile
29 import tempfile
30
30
31 from contextlib import contextmanager
31 from contextlib import contextmanager
32 from io import StringIO
32 from io import StringIO
33
33
34 try:
34 try:
35 # These tools are used by parts of the runtime, so we make the nose
35 # These tools are used by parts of the runtime, so we make the nose
36 # dependency optional at this point. Nose is a hard dependency to run the
36 # dependency optional at this point. Nose is a hard dependency to run the
37 # test suite, but NOT to use ipython itself.
37 # test suite, but NOT to use ipython itself.
38 import nose.tools as nt
38 import nose.tools as nt
39 has_nose = True
39 has_nose = True
40 except ImportError:
40 except ImportError:
41 has_nose = False
41 has_nose = False
42
42
43 from IPython.config.loader import Config
43 from IPython.config.loader import Config
44 from IPython.utils.process import find_cmd, getoutputerror
44 from IPython.utils.process import find_cmd, getoutputerror
45 from IPython.utils.text import list_strings
45 from IPython.utils.text import list_strings
46 from IPython.utils.io import temp_pyfile, Tee
46 from IPython.utils.io import temp_pyfile, Tee
47 from IPython.utils import py3compat
47 from IPython.utils import py3compat
48 from IPython.utils.encoding import DEFAULT_ENCODING
48 from IPython.utils.encoding import DEFAULT_ENCODING
49
49
50 from . import decorators as dec
50 from . import decorators as dec
51 from . import skipdoctest
51 from . import skipdoctest
52
52
53 #-----------------------------------------------------------------------------
53 #-----------------------------------------------------------------------------
54 # Globals
54 # Globals
55 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
56
56
57 # Make a bunch of nose.tools assert wrappers that can be used in test
57 # Make a bunch of nose.tools assert wrappers that can be used in test
58 # generators. This will expose an assert* function for each one in nose.tools.
58 # generators. This will expose an assert* function for each one in nose.tools.
59
59
60 _tpl = """
60 _tpl = """
61 def %(name)s(*a,**kw):
61 def %(name)s(*a,**kw):
62 return nt.%(name)s(*a,**kw)
62 return nt.%(name)s(*a,**kw)
63 """
63 """
64
64
65 if has_nose:
65 if has_nose:
66 for _x in [a for a in dir(nt) if a.startswith('assert')]:
66 for _x in [a for a in dir(nt) if a.startswith('assert')]:
67 exec _tpl % dict(name=_x)
67 exec _tpl % dict(name=_x)
68
68
69 #-----------------------------------------------------------------------------
69 #-----------------------------------------------------------------------------
70 # Functions and classes
70 # Functions and classes
71 #-----------------------------------------------------------------------------
71 #-----------------------------------------------------------------------------
72
72
73 # The docstring for full_path doctests differently on win32 (different path
73 # The docstring for full_path doctests differently on win32 (different path
74 # separator) so just skip the doctest there. The example remains informative.
74 # separator) so just skip the doctest there. The example remains informative.
75 doctest_deco = skipdoctest.skip_doctest if sys.platform == 'win32' else dec.null_deco
75 doctest_deco = skipdoctest.skip_doctest if sys.platform == 'win32' else dec.null_deco
76
76
77 @doctest_deco
77 @doctest_deco
78 def full_path(startPath,files):
78 def full_path(startPath,files):
79 """Make full paths for all the listed files, based on startPath.
79 """Make full paths for all the listed files, based on startPath.
80
80
81 Only the base part of startPath is kept, since this routine is typically
81 Only the base part of startPath is kept, since this routine is typically
82 used with a script's __file__ variable as startPath. The base of startPath
82 used with a script's __file__ variable as startPath. The base of startPath
83 is then prepended to all the listed files, forming the output list.
83 is then prepended to all the listed files, forming the output list.
84
84
85 Parameters
85 Parameters
86 ----------
86 ----------
87 startPath : string
87 startPath : string
88 Initial path to use as the base for the results. This path is split
88 Initial path to use as the base for the results. This path is split
89 using os.path.split() and only its first component is kept.
89 using os.path.split() and only its first component is kept.
90
90
91 files : string or list
91 files : string or list
92 One or more files.
92 One or more files.
93
93
94 Examples
94 Examples
95 --------
95 --------
96
96
97 >>> full_path('/foo/bar.py',['a.txt','b.txt'])
97 >>> full_path('/foo/bar.py',['a.txt','b.txt'])
98 ['/foo/a.txt', '/foo/b.txt']
98 ['/foo/a.txt', '/foo/b.txt']
99
99
100 >>> full_path('/foo',['a.txt','b.txt'])
100 >>> full_path('/foo',['a.txt','b.txt'])
101 ['/a.txt', '/b.txt']
101 ['/a.txt', '/b.txt']
102
102
103 If a single file is given, the output is still a list:
103 If a single file is given, the output is still a list:
104 >>> full_path('/foo','a.txt')
104 >>> full_path('/foo','a.txt')
105 ['/a.txt']
105 ['/a.txt']
106 """
106 """
107
107
108 files = list_strings(files)
108 files = list_strings(files)
109 base = os.path.split(startPath)[0]
109 base = os.path.split(startPath)[0]
110 return [ os.path.join(base,f) for f in files ]
110 return [ os.path.join(base,f) for f in files ]
111
111
112
112
113 def parse_test_output(txt):
113 def parse_test_output(txt):
114 """Parse the output of a test run and return errors, failures.
114 """Parse the output of a test run and return errors, failures.
115
115
116 Parameters
116 Parameters
117 ----------
117 ----------
118 txt : str
118 txt : str
119 Text output of a test run, assumed to contain a line of one of the
119 Text output of a test run, assumed to contain a line of one of the
120 following forms::
120 following forms::
121 'FAILED (errors=1)'
121 'FAILED (errors=1)'
122 'FAILED (failures=1)'
122 'FAILED (failures=1)'
123 'FAILED (errors=1, failures=1)'
123 'FAILED (errors=1, failures=1)'
124
124
125 Returns
125 Returns
126 -------
126 -------
127 nerr, nfail: number of errors and failures.
127 nerr, nfail: number of errors and failures.
128 """
128 """
129
129
130 err_m = re.search(r'^FAILED \(errors=(\d+)\)', txt, re.MULTILINE)
130 err_m = re.search(r'^FAILED \(errors=(\d+)\)', txt, re.MULTILINE)
131 if err_m:
131 if err_m:
132 nerr = int(err_m.group(1))
132 nerr = int(err_m.group(1))
133 nfail = 0
133 nfail = 0
134 return nerr, nfail
134 return nerr, nfail
135
135
136 fail_m = re.search(r'^FAILED \(failures=(\d+)\)', txt, re.MULTILINE)
136 fail_m = re.search(r'^FAILED \(failures=(\d+)\)', txt, re.MULTILINE)
137 if fail_m:
137 if fail_m:
138 nerr = 0
138 nerr = 0
139 nfail = int(fail_m.group(1))
139 nfail = int(fail_m.group(1))
140 return nerr, nfail
140 return nerr, nfail
141
141
142 both_m = re.search(r'^FAILED \(errors=(\d+), failures=(\d+)\)', txt,
142 both_m = re.search(r'^FAILED \(errors=(\d+), failures=(\d+)\)', txt,
143 re.MULTILINE)
143 re.MULTILINE)
144 if both_m:
144 if both_m:
145 nerr = int(both_m.group(1))
145 nerr = int(both_m.group(1))
146 nfail = int(both_m.group(2))
146 nfail = int(both_m.group(2))
147 return nerr, nfail
147 return nerr, nfail
148
148
149 # If the input didn't match any of these forms, assume no error/failures
149 # If the input didn't match any of these forms, assume no error/failures
150 return 0, 0
150 return 0, 0
151
151
152
152
153 # So nose doesn't think this is a test
153 # So nose doesn't think this is a test
154 parse_test_output.__test__ = False
154 parse_test_output.__test__ = False
155
155
156
156
157 def default_argv():
157 def default_argv():
158 """Return a valid default argv for creating testing instances of ipython"""
158 """Return a valid default argv for creating testing instances of ipython"""
159
159
160 return ['--quick', # so no config file is loaded
160 return ['--quick', # so no config file is loaded
161 # Other defaults to minimize side effects on stdout
161 # Other defaults to minimize side effects on stdout
162 '--colors=NoColor', '--no-term-title','--no-banner',
162 '--colors=NoColor', '--no-term-title','--no-banner',
163 '--autocall=0']
163 '--autocall=0']
164
164
165
165
166 def default_config():
166 def default_config():
167 """Return a config object with good defaults for testing."""
167 """Return a config object with good defaults for testing."""
168 config = Config()
168 config = Config()
169 config.TerminalInteractiveShell.colors = 'NoColor'
169 config.TerminalInteractiveShell.colors = 'NoColor'
170 config.TerminalTerminalInteractiveShell.term_title = False,
170 config.TerminalTerminalInteractiveShell.term_title = False,
171 config.TerminalInteractiveShell.autocall = 0
171 config.TerminalInteractiveShell.autocall = 0
172 config.HistoryManager.hist_file = tempfile.mktemp(u'test_hist.sqlite')
172 config.HistoryManager.hist_file = tempfile.mktemp(u'test_hist.sqlite')
173 config.HistoryManager.db_cache_size = 10000
173 config.HistoryManager.db_cache_size = 10000
174 return config
174 return config
175
175
176
176
177 def ipexec(fname, options=None):
177 def ipexec(fname, options=None):
178 """Utility to call 'ipython filename'.
178 """Utility to call 'ipython filename'.
179
179
180 Starts IPython witha minimal and safe configuration to make startup as fast
180 Starts IPython witha minimal and safe configuration to make startup as fast
181 as possible.
181 as possible.
182
182
183 Note that this starts IPython in a subprocess!
183 Note that this starts IPython in a subprocess!
184
184
185 Parameters
185 Parameters
186 ----------
186 ----------
187 fname : str
187 fname : str
188 Name of file to be executed (should have .py or .ipy extension).
188 Name of file to be executed (should have .py or .ipy extension).
189
189
190 options : optional, list
190 options : optional, list
191 Extra command-line flags to be passed to IPython.
191 Extra command-line flags to be passed to IPython.
192
192
193 Returns
193 Returns
194 -------
194 -------
195 (stdout, stderr) of ipython subprocess.
195 (stdout, stderr) of ipython subprocess.
196 """
196 """
197 if options is None: options = []
197 if options is None: options = []
198
198
199 # For these subprocess calls, eliminate all prompt printing so we only see
199 # For these subprocess calls, eliminate all prompt printing so we only see
200 # output from script execution
200 # output from script execution
201 prompt_opts = [ '--PromptManager.in_template=""',
201 prompt_opts = [ '--PromptManager.in_template=""',
202 '--PromptManager.in2_template=""',
202 '--PromptManager.in2_template=""',
203 '--PromptManager.out_template=""'
203 '--PromptManager.out_template=""'
204 ]
204 ]
205 cmdargs = ' '.join(default_argv() + prompt_opts + options)
205 cmdargs = ' '.join(default_argv() + prompt_opts + options)
206
206
207 _ip = get_ipython()
207 _ip = get_ipython()
208 test_dir = os.path.dirname(__file__)
208 test_dir = os.path.dirname(__file__)
209
209
210 ipython_cmd = find_cmd('ipython3' if py3compat.PY3 else 'ipython')
210 ipython_cmd = find_cmd('ipython3' if py3compat.PY3 else 'ipython')
211 # Absolute path for filename
211 # Absolute path for filename
212 full_fname = os.path.join(test_dir, fname)
212 full_fname = os.path.join(test_dir, fname)
213 full_cmd = '%s %s %s' % (ipython_cmd, cmdargs, full_fname)
213 full_cmd = '%s %s %s' % (ipython_cmd, cmdargs, full_fname)
214 #print >> sys.stderr, 'FULL CMD:', full_cmd # dbg
214 #print >> sys.stderr, 'FULL CMD:', full_cmd # dbg
215 out, err = getoutputerror(full_cmd)
215 out, err = getoutputerror(full_cmd)
216 # `import readline` causes 'ESC[?1034h' to be output sometimes,
216 # `import readline` causes 'ESC[?1034h' to be output sometimes,
217 # so strip that out before doing comparisons
217 # so strip that out before doing comparisons
218 if out:
218 if out:
219 out = re.sub(r'\x1b\[[^h]+h', '', out)
219 out = re.sub(r'\x1b\[[^h]+h', '', out)
220 return out, err
220 return out, err
221
221
222
222
223 def ipexec_validate(fname, expected_out, expected_err='',
223 def ipexec_validate(fname, expected_out, expected_err='',
224 options=None):
224 options=None):
225 """Utility to call 'ipython filename' and validate output/error.
225 """Utility to call 'ipython filename' and validate output/error.
226
226
227 This function raises an AssertionError if the validation fails.
227 This function raises an AssertionError if the validation fails.
228
228
229 Note that this starts IPython in a subprocess!
229 Note that this starts IPython in a subprocess!
230
230
231 Parameters
231 Parameters
232 ----------
232 ----------
233 fname : str
233 fname : str
234 Name of the file to be executed (should have .py or .ipy extension).
234 Name of the file to be executed (should have .py or .ipy extension).
235
235
236 expected_out : str
236 expected_out : str
237 Expected stdout of the process.
237 Expected stdout of the process.
238
238
239 expected_err : optional, str
239 expected_err : optional, str
240 Expected stderr of the process.
240 Expected stderr of the process.
241
241
242 options : optional, list
242 options : optional, list
243 Extra command-line flags to be passed to IPython.
243 Extra command-line flags to be passed to IPython.
244
244
245 Returns
245 Returns
246 -------
246 -------
247 None
247 None
248 """
248 """
249
249
250 import nose.tools as nt
250 import nose.tools as nt
251
251
252 out, err = ipexec(fname, options)
252 out, err = ipexec(fname, options)
253 #print 'OUT', out # dbg
253 #print 'OUT', out # dbg
254 #print 'ERR', err # dbg
254 #print 'ERR', err # dbg
255 # If there are any errors, we must check those befor stdout, as they may be
255 # If there are any errors, we must check those befor stdout, as they may be
256 # more informative than simply having an empty stdout.
256 # more informative than simply having an empty stdout.
257 if err:
257 if err:
258 if expected_err:
258 if expected_err:
259 nt.assert_equals(err.strip(), expected_err.strip())
259 nt.assert_equals(err.strip(), expected_err.strip())
260 else:
260 else:
261 raise ValueError('Running file %r produced error: %r' %
261 raise ValueError('Running file %r produced error: %r' %
262 (fname, err))
262 (fname, err))
263 # If no errors or output on stderr was expected, match stdout
263 # If no errors or output on stderr was expected, match stdout
264 nt.assert_equals(out.strip(), expected_out.strip())
264 nt.assert_equals(out.strip(), expected_out.strip())
265
265
266
266
267 class TempFileMixin(object):
267 class TempFileMixin(object):
268 """Utility class to create temporary Python/IPython files.
268 """Utility class to create temporary Python/IPython files.
269
269
270 Meant as a mixin class for test cases."""
270 Meant as a mixin class for test cases."""
271
271
272 def mktmp(self, src, ext='.py'):
272 def mktmp(self, src, ext='.py'):
273 """Make a valid python temp file."""
273 """Make a valid python temp file."""
274 fname, f = temp_pyfile(src, ext)
274 fname, f = temp_pyfile(src, ext)
275 self.tmpfile = f
275 self.tmpfile = f
276 self.fname = fname
276 self.fname = fname
277
277
278 def tearDown(self):
278 def tearDown(self):
279 if hasattr(self, 'tmpfile'):
279 if hasattr(self, 'tmpfile'):
280 # If the tmpfile wasn't made because of skipped tests, like in
280 # If the tmpfile wasn't made because of skipped tests, like in
281 # win32, there's nothing to cleanup.
281 # win32, there's nothing to cleanup.
282 self.tmpfile.close()
282 self.tmpfile.close()
283 try:
283 try:
284 os.unlink(self.fname)
284 os.unlink(self.fname)
285 except:
285 except:
286 # On Windows, even though we close the file, we still can't
286 # On Windows, even though we close the file, we still can't
287 # delete it. I have no clue why
287 # delete it. I have no clue why
288 pass
288 pass
289
289
290 pair_fail_msg = ("Testing {0}\n\n"
290 pair_fail_msg = ("Testing {0}\n\n"
291 "In:\n"
291 "In:\n"
292 " {1!r}\n"
292 " {1!r}\n"
293 "Expected:\n"
293 "Expected:\n"
294 " {2!r}\n"
294 " {2!r}\n"
295 "Got:\n"
295 "Got:\n"
296 " {3!r}\n")
296 " {3!r}\n")
297 def check_pairs(func, pairs):
297 def check_pairs(func, pairs):
298 """Utility function for the common case of checking a function with a
298 """Utility function for the common case of checking a function with a
299 sequence of input/output pairs.
299 sequence of input/output pairs.
300
300
301 Parameters
301 Parameters
302 ----------
302 ----------
303 func : callable
303 func : callable
304 The function to be tested. Should accept a single argument.
304 The function to be tested. Should accept a single argument.
305 pairs : iterable
305 pairs : iterable
306 A list of (input, expected_output) tuples.
306 A list of (input, expected_output) tuples.
307
307
308 Returns
308 Returns
309 -------
309 -------
310 None. Raises an AssertionError if any output does not match the expected
310 None. Raises an AssertionError if any output does not match the expected
311 value.
311 value.
312 """
312 """
313 name = getattr(func, "func_name", getattr(func, "__name__", "<unknown>"))
313 name = getattr(func, "func_name", getattr(func, "__name__", "<unknown>"))
314 for inp, expected in pairs:
314 for inp, expected in pairs:
315 out = func(inp)
315 out = func(inp)
316 assert out == expected, pair_fail_msg.format(name, inp, expected, out)
316 assert out == expected, pair_fail_msg.format(name, inp, expected, out)
317
317
318
318
319 if py3compat.PY3:
319 if py3compat.PY3:
320 MyStringIO = StringIO
320 MyStringIO = StringIO
321 else:
321 else:
322 # In Python 2, stdout/stderr can have either bytes or unicode written to them,
322 # In Python 2, stdout/stderr can have either bytes or unicode written to them,
323 # so we need a class that can handle both.
323 # so we need a class that can handle both.
324 class MyStringIO(StringIO):
324 class MyStringIO(StringIO):
325 def write(self, s):
325 def write(self, s):
326 s = py3compat.cast_unicode(s, encoding=DEFAULT_ENCODING)
326 s = py3compat.cast_unicode(s, encoding=DEFAULT_ENCODING)
327 super(MyStringIO, self).write(s)
327 super(MyStringIO, self).write(s)
328
328
329 notprinted_msg = """Did not find {0!r} in printed output (on {1}):
329 notprinted_msg = """Did not find {0!r} in printed output (on {1}):
330 {2!r}"""
330 {2!r}"""
331
331
332 class AssertPrints(object):
332 class AssertPrints(object):
333 """Context manager for testing that code prints certain text.
333 """Context manager for testing that code prints certain text.
334
334
335 Examples
335 Examples
336 --------
336 --------
337 >>> with AssertPrints("abc", suppress=False):
337 >>> with AssertPrints("abc", suppress=False):
338 ... print "abcd"
338 ... print "abcd"
339 ... print "def"
339 ... print "def"
340 ...
340 ...
341 abcd
341 abcd
342 def
342 def
343 """
343 """
344 def __init__(self, s, channel='stdout', suppress=True):
344 def __init__(self, s, channel='stdout', suppress=True):
345 self.s = s
345 self.s = s
346 self.channel = channel
346 self.channel = channel
347 self.suppress = suppress
347 self.suppress = suppress
348
348
349 def __enter__(self):
349 def __enter__(self):
350 self.orig_stream = getattr(sys, self.channel)
350 self.orig_stream = getattr(sys, self.channel)
351 self.buffer = MyStringIO()
351 self.buffer = MyStringIO()
352 self.tee = Tee(self.buffer, channel=self.channel)
352 self.tee = Tee(self.buffer, channel=self.channel)
353 setattr(sys, self.channel, self.buffer if self.suppress else self.tee)
353 setattr(sys, self.channel, self.buffer if self.suppress else self.tee)
354
354
355 def __exit__(self, etype, value, traceback):
355 def __exit__(self, etype, value, traceback):
356 self.tee.flush()
356 self.tee.flush()
357 setattr(sys, self.channel, self.orig_stream)
357 setattr(sys, self.channel, self.orig_stream)
358 printed = self.buffer.getvalue()
358 printed = self.buffer.getvalue()
359 assert self.s in printed, notprinted_msg.format(self.s, self.channel, printed)
359 assert self.s in printed, notprinted_msg.format(self.s, self.channel, printed)
360 return False
360 return False
361
361
362 class AssertNotPrints(AssertPrints):
362 class AssertNotPrints(AssertPrints):
363 """Context manager for checking that certain output *isn't* produced.
363 """Context manager for checking that certain output *isn't* produced.
364
364
365 Counterpart of AssertPrints"""
365 Counterpart of AssertPrints"""
366 def __exit__(self, etype, value, traceback):
366 def __exit__(self, etype, value, traceback):
367 self.tee.flush()
367 self.tee.flush()
368 setattr(sys, self.channel, self.orig_stream)
368 setattr(sys, self.channel, self.orig_stream)
369 printed = self.buffer.getvalue()
369 printed = self.buffer.getvalue()
370 assert self.s not in printed, notprinted_msg.format(self.s, self.channel, printed)
370 assert self.s not in printed, notprinted_msg.format(self.s, self.channel, printed)
371 return False
371 return False
372
372
373 @contextmanager
373 @contextmanager
374 def mute_warn():
374 def mute_warn():
375 from IPython.utils import warn
375 from IPython.utils import warn
376 save_warn = warn.warn
376 save_warn = warn.warn
377 warn.warn = lambda *a, **kw: None
377 warn.warn = lambda *a, **kw: None
378 try:
378 try:
379 yield
379 yield
380 finally:
380 finally:
381 warn.warn = save_warn
381 warn.warn = save_warn
382
382
383 @contextmanager
383 @contextmanager
384 def make_tempfile(name):
384 def make_tempfile(name):
385 """ Create an empty, named, temporary file for the duration of the context.
385 """ Create an empty, named, temporary file for the duration of the context.
386 """
386 """
387 f = open(name, 'w')
387 f = open(name, 'w')
388 f.close()
388 f.close()
389 try:
389 try:
390 yield
390 yield
391 finally:
391 finally:
392 os.unlink(name)
392 os.unlink(name)
393
394
395 @contextmanager
396 def monkeypatch(obj, name, attr):
397 """
398 Context manager to replace attribute named `name` in `obj` with `attr`.
399 """
400 orig = getattr(obj, name)
401 setattr(obj, name, attr)
402 yield
403 setattr(obj, name, orig)
@@ -1,117 +1,126 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Utilities for working with external processes.
3 Utilities for working with external processes.
4 """
4 """
5
5
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2008-2011 The IPython Development Team
7 # Copyright (C) 2008-2011 The IPython Development Team
8 #
8 #
9 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 from __future__ import print_function
16 from __future__ import print_function
17
17
18 # Stdlib
18 # Stdlib
19 import os
19 import os
20 import sys
20 import sys
21 import shlex
21 import shlex
22
22
23 # Our own
23 # Our own
24 if sys.platform == 'win32':
24 if sys.platform == 'win32':
25 from ._process_win32 import _find_cmd, system, getoutput, AvoidUNCPath, arg_split
25 from ._process_win32 import _find_cmd, system, getoutput, AvoidUNCPath, arg_split
26 else:
26 else:
27 from ._process_posix import _find_cmd, system, getoutput, arg_split
27 from ._process_posix import _find_cmd, system, getoutput, arg_split
28
28
29
29
30 from ._process_common import getoutputerror
30 from ._process_common import getoutputerror
31 from IPython.utils import py3compat
31 from IPython.utils import py3compat
32
32
33 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34 # Code
34 # Code
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36
36
37
37
38 class FindCmdError(Exception):
38 class FindCmdError(Exception):
39 pass
39 pass
40
40
41
41
42 def find_cmd(cmd):
42 def find_cmd(cmd):
43 """Find absolute path to executable cmd in a cross platform manner.
43 """Find absolute path to executable cmd in a cross platform manner.
44
44
45 This function tries to determine the full path to a command line program
45 This function tries to determine the full path to a command line program
46 using `which` on Unix/Linux/OS X and `win32api` on Windows. Most of the
46 using `which` on Unix/Linux/OS X and `win32api` on Windows. Most of the
47 time it will use the version that is first on the users `PATH`. If
47 time it will use the version that is first on the users `PATH`. If
48 cmd is `python` return `sys.executable`.
48 cmd is `python` return `sys.executable`.
49
49
50 Warning, don't use this to find IPython command line programs as there
50 Warning, don't use this to find IPython command line programs as there
51 is a risk you will find the wrong one. Instead find those using the
51 is a risk you will find the wrong one. Instead find those using the
52 following code and looking for the application itself::
52 following code and looking for the application itself::
53
53
54 from IPython.utils.path import get_ipython_module_path
54 from IPython.utils.path import get_ipython_module_path
55 from IPython.utils.process import pycmd2argv
55 from IPython.utils.process import pycmd2argv
56 argv = pycmd2argv(get_ipython_module_path('IPython.frontend.terminal.ipapp'))
56 argv = pycmd2argv(get_ipython_module_path('IPython.frontend.terminal.ipapp'))
57
57
58 Parameters
58 Parameters
59 ----------
59 ----------
60 cmd : str
60 cmd : str
61 The command line program to look for.
61 The command line program to look for.
62 """
62 """
63 if cmd == 'python':
63 if cmd == 'python':
64 return os.path.abspath(sys.executable)
64 return os.path.abspath(sys.executable)
65 try:
65 try:
66 path = _find_cmd(cmd).rstrip()
66 path = _find_cmd(cmd).rstrip()
67 except OSError:
67 except OSError:
68 raise FindCmdError('command could not be found: %s' % cmd)
68 raise FindCmdError('command could not be found: %s' % cmd)
69 # which returns empty if not found
69 # which returns empty if not found
70 if path == '':
70 if path == '':
71 raise FindCmdError('command could not be found: %s' % cmd)
71 raise FindCmdError('command could not be found: %s' % cmd)
72 return os.path.abspath(path)
72 return os.path.abspath(path)
73
73
74
74
75 def is_cmd_found(cmd):
76 """Check whether executable `cmd` exists or not and return a bool."""
77 try:
78 find_cmd(cmd)
79 return True
80 except FindCmdError:
81 return False
82
83
75 def pycmd2argv(cmd):
84 def pycmd2argv(cmd):
76 r"""Take the path of a python command and return a list (argv-style).
85 r"""Take the path of a python command and return a list (argv-style).
77
86
78 This only works on Python based command line programs and will find the
87 This only works on Python based command line programs and will find the
79 location of the ``python`` executable using ``sys.executable`` to make
88 location of the ``python`` executable using ``sys.executable`` to make
80 sure the right version is used.
89 sure the right version is used.
81
90
82 For a given path ``cmd``, this returns [cmd] if cmd's extension is .exe,
91 For a given path ``cmd``, this returns [cmd] if cmd's extension is .exe,
83 .com or .bat, and [, cmd] otherwise.
92 .com or .bat, and [, cmd] otherwise.
84
93
85 Parameters
94 Parameters
86 ----------
95 ----------
87 cmd : string
96 cmd : string
88 The path of the command.
97 The path of the command.
89
98
90 Returns
99 Returns
91 -------
100 -------
92 argv-style list.
101 argv-style list.
93 """
102 """
94 ext = os.path.splitext(cmd)[1]
103 ext = os.path.splitext(cmd)[1]
95 if ext in ['.exe', '.com', '.bat']:
104 if ext in ['.exe', '.com', '.bat']:
96 return [cmd]
105 return [cmd]
97 else:
106 else:
98 return [sys.executable, cmd]
107 return [sys.executable, cmd]
99
108
100
109
101 def abbrev_cwd():
110 def abbrev_cwd():
102 """ Return abbreviated version of cwd, e.g. d:mydir """
111 """ Return abbreviated version of cwd, e.g. d:mydir """
103 cwd = os.getcwdu().replace('\\','/')
112 cwd = os.getcwdu().replace('\\','/')
104 drivepart = ''
113 drivepart = ''
105 tail = cwd
114 tail = cwd
106 if sys.platform == 'win32':
115 if sys.platform == 'win32':
107 if len(cwd) < 4:
116 if len(cwd) < 4:
108 return cwd
117 return cwd
109 drivepart,tail = os.path.splitdrive(cwd)
118 drivepart,tail = os.path.splitdrive(cwd)
110
119
111
120
112 parts = tail.split('/')
121 parts = tail.split('/')
113 if len(parts) > 2:
122 if len(parts) > 2:
114 tail = '/'.join(parts[-2:])
123 tail = '/'.join(parts[-2:])
115
124
116 return (drivepart + (
125 return (drivepart + (
117 cwd == '/' and '/' or tail))
126 cwd == '/' and '/' or tail))
General Comments 0
You need to be logged in to leave comments. Login now