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