##// END OF EJS Templates
Merge pull request #10125 from srinivasreddy/rm_python2_import...
Thomas Kluyver -
r23072:df22d46e merge
parent child Browse files
Show More
@@ -1,54 +1,51 b''
1 import unittest
1 import unittest
2 try: # Python 3.3 +
2 from unittest.mock import Mock
3 from unittest.mock import Mock
4 except ImportError:
5 from mock import Mock
6
3
7 from IPython.core import events
4 from IPython.core import events
8 import IPython.testing.tools as tt
5 import IPython.testing.tools as tt
9
6
10 def ping_received():
7 def ping_received():
11 pass
8 pass
12
9
13 class CallbackTests(unittest.TestCase):
10 class CallbackTests(unittest.TestCase):
14 def setUp(self):
11 def setUp(self):
15 self.em = events.EventManager(get_ipython(), {'ping_received': ping_received})
12 self.em = events.EventManager(get_ipython(), {'ping_received': ping_received})
16
13
17 def test_register_unregister(self):
14 def test_register_unregister(self):
18 cb = Mock()
15 cb = Mock()
19
16
20 self.em.register('ping_received', cb)
17 self.em.register('ping_received', cb)
21 self.em.trigger('ping_received')
18 self.em.trigger('ping_received')
22 self.assertEqual(cb.call_count, 1)
19 self.assertEqual(cb.call_count, 1)
23
20
24 self.em.unregister('ping_received', cb)
21 self.em.unregister('ping_received', cb)
25 self.em.trigger('ping_received')
22 self.em.trigger('ping_received')
26 self.assertEqual(cb.call_count, 1)
23 self.assertEqual(cb.call_count, 1)
27
24
28 def test_cb_error(self):
25 def test_cb_error(self):
29 cb = Mock(side_effect=ValueError)
26 cb = Mock(side_effect=ValueError)
30 self.em.register('ping_received', cb)
27 self.em.register('ping_received', cb)
31 with tt.AssertPrints("Error in callback"):
28 with tt.AssertPrints("Error in callback"):
32 self.em.trigger('ping_received')
29 self.em.trigger('ping_received')
33
30
34 def test_unregister_during_callback(self):
31 def test_unregister_during_callback(self):
35 invoked = [False] * 3
32 invoked = [False] * 3
36
33
37 def func1(*_):
34 def func1(*_):
38 invoked[0] = True
35 invoked[0] = True
39 self.em.unregister('ping_received', func1)
36 self.em.unregister('ping_received', func1)
40 self.em.register('ping_received', func3)
37 self.em.register('ping_received', func3)
41
38
42 def func2(*_):
39 def func2(*_):
43 invoked[1] = True
40 invoked[1] = True
44 self.em.unregister('ping_received', func2)
41 self.em.unregister('ping_received', func2)
45
42
46 def func3(*_):
43 def func3(*_):
47 invoked[2] = True
44 invoked[2] = True
48
45
49 self.em.register('ping_received', func1)
46 self.em.register('ping_received', func1)
50 self.em.register('ping_received', func2)
47 self.em.register('ping_received', func2)
51
48
52 self.em.trigger('ping_received')
49 self.em.trigger('ping_received')
53 self.assertEqual([True, True, False], invoked)
50 self.assertEqual([True, True, False], invoked)
54 self.assertEqual([func3], self.em.callbacks['ping_received'])
51 self.assertEqual([func3], self.em.callbacks['ping_received'])
@@ -1,906 +1,904 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tests for the key interactiveshell module.
2 """Tests for the key interactiveshell module.
3
3
4 Historically the main classes in interactiveshell have been under-tested. This
4 Historically the main classes in interactiveshell have been under-tested. This
5 module should grow as many single-method tests as possible to trap many of the
5 module should grow as many single-method tests as possible to trap many of the
6 recurring bugs we seem to encounter with high-level interaction.
6 recurring bugs we seem to encounter with high-level interaction.
7 """
7 """
8
8
9 # Copyright (c) IPython Development Team.
9 # Copyright (c) IPython Development Team.
10 # Distributed under the terms of the Modified BSD License.
10 # Distributed under the terms of the Modified BSD License.
11
11
12 import ast
12 import ast
13 import os
13 import os
14 import signal
14 import signal
15 import shutil
15 import shutil
16 import sys
16 import sys
17 import tempfile
17 import tempfile
18 import unittest
18 import unittest
19 try:
19 from unittest import mock
20 from unittest import mock
20
21 except ImportError:
22 import mock
23 from os.path import join
21 from os.path import join
24
22
25 import nose.tools as nt
23 import nose.tools as nt
26
24
27 from IPython.core.error import InputRejected
25 from IPython.core.error import InputRejected
28 from IPython.core.inputtransformer import InputTransformer
26 from IPython.core.inputtransformer import InputTransformer
29 from IPython.testing.decorators import (
27 from IPython.testing.decorators import (
30 skipif, skip_win32, onlyif_unicode_paths, onlyif_cmds_exist,
28 skipif, skip_win32, onlyif_unicode_paths, onlyif_cmds_exist,
31 )
29 )
32 from IPython.testing import tools as tt
30 from IPython.testing import tools as tt
33 from IPython.utils.process import find_cmd
31 from IPython.utils.process import find_cmd
34 from IPython.utils import py3compat
32 from IPython.utils import py3compat
35 from IPython.utils.py3compat import PY3
33 from IPython.utils.py3compat import PY3
36
34
37 if PY3:
35 if PY3:
38 from io import StringIO
36 from io import StringIO
39 else:
37 else:
40 from StringIO import StringIO
38 from StringIO import StringIO
41
39
42 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
43 # Globals
41 # Globals
44 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
45 # This is used by every single test, no point repeating it ad nauseam
43 # This is used by every single test, no point repeating it ad nauseam
46 ip = get_ipython()
44 ip = get_ipython()
47
45
48 #-----------------------------------------------------------------------------
46 #-----------------------------------------------------------------------------
49 # Tests
47 # Tests
50 #-----------------------------------------------------------------------------
48 #-----------------------------------------------------------------------------
51
49
52 class DerivedInterrupt(KeyboardInterrupt):
50 class DerivedInterrupt(KeyboardInterrupt):
53 pass
51 pass
54
52
55 class InteractiveShellTestCase(unittest.TestCase):
53 class InteractiveShellTestCase(unittest.TestCase):
56 def test_naked_string_cells(self):
54 def test_naked_string_cells(self):
57 """Test that cells with only naked strings are fully executed"""
55 """Test that cells with only naked strings are fully executed"""
58 # First, single-line inputs
56 # First, single-line inputs
59 ip.run_cell('"a"\n')
57 ip.run_cell('"a"\n')
60 self.assertEqual(ip.user_ns['_'], 'a')
58 self.assertEqual(ip.user_ns['_'], 'a')
61 # And also multi-line cells
59 # And also multi-line cells
62 ip.run_cell('"""a\nb"""\n')
60 ip.run_cell('"""a\nb"""\n')
63 self.assertEqual(ip.user_ns['_'], 'a\nb')
61 self.assertEqual(ip.user_ns['_'], 'a\nb')
64
62
65 def test_run_empty_cell(self):
63 def test_run_empty_cell(self):
66 """Just make sure we don't get a horrible error with a blank
64 """Just make sure we don't get a horrible error with a blank
67 cell of input. Yes, I did overlook that."""
65 cell of input. Yes, I did overlook that."""
68 old_xc = ip.execution_count
66 old_xc = ip.execution_count
69 res = ip.run_cell('')
67 res = ip.run_cell('')
70 self.assertEqual(ip.execution_count, old_xc)
68 self.assertEqual(ip.execution_count, old_xc)
71 self.assertEqual(res.execution_count, None)
69 self.assertEqual(res.execution_count, None)
72
70
73 def test_run_cell_multiline(self):
71 def test_run_cell_multiline(self):
74 """Multi-block, multi-line cells must execute correctly.
72 """Multi-block, multi-line cells must execute correctly.
75 """
73 """
76 src = '\n'.join(["x=1",
74 src = '\n'.join(["x=1",
77 "y=2",
75 "y=2",
78 "if 1:",
76 "if 1:",
79 " x += 1",
77 " x += 1",
80 " y += 1",])
78 " y += 1",])
81 res = ip.run_cell(src)
79 res = ip.run_cell(src)
82 self.assertEqual(ip.user_ns['x'], 2)
80 self.assertEqual(ip.user_ns['x'], 2)
83 self.assertEqual(ip.user_ns['y'], 3)
81 self.assertEqual(ip.user_ns['y'], 3)
84 self.assertEqual(res.success, True)
82 self.assertEqual(res.success, True)
85 self.assertEqual(res.result, None)
83 self.assertEqual(res.result, None)
86
84
87 def test_multiline_string_cells(self):
85 def test_multiline_string_cells(self):
88 "Code sprinkled with multiline strings should execute (GH-306)"
86 "Code sprinkled with multiline strings should execute (GH-306)"
89 ip.run_cell('tmp=0')
87 ip.run_cell('tmp=0')
90 self.assertEqual(ip.user_ns['tmp'], 0)
88 self.assertEqual(ip.user_ns['tmp'], 0)
91 res = ip.run_cell('tmp=1;"""a\nb"""\n')
89 res = ip.run_cell('tmp=1;"""a\nb"""\n')
92 self.assertEqual(ip.user_ns['tmp'], 1)
90 self.assertEqual(ip.user_ns['tmp'], 1)
93 self.assertEqual(res.success, True)
91 self.assertEqual(res.success, True)
94 self.assertEqual(res.result, "a\nb")
92 self.assertEqual(res.result, "a\nb")
95
93
96 def test_dont_cache_with_semicolon(self):
94 def test_dont_cache_with_semicolon(self):
97 "Ending a line with semicolon should not cache the returned object (GH-307)"
95 "Ending a line with semicolon should not cache the returned object (GH-307)"
98 oldlen = len(ip.user_ns['Out'])
96 oldlen = len(ip.user_ns['Out'])
99 for cell in ['1;', '1;1;']:
97 for cell in ['1;', '1;1;']:
100 res = ip.run_cell(cell, store_history=True)
98 res = ip.run_cell(cell, store_history=True)
101 newlen = len(ip.user_ns['Out'])
99 newlen = len(ip.user_ns['Out'])
102 self.assertEqual(oldlen, newlen)
100 self.assertEqual(oldlen, newlen)
103 self.assertIsNone(res.result)
101 self.assertIsNone(res.result)
104 i = 0
102 i = 0
105 #also test the default caching behavior
103 #also test the default caching behavior
106 for cell in ['1', '1;1']:
104 for cell in ['1', '1;1']:
107 ip.run_cell(cell, store_history=True)
105 ip.run_cell(cell, store_history=True)
108 newlen = len(ip.user_ns['Out'])
106 newlen = len(ip.user_ns['Out'])
109 i += 1
107 i += 1
110 self.assertEqual(oldlen+i, newlen)
108 self.assertEqual(oldlen+i, newlen)
111
109
112 def test_syntax_error(self):
110 def test_syntax_error(self):
113 res = ip.run_cell("raise = 3")
111 res = ip.run_cell("raise = 3")
114 self.assertIsInstance(res.error_before_exec, SyntaxError)
112 self.assertIsInstance(res.error_before_exec, SyntaxError)
115
113
116 def test_In_variable(self):
114 def test_In_variable(self):
117 "Verify that In variable grows with user input (GH-284)"
115 "Verify that In variable grows with user input (GH-284)"
118 oldlen = len(ip.user_ns['In'])
116 oldlen = len(ip.user_ns['In'])
119 ip.run_cell('1;', store_history=True)
117 ip.run_cell('1;', store_history=True)
120 newlen = len(ip.user_ns['In'])
118 newlen = len(ip.user_ns['In'])
121 self.assertEqual(oldlen+1, newlen)
119 self.assertEqual(oldlen+1, newlen)
122 self.assertEqual(ip.user_ns['In'][-1],'1;')
120 self.assertEqual(ip.user_ns['In'][-1],'1;')
123
121
124 def test_magic_names_in_string(self):
122 def test_magic_names_in_string(self):
125 ip.run_cell('a = """\n%exit\n"""')
123 ip.run_cell('a = """\n%exit\n"""')
126 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
124 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
127
125
128 def test_trailing_newline(self):
126 def test_trailing_newline(self):
129 """test that running !(command) does not raise a SyntaxError"""
127 """test that running !(command) does not raise a SyntaxError"""
130 ip.run_cell('!(true)\n', False)
128 ip.run_cell('!(true)\n', False)
131 ip.run_cell('!(true)\n\n\n', False)
129 ip.run_cell('!(true)\n\n\n', False)
132
130
133 def test_gh_597(self):
131 def test_gh_597(self):
134 """Pretty-printing lists of objects with non-ascii reprs may cause
132 """Pretty-printing lists of objects with non-ascii reprs may cause
135 problems."""
133 problems."""
136 class Spam(object):
134 class Spam(object):
137 def __repr__(self):
135 def __repr__(self):
138 return "\xe9"*50
136 return "\xe9"*50
139 import IPython.core.formatters
137 import IPython.core.formatters
140 f = IPython.core.formatters.PlainTextFormatter()
138 f = IPython.core.formatters.PlainTextFormatter()
141 f([Spam(),Spam()])
139 f([Spam(),Spam()])
142
140
143
141
144 def test_future_flags(self):
142 def test_future_flags(self):
145 """Check that future flags are used for parsing code (gh-777)"""
143 """Check that future flags are used for parsing code (gh-777)"""
146 ip.run_cell('from __future__ import barry_as_FLUFL')
144 ip.run_cell('from __future__ import barry_as_FLUFL')
147 try:
145 try:
148 ip.run_cell('prfunc_return_val = 1 <> 2')
146 ip.run_cell('prfunc_return_val = 1 <> 2')
149 assert 'prfunc_return_val' in ip.user_ns
147 assert 'prfunc_return_val' in ip.user_ns
150 finally:
148 finally:
151 # Reset compiler flags so we don't mess up other tests.
149 # Reset compiler flags so we don't mess up other tests.
152 ip.compile.reset_compiler_flags()
150 ip.compile.reset_compiler_flags()
153
151
154 def test_can_pickle(self):
152 def test_can_pickle(self):
155 "Can we pickle objects defined interactively (GH-29)"
153 "Can we pickle objects defined interactively (GH-29)"
156 ip = get_ipython()
154 ip = get_ipython()
157 ip.reset()
155 ip.reset()
158 ip.run_cell(("class Mylist(list):\n"
156 ip.run_cell(("class Mylist(list):\n"
159 " def __init__(self,x=[]):\n"
157 " def __init__(self,x=[]):\n"
160 " list.__init__(self,x)"))
158 " list.__init__(self,x)"))
161 ip.run_cell("w=Mylist([1,2,3])")
159 ip.run_cell("w=Mylist([1,2,3])")
162
160
163 from pickle import dumps
161 from pickle import dumps
164
162
165 # We need to swap in our main module - this is only necessary
163 # We need to swap in our main module - this is only necessary
166 # inside the test framework, because IPython puts the interactive module
164 # inside the test framework, because IPython puts the interactive module
167 # in place (but the test framework undoes this).
165 # in place (but the test framework undoes this).
168 _main = sys.modules['__main__']
166 _main = sys.modules['__main__']
169 sys.modules['__main__'] = ip.user_module
167 sys.modules['__main__'] = ip.user_module
170 try:
168 try:
171 res = dumps(ip.user_ns["w"])
169 res = dumps(ip.user_ns["w"])
172 finally:
170 finally:
173 sys.modules['__main__'] = _main
171 sys.modules['__main__'] = _main
174 self.assertTrue(isinstance(res, bytes))
172 self.assertTrue(isinstance(res, bytes))
175
173
176 def test_global_ns(self):
174 def test_global_ns(self):
177 "Code in functions must be able to access variables outside them."
175 "Code in functions must be able to access variables outside them."
178 ip = get_ipython()
176 ip = get_ipython()
179 ip.run_cell("a = 10")
177 ip.run_cell("a = 10")
180 ip.run_cell(("def f(x):\n"
178 ip.run_cell(("def f(x):\n"
181 " return x + a"))
179 " return x + a"))
182 ip.run_cell("b = f(12)")
180 ip.run_cell("b = f(12)")
183 self.assertEqual(ip.user_ns["b"], 22)
181 self.assertEqual(ip.user_ns["b"], 22)
184
182
185 def test_bad_custom_tb(self):
183 def test_bad_custom_tb(self):
186 """Check that InteractiveShell is protected from bad custom exception handlers"""
184 """Check that InteractiveShell is protected from bad custom exception handlers"""
187 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
185 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
188 self.assertEqual(ip.custom_exceptions, (IOError,))
186 self.assertEqual(ip.custom_exceptions, (IOError,))
189 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
187 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
190 ip.run_cell(u'raise IOError("foo")')
188 ip.run_cell(u'raise IOError("foo")')
191 self.assertEqual(ip.custom_exceptions, ())
189 self.assertEqual(ip.custom_exceptions, ())
192
190
193 def test_bad_custom_tb_return(self):
191 def test_bad_custom_tb_return(self):
194 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
192 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
195 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
193 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
196 self.assertEqual(ip.custom_exceptions, (NameError,))
194 self.assertEqual(ip.custom_exceptions, (NameError,))
197 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
195 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
198 ip.run_cell(u'a=abracadabra')
196 ip.run_cell(u'a=abracadabra')
199 self.assertEqual(ip.custom_exceptions, ())
197 self.assertEqual(ip.custom_exceptions, ())
200
198
201 def test_drop_by_id(self):
199 def test_drop_by_id(self):
202 myvars = {"a":object(), "b":object(), "c": object()}
200 myvars = {"a":object(), "b":object(), "c": object()}
203 ip.push(myvars, interactive=False)
201 ip.push(myvars, interactive=False)
204 for name in myvars:
202 for name in myvars:
205 assert name in ip.user_ns, name
203 assert name in ip.user_ns, name
206 assert name in ip.user_ns_hidden, name
204 assert name in ip.user_ns_hidden, name
207 ip.user_ns['b'] = 12
205 ip.user_ns['b'] = 12
208 ip.drop_by_id(myvars)
206 ip.drop_by_id(myvars)
209 for name in ["a", "c"]:
207 for name in ["a", "c"]:
210 assert name not in ip.user_ns, name
208 assert name not in ip.user_ns, name
211 assert name not in ip.user_ns_hidden, name
209 assert name not in ip.user_ns_hidden, name
212 assert ip.user_ns['b'] == 12
210 assert ip.user_ns['b'] == 12
213 ip.reset()
211 ip.reset()
214
212
215 def test_var_expand(self):
213 def test_var_expand(self):
216 ip.user_ns['f'] = u'Ca\xf1o'
214 ip.user_ns['f'] = u'Ca\xf1o'
217 self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o')
215 self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o')
218 self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o')
216 self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o')
219 self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1')
217 self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1')
220 self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2')
218 self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2')
221
219
222 ip.user_ns['f'] = b'Ca\xc3\xb1o'
220 ip.user_ns['f'] = b'Ca\xc3\xb1o'
223 # This should not raise any exception:
221 # This should not raise any exception:
224 ip.var_expand(u'echo $f')
222 ip.var_expand(u'echo $f')
225
223
226 def test_var_expand_local(self):
224 def test_var_expand_local(self):
227 """Test local variable expansion in !system and %magic calls"""
225 """Test local variable expansion in !system and %magic calls"""
228 # !system
226 # !system
229 ip.run_cell('def test():\n'
227 ip.run_cell('def test():\n'
230 ' lvar = "ttt"\n'
228 ' lvar = "ttt"\n'
231 ' ret = !echo {lvar}\n'
229 ' ret = !echo {lvar}\n'
232 ' return ret[0]\n')
230 ' return ret[0]\n')
233 res = ip.user_ns['test']()
231 res = ip.user_ns['test']()
234 nt.assert_in('ttt', res)
232 nt.assert_in('ttt', res)
235
233
236 # %magic
234 # %magic
237 ip.run_cell('def makemacro():\n'
235 ip.run_cell('def makemacro():\n'
238 ' macroname = "macro_var_expand_locals"\n'
236 ' macroname = "macro_var_expand_locals"\n'
239 ' %macro {macroname} codestr\n')
237 ' %macro {macroname} codestr\n')
240 ip.user_ns['codestr'] = "str(12)"
238 ip.user_ns['codestr'] = "str(12)"
241 ip.run_cell('makemacro()')
239 ip.run_cell('makemacro()')
242 nt.assert_in('macro_var_expand_locals', ip.user_ns)
240 nt.assert_in('macro_var_expand_locals', ip.user_ns)
243
241
244 def test_var_expand_self(self):
242 def test_var_expand_self(self):
245 """Test variable expansion with the name 'self', which was failing.
243 """Test variable expansion with the name 'self', which was failing.
246
244
247 See https://github.com/ipython/ipython/issues/1878#issuecomment-7698218
245 See https://github.com/ipython/ipython/issues/1878#issuecomment-7698218
248 """
246 """
249 ip.run_cell('class cTest:\n'
247 ip.run_cell('class cTest:\n'
250 ' classvar="see me"\n'
248 ' classvar="see me"\n'
251 ' def test(self):\n'
249 ' def test(self):\n'
252 ' res = !echo Variable: {self.classvar}\n'
250 ' res = !echo Variable: {self.classvar}\n'
253 ' return res[0]\n')
251 ' return res[0]\n')
254 nt.assert_in('see me', ip.user_ns['cTest']().test())
252 nt.assert_in('see me', ip.user_ns['cTest']().test())
255
253
256 def test_bad_var_expand(self):
254 def test_bad_var_expand(self):
257 """var_expand on invalid formats shouldn't raise"""
255 """var_expand on invalid formats shouldn't raise"""
258 # SyntaxError
256 # SyntaxError
259 self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}")
257 self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}")
260 # NameError
258 # NameError
261 self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}")
259 self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}")
262 # ZeroDivisionError
260 # ZeroDivisionError
263 self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}")
261 self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}")
264
262
265 def test_silent_postexec(self):
263 def test_silent_postexec(self):
266 """run_cell(silent=True) doesn't invoke pre/post_run_cell callbacks"""
264 """run_cell(silent=True) doesn't invoke pre/post_run_cell callbacks"""
267 pre_explicit = mock.Mock()
265 pre_explicit = mock.Mock()
268 pre_always = mock.Mock()
266 pre_always = mock.Mock()
269 post_explicit = mock.Mock()
267 post_explicit = mock.Mock()
270 post_always = mock.Mock()
268 post_always = mock.Mock()
271
269
272 ip.events.register('pre_run_cell', pre_explicit)
270 ip.events.register('pre_run_cell', pre_explicit)
273 ip.events.register('pre_execute', pre_always)
271 ip.events.register('pre_execute', pre_always)
274 ip.events.register('post_run_cell', post_explicit)
272 ip.events.register('post_run_cell', post_explicit)
275 ip.events.register('post_execute', post_always)
273 ip.events.register('post_execute', post_always)
276
274
277 try:
275 try:
278 ip.run_cell("1", silent=True)
276 ip.run_cell("1", silent=True)
279 assert pre_always.called
277 assert pre_always.called
280 assert not pre_explicit.called
278 assert not pre_explicit.called
281 assert post_always.called
279 assert post_always.called
282 assert not post_explicit.called
280 assert not post_explicit.called
283 # double-check that non-silent exec did what we expected
281 # double-check that non-silent exec did what we expected
284 # silent to avoid
282 # silent to avoid
285 ip.run_cell("1")
283 ip.run_cell("1")
286 assert pre_explicit.called
284 assert pre_explicit.called
287 assert post_explicit.called
285 assert post_explicit.called
288 finally:
286 finally:
289 # remove post-exec
287 # remove post-exec
290 ip.events.unregister('pre_run_cell', pre_explicit)
288 ip.events.unregister('pre_run_cell', pre_explicit)
291 ip.events.unregister('pre_execute', pre_always)
289 ip.events.unregister('pre_execute', pre_always)
292 ip.events.unregister('post_run_cell', post_explicit)
290 ip.events.unregister('post_run_cell', post_explicit)
293 ip.events.unregister('post_execute', post_always)
291 ip.events.unregister('post_execute', post_always)
294
292
295 def test_silent_noadvance(self):
293 def test_silent_noadvance(self):
296 """run_cell(silent=True) doesn't advance execution_count"""
294 """run_cell(silent=True) doesn't advance execution_count"""
297 ec = ip.execution_count
295 ec = ip.execution_count
298 # silent should force store_history=False
296 # silent should force store_history=False
299 ip.run_cell("1", store_history=True, silent=True)
297 ip.run_cell("1", store_history=True, silent=True)
300
298
301 self.assertEqual(ec, ip.execution_count)
299 self.assertEqual(ec, ip.execution_count)
302 # double-check that non-silent exec did what we expected
300 # double-check that non-silent exec did what we expected
303 # silent to avoid
301 # silent to avoid
304 ip.run_cell("1", store_history=True)
302 ip.run_cell("1", store_history=True)
305 self.assertEqual(ec+1, ip.execution_count)
303 self.assertEqual(ec+1, ip.execution_count)
306
304
307 def test_silent_nodisplayhook(self):
305 def test_silent_nodisplayhook(self):
308 """run_cell(silent=True) doesn't trigger displayhook"""
306 """run_cell(silent=True) doesn't trigger displayhook"""
309 d = dict(called=False)
307 d = dict(called=False)
310
308
311 trap = ip.display_trap
309 trap = ip.display_trap
312 save_hook = trap.hook
310 save_hook = trap.hook
313
311
314 def failing_hook(*args, **kwargs):
312 def failing_hook(*args, **kwargs):
315 d['called'] = True
313 d['called'] = True
316
314
317 try:
315 try:
318 trap.hook = failing_hook
316 trap.hook = failing_hook
319 res = ip.run_cell("1", silent=True)
317 res = ip.run_cell("1", silent=True)
320 self.assertFalse(d['called'])
318 self.assertFalse(d['called'])
321 self.assertIsNone(res.result)
319 self.assertIsNone(res.result)
322 # double-check that non-silent exec did what we expected
320 # double-check that non-silent exec did what we expected
323 # silent to avoid
321 # silent to avoid
324 ip.run_cell("1")
322 ip.run_cell("1")
325 self.assertTrue(d['called'])
323 self.assertTrue(d['called'])
326 finally:
324 finally:
327 trap.hook = save_hook
325 trap.hook = save_hook
328
326
329 def test_ofind_line_magic(self):
327 def test_ofind_line_magic(self):
330 from IPython.core.magic import register_line_magic
328 from IPython.core.magic import register_line_magic
331
329
332 @register_line_magic
330 @register_line_magic
333 def lmagic(line):
331 def lmagic(line):
334 "A line magic"
332 "A line magic"
335
333
336 # Get info on line magic
334 # Get info on line magic
337 lfind = ip._ofind('lmagic')
335 lfind = ip._ofind('lmagic')
338 info = dict(found=True, isalias=False, ismagic=True,
336 info = dict(found=True, isalias=False, ismagic=True,
339 namespace = 'IPython internal', obj= lmagic.__wrapped__,
337 namespace = 'IPython internal', obj= lmagic.__wrapped__,
340 parent = None)
338 parent = None)
341 nt.assert_equal(lfind, info)
339 nt.assert_equal(lfind, info)
342
340
343 def test_ofind_cell_magic(self):
341 def test_ofind_cell_magic(self):
344 from IPython.core.magic import register_cell_magic
342 from IPython.core.magic import register_cell_magic
345
343
346 @register_cell_magic
344 @register_cell_magic
347 def cmagic(line, cell):
345 def cmagic(line, cell):
348 "A cell magic"
346 "A cell magic"
349
347
350 # Get info on cell magic
348 # Get info on cell magic
351 find = ip._ofind('cmagic')
349 find = ip._ofind('cmagic')
352 info = dict(found=True, isalias=False, ismagic=True,
350 info = dict(found=True, isalias=False, ismagic=True,
353 namespace = 'IPython internal', obj= cmagic.__wrapped__,
351 namespace = 'IPython internal', obj= cmagic.__wrapped__,
354 parent = None)
352 parent = None)
355 nt.assert_equal(find, info)
353 nt.assert_equal(find, info)
356
354
357 def test_ofind_property_with_error(self):
355 def test_ofind_property_with_error(self):
358 class A(object):
356 class A(object):
359 @property
357 @property
360 def foo(self):
358 def foo(self):
361 raise NotImplementedError()
359 raise NotImplementedError()
362 a = A()
360 a = A()
363
361
364 found = ip._ofind('a.foo', [('locals', locals())])
362 found = ip._ofind('a.foo', [('locals', locals())])
365 info = dict(found=True, isalias=False, ismagic=False,
363 info = dict(found=True, isalias=False, ismagic=False,
366 namespace='locals', obj=A.foo, parent=a)
364 namespace='locals', obj=A.foo, parent=a)
367 nt.assert_equal(found, info)
365 nt.assert_equal(found, info)
368
366
369 def test_ofind_multiple_attribute_lookups(self):
367 def test_ofind_multiple_attribute_lookups(self):
370 class A(object):
368 class A(object):
371 @property
369 @property
372 def foo(self):
370 def foo(self):
373 raise NotImplementedError()
371 raise NotImplementedError()
374
372
375 a = A()
373 a = A()
376 a.a = A()
374 a.a = A()
377 a.a.a = A()
375 a.a.a = A()
378
376
379 found = ip._ofind('a.a.a.foo', [('locals', locals())])
377 found = ip._ofind('a.a.a.foo', [('locals', locals())])
380 info = dict(found=True, isalias=False, ismagic=False,
378 info = dict(found=True, isalias=False, ismagic=False,
381 namespace='locals', obj=A.foo, parent=a.a.a)
379 namespace='locals', obj=A.foo, parent=a.a.a)
382 nt.assert_equal(found, info)
380 nt.assert_equal(found, info)
383
381
384 def test_ofind_slotted_attributes(self):
382 def test_ofind_slotted_attributes(self):
385 class A(object):
383 class A(object):
386 __slots__ = ['foo']
384 __slots__ = ['foo']
387 def __init__(self):
385 def __init__(self):
388 self.foo = 'bar'
386 self.foo = 'bar'
389
387
390 a = A()
388 a = A()
391 found = ip._ofind('a.foo', [('locals', locals())])
389 found = ip._ofind('a.foo', [('locals', locals())])
392 info = dict(found=True, isalias=False, ismagic=False,
390 info = dict(found=True, isalias=False, ismagic=False,
393 namespace='locals', obj=a.foo, parent=a)
391 namespace='locals', obj=a.foo, parent=a)
394 nt.assert_equal(found, info)
392 nt.assert_equal(found, info)
395
393
396 found = ip._ofind('a.bar', [('locals', locals())])
394 found = ip._ofind('a.bar', [('locals', locals())])
397 info = dict(found=False, isalias=False, ismagic=False,
395 info = dict(found=False, isalias=False, ismagic=False,
398 namespace=None, obj=None, parent=a)
396 namespace=None, obj=None, parent=a)
399 nt.assert_equal(found, info)
397 nt.assert_equal(found, info)
400
398
401 def test_ofind_prefers_property_to_instance_level_attribute(self):
399 def test_ofind_prefers_property_to_instance_level_attribute(self):
402 class A(object):
400 class A(object):
403 @property
401 @property
404 def foo(self):
402 def foo(self):
405 return 'bar'
403 return 'bar'
406 a = A()
404 a = A()
407 a.__dict__['foo'] = 'baz'
405 a.__dict__['foo'] = 'baz'
408 nt.assert_equal(a.foo, 'bar')
406 nt.assert_equal(a.foo, 'bar')
409 found = ip._ofind('a.foo', [('locals', locals())])
407 found = ip._ofind('a.foo', [('locals', locals())])
410 nt.assert_is(found['obj'], A.foo)
408 nt.assert_is(found['obj'], A.foo)
411
409
412 def test_custom_syntaxerror_exception(self):
410 def test_custom_syntaxerror_exception(self):
413 called = []
411 called = []
414 def my_handler(shell, etype, value, tb, tb_offset=None):
412 def my_handler(shell, etype, value, tb, tb_offset=None):
415 called.append(etype)
413 called.append(etype)
416 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
414 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
417
415
418 ip.set_custom_exc((SyntaxError,), my_handler)
416 ip.set_custom_exc((SyntaxError,), my_handler)
419 try:
417 try:
420 ip.run_cell("1f")
418 ip.run_cell("1f")
421 # Check that this was called, and only once.
419 # Check that this was called, and only once.
422 self.assertEqual(called, [SyntaxError])
420 self.assertEqual(called, [SyntaxError])
423 finally:
421 finally:
424 # Reset the custom exception hook
422 # Reset the custom exception hook
425 ip.set_custom_exc((), None)
423 ip.set_custom_exc((), None)
426
424
427 def test_custom_exception(self):
425 def test_custom_exception(self):
428 called = []
426 called = []
429 def my_handler(shell, etype, value, tb, tb_offset=None):
427 def my_handler(shell, etype, value, tb, tb_offset=None):
430 called.append(etype)
428 called.append(etype)
431 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
429 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
432
430
433 ip.set_custom_exc((ValueError,), my_handler)
431 ip.set_custom_exc((ValueError,), my_handler)
434 try:
432 try:
435 res = ip.run_cell("raise ValueError('test')")
433 res = ip.run_cell("raise ValueError('test')")
436 # Check that this was called, and only once.
434 # Check that this was called, and only once.
437 self.assertEqual(called, [ValueError])
435 self.assertEqual(called, [ValueError])
438 # Check that the error is on the result object
436 # Check that the error is on the result object
439 self.assertIsInstance(res.error_in_exec, ValueError)
437 self.assertIsInstance(res.error_in_exec, ValueError)
440 finally:
438 finally:
441 # Reset the custom exception hook
439 # Reset the custom exception hook
442 ip.set_custom_exc((), None)
440 ip.set_custom_exc((), None)
443
441
444 def test_mktempfile(self):
442 def test_mktempfile(self):
445 filename = ip.mktempfile()
443 filename = ip.mktempfile()
446 # Check that we can open the file again on Windows
444 # Check that we can open the file again on Windows
447 with open(filename, 'w') as f:
445 with open(filename, 'w') as f:
448 f.write('abc')
446 f.write('abc')
449
447
450 filename = ip.mktempfile(data='blah')
448 filename = ip.mktempfile(data='blah')
451 with open(filename, 'r') as f:
449 with open(filename, 'r') as f:
452 self.assertEqual(f.read(), 'blah')
450 self.assertEqual(f.read(), 'blah')
453
451
454 def test_new_main_mod(self):
452 def test_new_main_mod(self):
455 # Smoketest to check that this accepts a unicode module name
453 # Smoketest to check that this accepts a unicode module name
456 name = u'jiefmw'
454 name = u'jiefmw'
457 mod = ip.new_main_mod(u'%s.py' % name, name)
455 mod = ip.new_main_mod(u'%s.py' % name, name)
458 self.assertEqual(mod.__name__, name)
456 self.assertEqual(mod.__name__, name)
459
457
460 def test_get_exception_only(self):
458 def test_get_exception_only(self):
461 try:
459 try:
462 raise KeyboardInterrupt
460 raise KeyboardInterrupt
463 except KeyboardInterrupt:
461 except KeyboardInterrupt:
464 msg = ip.get_exception_only()
462 msg = ip.get_exception_only()
465 self.assertEqual(msg, 'KeyboardInterrupt\n')
463 self.assertEqual(msg, 'KeyboardInterrupt\n')
466
464
467 try:
465 try:
468 raise DerivedInterrupt("foo")
466 raise DerivedInterrupt("foo")
469 except KeyboardInterrupt:
467 except KeyboardInterrupt:
470 msg = ip.get_exception_only()
468 msg = ip.get_exception_only()
471 self.assertEqual(msg, 'IPython.core.tests.test_interactiveshell.DerivedInterrupt: foo\n')
469 self.assertEqual(msg, 'IPython.core.tests.test_interactiveshell.DerivedInterrupt: foo\n')
472
470
473 def test_inspect_text(self):
471 def test_inspect_text(self):
474 ip.run_cell('a = 5')
472 ip.run_cell('a = 5')
475 text = ip.object_inspect_text('a')
473 text = ip.object_inspect_text('a')
476 self.assertIsInstance(text, str)
474 self.assertIsInstance(text, str)
477
475
478
476
479 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
477 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
480
478
481 @onlyif_unicode_paths
479 @onlyif_unicode_paths
482 def setUp(self):
480 def setUp(self):
483 self.BASETESTDIR = tempfile.mkdtemp()
481 self.BASETESTDIR = tempfile.mkdtemp()
484 self.TESTDIR = join(self.BASETESTDIR, u"Γ₯Àâ")
482 self.TESTDIR = join(self.BASETESTDIR, u"Γ₯Àâ")
485 os.mkdir(self.TESTDIR)
483 os.mkdir(self.TESTDIR)
486 with open(join(self.TESTDIR, u"Γ₯Àâtestscript.py"), "w") as sfile:
484 with open(join(self.TESTDIR, u"Γ₯Àâtestscript.py"), "w") as sfile:
487 sfile.write("pass\n")
485 sfile.write("pass\n")
488 self.oldpath = os.getcwd()
486 self.oldpath = os.getcwd()
489 os.chdir(self.TESTDIR)
487 os.chdir(self.TESTDIR)
490 self.fname = u"Γ₯Àâtestscript.py"
488 self.fname = u"Γ₯Àâtestscript.py"
491
489
492 def tearDown(self):
490 def tearDown(self):
493 os.chdir(self.oldpath)
491 os.chdir(self.oldpath)
494 shutil.rmtree(self.BASETESTDIR)
492 shutil.rmtree(self.BASETESTDIR)
495
493
496 @onlyif_unicode_paths
494 @onlyif_unicode_paths
497 def test_1(self):
495 def test_1(self):
498 """Test safe_execfile with non-ascii path
496 """Test safe_execfile with non-ascii path
499 """
497 """
500 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
498 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
501
499
502 class ExitCodeChecks(tt.TempFileMixin):
500 class ExitCodeChecks(tt.TempFileMixin):
503 def test_exit_code_ok(self):
501 def test_exit_code_ok(self):
504 self.system('exit 0')
502 self.system('exit 0')
505 self.assertEqual(ip.user_ns['_exit_code'], 0)
503 self.assertEqual(ip.user_ns['_exit_code'], 0)
506
504
507 def test_exit_code_error(self):
505 def test_exit_code_error(self):
508 self.system('exit 1')
506 self.system('exit 1')
509 self.assertEqual(ip.user_ns['_exit_code'], 1)
507 self.assertEqual(ip.user_ns['_exit_code'], 1)
510
508
511 @skipif(not hasattr(signal, 'SIGALRM'))
509 @skipif(not hasattr(signal, 'SIGALRM'))
512 def test_exit_code_signal(self):
510 def test_exit_code_signal(self):
513 self.mktmp("import signal, time\n"
511 self.mktmp("import signal, time\n"
514 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
512 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
515 "time.sleep(1)\n")
513 "time.sleep(1)\n")
516 self.system("%s %s" % (sys.executable, self.fname))
514 self.system("%s %s" % (sys.executable, self.fname))
517 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
515 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
518
516
519 @onlyif_cmds_exist("csh")
517 @onlyif_cmds_exist("csh")
520 def test_exit_code_signal_csh(self):
518 def test_exit_code_signal_csh(self):
521 SHELL = os.environ.get('SHELL', None)
519 SHELL = os.environ.get('SHELL', None)
522 os.environ['SHELL'] = find_cmd("csh")
520 os.environ['SHELL'] = find_cmd("csh")
523 try:
521 try:
524 self.test_exit_code_signal()
522 self.test_exit_code_signal()
525 finally:
523 finally:
526 if SHELL is not None:
524 if SHELL is not None:
527 os.environ['SHELL'] = SHELL
525 os.environ['SHELL'] = SHELL
528 else:
526 else:
529 del os.environ['SHELL']
527 del os.environ['SHELL']
530
528
531 class TestSystemRaw(unittest.TestCase, ExitCodeChecks):
529 class TestSystemRaw(unittest.TestCase, ExitCodeChecks):
532 system = ip.system_raw
530 system = ip.system_raw
533
531
534 @onlyif_unicode_paths
532 @onlyif_unicode_paths
535 def test_1(self):
533 def test_1(self):
536 """Test system_raw with non-ascii cmd
534 """Test system_raw with non-ascii cmd
537 """
535 """
538 cmd = u'''python -c "'Γ₯Àâ'" '''
536 cmd = u'''python -c "'Γ₯Àâ'" '''
539 ip.system_raw(cmd)
537 ip.system_raw(cmd)
540
538
541 @mock.patch('subprocess.call', side_effect=KeyboardInterrupt)
539 @mock.patch('subprocess.call', side_effect=KeyboardInterrupt)
542 @mock.patch('os.system', side_effect=KeyboardInterrupt)
540 @mock.patch('os.system', side_effect=KeyboardInterrupt)
543 def test_control_c(self, *mocks):
541 def test_control_c(self, *mocks):
544 try:
542 try:
545 self.system("sleep 1 # wont happen")
543 self.system("sleep 1 # wont happen")
546 except KeyboardInterrupt:
544 except KeyboardInterrupt:
547 self.fail("system call should intercept "
545 self.fail("system call should intercept "
548 "keyboard interrupt from subprocess.call")
546 "keyboard interrupt from subprocess.call")
549 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGINT)
547 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGINT)
550
548
551 # TODO: Exit codes are currently ignored on Windows.
549 # TODO: Exit codes are currently ignored on Windows.
552 class TestSystemPipedExitCode(unittest.TestCase, ExitCodeChecks):
550 class TestSystemPipedExitCode(unittest.TestCase, ExitCodeChecks):
553 system = ip.system_piped
551 system = ip.system_piped
554
552
555 @skip_win32
553 @skip_win32
556 def test_exit_code_ok(self):
554 def test_exit_code_ok(self):
557 ExitCodeChecks.test_exit_code_ok(self)
555 ExitCodeChecks.test_exit_code_ok(self)
558
556
559 @skip_win32
557 @skip_win32
560 def test_exit_code_error(self):
558 def test_exit_code_error(self):
561 ExitCodeChecks.test_exit_code_error(self)
559 ExitCodeChecks.test_exit_code_error(self)
562
560
563 @skip_win32
561 @skip_win32
564 def test_exit_code_signal(self):
562 def test_exit_code_signal(self):
565 ExitCodeChecks.test_exit_code_signal(self)
563 ExitCodeChecks.test_exit_code_signal(self)
566
564
567 class TestModules(unittest.TestCase, tt.TempFileMixin):
565 class TestModules(unittest.TestCase, tt.TempFileMixin):
568 def test_extraneous_loads(self):
566 def test_extraneous_loads(self):
569 """Test we're not loading modules on startup that we shouldn't.
567 """Test we're not loading modules on startup that we shouldn't.
570 """
568 """
571 self.mktmp("import sys\n"
569 self.mktmp("import sys\n"
572 "print('numpy' in sys.modules)\n"
570 "print('numpy' in sys.modules)\n"
573 "print('ipyparallel' in sys.modules)\n"
571 "print('ipyparallel' in sys.modules)\n"
574 "print('ipykernel' in sys.modules)\n"
572 "print('ipykernel' in sys.modules)\n"
575 )
573 )
576 out = "False\nFalse\nFalse\n"
574 out = "False\nFalse\nFalse\n"
577 tt.ipexec_validate(self.fname, out)
575 tt.ipexec_validate(self.fname, out)
578
576
579 class Negator(ast.NodeTransformer):
577 class Negator(ast.NodeTransformer):
580 """Negates all number literals in an AST."""
578 """Negates all number literals in an AST."""
581 def visit_Num(self, node):
579 def visit_Num(self, node):
582 node.n = -node.n
580 node.n = -node.n
583 return node
581 return node
584
582
585 class TestAstTransform(unittest.TestCase):
583 class TestAstTransform(unittest.TestCase):
586 def setUp(self):
584 def setUp(self):
587 self.negator = Negator()
585 self.negator = Negator()
588 ip.ast_transformers.append(self.negator)
586 ip.ast_transformers.append(self.negator)
589
587
590 def tearDown(self):
588 def tearDown(self):
591 ip.ast_transformers.remove(self.negator)
589 ip.ast_transformers.remove(self.negator)
592
590
593 def test_run_cell(self):
591 def test_run_cell(self):
594 with tt.AssertPrints('-34'):
592 with tt.AssertPrints('-34'):
595 ip.run_cell('print (12 + 22)')
593 ip.run_cell('print (12 + 22)')
596
594
597 # A named reference to a number shouldn't be transformed.
595 # A named reference to a number shouldn't be transformed.
598 ip.user_ns['n'] = 55
596 ip.user_ns['n'] = 55
599 with tt.AssertNotPrints('-55'):
597 with tt.AssertNotPrints('-55'):
600 ip.run_cell('print (n)')
598 ip.run_cell('print (n)')
601
599
602 def test_timeit(self):
600 def test_timeit(self):
603 called = set()
601 called = set()
604 def f(x):
602 def f(x):
605 called.add(x)
603 called.add(x)
606 ip.push({'f':f})
604 ip.push({'f':f})
607
605
608 with tt.AssertPrints("average of "):
606 with tt.AssertPrints("average of "):
609 ip.run_line_magic("timeit", "-n1 f(1)")
607 ip.run_line_magic("timeit", "-n1 f(1)")
610 self.assertEqual(called, {-1})
608 self.assertEqual(called, {-1})
611 called.clear()
609 called.clear()
612
610
613 with tt.AssertPrints("average of "):
611 with tt.AssertPrints("average of "):
614 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
612 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
615 self.assertEqual(called, {-2, -3})
613 self.assertEqual(called, {-2, -3})
616
614
617 def test_time(self):
615 def test_time(self):
618 called = []
616 called = []
619 def f(x):
617 def f(x):
620 called.append(x)
618 called.append(x)
621 ip.push({'f':f})
619 ip.push({'f':f})
622
620
623 # Test with an expression
621 # Test with an expression
624 with tt.AssertPrints("Wall time: "):
622 with tt.AssertPrints("Wall time: "):
625 ip.run_line_magic("time", "f(5+9)")
623 ip.run_line_magic("time", "f(5+9)")
626 self.assertEqual(called, [-14])
624 self.assertEqual(called, [-14])
627 called[:] = []
625 called[:] = []
628
626
629 # Test with a statement (different code path)
627 # Test with a statement (different code path)
630 with tt.AssertPrints("Wall time: "):
628 with tt.AssertPrints("Wall time: "):
631 ip.run_line_magic("time", "a = f(-3 + -2)")
629 ip.run_line_magic("time", "a = f(-3 + -2)")
632 self.assertEqual(called, [5])
630 self.assertEqual(called, [5])
633
631
634 def test_macro(self):
632 def test_macro(self):
635 ip.push({'a':10})
633 ip.push({'a':10})
636 # The AST transformation makes this do a+=-1
634 # The AST transformation makes this do a+=-1
637 ip.define_macro("amacro", "a+=1\nprint(a)")
635 ip.define_macro("amacro", "a+=1\nprint(a)")
638
636
639 with tt.AssertPrints("9"):
637 with tt.AssertPrints("9"):
640 ip.run_cell("amacro")
638 ip.run_cell("amacro")
641 with tt.AssertPrints("8"):
639 with tt.AssertPrints("8"):
642 ip.run_cell("amacro")
640 ip.run_cell("amacro")
643
641
644 class IntegerWrapper(ast.NodeTransformer):
642 class IntegerWrapper(ast.NodeTransformer):
645 """Wraps all integers in a call to Integer()"""
643 """Wraps all integers in a call to Integer()"""
646 def visit_Num(self, node):
644 def visit_Num(self, node):
647 if isinstance(node.n, int):
645 if isinstance(node.n, int):
648 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
646 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
649 args=[node], keywords=[])
647 args=[node], keywords=[])
650 return node
648 return node
651
649
652 class TestAstTransform2(unittest.TestCase):
650 class TestAstTransform2(unittest.TestCase):
653 def setUp(self):
651 def setUp(self):
654 self.intwrapper = IntegerWrapper()
652 self.intwrapper = IntegerWrapper()
655 ip.ast_transformers.append(self.intwrapper)
653 ip.ast_transformers.append(self.intwrapper)
656
654
657 self.calls = []
655 self.calls = []
658 def Integer(*args):
656 def Integer(*args):
659 self.calls.append(args)
657 self.calls.append(args)
660 return args
658 return args
661 ip.push({"Integer": Integer})
659 ip.push({"Integer": Integer})
662
660
663 def tearDown(self):
661 def tearDown(self):
664 ip.ast_transformers.remove(self.intwrapper)
662 ip.ast_transformers.remove(self.intwrapper)
665 del ip.user_ns['Integer']
663 del ip.user_ns['Integer']
666
664
667 def test_run_cell(self):
665 def test_run_cell(self):
668 ip.run_cell("n = 2")
666 ip.run_cell("n = 2")
669 self.assertEqual(self.calls, [(2,)])
667 self.assertEqual(self.calls, [(2,)])
670
668
671 # This shouldn't throw an error
669 # This shouldn't throw an error
672 ip.run_cell("o = 2.0")
670 ip.run_cell("o = 2.0")
673 self.assertEqual(ip.user_ns['o'], 2.0)
671 self.assertEqual(ip.user_ns['o'], 2.0)
674
672
675 def test_timeit(self):
673 def test_timeit(self):
676 called = set()
674 called = set()
677 def f(x):
675 def f(x):
678 called.add(x)
676 called.add(x)
679 ip.push({'f':f})
677 ip.push({'f':f})
680
678
681 with tt.AssertPrints("average of "):
679 with tt.AssertPrints("average of "):
682 ip.run_line_magic("timeit", "-n1 f(1)")
680 ip.run_line_magic("timeit", "-n1 f(1)")
683 self.assertEqual(called, {(1,)})
681 self.assertEqual(called, {(1,)})
684 called.clear()
682 called.clear()
685
683
686 with tt.AssertPrints("average of "):
684 with tt.AssertPrints("average of "):
687 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
685 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
688 self.assertEqual(called, {(2,), (3,)})
686 self.assertEqual(called, {(2,), (3,)})
689
687
690 class ErrorTransformer(ast.NodeTransformer):
688 class ErrorTransformer(ast.NodeTransformer):
691 """Throws an error when it sees a number."""
689 """Throws an error when it sees a number."""
692 def visit_Num(self, node):
690 def visit_Num(self, node):
693 raise ValueError("test")
691 raise ValueError("test")
694
692
695 class TestAstTransformError(unittest.TestCase):
693 class TestAstTransformError(unittest.TestCase):
696 def test_unregistering(self):
694 def test_unregistering(self):
697 err_transformer = ErrorTransformer()
695 err_transformer = ErrorTransformer()
698 ip.ast_transformers.append(err_transformer)
696 ip.ast_transformers.append(err_transformer)
699
697
700 with tt.AssertPrints("unregister", channel='stderr'):
698 with tt.AssertPrints("unregister", channel='stderr'):
701 ip.run_cell("1 + 2")
699 ip.run_cell("1 + 2")
702
700
703 # This should have been removed.
701 # This should have been removed.
704 nt.assert_not_in(err_transformer, ip.ast_transformers)
702 nt.assert_not_in(err_transformer, ip.ast_transformers)
705
703
706
704
707 class StringRejector(ast.NodeTransformer):
705 class StringRejector(ast.NodeTransformer):
708 """Throws an InputRejected when it sees a string literal.
706 """Throws an InputRejected when it sees a string literal.
709
707
710 Used to verify that NodeTransformers can signal that a piece of code should
708 Used to verify that NodeTransformers can signal that a piece of code should
711 not be executed by throwing an InputRejected.
709 not be executed by throwing an InputRejected.
712 """
710 """
713
711
714 def visit_Str(self, node):
712 def visit_Str(self, node):
715 raise InputRejected("test")
713 raise InputRejected("test")
716
714
717
715
718 class TestAstTransformInputRejection(unittest.TestCase):
716 class TestAstTransformInputRejection(unittest.TestCase):
719
717
720 def setUp(self):
718 def setUp(self):
721 self.transformer = StringRejector()
719 self.transformer = StringRejector()
722 ip.ast_transformers.append(self.transformer)
720 ip.ast_transformers.append(self.transformer)
723
721
724 def tearDown(self):
722 def tearDown(self):
725 ip.ast_transformers.remove(self.transformer)
723 ip.ast_transformers.remove(self.transformer)
726
724
727 def test_input_rejection(self):
725 def test_input_rejection(self):
728 """Check that NodeTransformers can reject input."""
726 """Check that NodeTransformers can reject input."""
729
727
730 expect_exception_tb = tt.AssertPrints("InputRejected: test")
728 expect_exception_tb = tt.AssertPrints("InputRejected: test")
731 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
729 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
732
730
733 # Run the same check twice to verify that the transformer is not
731 # Run the same check twice to verify that the transformer is not
734 # disabled after raising.
732 # disabled after raising.
735 with expect_exception_tb, expect_no_cell_output:
733 with expect_exception_tb, expect_no_cell_output:
736 ip.run_cell("'unsafe'")
734 ip.run_cell("'unsafe'")
737
735
738 with expect_exception_tb, expect_no_cell_output:
736 with expect_exception_tb, expect_no_cell_output:
739 res = ip.run_cell("'unsafe'")
737 res = ip.run_cell("'unsafe'")
740
738
741 self.assertIsInstance(res.error_before_exec, InputRejected)
739 self.assertIsInstance(res.error_before_exec, InputRejected)
742
740
743 def test__IPYTHON__():
741 def test__IPYTHON__():
744 # This shouldn't raise a NameError, that's all
742 # This shouldn't raise a NameError, that's all
745 __IPYTHON__
743 __IPYTHON__
746
744
747
745
748 class DummyRepr(object):
746 class DummyRepr(object):
749 def __repr__(self):
747 def __repr__(self):
750 return "DummyRepr"
748 return "DummyRepr"
751
749
752 def _repr_html_(self):
750 def _repr_html_(self):
753 return "<b>dummy</b>"
751 return "<b>dummy</b>"
754
752
755 def _repr_javascript_(self):
753 def _repr_javascript_(self):
756 return "console.log('hi');", {'key': 'value'}
754 return "console.log('hi');", {'key': 'value'}
757
755
758
756
759 def test_user_variables():
757 def test_user_variables():
760 # enable all formatters
758 # enable all formatters
761 ip.display_formatter.active_types = ip.display_formatter.format_types
759 ip.display_formatter.active_types = ip.display_formatter.format_types
762
760
763 ip.user_ns['dummy'] = d = DummyRepr()
761 ip.user_ns['dummy'] = d = DummyRepr()
764 keys = {'dummy', 'doesnotexist'}
762 keys = {'dummy', 'doesnotexist'}
765 r = ip.user_expressions({ key:key for key in keys})
763 r = ip.user_expressions({ key:key for key in keys})
766
764
767 nt.assert_equal(keys, set(r.keys()))
765 nt.assert_equal(keys, set(r.keys()))
768 dummy = r['dummy']
766 dummy = r['dummy']
769 nt.assert_equal({'status', 'data', 'metadata'}, set(dummy.keys()))
767 nt.assert_equal({'status', 'data', 'metadata'}, set(dummy.keys()))
770 nt.assert_equal(dummy['status'], 'ok')
768 nt.assert_equal(dummy['status'], 'ok')
771 data = dummy['data']
769 data = dummy['data']
772 metadata = dummy['metadata']
770 metadata = dummy['metadata']
773 nt.assert_equal(data.get('text/html'), d._repr_html_())
771 nt.assert_equal(data.get('text/html'), d._repr_html_())
774 js, jsmd = d._repr_javascript_()
772 js, jsmd = d._repr_javascript_()
775 nt.assert_equal(data.get('application/javascript'), js)
773 nt.assert_equal(data.get('application/javascript'), js)
776 nt.assert_equal(metadata.get('application/javascript'), jsmd)
774 nt.assert_equal(metadata.get('application/javascript'), jsmd)
777
775
778 dne = r['doesnotexist']
776 dne = r['doesnotexist']
779 nt.assert_equal(dne['status'], 'error')
777 nt.assert_equal(dne['status'], 'error')
780 nt.assert_equal(dne['ename'], 'NameError')
778 nt.assert_equal(dne['ename'], 'NameError')
781
779
782 # back to text only
780 # back to text only
783 ip.display_formatter.active_types = ['text/plain']
781 ip.display_formatter.active_types = ['text/plain']
784
782
785 def test_user_expression():
783 def test_user_expression():
786 # enable all formatters
784 # enable all formatters
787 ip.display_formatter.active_types = ip.display_formatter.format_types
785 ip.display_formatter.active_types = ip.display_formatter.format_types
788 query = {
786 query = {
789 'a' : '1 + 2',
787 'a' : '1 + 2',
790 'b' : '1/0',
788 'b' : '1/0',
791 }
789 }
792 r = ip.user_expressions(query)
790 r = ip.user_expressions(query)
793 import pprint
791 import pprint
794 pprint.pprint(r)
792 pprint.pprint(r)
795 nt.assert_equal(set(r.keys()), set(query.keys()))
793 nt.assert_equal(set(r.keys()), set(query.keys()))
796 a = r['a']
794 a = r['a']
797 nt.assert_equal({'status', 'data', 'metadata'}, set(a.keys()))
795 nt.assert_equal({'status', 'data', 'metadata'}, set(a.keys()))
798 nt.assert_equal(a['status'], 'ok')
796 nt.assert_equal(a['status'], 'ok')
799 data = a['data']
797 data = a['data']
800 metadata = a['metadata']
798 metadata = a['metadata']
801 nt.assert_equal(data.get('text/plain'), '3')
799 nt.assert_equal(data.get('text/plain'), '3')
802
800
803 b = r['b']
801 b = r['b']
804 nt.assert_equal(b['status'], 'error')
802 nt.assert_equal(b['status'], 'error')
805 nt.assert_equal(b['ename'], 'ZeroDivisionError')
803 nt.assert_equal(b['ename'], 'ZeroDivisionError')
806
804
807 # back to text only
805 # back to text only
808 ip.display_formatter.active_types = ['text/plain']
806 ip.display_formatter.active_types = ['text/plain']
809
807
810
808
811
809
812
810
813
811
814 class TestSyntaxErrorTransformer(unittest.TestCase):
812 class TestSyntaxErrorTransformer(unittest.TestCase):
815 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
813 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
816
814
817 class SyntaxErrorTransformer(InputTransformer):
815 class SyntaxErrorTransformer(InputTransformer):
818
816
819 def push(self, line):
817 def push(self, line):
820 pos = line.find('syntaxerror')
818 pos = line.find('syntaxerror')
821 if pos >= 0:
819 if pos >= 0:
822 e = SyntaxError('input contains "syntaxerror"')
820 e = SyntaxError('input contains "syntaxerror"')
823 e.text = line
821 e.text = line
824 e.offset = pos + 1
822 e.offset = pos + 1
825 raise e
823 raise e
826 return line
824 return line
827
825
828 def reset(self):
826 def reset(self):
829 pass
827 pass
830
828
831 def setUp(self):
829 def setUp(self):
832 self.transformer = TestSyntaxErrorTransformer.SyntaxErrorTransformer()
830 self.transformer = TestSyntaxErrorTransformer.SyntaxErrorTransformer()
833 ip.input_splitter.python_line_transforms.append(self.transformer)
831 ip.input_splitter.python_line_transforms.append(self.transformer)
834 ip.input_transformer_manager.python_line_transforms.append(self.transformer)
832 ip.input_transformer_manager.python_line_transforms.append(self.transformer)
835
833
836 def tearDown(self):
834 def tearDown(self):
837 ip.input_splitter.python_line_transforms.remove(self.transformer)
835 ip.input_splitter.python_line_transforms.remove(self.transformer)
838 ip.input_transformer_manager.python_line_transforms.remove(self.transformer)
836 ip.input_transformer_manager.python_line_transforms.remove(self.transformer)
839
837
840 def test_syntaxerror_input_transformer(self):
838 def test_syntaxerror_input_transformer(self):
841 with tt.AssertPrints('1234'):
839 with tt.AssertPrints('1234'):
842 ip.run_cell('1234')
840 ip.run_cell('1234')
843 with tt.AssertPrints('SyntaxError: invalid syntax'):
841 with tt.AssertPrints('SyntaxError: invalid syntax'):
844 ip.run_cell('1 2 3') # plain python syntax error
842 ip.run_cell('1 2 3') # plain python syntax error
845 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
843 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
846 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
844 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
847 with tt.AssertPrints('3456'):
845 with tt.AssertPrints('3456'):
848 ip.run_cell('3456')
846 ip.run_cell('3456')
849
847
850
848
851
849
852 def test_warning_suppression():
850 def test_warning_suppression():
853 ip.run_cell("import warnings")
851 ip.run_cell("import warnings")
854 try:
852 try:
855 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
853 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
856 ip.run_cell("warnings.warn('asdf')")
854 ip.run_cell("warnings.warn('asdf')")
857 # Here's the real test -- if we run that again, we should get the
855 # Here's the real test -- if we run that again, we should get the
858 # warning again. Traditionally, each warning was only issued once per
856 # warning again. Traditionally, each warning was only issued once per
859 # IPython session (approximately), even if the user typed in new and
857 # IPython session (approximately), even if the user typed in new and
860 # different code that should have also triggered the warning, leading
858 # different code that should have also triggered the warning, leading
861 # to much confusion.
859 # to much confusion.
862 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
860 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
863 ip.run_cell("warnings.warn('asdf')")
861 ip.run_cell("warnings.warn('asdf')")
864 finally:
862 finally:
865 ip.run_cell("del warnings")
863 ip.run_cell("del warnings")
866
864
867
865
868 def test_deprecation_warning():
866 def test_deprecation_warning():
869 ip.run_cell("""
867 ip.run_cell("""
870 import warnings
868 import warnings
871 def wrn():
869 def wrn():
872 warnings.warn(
870 warnings.warn(
873 "I AM A WARNING",
871 "I AM A WARNING",
874 DeprecationWarning
872 DeprecationWarning
875 )
873 )
876 """)
874 """)
877 try:
875 try:
878 with tt.AssertPrints("I AM A WARNING", channel="stderr"):
876 with tt.AssertPrints("I AM A WARNING", channel="stderr"):
879 ip.run_cell("wrn()")
877 ip.run_cell("wrn()")
880 finally:
878 finally:
881 ip.run_cell("del warnings")
879 ip.run_cell("del warnings")
882 ip.run_cell("del wrn")
880 ip.run_cell("del wrn")
883
881
884
882
885 class TestImportNoDeprecate(tt.TempFileMixin):
883 class TestImportNoDeprecate(tt.TempFileMixin):
886
884
887 def setup(self):
885 def setup(self):
888 """Make a valid python temp file."""
886 """Make a valid python temp file."""
889 self.mktmp("""
887 self.mktmp("""
890 import warnings
888 import warnings
891 def wrn():
889 def wrn():
892 warnings.warn(
890 warnings.warn(
893 "I AM A WARNING",
891 "I AM A WARNING",
894 DeprecationWarning
892 DeprecationWarning
895 )
893 )
896 """)
894 """)
897
895
898 def test_no_dep(self):
896 def test_no_dep(self):
899 """
897 """
900 No deprecation warning should be raised from imported functions
898 No deprecation warning should be raised from imported functions
901 """
899 """
902 ip.run_cell("from {} import wrn".format(self.fname))
900 ip.run_cell("from {} import wrn".format(self.fname))
903
901
904 with tt.AssertNotPrints("I AM A WARNING"):
902 with tt.AssertNotPrints("I AM A WARNING"):
905 ip.run_cell("wrn()")
903 ip.run_cell("wrn()")
906 ip.run_cell("del wrn")
904 ip.run_cell("del wrn")
@@ -1,204 +1,200 b''
1 import errno
1 import errno
2 import os
2 import os
3 import shutil
3 import shutil
4 import sys
4 import sys
5 import tempfile
5 import tempfile
6 import warnings
6 import warnings
7
7 from unittest.mock import patch
8 try: # Python 3
9 from unittest.mock import patch
10 except ImportError: # Python 2
11 from mock import patch
12
8
13 import nose.tools as nt
9 import nose.tools as nt
14 from testpath import modified_env, assert_isdir, assert_isfile
10 from testpath import modified_env, assert_isdir, assert_isfile
15
11
16 from IPython import paths
12 from IPython import paths
17 from IPython.testing.decorators import skip_win32
13 from IPython.testing.decorators import skip_win32
18 from IPython.utils.tempdir import TemporaryDirectory
14 from IPython.utils.tempdir import TemporaryDirectory
19
15
20 TMP_TEST_DIR = os.path.realpath(tempfile.mkdtemp())
16 TMP_TEST_DIR = os.path.realpath(tempfile.mkdtemp())
21 HOME_TEST_DIR = os.path.join(TMP_TEST_DIR, "home_test_dir")
17 HOME_TEST_DIR = os.path.join(TMP_TEST_DIR, "home_test_dir")
22 XDG_TEST_DIR = os.path.join(HOME_TEST_DIR, "xdg_test_dir")
18 XDG_TEST_DIR = os.path.join(HOME_TEST_DIR, "xdg_test_dir")
23 XDG_CACHE_DIR = os.path.join(HOME_TEST_DIR, "xdg_cache_dir")
19 XDG_CACHE_DIR = os.path.join(HOME_TEST_DIR, "xdg_cache_dir")
24 IP_TEST_DIR = os.path.join(HOME_TEST_DIR,'.ipython')
20 IP_TEST_DIR = os.path.join(HOME_TEST_DIR,'.ipython')
25
21
26 def setup():
22 def setup():
27 """Setup testenvironment for the module:
23 """Setup testenvironment for the module:
28
24
29 - Adds dummy home dir tree
25 - Adds dummy home dir tree
30 """
26 """
31 # Do not mask exceptions here. In particular, catching WindowsError is a
27 # Do not mask exceptions here. In particular, catching WindowsError is a
32 # problem because that exception is only defined on Windows...
28 # problem because that exception is only defined on Windows...
33 os.makedirs(IP_TEST_DIR)
29 os.makedirs(IP_TEST_DIR)
34 os.makedirs(os.path.join(XDG_TEST_DIR, 'ipython'))
30 os.makedirs(os.path.join(XDG_TEST_DIR, 'ipython'))
35 os.makedirs(os.path.join(XDG_CACHE_DIR, 'ipython'))
31 os.makedirs(os.path.join(XDG_CACHE_DIR, 'ipython'))
36
32
37
33
38 def teardown():
34 def teardown():
39 """Teardown testenvironment for the module:
35 """Teardown testenvironment for the module:
40
36
41 - Remove dummy home dir tree
37 - Remove dummy home dir tree
42 """
38 """
43 # Note: we remove the parent test dir, which is the root of all test
39 # Note: we remove the parent test dir, which is the root of all test
44 # subdirs we may have created. Use shutil instead of os.removedirs, so
40 # subdirs we may have created. Use shutil instead of os.removedirs, so
45 # that non-empty directories are all recursively removed.
41 # that non-empty directories are all recursively removed.
46 shutil.rmtree(TMP_TEST_DIR)
42 shutil.rmtree(TMP_TEST_DIR)
47
43
48 def patch_get_home_dir(dirpath):
44 def patch_get_home_dir(dirpath):
49 return patch.object(paths, 'get_home_dir', return_value=dirpath)
45 return patch.object(paths, 'get_home_dir', return_value=dirpath)
50
46
51
47
52 def test_get_ipython_dir_1():
48 def test_get_ipython_dir_1():
53 """test_get_ipython_dir_1, Testcase to see if we can call get_ipython_dir without Exceptions."""
49 """test_get_ipython_dir_1, Testcase to see if we can call get_ipython_dir without Exceptions."""
54 env_ipdir = os.path.join("someplace", ".ipython")
50 env_ipdir = os.path.join("someplace", ".ipython")
55 with patch.object(paths, '_writable_dir', return_value=True), \
51 with patch.object(paths, '_writable_dir', return_value=True), \
56 modified_env({'IPYTHONDIR': env_ipdir}):
52 modified_env({'IPYTHONDIR': env_ipdir}):
57 ipdir = paths.get_ipython_dir()
53 ipdir = paths.get_ipython_dir()
58
54
59 nt.assert_equal(ipdir, env_ipdir)
55 nt.assert_equal(ipdir, env_ipdir)
60
56
61 def test_get_ipython_dir_2():
57 def test_get_ipython_dir_2():
62 """test_get_ipython_dir_2, Testcase to see if we can call get_ipython_dir without Exceptions."""
58 """test_get_ipython_dir_2, Testcase to see if we can call get_ipython_dir without Exceptions."""
63 with patch_get_home_dir('someplace'), \
59 with patch_get_home_dir('someplace'), \
64 patch.object(paths, 'get_xdg_dir', return_value=None), \
60 patch.object(paths, 'get_xdg_dir', return_value=None), \
65 patch.object(paths, '_writable_dir', return_value=True), \
61 patch.object(paths, '_writable_dir', return_value=True), \
66 patch('os.name', "posix"), \
62 patch('os.name', "posix"), \
67 modified_env({'IPYTHON_DIR': None,
63 modified_env({'IPYTHON_DIR': None,
68 'IPYTHONDIR': None,
64 'IPYTHONDIR': None,
69 'XDG_CONFIG_HOME': None
65 'XDG_CONFIG_HOME': None
70 }):
66 }):
71 ipdir = paths.get_ipython_dir()
67 ipdir = paths.get_ipython_dir()
72
68
73 nt.assert_equal(ipdir, os.path.join("someplace", ".ipython"))
69 nt.assert_equal(ipdir, os.path.join("someplace", ".ipython"))
74
70
75 def test_get_ipython_dir_3():
71 def test_get_ipython_dir_3():
76 """test_get_ipython_dir_3, move XDG if defined, and .ipython doesn't exist."""
72 """test_get_ipython_dir_3, move XDG if defined, and .ipython doesn't exist."""
77 tmphome = TemporaryDirectory()
73 tmphome = TemporaryDirectory()
78 try:
74 try:
79 with patch_get_home_dir(tmphome.name), \
75 with patch_get_home_dir(tmphome.name), \
80 patch('os.name', 'posix'), \
76 patch('os.name', 'posix'), \
81 modified_env({
77 modified_env({
82 'IPYTHON_DIR': None,
78 'IPYTHON_DIR': None,
83 'IPYTHONDIR': None,
79 'IPYTHONDIR': None,
84 'XDG_CONFIG_HOME': XDG_TEST_DIR,
80 'XDG_CONFIG_HOME': XDG_TEST_DIR,
85 }), warnings.catch_warnings(record=True) as w:
81 }), warnings.catch_warnings(record=True) as w:
86 ipdir = paths.get_ipython_dir()
82 ipdir = paths.get_ipython_dir()
87
83
88 nt.assert_equal(ipdir, os.path.join(tmphome.name, ".ipython"))
84 nt.assert_equal(ipdir, os.path.join(tmphome.name, ".ipython"))
89 if sys.platform != 'darwin':
85 if sys.platform != 'darwin':
90 nt.assert_equal(len(w), 1)
86 nt.assert_equal(len(w), 1)
91 nt.assert_in('Moving', str(w[0]))
87 nt.assert_in('Moving', str(w[0]))
92 finally:
88 finally:
93 tmphome.cleanup()
89 tmphome.cleanup()
94
90
95 def test_get_ipython_dir_4():
91 def test_get_ipython_dir_4():
96 """test_get_ipython_dir_4, warn if XDG and home both exist."""
92 """test_get_ipython_dir_4, warn if XDG and home both exist."""
97 with patch_get_home_dir(HOME_TEST_DIR), \
93 with patch_get_home_dir(HOME_TEST_DIR), \
98 patch('os.name', 'posix'):
94 patch('os.name', 'posix'):
99 try:
95 try:
100 os.mkdir(os.path.join(XDG_TEST_DIR, 'ipython'))
96 os.mkdir(os.path.join(XDG_TEST_DIR, 'ipython'))
101 except OSError as e:
97 except OSError as e:
102 if e.errno != errno.EEXIST:
98 if e.errno != errno.EEXIST:
103 raise
99 raise
104
100
105
101
106 with modified_env({
102 with modified_env({
107 'IPYTHON_DIR': None,
103 'IPYTHON_DIR': None,
108 'IPYTHONDIR': None,
104 'IPYTHONDIR': None,
109 'XDG_CONFIG_HOME': XDG_TEST_DIR,
105 'XDG_CONFIG_HOME': XDG_TEST_DIR,
110 }), warnings.catch_warnings(record=True) as w:
106 }), warnings.catch_warnings(record=True) as w:
111 ipdir = paths.get_ipython_dir()
107 ipdir = paths.get_ipython_dir()
112
108
113 nt.assert_equal(ipdir, os.path.join(HOME_TEST_DIR, ".ipython"))
109 nt.assert_equal(ipdir, os.path.join(HOME_TEST_DIR, ".ipython"))
114 if sys.platform != 'darwin':
110 if sys.platform != 'darwin':
115 nt.assert_equal(len(w), 1)
111 nt.assert_equal(len(w), 1)
116 nt.assert_in('Ignoring', str(w[0]))
112 nt.assert_in('Ignoring', str(w[0]))
117
113
118 def test_get_ipython_dir_5():
114 def test_get_ipython_dir_5():
119 """test_get_ipython_dir_5, use .ipython if exists and XDG defined, but doesn't exist."""
115 """test_get_ipython_dir_5, use .ipython if exists and XDG defined, but doesn't exist."""
120 with patch_get_home_dir(HOME_TEST_DIR), \
116 with patch_get_home_dir(HOME_TEST_DIR), \
121 patch('os.name', 'posix'):
117 patch('os.name', 'posix'):
122 try:
118 try:
123 os.rmdir(os.path.join(XDG_TEST_DIR, 'ipython'))
119 os.rmdir(os.path.join(XDG_TEST_DIR, 'ipython'))
124 except OSError as e:
120 except OSError as e:
125 if e.errno != errno.ENOENT:
121 if e.errno != errno.ENOENT:
126 raise
122 raise
127
123
128 with modified_env({
124 with modified_env({
129 'IPYTHON_DIR': None,
125 'IPYTHON_DIR': None,
130 'IPYTHONDIR': None,
126 'IPYTHONDIR': None,
131 'XDG_CONFIG_HOME': XDG_TEST_DIR,
127 'XDG_CONFIG_HOME': XDG_TEST_DIR,
132 }):
128 }):
133 ipdir = paths.get_ipython_dir()
129 ipdir = paths.get_ipython_dir()
134
130
135 nt.assert_equal(ipdir, IP_TEST_DIR)
131 nt.assert_equal(ipdir, IP_TEST_DIR)
136
132
137 def test_get_ipython_dir_6():
133 def test_get_ipython_dir_6():
138 """test_get_ipython_dir_6, use home over XDG if defined and neither exist."""
134 """test_get_ipython_dir_6, use home over XDG if defined and neither exist."""
139 xdg = os.path.join(HOME_TEST_DIR, 'somexdg')
135 xdg = os.path.join(HOME_TEST_DIR, 'somexdg')
140 os.mkdir(xdg)
136 os.mkdir(xdg)
141 shutil.rmtree(os.path.join(HOME_TEST_DIR, '.ipython'))
137 shutil.rmtree(os.path.join(HOME_TEST_DIR, '.ipython'))
142 print(paths._writable_dir)
138 print(paths._writable_dir)
143 with patch_get_home_dir(HOME_TEST_DIR), \
139 with patch_get_home_dir(HOME_TEST_DIR), \
144 patch.object(paths, 'get_xdg_dir', return_value=xdg), \
140 patch.object(paths, 'get_xdg_dir', return_value=xdg), \
145 patch('os.name', 'posix'), \
141 patch('os.name', 'posix'), \
146 modified_env({
142 modified_env({
147 'IPYTHON_DIR': None,
143 'IPYTHON_DIR': None,
148 'IPYTHONDIR': None,
144 'IPYTHONDIR': None,
149 'XDG_CONFIG_HOME': None,
145 'XDG_CONFIG_HOME': None,
150 }), warnings.catch_warnings(record=True) as w:
146 }), warnings.catch_warnings(record=True) as w:
151 ipdir = paths.get_ipython_dir()
147 ipdir = paths.get_ipython_dir()
152
148
153 nt.assert_equal(ipdir, os.path.join(HOME_TEST_DIR, '.ipython'))
149 nt.assert_equal(ipdir, os.path.join(HOME_TEST_DIR, '.ipython'))
154 nt.assert_equal(len(w), 0)
150 nt.assert_equal(len(w), 0)
155
151
156 def test_get_ipython_dir_7():
152 def test_get_ipython_dir_7():
157 """test_get_ipython_dir_7, test home directory expansion on IPYTHONDIR"""
153 """test_get_ipython_dir_7, test home directory expansion on IPYTHONDIR"""
158 home_dir = os.path.normpath(os.path.expanduser('~'))
154 home_dir = os.path.normpath(os.path.expanduser('~'))
159 with modified_env({'IPYTHONDIR': os.path.join('~', 'somewhere')}), \
155 with modified_env({'IPYTHONDIR': os.path.join('~', 'somewhere')}), \
160 patch.object(paths, '_writable_dir', return_value=True):
156 patch.object(paths, '_writable_dir', return_value=True):
161 ipdir = paths.get_ipython_dir()
157 ipdir = paths.get_ipython_dir()
162 nt.assert_equal(ipdir, os.path.join(home_dir, 'somewhere'))
158 nt.assert_equal(ipdir, os.path.join(home_dir, 'somewhere'))
163
159
164 @skip_win32
160 @skip_win32
165 def test_get_ipython_dir_8():
161 def test_get_ipython_dir_8():
166 """test_get_ipython_dir_8, test / home directory"""
162 """test_get_ipython_dir_8, test / home directory"""
167 with patch.object(paths, '_writable_dir', lambda path: bool(path)), \
163 with patch.object(paths, '_writable_dir', lambda path: bool(path)), \
168 patch.object(paths, 'get_xdg_dir', return_value=None), \
164 patch.object(paths, 'get_xdg_dir', return_value=None), \
169 modified_env({
165 modified_env({
170 'IPYTHON_DIR': None,
166 'IPYTHON_DIR': None,
171 'IPYTHONDIR': None,
167 'IPYTHONDIR': None,
172 'HOME': '/',
168 'HOME': '/',
173 }):
169 }):
174 nt.assert_equal(paths.get_ipython_dir(), '/.ipython')
170 nt.assert_equal(paths.get_ipython_dir(), '/.ipython')
175
171
176
172
177 def test_get_ipython_cache_dir():
173 def test_get_ipython_cache_dir():
178 with modified_env({'HOME': HOME_TEST_DIR}):
174 with modified_env({'HOME': HOME_TEST_DIR}):
179 if os.name == 'posix' and sys.platform != 'darwin':
175 if os.name == 'posix' and sys.platform != 'darwin':
180 # test default
176 # test default
181 os.makedirs(os.path.join(HOME_TEST_DIR, ".cache"))
177 os.makedirs(os.path.join(HOME_TEST_DIR, ".cache"))
182 with modified_env({'XDG_CACHE_HOME': None}):
178 with modified_env({'XDG_CACHE_HOME': None}):
183 ipdir = paths.get_ipython_cache_dir()
179 ipdir = paths.get_ipython_cache_dir()
184 nt.assert_equal(os.path.join(HOME_TEST_DIR, ".cache", "ipython"),
180 nt.assert_equal(os.path.join(HOME_TEST_DIR, ".cache", "ipython"),
185 ipdir)
181 ipdir)
186 assert_isdir(ipdir)
182 assert_isdir(ipdir)
187
183
188 # test env override
184 # test env override
189 with modified_env({"XDG_CACHE_HOME": XDG_CACHE_DIR}):
185 with modified_env({"XDG_CACHE_HOME": XDG_CACHE_DIR}):
190 ipdir = paths.get_ipython_cache_dir()
186 ipdir = paths.get_ipython_cache_dir()
191 assert_isdir(ipdir)
187 assert_isdir(ipdir)
192 nt.assert_equal(ipdir, os.path.join(XDG_CACHE_DIR, "ipython"))
188 nt.assert_equal(ipdir, os.path.join(XDG_CACHE_DIR, "ipython"))
193 else:
189 else:
194 nt.assert_equal(paths.get_ipython_cache_dir(),
190 nt.assert_equal(paths.get_ipython_cache_dir(),
195 paths.get_ipython_dir())
191 paths.get_ipython_dir())
196
192
197 def test_get_ipython_package_dir():
193 def test_get_ipython_package_dir():
198 ipdir = paths.get_ipython_package_dir()
194 ipdir = paths.get_ipython_package_dir()
199 assert_isdir(ipdir)
195 assert_isdir(ipdir)
200
196
201
197
202 def test_get_ipython_module_path():
198 def test_get_ipython_module_path():
203 ipapp_path = paths.get_ipython_module_path('IPython.terminal.ipapp')
199 ipapp_path = paths.get_ipython_module_path('IPython.terminal.ipapp')
204 assert_isfile(ipapp_path)
200 assert_isfile(ipapp_path)
@@ -1,514 +1,510 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Tests for code execution (%run and related), which is particularly tricky.
2 """Tests for code execution (%run and related), which is particularly tricky.
3
3
4 Because of how %run manages namespaces, and the fact that we are trying here to
4 Because of how %run manages namespaces, and the fact that we are trying here to
5 verify subtle object deletion and reference counting issues, the %run tests
5 verify subtle object deletion and reference counting issues, the %run tests
6 will be kept in this separate file. This makes it easier to aggregate in one
6 will be kept in this separate file. This makes it easier to aggregate in one
7 place the tricks needed to handle it; most other magics are much easier to test
7 place the tricks needed to handle it; most other magics are much easier to test
8 and we do so in a common test_magic file.
8 and we do so in a common test_magic file.
9 """
9 """
10
10
11 # Copyright (c) IPython Development Team.
11 # Copyright (c) IPython Development Team.
12 # Distributed under the terms of the Modified BSD License.
12 # Distributed under the terms of the Modified BSD License.
13
13
14
14
15
15
16 import functools
16 import functools
17 import os
17 import os
18 from os.path import join as pjoin
18 from os.path import join as pjoin
19 import random
19 import random
20 import sys
20 import sys
21 import textwrap
21 import textwrap
22 import unittest
22 import unittest
23
23 from unittest.mock import patch
24 try:
25 from unittest.mock import patch
26 except ImportError:
27 from mock import patch
28
24
29 import nose.tools as nt
25 import nose.tools as nt
30 from nose import SkipTest
26 from nose import SkipTest
31
27
32 from IPython.testing import decorators as dec
28 from IPython.testing import decorators as dec
33 from IPython.testing import tools as tt
29 from IPython.testing import tools as tt
34 from IPython.utils import py3compat
30 from IPython.utils import py3compat
35 from IPython.utils.io import capture_output
31 from IPython.utils.io import capture_output
36 from IPython.utils.tempdir import TemporaryDirectory
32 from IPython.utils.tempdir import TemporaryDirectory
37 from IPython.core import debugger
33 from IPython.core import debugger
38
34
39
35
40 def doctest_refbug():
36 def doctest_refbug():
41 """Very nasty problem with references held by multiple runs of a script.
37 """Very nasty problem with references held by multiple runs of a script.
42 See: https://github.com/ipython/ipython/issues/141
38 See: https://github.com/ipython/ipython/issues/141
43
39
44 In [1]: _ip.clear_main_mod_cache()
40 In [1]: _ip.clear_main_mod_cache()
45 # random
41 # random
46
42
47 In [2]: %run refbug
43 In [2]: %run refbug
48
44
49 In [3]: call_f()
45 In [3]: call_f()
50 lowercased: hello
46 lowercased: hello
51
47
52 In [4]: %run refbug
48 In [4]: %run refbug
53
49
54 In [5]: call_f()
50 In [5]: call_f()
55 lowercased: hello
51 lowercased: hello
56 lowercased: hello
52 lowercased: hello
57 """
53 """
58
54
59
55
60 def doctest_run_builtins():
56 def doctest_run_builtins():
61 r"""Check that %run doesn't damage __builtins__.
57 r"""Check that %run doesn't damage __builtins__.
62
58
63 In [1]: import tempfile
59 In [1]: import tempfile
64
60
65 In [2]: bid1 = id(__builtins__)
61 In [2]: bid1 = id(__builtins__)
66
62
67 In [3]: fname = tempfile.mkstemp('.py')[1]
63 In [3]: fname = tempfile.mkstemp('.py')[1]
68
64
69 In [3]: f = open(fname,'w')
65 In [3]: f = open(fname,'w')
70
66
71 In [4]: dummy= f.write('pass\n')
67 In [4]: dummy= f.write('pass\n')
72
68
73 In [5]: f.flush()
69 In [5]: f.flush()
74
70
75 In [6]: t1 = type(__builtins__)
71 In [6]: t1 = type(__builtins__)
76
72
77 In [7]: %run $fname
73 In [7]: %run $fname
78
74
79 In [7]: f.close()
75 In [7]: f.close()
80
76
81 In [8]: bid2 = id(__builtins__)
77 In [8]: bid2 = id(__builtins__)
82
78
83 In [9]: t2 = type(__builtins__)
79 In [9]: t2 = type(__builtins__)
84
80
85 In [10]: t1 == t2
81 In [10]: t1 == t2
86 Out[10]: True
82 Out[10]: True
87
83
88 In [10]: bid1 == bid2
84 In [10]: bid1 == bid2
89 Out[10]: True
85 Out[10]: True
90
86
91 In [12]: try:
87 In [12]: try:
92 ....: os.unlink(fname)
88 ....: os.unlink(fname)
93 ....: except:
89 ....: except:
94 ....: pass
90 ....: pass
95 ....:
91 ....:
96 """
92 """
97
93
98
94
99 def doctest_run_option_parser():
95 def doctest_run_option_parser():
100 r"""Test option parser in %run.
96 r"""Test option parser in %run.
101
97
102 In [1]: %run print_argv.py
98 In [1]: %run print_argv.py
103 []
99 []
104
100
105 In [2]: %run print_argv.py print*.py
101 In [2]: %run print_argv.py print*.py
106 ['print_argv.py']
102 ['print_argv.py']
107
103
108 In [3]: %run -G print_argv.py print*.py
104 In [3]: %run -G print_argv.py print*.py
109 ['print*.py']
105 ['print*.py']
110
106
111 """
107 """
112
108
113
109
114 @dec.skip_win32
110 @dec.skip_win32
115 def doctest_run_option_parser_for_posix():
111 def doctest_run_option_parser_for_posix():
116 r"""Test option parser in %run (Linux/OSX specific).
112 r"""Test option parser in %run (Linux/OSX specific).
117
113
118 You need double quote to escape glob in POSIX systems:
114 You need double quote to escape glob in POSIX systems:
119
115
120 In [1]: %run print_argv.py print\\*.py
116 In [1]: %run print_argv.py print\\*.py
121 ['print*.py']
117 ['print*.py']
122
118
123 You can't use quote to escape glob in POSIX systems:
119 You can't use quote to escape glob in POSIX systems:
124
120
125 In [2]: %run print_argv.py 'print*.py'
121 In [2]: %run print_argv.py 'print*.py'
126 ['print_argv.py']
122 ['print_argv.py']
127
123
128 """
124 """
129
125
130
126
131 @dec.skip_if_not_win32
127 @dec.skip_if_not_win32
132 def doctest_run_option_parser_for_windows():
128 def doctest_run_option_parser_for_windows():
133 r"""Test option parser in %run (Windows specific).
129 r"""Test option parser in %run (Windows specific).
134
130
135 In Windows, you can't escape ``*` `by backslash:
131 In Windows, you can't escape ``*` `by backslash:
136
132
137 In [1]: %run print_argv.py print\\*.py
133 In [1]: %run print_argv.py print\\*.py
138 ['print\\*.py']
134 ['print\\*.py']
139
135
140 You can use quote to escape glob:
136 You can use quote to escape glob:
141
137
142 In [2]: %run print_argv.py 'print*.py'
138 In [2]: %run print_argv.py 'print*.py'
143 ['print*.py']
139 ['print*.py']
144
140
145 """
141 """
146
142
147
143
148 @py3compat.doctest_refactor_print
144 @py3compat.doctest_refactor_print
149 def doctest_reset_del():
145 def doctest_reset_del():
150 """Test that resetting doesn't cause errors in __del__ methods.
146 """Test that resetting doesn't cause errors in __del__ methods.
151
147
152 In [2]: class A(object):
148 In [2]: class A(object):
153 ...: def __del__(self):
149 ...: def __del__(self):
154 ...: print str("Hi")
150 ...: print str("Hi")
155 ...:
151 ...:
156
152
157 In [3]: a = A()
153 In [3]: a = A()
158
154
159 In [4]: get_ipython().reset()
155 In [4]: get_ipython().reset()
160 Hi
156 Hi
161
157
162 In [5]: 1+1
158 In [5]: 1+1
163 Out[5]: 2
159 Out[5]: 2
164 """
160 """
165
161
166 # For some tests, it will be handy to organize them in a class with a common
162 # For some tests, it will be handy to organize them in a class with a common
167 # setup that makes a temp file
163 # setup that makes a temp file
168
164
169 class TestMagicRunPass(tt.TempFileMixin):
165 class TestMagicRunPass(tt.TempFileMixin):
170
166
171 def setup(self):
167 def setup(self):
172 """Make a valid python temp file."""
168 """Make a valid python temp file."""
173 self.mktmp('pass\n')
169 self.mktmp('pass\n')
174
170
175 def run_tmpfile(self):
171 def run_tmpfile(self):
176 _ip = get_ipython()
172 _ip = get_ipython()
177 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
173 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
178 # See below and ticket https://bugs.launchpad.net/bugs/366353
174 # See below and ticket https://bugs.launchpad.net/bugs/366353
179 _ip.magic('run %s' % self.fname)
175 _ip.magic('run %s' % self.fname)
180
176
181 def run_tmpfile_p(self):
177 def run_tmpfile_p(self):
182 _ip = get_ipython()
178 _ip = get_ipython()
183 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
179 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
184 # See below and ticket https://bugs.launchpad.net/bugs/366353
180 # See below and ticket https://bugs.launchpad.net/bugs/366353
185 _ip.magic('run -p %s' % self.fname)
181 _ip.magic('run -p %s' % self.fname)
186
182
187 def test_builtins_id(self):
183 def test_builtins_id(self):
188 """Check that %run doesn't damage __builtins__ """
184 """Check that %run doesn't damage __builtins__ """
189 _ip = get_ipython()
185 _ip = get_ipython()
190 # Test that the id of __builtins__ is not modified by %run
186 # Test that the id of __builtins__ is not modified by %run
191 bid1 = id(_ip.user_ns['__builtins__'])
187 bid1 = id(_ip.user_ns['__builtins__'])
192 self.run_tmpfile()
188 self.run_tmpfile()
193 bid2 = id(_ip.user_ns['__builtins__'])
189 bid2 = id(_ip.user_ns['__builtins__'])
194 nt.assert_equal(bid1, bid2)
190 nt.assert_equal(bid1, bid2)
195
191
196 def test_builtins_type(self):
192 def test_builtins_type(self):
197 """Check that the type of __builtins__ doesn't change with %run.
193 """Check that the type of __builtins__ doesn't change with %run.
198
194
199 However, the above could pass if __builtins__ was already modified to
195 However, the above could pass if __builtins__ was already modified to
200 be a dict (it should be a module) by a previous use of %run. So we
196 be a dict (it should be a module) by a previous use of %run. So we
201 also check explicitly that it really is a module:
197 also check explicitly that it really is a module:
202 """
198 """
203 _ip = get_ipython()
199 _ip = get_ipython()
204 self.run_tmpfile()
200 self.run_tmpfile()
205 nt.assert_equal(type(_ip.user_ns['__builtins__']),type(sys))
201 nt.assert_equal(type(_ip.user_ns['__builtins__']),type(sys))
206
202
207 def test_run_profile( self ):
203 def test_run_profile( self ):
208 """Test that the option -p, which invokes the profiler, do not
204 """Test that the option -p, which invokes the profiler, do not
209 crash by invoking execfile"""
205 crash by invoking execfile"""
210 self.run_tmpfile_p()
206 self.run_tmpfile_p()
211
207
212 def test_run_debug_twice(self):
208 def test_run_debug_twice(self):
213 # https://github.com/ipython/ipython/issues/10028
209 # https://github.com/ipython/ipython/issues/10028
214 _ip = get_ipython()
210 _ip = get_ipython()
215 with tt.fake_input(['c']):
211 with tt.fake_input(['c']):
216 _ip.magic('run -d %s' % self.fname)
212 _ip.magic('run -d %s' % self.fname)
217 with tt.fake_input(['c']):
213 with tt.fake_input(['c']):
218 _ip.magic('run -d %s' % self.fname)
214 _ip.magic('run -d %s' % self.fname)
219
215
220
216
221 class TestMagicRunSimple(tt.TempFileMixin):
217 class TestMagicRunSimple(tt.TempFileMixin):
222
218
223 def test_simpledef(self):
219 def test_simpledef(self):
224 """Test that simple class definitions work."""
220 """Test that simple class definitions work."""
225 src = ("class foo: pass\n"
221 src = ("class foo: pass\n"
226 "def f(): return foo()")
222 "def f(): return foo()")
227 self.mktmp(src)
223 self.mktmp(src)
228 _ip.magic('run %s' % self.fname)
224 _ip.magic('run %s' % self.fname)
229 _ip.run_cell('t = isinstance(f(), foo)')
225 _ip.run_cell('t = isinstance(f(), foo)')
230 nt.assert_true(_ip.user_ns['t'])
226 nt.assert_true(_ip.user_ns['t'])
231
227
232 def test_obj_del(self):
228 def test_obj_del(self):
233 """Test that object's __del__ methods are called on exit."""
229 """Test that object's __del__ methods are called on exit."""
234 if sys.platform == 'win32':
230 if sys.platform == 'win32':
235 try:
231 try:
236 import win32api
232 import win32api
237 except ImportError:
233 except ImportError:
238 raise SkipTest("Test requires pywin32")
234 raise SkipTest("Test requires pywin32")
239 src = ("class A(object):\n"
235 src = ("class A(object):\n"
240 " def __del__(self):\n"
236 " def __del__(self):\n"
241 " print 'object A deleted'\n"
237 " print 'object A deleted'\n"
242 "a = A()\n")
238 "a = A()\n")
243 self.mktmp(py3compat.doctest_refactor_print(src))
239 self.mktmp(py3compat.doctest_refactor_print(src))
244 if dec.module_not_available('sqlite3'):
240 if dec.module_not_available('sqlite3'):
245 err = 'WARNING: IPython History requires SQLite, your history will not be saved\n'
241 err = 'WARNING: IPython History requires SQLite, your history will not be saved\n'
246 else:
242 else:
247 err = None
243 err = None
248 tt.ipexec_validate(self.fname, 'object A deleted', err)
244 tt.ipexec_validate(self.fname, 'object A deleted', err)
249
245
250 def test_aggressive_namespace_cleanup(self):
246 def test_aggressive_namespace_cleanup(self):
251 """Test that namespace cleanup is not too aggressive GH-238
247 """Test that namespace cleanup is not too aggressive GH-238
252
248
253 Returning from another run magic deletes the namespace"""
249 Returning from another run magic deletes the namespace"""
254 # see ticket https://github.com/ipython/ipython/issues/238
250 # see ticket https://github.com/ipython/ipython/issues/238
255
251
256 with tt.TempFileMixin() as empty:
252 with tt.TempFileMixin() as empty:
257 empty.mktmp('')
253 empty.mktmp('')
258 # On Windows, the filename will have \users in it, so we need to use the
254 # On Windows, the filename will have \users in it, so we need to use the
259 # repr so that the \u becomes \\u.
255 # repr so that the \u becomes \\u.
260 src = ("ip = get_ipython()\n"
256 src = ("ip = get_ipython()\n"
261 "for i in range(5):\n"
257 "for i in range(5):\n"
262 " try:\n"
258 " try:\n"
263 " ip.magic(%r)\n"
259 " ip.magic(%r)\n"
264 " except NameError as e:\n"
260 " except NameError as e:\n"
265 " print(i)\n"
261 " print(i)\n"
266 " break\n" % ('run ' + empty.fname))
262 " break\n" % ('run ' + empty.fname))
267 self.mktmp(src)
263 self.mktmp(src)
268 _ip.magic('run %s' % self.fname)
264 _ip.magic('run %s' % self.fname)
269 _ip.run_cell('ip == get_ipython()')
265 _ip.run_cell('ip == get_ipython()')
270 nt.assert_equal(_ip.user_ns['i'], 4)
266 nt.assert_equal(_ip.user_ns['i'], 4)
271
267
272 def test_run_second(self):
268 def test_run_second(self):
273 """Test that running a second file doesn't clobber the first, gh-3547
269 """Test that running a second file doesn't clobber the first, gh-3547
274 """
270 """
275 self.mktmp("avar = 1\n"
271 self.mktmp("avar = 1\n"
276 "def afunc():\n"
272 "def afunc():\n"
277 " return avar\n")
273 " return avar\n")
278
274
279 with tt.TempFileMixin() as empty:
275 with tt.TempFileMixin() as empty:
280 empty.mktmp("")
276 empty.mktmp("")
281
277
282 _ip.magic('run %s' % self.fname)
278 _ip.magic('run %s' % self.fname)
283 _ip.magic('run %s' % empty.fname)
279 _ip.magic('run %s' % empty.fname)
284 nt.assert_equal(_ip.user_ns['afunc'](), 1)
280 nt.assert_equal(_ip.user_ns['afunc'](), 1)
285
281
286 @dec.skip_win32
282 @dec.skip_win32
287 def test_tclass(self):
283 def test_tclass(self):
288 mydir = os.path.dirname(__file__)
284 mydir = os.path.dirname(__file__)
289 tc = os.path.join(mydir, 'tclass')
285 tc = os.path.join(mydir, 'tclass')
290 src = ("%%run '%s' C-first\n"
286 src = ("%%run '%s' C-first\n"
291 "%%run '%s' C-second\n"
287 "%%run '%s' C-second\n"
292 "%%run '%s' C-third\n") % (tc, tc, tc)
288 "%%run '%s' C-third\n") % (tc, tc, tc)
293 self.mktmp(src, '.ipy')
289 self.mktmp(src, '.ipy')
294 out = """\
290 out = """\
295 ARGV 1-: ['C-first']
291 ARGV 1-: ['C-first']
296 ARGV 1-: ['C-second']
292 ARGV 1-: ['C-second']
297 tclass.py: deleting object: C-first
293 tclass.py: deleting object: C-first
298 ARGV 1-: ['C-third']
294 ARGV 1-: ['C-third']
299 tclass.py: deleting object: C-second
295 tclass.py: deleting object: C-second
300 tclass.py: deleting object: C-third
296 tclass.py: deleting object: C-third
301 """
297 """
302 if dec.module_not_available('sqlite3'):
298 if dec.module_not_available('sqlite3'):
303 err = 'WARNING: IPython History requires SQLite, your history will not be saved\n'
299 err = 'WARNING: IPython History requires SQLite, your history will not be saved\n'
304 else:
300 else:
305 err = None
301 err = None
306 tt.ipexec_validate(self.fname, out, err)
302 tt.ipexec_validate(self.fname, out, err)
307
303
308 def test_run_i_after_reset(self):
304 def test_run_i_after_reset(self):
309 """Check that %run -i still works after %reset (gh-693)"""
305 """Check that %run -i still works after %reset (gh-693)"""
310 src = "yy = zz\n"
306 src = "yy = zz\n"
311 self.mktmp(src)
307 self.mktmp(src)
312 _ip.run_cell("zz = 23")
308 _ip.run_cell("zz = 23")
313 _ip.magic('run -i %s' % self.fname)
309 _ip.magic('run -i %s' % self.fname)
314 nt.assert_equal(_ip.user_ns['yy'], 23)
310 nt.assert_equal(_ip.user_ns['yy'], 23)
315 _ip.magic('reset -f')
311 _ip.magic('reset -f')
316 _ip.run_cell("zz = 23")
312 _ip.run_cell("zz = 23")
317 _ip.magic('run -i %s' % self.fname)
313 _ip.magic('run -i %s' % self.fname)
318 nt.assert_equal(_ip.user_ns['yy'], 23)
314 nt.assert_equal(_ip.user_ns['yy'], 23)
319
315
320 def test_unicode(self):
316 def test_unicode(self):
321 """Check that files in odd encodings are accepted."""
317 """Check that files in odd encodings are accepted."""
322 mydir = os.path.dirname(__file__)
318 mydir = os.path.dirname(__file__)
323 na = os.path.join(mydir, 'nonascii.py')
319 na = os.path.join(mydir, 'nonascii.py')
324 _ip.magic('run "%s"' % na)
320 _ip.magic('run "%s"' % na)
325 nt.assert_equal(_ip.user_ns['u'], u'ΠŽΡ‚β„–Π€')
321 nt.assert_equal(_ip.user_ns['u'], u'ΠŽΡ‚β„–Π€')
326
322
327 def test_run_py_file_attribute(self):
323 def test_run_py_file_attribute(self):
328 """Test handling of `__file__` attribute in `%run <file>.py`."""
324 """Test handling of `__file__` attribute in `%run <file>.py`."""
329 src = "t = __file__\n"
325 src = "t = __file__\n"
330 self.mktmp(src)
326 self.mktmp(src)
331 _missing = object()
327 _missing = object()
332 file1 = _ip.user_ns.get('__file__', _missing)
328 file1 = _ip.user_ns.get('__file__', _missing)
333 _ip.magic('run %s' % self.fname)
329 _ip.magic('run %s' % self.fname)
334 file2 = _ip.user_ns.get('__file__', _missing)
330 file2 = _ip.user_ns.get('__file__', _missing)
335
331
336 # Check that __file__ was equal to the filename in the script's
332 # Check that __file__ was equal to the filename in the script's
337 # namespace.
333 # namespace.
338 nt.assert_equal(_ip.user_ns['t'], self.fname)
334 nt.assert_equal(_ip.user_ns['t'], self.fname)
339
335
340 # Check that __file__ was not leaked back into user_ns.
336 # Check that __file__ was not leaked back into user_ns.
341 nt.assert_equal(file1, file2)
337 nt.assert_equal(file1, file2)
342
338
343 def test_run_ipy_file_attribute(self):
339 def test_run_ipy_file_attribute(self):
344 """Test handling of `__file__` attribute in `%run <file.ipy>`."""
340 """Test handling of `__file__` attribute in `%run <file.ipy>`."""
345 src = "t = __file__\n"
341 src = "t = __file__\n"
346 self.mktmp(src, ext='.ipy')
342 self.mktmp(src, ext='.ipy')
347 _missing = object()
343 _missing = object()
348 file1 = _ip.user_ns.get('__file__', _missing)
344 file1 = _ip.user_ns.get('__file__', _missing)
349 _ip.magic('run %s' % self.fname)
345 _ip.magic('run %s' % self.fname)
350 file2 = _ip.user_ns.get('__file__', _missing)
346 file2 = _ip.user_ns.get('__file__', _missing)
351
347
352 # Check that __file__ was equal to the filename in the script's
348 # Check that __file__ was equal to the filename in the script's
353 # namespace.
349 # namespace.
354 nt.assert_equal(_ip.user_ns['t'], self.fname)
350 nt.assert_equal(_ip.user_ns['t'], self.fname)
355
351
356 # Check that __file__ was not leaked back into user_ns.
352 # Check that __file__ was not leaked back into user_ns.
357 nt.assert_equal(file1, file2)
353 nt.assert_equal(file1, file2)
358
354
359 def test_run_formatting(self):
355 def test_run_formatting(self):
360 """ Test that %run -t -N<N> does not raise a TypeError for N > 1."""
356 """ Test that %run -t -N<N> does not raise a TypeError for N > 1."""
361 src = "pass"
357 src = "pass"
362 self.mktmp(src)
358 self.mktmp(src)
363 _ip.magic('run -t -N 1 %s' % self.fname)
359 _ip.magic('run -t -N 1 %s' % self.fname)
364 _ip.magic('run -t -N 10 %s' % self.fname)
360 _ip.magic('run -t -N 10 %s' % self.fname)
365
361
366 def test_ignore_sys_exit(self):
362 def test_ignore_sys_exit(self):
367 """Test the -e option to ignore sys.exit()"""
363 """Test the -e option to ignore sys.exit()"""
368 src = "import sys; sys.exit(1)"
364 src = "import sys; sys.exit(1)"
369 self.mktmp(src)
365 self.mktmp(src)
370 with tt.AssertPrints('SystemExit'):
366 with tt.AssertPrints('SystemExit'):
371 _ip.magic('run %s' % self.fname)
367 _ip.magic('run %s' % self.fname)
372
368
373 with tt.AssertNotPrints('SystemExit'):
369 with tt.AssertNotPrints('SystemExit'):
374 _ip.magic('run -e %s' % self.fname)
370 _ip.magic('run -e %s' % self.fname)
375
371
376 def test_run_nb(self):
372 def test_run_nb(self):
377 """Test %run notebook.ipynb"""
373 """Test %run notebook.ipynb"""
378 from nbformat import v4, writes
374 from nbformat import v4, writes
379 nb = v4.new_notebook(
375 nb = v4.new_notebook(
380 cells=[
376 cells=[
381 v4.new_markdown_cell("The Ultimate Question of Everything"),
377 v4.new_markdown_cell("The Ultimate Question of Everything"),
382 v4.new_code_cell("answer=42")
378 v4.new_code_cell("answer=42")
383 ]
379 ]
384 )
380 )
385 src = writes(nb, version=4)
381 src = writes(nb, version=4)
386 self.mktmp(src, ext='.ipynb')
382 self.mktmp(src, ext='.ipynb')
387
383
388 _ip.magic("run %s" % self.fname)
384 _ip.magic("run %s" % self.fname)
389
385
390 nt.assert_equal(_ip.user_ns['answer'], 42)
386 nt.assert_equal(_ip.user_ns['answer'], 42)
391
387
392
388
393
389
394 class TestMagicRunWithPackage(unittest.TestCase):
390 class TestMagicRunWithPackage(unittest.TestCase):
395
391
396 def writefile(self, name, content):
392 def writefile(self, name, content):
397 path = os.path.join(self.tempdir.name, name)
393 path = os.path.join(self.tempdir.name, name)
398 d = os.path.dirname(path)
394 d = os.path.dirname(path)
399 if not os.path.isdir(d):
395 if not os.path.isdir(d):
400 os.makedirs(d)
396 os.makedirs(d)
401 with open(path, 'w') as f:
397 with open(path, 'w') as f:
402 f.write(textwrap.dedent(content))
398 f.write(textwrap.dedent(content))
403
399
404 def setUp(self):
400 def setUp(self):
405 self.package = package = 'tmp{0}'.format(repr(random.random())[2:])
401 self.package = package = 'tmp{0}'.format(repr(random.random())[2:])
406 """Temporary valid python package name."""
402 """Temporary valid python package name."""
407
403
408 self.value = int(random.random() * 10000)
404 self.value = int(random.random() * 10000)
409
405
410 self.tempdir = TemporaryDirectory()
406 self.tempdir = TemporaryDirectory()
411 self.__orig_cwd = os.getcwd()
407 self.__orig_cwd = os.getcwd()
412 sys.path.insert(0, self.tempdir.name)
408 sys.path.insert(0, self.tempdir.name)
413
409
414 self.writefile(os.path.join(package, '__init__.py'), '')
410 self.writefile(os.path.join(package, '__init__.py'), '')
415 self.writefile(os.path.join(package, 'sub.py'), """
411 self.writefile(os.path.join(package, 'sub.py'), """
416 x = {0!r}
412 x = {0!r}
417 """.format(self.value))
413 """.format(self.value))
418 self.writefile(os.path.join(package, 'relative.py'), """
414 self.writefile(os.path.join(package, 'relative.py'), """
419 from .sub import x
415 from .sub import x
420 """)
416 """)
421 self.writefile(os.path.join(package, 'absolute.py'), """
417 self.writefile(os.path.join(package, 'absolute.py'), """
422 from {0}.sub import x
418 from {0}.sub import x
423 """.format(package))
419 """.format(package))
424
420
425 def tearDown(self):
421 def tearDown(self):
426 os.chdir(self.__orig_cwd)
422 os.chdir(self.__orig_cwd)
427 sys.path[:] = [p for p in sys.path if p != self.tempdir.name]
423 sys.path[:] = [p for p in sys.path if p != self.tempdir.name]
428 self.tempdir.cleanup()
424 self.tempdir.cleanup()
429
425
430 def check_run_submodule(self, submodule, opts=''):
426 def check_run_submodule(self, submodule, opts=''):
431 _ip.user_ns.pop('x', None)
427 _ip.user_ns.pop('x', None)
432 _ip.magic('run {2} -m {0}.{1}'.format(self.package, submodule, opts))
428 _ip.magic('run {2} -m {0}.{1}'.format(self.package, submodule, opts))
433 self.assertEqual(_ip.user_ns['x'], self.value,
429 self.assertEqual(_ip.user_ns['x'], self.value,
434 'Variable `x` is not loaded from module `{0}`.'
430 'Variable `x` is not loaded from module `{0}`.'
435 .format(submodule))
431 .format(submodule))
436
432
437 def test_run_submodule_with_absolute_import(self):
433 def test_run_submodule_with_absolute_import(self):
438 self.check_run_submodule('absolute')
434 self.check_run_submodule('absolute')
439
435
440 def test_run_submodule_with_relative_import(self):
436 def test_run_submodule_with_relative_import(self):
441 """Run submodule that has a relative import statement (#2727)."""
437 """Run submodule that has a relative import statement (#2727)."""
442 self.check_run_submodule('relative')
438 self.check_run_submodule('relative')
443
439
444 def test_prun_submodule_with_absolute_import(self):
440 def test_prun_submodule_with_absolute_import(self):
445 self.check_run_submodule('absolute', '-p')
441 self.check_run_submodule('absolute', '-p')
446
442
447 def test_prun_submodule_with_relative_import(self):
443 def test_prun_submodule_with_relative_import(self):
448 self.check_run_submodule('relative', '-p')
444 self.check_run_submodule('relative', '-p')
449
445
450 def with_fake_debugger(func):
446 def with_fake_debugger(func):
451 @functools.wraps(func)
447 @functools.wraps(func)
452 def wrapper(*args, **kwds):
448 def wrapper(*args, **kwds):
453 with patch.object(debugger.Pdb, 'run', staticmethod(eval)):
449 with patch.object(debugger.Pdb, 'run', staticmethod(eval)):
454 return func(*args, **kwds)
450 return func(*args, **kwds)
455 return wrapper
451 return wrapper
456
452
457 @with_fake_debugger
453 @with_fake_debugger
458 def test_debug_run_submodule_with_absolute_import(self):
454 def test_debug_run_submodule_with_absolute_import(self):
459 self.check_run_submodule('absolute', '-d')
455 self.check_run_submodule('absolute', '-d')
460
456
461 @with_fake_debugger
457 @with_fake_debugger
462 def test_debug_run_submodule_with_relative_import(self):
458 def test_debug_run_submodule_with_relative_import(self):
463 self.check_run_submodule('relative', '-d')
459 self.check_run_submodule('relative', '-d')
464
460
465 def test_run__name__():
461 def test_run__name__():
466 with TemporaryDirectory() as td:
462 with TemporaryDirectory() as td:
467 path = pjoin(td, 'foo.py')
463 path = pjoin(td, 'foo.py')
468 with open(path, 'w') as f:
464 with open(path, 'w') as f:
469 f.write("q = __name__")
465 f.write("q = __name__")
470
466
471 _ip.user_ns.pop('q', None)
467 _ip.user_ns.pop('q', None)
472 _ip.magic('run {}'.format(path))
468 _ip.magic('run {}'.format(path))
473 nt.assert_equal(_ip.user_ns.pop('q'), '__main__')
469 nt.assert_equal(_ip.user_ns.pop('q'), '__main__')
474
470
475 _ip.magic('run -n {}'.format(path))
471 _ip.magic('run -n {}'.format(path))
476 nt.assert_equal(_ip.user_ns.pop('q'), 'foo')
472 nt.assert_equal(_ip.user_ns.pop('q'), 'foo')
477
473
478 def test_run_tb():
474 def test_run_tb():
479 """Test traceback offset in %run"""
475 """Test traceback offset in %run"""
480 with TemporaryDirectory() as td:
476 with TemporaryDirectory() as td:
481 path = pjoin(td, 'foo.py')
477 path = pjoin(td, 'foo.py')
482 with open(path, 'w') as f:
478 with open(path, 'w') as f:
483 f.write('\n'.join([
479 f.write('\n'.join([
484 "def foo():",
480 "def foo():",
485 " return bar()",
481 " return bar()",
486 "def bar():",
482 "def bar():",
487 " raise RuntimeError('hello!')",
483 " raise RuntimeError('hello!')",
488 "foo()",
484 "foo()",
489 ]))
485 ]))
490 with capture_output() as io:
486 with capture_output() as io:
491 _ip.magic('run {}'.format(path))
487 _ip.magic('run {}'.format(path))
492 out = io.stdout
488 out = io.stdout
493 nt.assert_not_in("execfile", out)
489 nt.assert_not_in("execfile", out)
494 nt.assert_in("RuntimeError", out)
490 nt.assert_in("RuntimeError", out)
495 nt.assert_equal(out.count("---->"), 3)
491 nt.assert_equal(out.count("---->"), 3)
496
492
497 @dec.knownfailureif(sys.platform == 'win32', "writes to io.stdout aren't captured on Windows")
493 @dec.knownfailureif(sys.platform == 'win32', "writes to io.stdout aren't captured on Windows")
498 def test_script_tb():
494 def test_script_tb():
499 """Test traceback offset in `ipython script.py`"""
495 """Test traceback offset in `ipython script.py`"""
500 with TemporaryDirectory() as td:
496 with TemporaryDirectory() as td:
501 path = pjoin(td, 'foo.py')
497 path = pjoin(td, 'foo.py')
502 with open(path, 'w') as f:
498 with open(path, 'w') as f:
503 f.write('\n'.join([
499 f.write('\n'.join([
504 "def foo():",
500 "def foo():",
505 " return bar()",
501 " return bar()",
506 "def bar():",
502 "def bar():",
507 " raise RuntimeError('hello!')",
503 " raise RuntimeError('hello!')",
508 "foo()",
504 "foo()",
509 ]))
505 ]))
510 out, err = tt.ipexec(path)
506 out, err = tt.ipexec(path)
511 nt.assert_not_in("execfile", out)
507 nt.assert_not_in("execfile", out)
512 nt.assert_in("RuntimeError", out)
508 nt.assert_in("RuntimeError", out)
513 nt.assert_equal(out.count("---->"), 3)
509 nt.assert_equal(out.count("---->"), 3)
514
510
@@ -1,355 +1,351 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Tests for IPython.core.ultratb
2 """Tests for IPython.core.ultratb
3 """
3 """
4 import io
4 import io
5 import sys
5 import sys
6 import os.path
6 import os.path
7 from textwrap import dedent
7 from textwrap import dedent
8 import traceback
8 import traceback
9 import unittest
9 import unittest
10
10 from unittest import mock
11 try:
12 from unittest import mock
13 except ImportError:
14 import mock # Python 2
15
11
16 from ..ultratb import ColorTB, VerboseTB, find_recursion
12 from ..ultratb import ColorTB, VerboseTB, find_recursion
17
13
18
14
19 from IPython.testing import tools as tt
15 from IPython.testing import tools as tt
20 from IPython.testing.decorators import onlyif_unicode_paths
16 from IPython.testing.decorators import onlyif_unicode_paths
21 from IPython.utils.syspathcontext import prepended_to_syspath
17 from IPython.utils.syspathcontext import prepended_to_syspath
22 from IPython.utils.tempdir import TemporaryDirectory
18 from IPython.utils.tempdir import TemporaryDirectory
23 from IPython.utils.py3compat import PY3
19 from IPython.utils.py3compat import PY3
24
20
25 ip = get_ipython()
21 ip = get_ipython()
26
22
27 file_1 = """1
23 file_1 = """1
28 2
24 2
29 3
25 3
30 def f():
26 def f():
31 1/0
27 1/0
32 """
28 """
33
29
34 file_2 = """def f():
30 file_2 = """def f():
35 1/0
31 1/0
36 """
32 """
37
33
38 class ChangedPyFileTest(unittest.TestCase):
34 class ChangedPyFileTest(unittest.TestCase):
39 def test_changing_py_file(self):
35 def test_changing_py_file(self):
40 """Traceback produced if the line where the error occurred is missing?
36 """Traceback produced if the line where the error occurred is missing?
41
37
42 https://github.com/ipython/ipython/issues/1456
38 https://github.com/ipython/ipython/issues/1456
43 """
39 """
44 with TemporaryDirectory() as td:
40 with TemporaryDirectory() as td:
45 fname = os.path.join(td, "foo.py")
41 fname = os.path.join(td, "foo.py")
46 with open(fname, "w") as f:
42 with open(fname, "w") as f:
47 f.write(file_1)
43 f.write(file_1)
48
44
49 with prepended_to_syspath(td):
45 with prepended_to_syspath(td):
50 ip.run_cell("import foo")
46 ip.run_cell("import foo")
51
47
52 with tt.AssertPrints("ZeroDivisionError"):
48 with tt.AssertPrints("ZeroDivisionError"):
53 ip.run_cell("foo.f()")
49 ip.run_cell("foo.f()")
54
50
55 # Make the file shorter, so the line of the error is missing.
51 # Make the file shorter, so the line of the error is missing.
56 with open(fname, "w") as f:
52 with open(fname, "w") as f:
57 f.write(file_2)
53 f.write(file_2)
58
54
59 # For some reason, this was failing on the *second* call after
55 # For some reason, this was failing on the *second* call after
60 # changing the file, so we call f() twice.
56 # changing the file, so we call f() twice.
61 with tt.AssertNotPrints("Internal Python error", channel='stderr'):
57 with tt.AssertNotPrints("Internal Python error", channel='stderr'):
62 with tt.AssertPrints("ZeroDivisionError"):
58 with tt.AssertPrints("ZeroDivisionError"):
63 ip.run_cell("foo.f()")
59 ip.run_cell("foo.f()")
64 with tt.AssertPrints("ZeroDivisionError"):
60 with tt.AssertPrints("ZeroDivisionError"):
65 ip.run_cell("foo.f()")
61 ip.run_cell("foo.f()")
66
62
67 iso_8859_5_file = u'''# coding: iso-8859-5
63 iso_8859_5_file = u'''# coding: iso-8859-5
68
64
69 def fail():
65 def fail():
70 """Π΄Π±Π˜Π–"""
66 """Π΄Π±Π˜Π–"""
71 1/0 # Π΄Π±Π˜Π–
67 1/0 # Π΄Π±Π˜Π–
72 '''
68 '''
73
69
74 class NonAsciiTest(unittest.TestCase):
70 class NonAsciiTest(unittest.TestCase):
75 @onlyif_unicode_paths
71 @onlyif_unicode_paths
76 def test_nonascii_path(self):
72 def test_nonascii_path(self):
77 # Non-ascii directory name as well.
73 # Non-ascii directory name as well.
78 with TemporaryDirectory(suffix=u'Γ©') as td:
74 with TemporaryDirectory(suffix=u'Γ©') as td:
79 fname = os.path.join(td, u"fooΓ©.py")
75 fname = os.path.join(td, u"fooΓ©.py")
80 with open(fname, "w") as f:
76 with open(fname, "w") as f:
81 f.write(file_1)
77 f.write(file_1)
82
78
83 with prepended_to_syspath(td):
79 with prepended_to_syspath(td):
84 ip.run_cell("import foo")
80 ip.run_cell("import foo")
85
81
86 with tt.AssertPrints("ZeroDivisionError"):
82 with tt.AssertPrints("ZeroDivisionError"):
87 ip.run_cell("foo.f()")
83 ip.run_cell("foo.f()")
88
84
89 def test_iso8859_5(self):
85 def test_iso8859_5(self):
90 with TemporaryDirectory() as td:
86 with TemporaryDirectory() as td:
91 fname = os.path.join(td, 'dfghjkl.py')
87 fname = os.path.join(td, 'dfghjkl.py')
92
88
93 with io.open(fname, 'w', encoding='iso-8859-5') as f:
89 with io.open(fname, 'w', encoding='iso-8859-5') as f:
94 f.write(iso_8859_5_file)
90 f.write(iso_8859_5_file)
95
91
96 with prepended_to_syspath(td):
92 with prepended_to_syspath(td):
97 ip.run_cell("from dfghjkl import fail")
93 ip.run_cell("from dfghjkl import fail")
98
94
99 with tt.AssertPrints("ZeroDivisionError"):
95 with tt.AssertPrints("ZeroDivisionError"):
100 with tt.AssertPrints(u'Π΄Π±Π˜Π–', suppress=False):
96 with tt.AssertPrints(u'Π΄Π±Π˜Π–', suppress=False):
101 ip.run_cell('fail()')
97 ip.run_cell('fail()')
102
98
103 def test_nonascii_msg(self):
99 def test_nonascii_msg(self):
104 cell = u"raise Exception('Γ©')"
100 cell = u"raise Exception('Γ©')"
105 expected = u"Exception('Γ©')"
101 expected = u"Exception('Γ©')"
106 ip.run_cell("%xmode plain")
102 ip.run_cell("%xmode plain")
107 with tt.AssertPrints(expected):
103 with tt.AssertPrints(expected):
108 ip.run_cell(cell)
104 ip.run_cell(cell)
109
105
110 ip.run_cell("%xmode verbose")
106 ip.run_cell("%xmode verbose")
111 with tt.AssertPrints(expected):
107 with tt.AssertPrints(expected):
112 ip.run_cell(cell)
108 ip.run_cell(cell)
113
109
114 ip.run_cell("%xmode context")
110 ip.run_cell("%xmode context")
115 with tt.AssertPrints(expected):
111 with tt.AssertPrints(expected):
116 ip.run_cell(cell)
112 ip.run_cell(cell)
117
113
118
114
119 class NestedGenExprTestCase(unittest.TestCase):
115 class NestedGenExprTestCase(unittest.TestCase):
120 """
116 """
121 Regression test for the following issues:
117 Regression test for the following issues:
122 https://github.com/ipython/ipython/issues/8293
118 https://github.com/ipython/ipython/issues/8293
123 https://github.com/ipython/ipython/issues/8205
119 https://github.com/ipython/ipython/issues/8205
124 """
120 """
125 def test_nested_genexpr(self):
121 def test_nested_genexpr(self):
126 code = dedent(
122 code = dedent(
127 """\
123 """\
128 class SpecificException(Exception):
124 class SpecificException(Exception):
129 pass
125 pass
130
126
131 def foo(x):
127 def foo(x):
132 raise SpecificException("Success!")
128 raise SpecificException("Success!")
133
129
134 sum(sum(foo(x) for _ in [0]) for x in [0])
130 sum(sum(foo(x) for _ in [0]) for x in [0])
135 """
131 """
136 )
132 )
137 with tt.AssertPrints('SpecificException: Success!', suppress=False):
133 with tt.AssertPrints('SpecificException: Success!', suppress=False):
138 ip.run_cell(code)
134 ip.run_cell(code)
139
135
140
136
141 indentationerror_file = """if True:
137 indentationerror_file = """if True:
142 zoon()
138 zoon()
143 """
139 """
144
140
145 class IndentationErrorTest(unittest.TestCase):
141 class IndentationErrorTest(unittest.TestCase):
146 def test_indentationerror_shows_line(self):
142 def test_indentationerror_shows_line(self):
147 # See issue gh-2398
143 # See issue gh-2398
148 with tt.AssertPrints("IndentationError"):
144 with tt.AssertPrints("IndentationError"):
149 with tt.AssertPrints("zoon()", suppress=False):
145 with tt.AssertPrints("zoon()", suppress=False):
150 ip.run_cell(indentationerror_file)
146 ip.run_cell(indentationerror_file)
151
147
152 with TemporaryDirectory() as td:
148 with TemporaryDirectory() as td:
153 fname = os.path.join(td, "foo.py")
149 fname = os.path.join(td, "foo.py")
154 with open(fname, "w") as f:
150 with open(fname, "w") as f:
155 f.write(indentationerror_file)
151 f.write(indentationerror_file)
156
152
157 with tt.AssertPrints("IndentationError"):
153 with tt.AssertPrints("IndentationError"):
158 with tt.AssertPrints("zoon()", suppress=False):
154 with tt.AssertPrints("zoon()", suppress=False):
159 ip.magic('run %s' % fname)
155 ip.magic('run %s' % fname)
160
156
161 se_file_1 = """1
157 se_file_1 = """1
162 2
158 2
163 7/
159 7/
164 """
160 """
165
161
166 se_file_2 = """7/
162 se_file_2 = """7/
167 """
163 """
168
164
169 class SyntaxErrorTest(unittest.TestCase):
165 class SyntaxErrorTest(unittest.TestCase):
170 def test_syntaxerror_without_lineno(self):
166 def test_syntaxerror_without_lineno(self):
171 with tt.AssertNotPrints("TypeError"):
167 with tt.AssertNotPrints("TypeError"):
172 with tt.AssertPrints("line unknown"):
168 with tt.AssertPrints("line unknown"):
173 ip.run_cell("raise SyntaxError()")
169 ip.run_cell("raise SyntaxError()")
174
170
175 def test_changing_py_file(self):
171 def test_changing_py_file(self):
176 with TemporaryDirectory() as td:
172 with TemporaryDirectory() as td:
177 fname = os.path.join(td, "foo.py")
173 fname = os.path.join(td, "foo.py")
178 with open(fname, 'w') as f:
174 with open(fname, 'w') as f:
179 f.write(se_file_1)
175 f.write(se_file_1)
180
176
181 with tt.AssertPrints(["7/", "SyntaxError"]):
177 with tt.AssertPrints(["7/", "SyntaxError"]):
182 ip.magic("run " + fname)
178 ip.magic("run " + fname)
183
179
184 # Modify the file
180 # Modify the file
185 with open(fname, 'w') as f:
181 with open(fname, 'w') as f:
186 f.write(se_file_2)
182 f.write(se_file_2)
187
183
188 # The SyntaxError should point to the correct line
184 # The SyntaxError should point to the correct line
189 with tt.AssertPrints(["7/", "SyntaxError"]):
185 with tt.AssertPrints(["7/", "SyntaxError"]):
190 ip.magic("run " + fname)
186 ip.magic("run " + fname)
191
187
192 def test_non_syntaxerror(self):
188 def test_non_syntaxerror(self):
193 # SyntaxTB may be called with an error other than a SyntaxError
189 # SyntaxTB may be called with an error other than a SyntaxError
194 # See e.g. gh-4361
190 # See e.g. gh-4361
195 try:
191 try:
196 raise ValueError('QWERTY')
192 raise ValueError('QWERTY')
197 except ValueError:
193 except ValueError:
198 with tt.AssertPrints('QWERTY'):
194 with tt.AssertPrints('QWERTY'):
199 ip.showsyntaxerror()
195 ip.showsyntaxerror()
200
196
201
197
202 class Python3ChainedExceptionsTest(unittest.TestCase):
198 class Python3ChainedExceptionsTest(unittest.TestCase):
203 DIRECT_CAUSE_ERROR_CODE = """
199 DIRECT_CAUSE_ERROR_CODE = """
204 try:
200 try:
205 x = 1 + 2
201 x = 1 + 2
206 print(not_defined_here)
202 print(not_defined_here)
207 except Exception as e:
203 except Exception as e:
208 x += 55
204 x += 55
209 x - 1
205 x - 1
210 y = {}
206 y = {}
211 raise KeyError('uh') from e
207 raise KeyError('uh') from e
212 """
208 """
213
209
214 EXCEPTION_DURING_HANDLING_CODE = """
210 EXCEPTION_DURING_HANDLING_CODE = """
215 try:
211 try:
216 x = 1 + 2
212 x = 1 + 2
217 print(not_defined_here)
213 print(not_defined_here)
218 except Exception as e:
214 except Exception as e:
219 x += 55
215 x += 55
220 x - 1
216 x - 1
221 y = {}
217 y = {}
222 raise KeyError('uh')
218 raise KeyError('uh')
223 """
219 """
224
220
225 SUPPRESS_CHAINING_CODE = """
221 SUPPRESS_CHAINING_CODE = """
226 try:
222 try:
227 1/0
223 1/0
228 except Exception:
224 except Exception:
229 raise ValueError("Yikes") from None
225 raise ValueError("Yikes") from None
230 """
226 """
231
227
232 def test_direct_cause_error(self):
228 def test_direct_cause_error(self):
233 if PY3:
229 if PY3:
234 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
230 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
235 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
231 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
236
232
237 def test_exception_during_handling_error(self):
233 def test_exception_during_handling_error(self):
238 if PY3:
234 if PY3:
239 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
235 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
240 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
236 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
241
237
242 def test_suppress_exception_chaining(self):
238 def test_suppress_exception_chaining(self):
243 if PY3:
239 if PY3:
244 with tt.AssertNotPrints("ZeroDivisionError"), \
240 with tt.AssertNotPrints("ZeroDivisionError"), \
245 tt.AssertPrints("ValueError", suppress=False):
241 tt.AssertPrints("ValueError", suppress=False):
246 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
242 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
247
243
248
244
249 class RecursionTest(unittest.TestCase):
245 class RecursionTest(unittest.TestCase):
250 DEFINITIONS = """
246 DEFINITIONS = """
251 def non_recurs():
247 def non_recurs():
252 1/0
248 1/0
253
249
254 def r1():
250 def r1():
255 r1()
251 r1()
256
252
257 def r3a():
253 def r3a():
258 r3b()
254 r3b()
259
255
260 def r3b():
256 def r3b():
261 r3c()
257 r3c()
262
258
263 def r3c():
259 def r3c():
264 r3a()
260 r3a()
265
261
266 def r3o1():
262 def r3o1():
267 r3a()
263 r3a()
268
264
269 def r3o2():
265 def r3o2():
270 r3o1()
266 r3o1()
271 """
267 """
272 def setUp(self):
268 def setUp(self):
273 ip.run_cell(self.DEFINITIONS)
269 ip.run_cell(self.DEFINITIONS)
274
270
275 def test_no_recursion(self):
271 def test_no_recursion(self):
276 with tt.AssertNotPrints("frames repeated"):
272 with tt.AssertNotPrints("frames repeated"):
277 ip.run_cell("non_recurs()")
273 ip.run_cell("non_recurs()")
278
274
279 def test_recursion_one_frame(self):
275 def test_recursion_one_frame(self):
280 with tt.AssertPrints("1 frames repeated"):
276 with tt.AssertPrints("1 frames repeated"):
281 ip.run_cell("r1()")
277 ip.run_cell("r1()")
282
278
283 def test_recursion_three_frames(self):
279 def test_recursion_three_frames(self):
284 with tt.AssertPrints("3 frames repeated"):
280 with tt.AssertPrints("3 frames repeated"):
285 ip.run_cell("r3o2()")
281 ip.run_cell("r3o2()")
286
282
287 def test_find_recursion(self):
283 def test_find_recursion(self):
288 captured = []
284 captured = []
289 def capture_exc(*args, **kwargs):
285 def capture_exc(*args, **kwargs):
290 captured.append(sys.exc_info())
286 captured.append(sys.exc_info())
291 with mock.patch.object(ip, 'showtraceback', capture_exc):
287 with mock.patch.object(ip, 'showtraceback', capture_exc):
292 ip.run_cell("r3o2()")
288 ip.run_cell("r3o2()")
293
289
294 self.assertEqual(len(captured), 1)
290 self.assertEqual(len(captured), 1)
295 etype, evalue, tb = captured[0]
291 etype, evalue, tb = captured[0]
296 self.assertIn("recursion", str(evalue))
292 self.assertIn("recursion", str(evalue))
297
293
298 records = ip.InteractiveTB.get_records(tb, 3, ip.InteractiveTB.tb_offset)
294 records = ip.InteractiveTB.get_records(tb, 3, ip.InteractiveTB.tb_offset)
299 for r in records[:10]:
295 for r in records[:10]:
300 print(r[1:4])
296 print(r[1:4])
301
297
302 # The outermost frames should be:
298 # The outermost frames should be:
303 # 0: the 'cell' that was running when the exception came up
299 # 0: the 'cell' that was running when the exception came up
304 # 1: r3o2()
300 # 1: r3o2()
305 # 2: r3o1()
301 # 2: r3o1()
306 # 3: r3a()
302 # 3: r3a()
307 # Then repeating r3b, r3c, r3a
303 # Then repeating r3b, r3c, r3a
308 last_unique, repeat_length = find_recursion(etype, evalue, records)
304 last_unique, repeat_length = find_recursion(etype, evalue, records)
309 self.assertEqual(last_unique, 2)
305 self.assertEqual(last_unique, 2)
310 self.assertEqual(repeat_length, 3)
306 self.assertEqual(repeat_length, 3)
311
307
312
308
313 #----------------------------------------------------------------------------
309 #----------------------------------------------------------------------------
314
310
315 # module testing (minimal)
311 # module testing (minimal)
316 def test_handlers():
312 def test_handlers():
317 def spam(c, d_e):
313 def spam(c, d_e):
318 (d, e) = d_e
314 (d, e) = d_e
319 x = c + d
315 x = c + d
320 y = c * d
316 y = c * d
321 foo(x, y)
317 foo(x, y)
322
318
323 def foo(a, b, bar=1):
319 def foo(a, b, bar=1):
324 eggs(a, b + bar)
320 eggs(a, b + bar)
325
321
326 def eggs(f, g, z=globals()):
322 def eggs(f, g, z=globals()):
327 h = f + g
323 h = f + g
328 i = f - g
324 i = f - g
329 return h / i
325 return h / i
330
326
331 buff = io.StringIO()
327 buff = io.StringIO()
332
328
333 buff.write('')
329 buff.write('')
334 buff.write('*** Before ***')
330 buff.write('*** Before ***')
335 try:
331 try:
336 buff.write(spam(1, (2, 3)))
332 buff.write(spam(1, (2, 3)))
337 except:
333 except:
338 traceback.print_exc(file=buff)
334 traceback.print_exc(file=buff)
339
335
340 handler = ColorTB(ostream=buff)
336 handler = ColorTB(ostream=buff)
341 buff.write('*** ColorTB ***')
337 buff.write('*** ColorTB ***')
342 try:
338 try:
343 buff.write(spam(1, (2, 3)))
339 buff.write(spam(1, (2, 3)))
344 except:
340 except:
345 handler(*sys.exc_info())
341 handler(*sys.exc_info())
346 buff.write('')
342 buff.write('')
347
343
348 handler = VerboseTB(ostream=buff)
344 handler = VerboseTB(ostream=buff)
349 buff.write('*** VerboseTB ***')
345 buff.write('*** VerboseTB ***')
350 try:
346 try:
351 buff.write(spam(1, (2, 3)))
347 buff.write(spam(1, (2, 3)))
352 except:
348 except:
353 handler(*sys.exc_info())
349 handler(*sys.exc_info())
354 buff.write('')
350 buff.write('')
355
351
@@ -1,205 +1,201 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tools for handling LaTeX."""
2 """Tools for handling LaTeX."""
3
3
4 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7 from io import BytesIO, open
7 from io import BytesIO, open
8 import os
8 import os
9 import tempfile
9 import tempfile
10 import shutil
10 import shutil
11 import subprocess
11 import subprocess
12 from base64 import encodebytes
12
13
13 from IPython.utils.process import find_cmd, FindCmdError
14 from IPython.utils.process import find_cmd, FindCmdError
14 from traitlets.config import get_config
15 from traitlets.config import get_config
15 from traitlets.config.configurable import SingletonConfigurable
16 from traitlets.config.configurable import SingletonConfigurable
16 from traitlets import List, Bool, Unicode
17 from traitlets import List, Bool, Unicode
17 from IPython.utils.py3compat import cast_unicode, cast_unicode_py2 as u, PY3
18 from IPython.utils.py3compat import cast_unicode, cast_unicode_py2 as u, PY3
18
19
19 try: # Py3
20 from base64 import encodebytes
21 except ImportError: # Py2
22 from base64 import encodestring as encodebytes
23
24
20
25 class LaTeXTool(SingletonConfigurable):
21 class LaTeXTool(SingletonConfigurable):
26 """An object to store configuration of the LaTeX tool."""
22 """An object to store configuration of the LaTeX tool."""
27 def _config_default(self):
23 def _config_default(self):
28 return get_config()
24 return get_config()
29
25
30 backends = List(
26 backends = List(
31 Unicode(), ["matplotlib", "dvipng"],
27 Unicode(), ["matplotlib", "dvipng"],
32 help="Preferred backend to draw LaTeX math equations. "
28 help="Preferred backend to draw LaTeX math equations. "
33 "Backends in the list are checked one by one and the first "
29 "Backends in the list are checked one by one and the first "
34 "usable one is used. Note that `matplotlib` backend "
30 "usable one is used. Note that `matplotlib` backend "
35 "is usable only for inline style equations. To draw "
31 "is usable only for inline style equations. To draw "
36 "display style equations, `dvipng` backend must be specified. ",
32 "display style equations, `dvipng` backend must be specified. ",
37 # It is a List instead of Enum, to make configuration more
33 # It is a List instead of Enum, to make configuration more
38 # flexible. For example, to use matplotlib mainly but dvipng
34 # flexible. For example, to use matplotlib mainly but dvipng
39 # for display style, the default ["matplotlib", "dvipng"] can
35 # for display style, the default ["matplotlib", "dvipng"] can
40 # be used. To NOT use dvipng so that other repr such as
36 # be used. To NOT use dvipng so that other repr such as
41 # unicode pretty printing is used, you can use ["matplotlib"].
37 # unicode pretty printing is used, you can use ["matplotlib"].
42 ).tag(config=True)
38 ).tag(config=True)
43
39
44 use_breqn = Bool(
40 use_breqn = Bool(
45 True,
41 True,
46 help="Use breqn.sty to automatically break long equations. "
42 help="Use breqn.sty to automatically break long equations. "
47 "This configuration takes effect only for dvipng backend.",
43 "This configuration takes effect only for dvipng backend.",
48 ).tag(config=True)
44 ).tag(config=True)
49
45
50 packages = List(
46 packages = List(
51 ['amsmath', 'amsthm', 'amssymb', 'bm'],
47 ['amsmath', 'amsthm', 'amssymb', 'bm'],
52 help="A list of packages to use for dvipng backend. "
48 help="A list of packages to use for dvipng backend. "
53 "'breqn' will be automatically appended when use_breqn=True.",
49 "'breqn' will be automatically appended when use_breqn=True.",
54 ).tag(config=True)
50 ).tag(config=True)
55
51
56 preamble = Unicode(
52 preamble = Unicode(
57 help="Additional preamble to use when generating LaTeX source "
53 help="Additional preamble to use when generating LaTeX source "
58 "for dvipng backend.",
54 "for dvipng backend.",
59 ).tag(config=True)
55 ).tag(config=True)
60
56
61
57
62 def latex_to_png(s, encode=False, backend=None, wrap=False):
58 def latex_to_png(s, encode=False, backend=None, wrap=False):
63 """Render a LaTeX string to PNG.
59 """Render a LaTeX string to PNG.
64
60
65 Parameters
61 Parameters
66 ----------
62 ----------
67 s : str
63 s : str
68 The raw string containing valid inline LaTeX.
64 The raw string containing valid inline LaTeX.
69 encode : bool, optional
65 encode : bool, optional
70 Should the PNG data base64 encoded to make it JSON'able.
66 Should the PNG data base64 encoded to make it JSON'able.
71 backend : {matplotlib, dvipng}
67 backend : {matplotlib, dvipng}
72 Backend for producing PNG data.
68 Backend for producing PNG data.
73 wrap : bool
69 wrap : bool
74 If true, Automatically wrap `s` as a LaTeX equation.
70 If true, Automatically wrap `s` as a LaTeX equation.
75
71
76 None is returned when the backend cannot be used.
72 None is returned when the backend cannot be used.
77
73
78 """
74 """
79 s = cast_unicode(s)
75 s = cast_unicode(s)
80 allowed_backends = LaTeXTool.instance().backends
76 allowed_backends = LaTeXTool.instance().backends
81 if backend is None:
77 if backend is None:
82 backend = allowed_backends[0]
78 backend = allowed_backends[0]
83 if backend not in allowed_backends:
79 if backend not in allowed_backends:
84 return None
80 return None
85 if backend == 'matplotlib':
81 if backend == 'matplotlib':
86 f = latex_to_png_mpl
82 f = latex_to_png_mpl
87 elif backend == 'dvipng':
83 elif backend == 'dvipng':
88 f = latex_to_png_dvipng
84 f = latex_to_png_dvipng
89 else:
85 else:
90 raise ValueError('No such backend {0}'.format(backend))
86 raise ValueError('No such backend {0}'.format(backend))
91 bin_data = f(s, wrap)
87 bin_data = f(s, wrap)
92 if encode and bin_data:
88 if encode and bin_data:
93 bin_data = encodebytes(bin_data)
89 bin_data = encodebytes(bin_data)
94 return bin_data
90 return bin_data
95
91
96
92
97 def latex_to_png_mpl(s, wrap):
93 def latex_to_png_mpl(s, wrap):
98 try:
94 try:
99 from matplotlib import mathtext
95 from matplotlib import mathtext
100 from pyparsing import ParseFatalException
96 from pyparsing import ParseFatalException
101 except ImportError:
97 except ImportError:
102 return None
98 return None
103
99
104 # mpl mathtext doesn't support display math, force inline
100 # mpl mathtext doesn't support display math, force inline
105 s = s.replace('$$', '$')
101 s = s.replace('$$', '$')
106 if wrap:
102 if wrap:
107 s = u'${0}$'.format(s)
103 s = u'${0}$'.format(s)
108
104
109 try:
105 try:
110 mt = mathtext.MathTextParser('bitmap')
106 mt = mathtext.MathTextParser('bitmap')
111 f = BytesIO()
107 f = BytesIO()
112 mt.to_png(f, s, fontsize=12)
108 mt.to_png(f, s, fontsize=12)
113 return f.getvalue()
109 return f.getvalue()
114 except (ValueError, RuntimeError, ParseFatalException):
110 except (ValueError, RuntimeError, ParseFatalException):
115 return None
111 return None
116
112
117
113
118 def latex_to_png_dvipng(s, wrap):
114 def latex_to_png_dvipng(s, wrap):
119 try:
115 try:
120 find_cmd('latex')
116 find_cmd('latex')
121 find_cmd('dvipng')
117 find_cmd('dvipng')
122 except FindCmdError:
118 except FindCmdError:
123 return None
119 return None
124 try:
120 try:
125 workdir = tempfile.mkdtemp()
121 workdir = tempfile.mkdtemp()
126 tmpfile = os.path.join(workdir, "tmp.tex")
122 tmpfile = os.path.join(workdir, "tmp.tex")
127 dvifile = os.path.join(workdir, "tmp.dvi")
123 dvifile = os.path.join(workdir, "tmp.dvi")
128 outfile = os.path.join(workdir, "tmp.png")
124 outfile = os.path.join(workdir, "tmp.png")
129
125
130 with open(tmpfile, "w", encoding='utf8') as f:
126 with open(tmpfile, "w", encoding='utf8') as f:
131 f.writelines(genelatex(s, wrap))
127 f.writelines(genelatex(s, wrap))
132
128
133 with open(os.devnull, 'wb') as devnull:
129 with open(os.devnull, 'wb') as devnull:
134 subprocess.check_call(
130 subprocess.check_call(
135 ["latex", "-halt-on-error", "-interaction", "batchmode", tmpfile],
131 ["latex", "-halt-on-error", "-interaction", "batchmode", tmpfile],
136 cwd=workdir, stdout=devnull, stderr=devnull)
132 cwd=workdir, stdout=devnull, stderr=devnull)
137
133
138 subprocess.check_call(
134 subprocess.check_call(
139 ["dvipng", "-T", "tight", "-x", "1500", "-z", "9",
135 ["dvipng", "-T", "tight", "-x", "1500", "-z", "9",
140 "-bg", "transparent", "-o", outfile, dvifile], cwd=workdir,
136 "-bg", "transparent", "-o", outfile, dvifile], cwd=workdir,
141 stdout=devnull, stderr=devnull)
137 stdout=devnull, stderr=devnull)
142
138
143 with open(outfile, "rb") as f:
139 with open(outfile, "rb") as f:
144 return f.read()
140 return f.read()
145 except subprocess.CalledProcessError:
141 except subprocess.CalledProcessError:
146 return None
142 return None
147 finally:
143 finally:
148 shutil.rmtree(workdir)
144 shutil.rmtree(workdir)
149
145
150
146
151 def kpsewhich(filename):
147 def kpsewhich(filename):
152 """Invoke kpsewhich command with an argument `filename`."""
148 """Invoke kpsewhich command with an argument `filename`."""
153 try:
149 try:
154 find_cmd("kpsewhich")
150 find_cmd("kpsewhich")
155 proc = subprocess.Popen(
151 proc = subprocess.Popen(
156 ["kpsewhich", filename],
152 ["kpsewhich", filename],
157 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
153 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
158 (stdout, stderr) = proc.communicate()
154 (stdout, stderr) = proc.communicate()
159 return stdout.strip().decode('utf8', 'replace')
155 return stdout.strip().decode('utf8', 'replace')
160 except FindCmdError:
156 except FindCmdError:
161 pass
157 pass
162
158
163
159
164 def genelatex(body, wrap):
160 def genelatex(body, wrap):
165 """Generate LaTeX document for dvipng backend."""
161 """Generate LaTeX document for dvipng backend."""
166 lt = LaTeXTool.instance()
162 lt = LaTeXTool.instance()
167 breqn = wrap and lt.use_breqn and kpsewhich("breqn.sty")
163 breqn = wrap and lt.use_breqn and kpsewhich("breqn.sty")
168 yield u(r'\documentclass{article}')
164 yield u(r'\documentclass{article}')
169 packages = lt.packages
165 packages = lt.packages
170 if breqn:
166 if breqn:
171 packages = packages + ['breqn']
167 packages = packages + ['breqn']
172 for pack in packages:
168 for pack in packages:
173 yield u(r'\usepackage{{{0}}}'.format(pack))
169 yield u(r'\usepackage{{{0}}}'.format(pack))
174 yield u(r'\pagestyle{empty}')
170 yield u(r'\pagestyle{empty}')
175 if lt.preamble:
171 if lt.preamble:
176 yield lt.preamble
172 yield lt.preamble
177 yield u(r'\begin{document}')
173 yield u(r'\begin{document}')
178 if breqn:
174 if breqn:
179 yield u(r'\begin{dmath*}')
175 yield u(r'\begin{dmath*}')
180 yield body
176 yield body
181 yield u(r'\end{dmath*}')
177 yield u(r'\end{dmath*}')
182 elif wrap:
178 elif wrap:
183 yield u'$${0}$$'.format(body)
179 yield u'$${0}$$'.format(body)
184 else:
180 else:
185 yield body
181 yield body
186 yield u'\end{document}'
182 yield u'\end{document}'
187
183
188
184
189 _data_uri_template_png = u"""<img src="data:image/png;base64,%s" alt=%s />"""
185 _data_uri_template_png = u"""<img src="data:image/png;base64,%s" alt=%s />"""
190
186
191 def latex_to_html(s, alt='image'):
187 def latex_to_html(s, alt='image'):
192 """Render LaTeX to HTML with embedded PNG data using data URIs.
188 """Render LaTeX to HTML with embedded PNG data using data URIs.
193
189
194 Parameters
190 Parameters
195 ----------
191 ----------
196 s : str
192 s : str
197 The raw string containing valid inline LateX.
193 The raw string containing valid inline LateX.
198 alt : str
194 alt : str
199 The alt text to use for the HTML.
195 The alt text to use for the HTML.
200 """
196 """
201 base64_data = latex_to_png(s, encode=True).decode('ascii')
197 base64_data = latex_to_png(s, encode=True).decode('ascii')
202 if base64_data:
198 if base64_data:
203 return _data_uri_template_png % (base64_data, alt)
199 return _data_uri_template_png % (base64_data, alt)
204
200
205
201
@@ -1,139 +1,134 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Tests for IPython.utils.path.py"""
2 """Tests for IPython.utils.path.py"""
3
3
4 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6 from unittest.mock import patch
7 try:
8 from unittest.mock import patch
9 except ImportError:
10 from mock import patch
11
12 import nose.tools as nt
7 import nose.tools as nt
13
8
14 from IPython.lib import latextools
9 from IPython.lib import latextools
15 from IPython.testing.decorators import onlyif_cmds_exist, skipif_not_matplotlib
10 from IPython.testing.decorators import onlyif_cmds_exist, skipif_not_matplotlib
16 from IPython.utils.process import FindCmdError
11 from IPython.utils.process import FindCmdError
17
12
18
13
19 def test_latex_to_png_dvipng_fails_when_no_cmd():
14 def test_latex_to_png_dvipng_fails_when_no_cmd():
20 """
15 """
21 `latex_to_png_dvipng` should return None when there is no required command
16 `latex_to_png_dvipng` should return None when there is no required command
22 """
17 """
23 for command in ['latex', 'dvipng']:
18 for command in ['latex', 'dvipng']:
24 yield (check_latex_to_png_dvipng_fails_when_no_cmd, command)
19 yield (check_latex_to_png_dvipng_fails_when_no_cmd, command)
25
20
26
21
27 def check_latex_to_png_dvipng_fails_when_no_cmd(command):
22 def check_latex_to_png_dvipng_fails_when_no_cmd(command):
28 def mock_find_cmd(arg):
23 def mock_find_cmd(arg):
29 if arg == command:
24 if arg == command:
30 raise FindCmdError
25 raise FindCmdError
31
26
32 with patch.object(latextools, "find_cmd", mock_find_cmd):
27 with patch.object(latextools, "find_cmd", mock_find_cmd):
33 nt.assert_equal(latextools.latex_to_png_dvipng("whatever", True),
28 nt.assert_equal(latextools.latex_to_png_dvipng("whatever", True),
34 None)
29 None)
35
30
36
31
37 @onlyif_cmds_exist('latex', 'dvipng')
32 @onlyif_cmds_exist('latex', 'dvipng')
38 def test_latex_to_png_dvipng_runs():
33 def test_latex_to_png_dvipng_runs():
39 """
34 """
40 Test that latex_to_png_dvipng just runs without error.
35 Test that latex_to_png_dvipng just runs without error.
41 """
36 """
42 def mock_kpsewhich(filename):
37 def mock_kpsewhich(filename):
43 nt.assert_equal(filename, "breqn.sty")
38 nt.assert_equal(filename, "breqn.sty")
44 return None
39 return None
45
40
46 for (s, wrap) in [(u"$$x^2$$", False), (u"x^2", True)]:
41 for (s, wrap) in [(u"$$x^2$$", False), (u"x^2", True)]:
47 yield (latextools.latex_to_png_dvipng, s, wrap)
42 yield (latextools.latex_to_png_dvipng, s, wrap)
48
43
49 with patch.object(latextools, "kpsewhich", mock_kpsewhich):
44 with patch.object(latextools, "kpsewhich", mock_kpsewhich):
50 yield (latextools.latex_to_png_dvipng, s, wrap)
45 yield (latextools.latex_to_png_dvipng, s, wrap)
51
46
52 @skipif_not_matplotlib
47 @skipif_not_matplotlib
53 def test_latex_to_png_mpl_runs():
48 def test_latex_to_png_mpl_runs():
54 """
49 """
55 Test that latex_to_png_mpl just runs without error.
50 Test that latex_to_png_mpl just runs without error.
56 """
51 """
57 def mock_kpsewhich(filename):
52 def mock_kpsewhich(filename):
58 nt.assert_equal(filename, "breqn.sty")
53 nt.assert_equal(filename, "breqn.sty")
59 return None
54 return None
60
55
61 for (s, wrap) in [("$x^2$", False), ("x^2", True)]:
56 for (s, wrap) in [("$x^2$", False), ("x^2", True)]:
62 yield (latextools.latex_to_png_mpl, s, wrap)
57 yield (latextools.latex_to_png_mpl, s, wrap)
63
58
64 with patch.object(latextools, "kpsewhich", mock_kpsewhich):
59 with patch.object(latextools, "kpsewhich", mock_kpsewhich):
65 yield (latextools.latex_to_png_mpl, s, wrap)
60 yield (latextools.latex_to_png_mpl, s, wrap)
66
61
67 @skipif_not_matplotlib
62 @skipif_not_matplotlib
68 def test_latex_to_html():
63 def test_latex_to_html():
69 img = latextools.latex_to_html("$x^2$")
64 img = latextools.latex_to_html("$x^2$")
70 nt.assert_in("data:image/png;base64,iVBOR", img)
65 nt.assert_in("data:image/png;base64,iVBOR", img)
71
66
72
67
73 def test_genelatex_no_wrap():
68 def test_genelatex_no_wrap():
74 """
69 """
75 Test genelatex with wrap=False.
70 Test genelatex with wrap=False.
76 """
71 """
77 def mock_kpsewhich(filename):
72 def mock_kpsewhich(filename):
78 assert False, ("kpsewhich should not be called "
73 assert False, ("kpsewhich should not be called "
79 "(called with {0})".format(filename))
74 "(called with {0})".format(filename))
80
75
81 with patch.object(latextools, "kpsewhich", mock_kpsewhich):
76 with patch.object(latextools, "kpsewhich", mock_kpsewhich):
82 nt.assert_equal(
77 nt.assert_equal(
83 '\n'.join(latextools.genelatex("body text", False)),
78 '\n'.join(latextools.genelatex("body text", False)),
84 r'''\documentclass{article}
79 r'''\documentclass{article}
85 \usepackage{amsmath}
80 \usepackage{amsmath}
86 \usepackage{amsthm}
81 \usepackage{amsthm}
87 \usepackage{amssymb}
82 \usepackage{amssymb}
88 \usepackage{bm}
83 \usepackage{bm}
89 \pagestyle{empty}
84 \pagestyle{empty}
90 \begin{document}
85 \begin{document}
91 body text
86 body text
92 \end{document}''')
87 \end{document}''')
93
88
94
89
95 def test_genelatex_wrap_with_breqn():
90 def test_genelatex_wrap_with_breqn():
96 """
91 """
97 Test genelatex with wrap=True for the case breqn.sty is installed.
92 Test genelatex with wrap=True for the case breqn.sty is installed.
98 """
93 """
99 def mock_kpsewhich(filename):
94 def mock_kpsewhich(filename):
100 nt.assert_equal(filename, "breqn.sty")
95 nt.assert_equal(filename, "breqn.sty")
101 return "path/to/breqn.sty"
96 return "path/to/breqn.sty"
102
97
103 with patch.object(latextools, "kpsewhich", mock_kpsewhich):
98 with patch.object(latextools, "kpsewhich", mock_kpsewhich):
104 nt.assert_equal(
99 nt.assert_equal(
105 '\n'.join(latextools.genelatex("x^2", True)),
100 '\n'.join(latextools.genelatex("x^2", True)),
106 r'''\documentclass{article}
101 r'''\documentclass{article}
107 \usepackage{amsmath}
102 \usepackage{amsmath}
108 \usepackage{amsthm}
103 \usepackage{amsthm}
109 \usepackage{amssymb}
104 \usepackage{amssymb}
110 \usepackage{bm}
105 \usepackage{bm}
111 \usepackage{breqn}
106 \usepackage{breqn}
112 \pagestyle{empty}
107 \pagestyle{empty}
113 \begin{document}
108 \begin{document}
114 \begin{dmath*}
109 \begin{dmath*}
115 x^2
110 x^2
116 \end{dmath*}
111 \end{dmath*}
117 \end{document}''')
112 \end{document}''')
118
113
119
114
120 def test_genelatex_wrap_without_breqn():
115 def test_genelatex_wrap_without_breqn():
121 """
116 """
122 Test genelatex with wrap=True for the case breqn.sty is not installed.
117 Test genelatex with wrap=True for the case breqn.sty is not installed.
123 """
118 """
124 def mock_kpsewhich(filename):
119 def mock_kpsewhich(filename):
125 nt.assert_equal(filename, "breqn.sty")
120 nt.assert_equal(filename, "breqn.sty")
126 return None
121 return None
127
122
128 with patch.object(latextools, "kpsewhich", mock_kpsewhich):
123 with patch.object(latextools, "kpsewhich", mock_kpsewhich):
129 nt.assert_equal(
124 nt.assert_equal(
130 '\n'.join(latextools.genelatex("x^2", True)),
125 '\n'.join(latextools.genelatex("x^2", True)),
131 r'''\documentclass{article}
126 r'''\documentclass{article}
132 \usepackage{amsmath}
127 \usepackage{amsmath}
133 \usepackage{amsthm}
128 \usepackage{amsthm}
134 \usepackage{amssymb}
129 \usepackage{amssymb}
135 \usepackage{bm}
130 \usepackage{bm}
136 \pagestyle{empty}
131 \pagestyle{empty}
137 \begin{document}
132 \begin{document}
138 $$x^2$$
133 $$x^2$$
139 \end{document}''')
134 \end{document}''')
@@ -1,496 +1,489 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Tests for IPython.utils.path.py"""
2 """Tests for IPython.utils.path.py"""
3
3
4 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7 import errno
7 import errno
8 import os
8 import os
9 import shutil
9 import shutil
10 import sys
10 import sys
11 import tempfile
11 import tempfile
12 import warnings
12 import warnings
13 from contextlib import contextmanager
13 from contextlib import contextmanager
14
14 from unittest.mock import patch
15 try: # Python 3.3+
16 from unittest.mock import patch
17 except ImportError:
18 from mock import patch
19
20 from os.path import join, abspath, split
15 from os.path import join, abspath, split
21
16
22 from nose import SkipTest
17 from nose import SkipTest, with_setup
23 import nose.tools as nt
18 import nose.tools as nt
24
19
25 from nose import with_setup
26
27 import IPython
20 import IPython
28 from IPython import paths
21 from IPython import paths
29 from IPython.testing import decorators as dec
22 from IPython.testing import decorators as dec
30 from IPython.testing.decorators import (skip_if_not_win32, skip_win32,
23 from IPython.testing.decorators import (skip_if_not_win32, skip_win32,
31 onlyif_unicode_paths,)
24 onlyif_unicode_paths,)
32 from IPython.testing.tools import make_tempfile, AssertPrints
25 from IPython.testing.tools import make_tempfile, AssertPrints
33 from IPython.utils import path
26 from IPython.utils import path
34 from IPython.utils import py3compat
27 from IPython.utils import py3compat
35 from IPython.utils.tempdir import TemporaryDirectory
28 from IPython.utils.tempdir import TemporaryDirectory
36
29
37 # Platform-dependent imports
30 # Platform-dependent imports
38 try:
31 try:
39 import winreg as wreg # Py 3
32 import winreg as wreg # Py 3
40 except ImportError:
33 except ImportError:
41 try:
34 try:
42 import _winreg as wreg # Py 2
35 import _winreg as wreg # Py 2
43 except ImportError:
36 except ImportError:
44 #Fake _winreg module on none windows platforms
37 #Fake _winreg module on none windows platforms
45 import types
38 import types
46 wr_name = "winreg" if py3compat.PY3 else "_winreg"
39 wr_name = "winreg" if py3compat.PY3 else "_winreg"
47 sys.modules[wr_name] = types.ModuleType(wr_name)
40 sys.modules[wr_name] = types.ModuleType(wr_name)
48 try:
41 try:
49 import winreg as wreg
42 import winreg as wreg
50 except ImportError:
43 except ImportError:
51 import _winreg as wreg
44 import _winreg as wreg
52 #Add entries that needs to be stubbed by the testing code
45 #Add entries that needs to be stubbed by the testing code
53 (wreg.OpenKey, wreg.QueryValueEx,) = (None, None)
46 (wreg.OpenKey, wreg.QueryValueEx,) = (None, None)
54
47
55 try:
48 try:
56 reload
49 reload
57 except NameError: # Python 3
50 except NameError: # Python 3
58 from imp import reload
51 from imp import reload
59
52
60 #-----------------------------------------------------------------------------
53 #-----------------------------------------------------------------------------
61 # Globals
54 # Globals
62 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
63 env = os.environ
56 env = os.environ
64 TMP_TEST_DIR = tempfile.mkdtemp()
57 TMP_TEST_DIR = tempfile.mkdtemp()
65 HOME_TEST_DIR = join(TMP_TEST_DIR, "home_test_dir")
58 HOME_TEST_DIR = join(TMP_TEST_DIR, "home_test_dir")
66 #
59 #
67 # Setup/teardown functions/decorators
60 # Setup/teardown functions/decorators
68 #
61 #
69
62
70 def setup():
63 def setup():
71 """Setup testenvironment for the module:
64 """Setup testenvironment for the module:
72
65
73 - Adds dummy home dir tree
66 - Adds dummy home dir tree
74 """
67 """
75 # Do not mask exceptions here. In particular, catching WindowsError is a
68 # Do not mask exceptions here. In particular, catching WindowsError is a
76 # problem because that exception is only defined on Windows...
69 # problem because that exception is only defined on Windows...
77 os.makedirs(os.path.join(HOME_TEST_DIR, 'ipython'))
70 os.makedirs(os.path.join(HOME_TEST_DIR, 'ipython'))
78
71
79
72
80 def teardown():
73 def teardown():
81 """Teardown testenvironment for the module:
74 """Teardown testenvironment for the module:
82
75
83 - Remove dummy home dir tree
76 - Remove dummy home dir tree
84 """
77 """
85 # Note: we remove the parent test dir, which is the root of all test
78 # Note: we remove the parent test dir, which is the root of all test
86 # subdirs we may have created. Use shutil instead of os.removedirs, so
79 # subdirs we may have created. Use shutil instead of os.removedirs, so
87 # that non-empty directories are all recursively removed.
80 # that non-empty directories are all recursively removed.
88 shutil.rmtree(TMP_TEST_DIR)
81 shutil.rmtree(TMP_TEST_DIR)
89
82
90
83
91 def setup_environment():
84 def setup_environment():
92 """Setup testenvironment for some functions that are tested
85 """Setup testenvironment for some functions that are tested
93 in this module. In particular this functions stores attributes
86 in this module. In particular this functions stores attributes
94 and other things that we need to stub in some test functions.
87 and other things that we need to stub in some test functions.
95 This needs to be done on a function level and not module level because
88 This needs to be done on a function level and not module level because
96 each testfunction needs a pristine environment.
89 each testfunction needs a pristine environment.
97 """
90 """
98 global oldstuff, platformstuff
91 global oldstuff, platformstuff
99 oldstuff = (env.copy(), os.name, sys.platform, path.get_home_dir, IPython.__file__, os.getcwd())
92 oldstuff = (env.copy(), os.name, sys.platform, path.get_home_dir, IPython.__file__, os.getcwd())
100
93
101 def teardown_environment():
94 def teardown_environment():
102 """Restore things that were remembered by the setup_environment function
95 """Restore things that were remembered by the setup_environment function
103 """
96 """
104 (oldenv, os.name, sys.platform, path.get_home_dir, IPython.__file__, old_wd) = oldstuff
97 (oldenv, os.name, sys.platform, path.get_home_dir, IPython.__file__, old_wd) = oldstuff
105 os.chdir(old_wd)
98 os.chdir(old_wd)
106 reload(path)
99 reload(path)
107
100
108 for key in list(env):
101 for key in list(env):
109 if key not in oldenv:
102 if key not in oldenv:
110 del env[key]
103 del env[key]
111 env.update(oldenv)
104 env.update(oldenv)
112 if hasattr(sys, 'frozen'):
105 if hasattr(sys, 'frozen'):
113 del sys.frozen
106 del sys.frozen
114
107
115 # Build decorator that uses the setup_environment/setup_environment
108 # Build decorator that uses the setup_environment/setup_environment
116 with_environment = with_setup(setup_environment, teardown_environment)
109 with_environment = with_setup(setup_environment, teardown_environment)
117
110
118 @skip_if_not_win32
111 @skip_if_not_win32
119 @with_environment
112 @with_environment
120 def test_get_home_dir_1():
113 def test_get_home_dir_1():
121 """Testcase for py2exe logic, un-compressed lib
114 """Testcase for py2exe logic, un-compressed lib
122 """
115 """
123 unfrozen = path.get_home_dir()
116 unfrozen = path.get_home_dir()
124 sys.frozen = True
117 sys.frozen = True
125
118
126 #fake filename for IPython.__init__
119 #fake filename for IPython.__init__
127 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Lib/IPython/__init__.py"))
120 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Lib/IPython/__init__.py"))
128
121
129 home_dir = path.get_home_dir()
122 home_dir = path.get_home_dir()
130 nt.assert_equal(home_dir, unfrozen)
123 nt.assert_equal(home_dir, unfrozen)
131
124
132
125
133 @skip_if_not_win32
126 @skip_if_not_win32
134 @with_environment
127 @with_environment
135 def test_get_home_dir_2():
128 def test_get_home_dir_2():
136 """Testcase for py2exe logic, compressed lib
129 """Testcase for py2exe logic, compressed lib
137 """
130 """
138 unfrozen = path.get_home_dir()
131 unfrozen = path.get_home_dir()
139 sys.frozen = True
132 sys.frozen = True
140 #fake filename for IPython.__init__
133 #fake filename for IPython.__init__
141 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Library.zip/IPython/__init__.py")).lower()
134 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Library.zip/IPython/__init__.py")).lower()
142
135
143 home_dir = path.get_home_dir(True)
136 home_dir = path.get_home_dir(True)
144 nt.assert_equal(home_dir, unfrozen)
137 nt.assert_equal(home_dir, unfrozen)
145
138
146
139
147 @with_environment
140 @with_environment
148 def test_get_home_dir_3():
141 def test_get_home_dir_3():
149 """get_home_dir() uses $HOME if set"""
142 """get_home_dir() uses $HOME if set"""
150 env["HOME"] = HOME_TEST_DIR
143 env["HOME"] = HOME_TEST_DIR
151 home_dir = path.get_home_dir(True)
144 home_dir = path.get_home_dir(True)
152 # get_home_dir expands symlinks
145 # get_home_dir expands symlinks
153 nt.assert_equal(home_dir, os.path.realpath(env["HOME"]))
146 nt.assert_equal(home_dir, os.path.realpath(env["HOME"]))
154
147
155
148
156 @with_environment
149 @with_environment
157 def test_get_home_dir_4():
150 def test_get_home_dir_4():
158 """get_home_dir() still works if $HOME is not set"""
151 """get_home_dir() still works if $HOME is not set"""
159
152
160 if 'HOME' in env: del env['HOME']
153 if 'HOME' in env: del env['HOME']
161 # this should still succeed, but we don't care what the answer is
154 # this should still succeed, but we don't care what the answer is
162 home = path.get_home_dir(False)
155 home = path.get_home_dir(False)
163
156
164 @with_environment
157 @with_environment
165 def test_get_home_dir_5():
158 def test_get_home_dir_5():
166 """raise HomeDirError if $HOME is specified, but not a writable dir"""
159 """raise HomeDirError if $HOME is specified, but not a writable dir"""
167 env['HOME'] = abspath(HOME_TEST_DIR+'garbage')
160 env['HOME'] = abspath(HOME_TEST_DIR+'garbage')
168 # set os.name = posix, to prevent My Documents fallback on Windows
161 # set os.name = posix, to prevent My Documents fallback on Windows
169 os.name = 'posix'
162 os.name = 'posix'
170 nt.assert_raises(path.HomeDirError, path.get_home_dir, True)
163 nt.assert_raises(path.HomeDirError, path.get_home_dir, True)
171
164
172 # Should we stub wreg fully so we can run the test on all platforms?
165 # Should we stub wreg fully so we can run the test on all platforms?
173 @skip_if_not_win32
166 @skip_if_not_win32
174 @with_environment
167 @with_environment
175 def test_get_home_dir_8():
168 def test_get_home_dir_8():
176 """Using registry hack for 'My Documents', os=='nt'
169 """Using registry hack for 'My Documents', os=='nt'
177
170
178 HOMESHARE, HOMEDRIVE, HOMEPATH, USERPROFILE and others are missing.
171 HOMESHARE, HOMEDRIVE, HOMEPATH, USERPROFILE and others are missing.
179 """
172 """
180 os.name = 'nt'
173 os.name = 'nt'
181 # Remove from stub environment all keys that may be set
174 # Remove from stub environment all keys that may be set
182 for key in ['HOME', 'HOMESHARE', 'HOMEDRIVE', 'HOMEPATH', 'USERPROFILE']:
175 for key in ['HOME', 'HOMESHARE', 'HOMEDRIVE', 'HOMEPATH', 'USERPROFILE']:
183 env.pop(key, None)
176 env.pop(key, None)
184
177
185 class key:
178 class key:
186 def Close(self):
179 def Close(self):
187 pass
180 pass
188
181
189 with patch.object(wreg, 'OpenKey', return_value=key()), \
182 with patch.object(wreg, 'OpenKey', return_value=key()), \
190 patch.object(wreg, 'QueryValueEx', return_value=[abspath(HOME_TEST_DIR)]):
183 patch.object(wreg, 'QueryValueEx', return_value=[abspath(HOME_TEST_DIR)]):
191 home_dir = path.get_home_dir()
184 home_dir = path.get_home_dir()
192 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
185 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
193
186
194 @with_environment
187 @with_environment
195 def test_get_xdg_dir_0():
188 def test_get_xdg_dir_0():
196 """test_get_xdg_dir_0, check xdg_dir"""
189 """test_get_xdg_dir_0, check xdg_dir"""
197 reload(path)
190 reload(path)
198 path._writable_dir = lambda path: True
191 path._writable_dir = lambda path: True
199 path.get_home_dir = lambda : 'somewhere'
192 path.get_home_dir = lambda : 'somewhere'
200 os.name = "posix"
193 os.name = "posix"
201 sys.platform = "linux2"
194 sys.platform = "linux2"
202 env.pop('IPYTHON_DIR', None)
195 env.pop('IPYTHON_DIR', None)
203 env.pop('IPYTHONDIR', None)
196 env.pop('IPYTHONDIR', None)
204 env.pop('XDG_CONFIG_HOME', None)
197 env.pop('XDG_CONFIG_HOME', None)
205
198
206 nt.assert_equal(path.get_xdg_dir(), os.path.join('somewhere', '.config'))
199 nt.assert_equal(path.get_xdg_dir(), os.path.join('somewhere', '.config'))
207
200
208
201
209 @with_environment
202 @with_environment
210 def test_get_xdg_dir_1():
203 def test_get_xdg_dir_1():
211 """test_get_xdg_dir_1, check nonexistant xdg_dir"""
204 """test_get_xdg_dir_1, check nonexistant xdg_dir"""
212 reload(path)
205 reload(path)
213 path.get_home_dir = lambda : HOME_TEST_DIR
206 path.get_home_dir = lambda : HOME_TEST_DIR
214 os.name = "posix"
207 os.name = "posix"
215 sys.platform = "linux2"
208 sys.platform = "linux2"
216 env.pop('IPYTHON_DIR', None)
209 env.pop('IPYTHON_DIR', None)
217 env.pop('IPYTHONDIR', None)
210 env.pop('IPYTHONDIR', None)
218 env.pop('XDG_CONFIG_HOME', None)
211 env.pop('XDG_CONFIG_HOME', None)
219 nt.assert_equal(path.get_xdg_dir(), None)
212 nt.assert_equal(path.get_xdg_dir(), None)
220
213
221 @with_environment
214 @with_environment
222 def test_get_xdg_dir_2():
215 def test_get_xdg_dir_2():
223 """test_get_xdg_dir_2, check xdg_dir default to ~/.config"""
216 """test_get_xdg_dir_2, check xdg_dir default to ~/.config"""
224 reload(path)
217 reload(path)
225 path.get_home_dir = lambda : HOME_TEST_DIR
218 path.get_home_dir = lambda : HOME_TEST_DIR
226 os.name = "posix"
219 os.name = "posix"
227 sys.platform = "linux2"
220 sys.platform = "linux2"
228 env.pop('IPYTHON_DIR', None)
221 env.pop('IPYTHON_DIR', None)
229 env.pop('IPYTHONDIR', None)
222 env.pop('IPYTHONDIR', None)
230 env.pop('XDG_CONFIG_HOME', None)
223 env.pop('XDG_CONFIG_HOME', None)
231 cfgdir=os.path.join(path.get_home_dir(), '.config')
224 cfgdir=os.path.join(path.get_home_dir(), '.config')
232 if not os.path.exists(cfgdir):
225 if not os.path.exists(cfgdir):
233 os.makedirs(cfgdir)
226 os.makedirs(cfgdir)
234
227
235 nt.assert_equal(path.get_xdg_dir(), cfgdir)
228 nt.assert_equal(path.get_xdg_dir(), cfgdir)
236
229
237 @with_environment
230 @with_environment
238 def test_get_xdg_dir_3():
231 def test_get_xdg_dir_3():
239 """test_get_xdg_dir_3, check xdg_dir not used on OS X"""
232 """test_get_xdg_dir_3, check xdg_dir not used on OS X"""
240 reload(path)
233 reload(path)
241 path.get_home_dir = lambda : HOME_TEST_DIR
234 path.get_home_dir = lambda : HOME_TEST_DIR
242 os.name = "posix"
235 os.name = "posix"
243 sys.platform = "darwin"
236 sys.platform = "darwin"
244 env.pop('IPYTHON_DIR', None)
237 env.pop('IPYTHON_DIR', None)
245 env.pop('IPYTHONDIR', None)
238 env.pop('IPYTHONDIR', None)
246 env.pop('XDG_CONFIG_HOME', None)
239 env.pop('XDG_CONFIG_HOME', None)
247 cfgdir=os.path.join(path.get_home_dir(), '.config')
240 cfgdir=os.path.join(path.get_home_dir(), '.config')
248 if not os.path.exists(cfgdir):
241 if not os.path.exists(cfgdir):
249 os.makedirs(cfgdir)
242 os.makedirs(cfgdir)
250
243
251 nt.assert_equal(path.get_xdg_dir(), None)
244 nt.assert_equal(path.get_xdg_dir(), None)
252
245
253 def test_filefind():
246 def test_filefind():
254 """Various tests for filefind"""
247 """Various tests for filefind"""
255 f = tempfile.NamedTemporaryFile()
248 f = tempfile.NamedTemporaryFile()
256 # print 'fname:',f.name
249 # print 'fname:',f.name
257 alt_dirs = paths.get_ipython_dir()
250 alt_dirs = paths.get_ipython_dir()
258 t = path.filefind(f.name, alt_dirs)
251 t = path.filefind(f.name, alt_dirs)
259 # print 'found:',t
252 # print 'found:',t
260
253
261
254
262 @dec.skip_if_not_win32
255 @dec.skip_if_not_win32
263 def test_get_long_path_name_win32():
256 def test_get_long_path_name_win32():
264 with TemporaryDirectory() as tmpdir:
257 with TemporaryDirectory() as tmpdir:
265
258
266 # Make a long path. Expands the path of tmpdir prematurely as it may already have a long
259 # Make a long path. Expands the path of tmpdir prematurely as it may already have a long
267 # path component, so ensure we include the long form of it
260 # path component, so ensure we include the long form of it
268 long_path = os.path.join(path.get_long_path_name(tmpdir), u'this is my long path name')
261 long_path = os.path.join(path.get_long_path_name(tmpdir), u'this is my long path name')
269 os.makedirs(long_path)
262 os.makedirs(long_path)
270
263
271 # Test to see if the short path evaluates correctly.
264 # Test to see if the short path evaluates correctly.
272 short_path = os.path.join(tmpdir, u'THISIS~1')
265 short_path = os.path.join(tmpdir, u'THISIS~1')
273 evaluated_path = path.get_long_path_name(short_path)
266 evaluated_path = path.get_long_path_name(short_path)
274 nt.assert_equal(evaluated_path.lower(), long_path.lower())
267 nt.assert_equal(evaluated_path.lower(), long_path.lower())
275
268
276
269
277 @dec.skip_win32
270 @dec.skip_win32
278 def test_get_long_path_name():
271 def test_get_long_path_name():
279 p = path.get_long_path_name('/usr/local')
272 p = path.get_long_path_name('/usr/local')
280 nt.assert_equal(p,'/usr/local')
273 nt.assert_equal(p,'/usr/local')
281
274
282 @dec.skip_win32 # can't create not-user-writable dir on win
275 @dec.skip_win32 # can't create not-user-writable dir on win
283 @with_environment
276 @with_environment
284 def test_not_writable_ipdir():
277 def test_not_writable_ipdir():
285 tmpdir = tempfile.mkdtemp()
278 tmpdir = tempfile.mkdtemp()
286 os.name = "posix"
279 os.name = "posix"
287 env.pop('IPYTHON_DIR', None)
280 env.pop('IPYTHON_DIR', None)
288 env.pop('IPYTHONDIR', None)
281 env.pop('IPYTHONDIR', None)
289 env.pop('XDG_CONFIG_HOME', None)
282 env.pop('XDG_CONFIG_HOME', None)
290 env['HOME'] = tmpdir
283 env['HOME'] = tmpdir
291 ipdir = os.path.join(tmpdir, '.ipython')
284 ipdir = os.path.join(tmpdir, '.ipython')
292 os.mkdir(ipdir, 0o555)
285 os.mkdir(ipdir, 0o555)
293 try:
286 try:
294 open(os.path.join(ipdir, "_foo_"), 'w').close()
287 open(os.path.join(ipdir, "_foo_"), 'w').close()
295 except IOError:
288 except IOError:
296 pass
289 pass
297 else:
290 else:
298 # I can still write to an unwritable dir,
291 # I can still write to an unwritable dir,
299 # assume I'm root and skip the test
292 # assume I'm root and skip the test
300 raise SkipTest("I can't create directories that I can't write to")
293 raise SkipTest("I can't create directories that I can't write to")
301 with AssertPrints('is not a writable location', channel='stderr'):
294 with AssertPrints('is not a writable location', channel='stderr'):
302 ipdir = paths.get_ipython_dir()
295 ipdir = paths.get_ipython_dir()
303 env.pop('IPYTHON_DIR', None)
296 env.pop('IPYTHON_DIR', None)
304
297
305 @with_environment
298 @with_environment
306 def test_get_py_filename():
299 def test_get_py_filename():
307 os.chdir(TMP_TEST_DIR)
300 os.chdir(TMP_TEST_DIR)
308 with make_tempfile('foo.py'):
301 with make_tempfile('foo.py'):
309 nt.assert_equal(path.get_py_filename('foo.py'), 'foo.py')
302 nt.assert_equal(path.get_py_filename('foo.py'), 'foo.py')
310 nt.assert_equal(path.get_py_filename('foo'), 'foo.py')
303 nt.assert_equal(path.get_py_filename('foo'), 'foo.py')
311 with make_tempfile('foo'):
304 with make_tempfile('foo'):
312 nt.assert_equal(path.get_py_filename('foo'), 'foo')
305 nt.assert_equal(path.get_py_filename('foo'), 'foo')
313 nt.assert_raises(IOError, path.get_py_filename, 'foo.py')
306 nt.assert_raises(IOError, path.get_py_filename, 'foo.py')
314 nt.assert_raises(IOError, path.get_py_filename, 'foo')
307 nt.assert_raises(IOError, path.get_py_filename, 'foo')
315 nt.assert_raises(IOError, path.get_py_filename, 'foo.py')
308 nt.assert_raises(IOError, path.get_py_filename, 'foo.py')
316 true_fn = 'foo with spaces.py'
309 true_fn = 'foo with spaces.py'
317 with make_tempfile(true_fn):
310 with make_tempfile(true_fn):
318 nt.assert_equal(path.get_py_filename('foo with spaces'), true_fn)
311 nt.assert_equal(path.get_py_filename('foo with spaces'), true_fn)
319 nt.assert_equal(path.get_py_filename('foo with spaces.py'), true_fn)
312 nt.assert_equal(path.get_py_filename('foo with spaces.py'), true_fn)
320 nt.assert_raises(IOError, path.get_py_filename, '"foo with spaces.py"')
313 nt.assert_raises(IOError, path.get_py_filename, '"foo with spaces.py"')
321 nt.assert_raises(IOError, path.get_py_filename, "'foo with spaces.py'")
314 nt.assert_raises(IOError, path.get_py_filename, "'foo with spaces.py'")
322
315
323 @onlyif_unicode_paths
316 @onlyif_unicode_paths
324 def test_unicode_in_filename():
317 def test_unicode_in_filename():
325 """When a file doesn't exist, the exception raised should be safe to call
318 """When a file doesn't exist, the exception raised should be safe to call
326 str() on - i.e. in Python 2 it must only have ASCII characters.
319 str() on - i.e. in Python 2 it must only have ASCII characters.
327
320
328 https://github.com/ipython/ipython/issues/875
321 https://github.com/ipython/ipython/issues/875
329 """
322 """
330 try:
323 try:
331 # these calls should not throw unicode encode exceptions
324 # these calls should not throw unicode encode exceptions
332 path.get_py_filename(u'fooéè.py', force_win32=False)
325 path.get_py_filename(u'fooéè.py', force_win32=False)
333 except IOError as ex:
326 except IOError as ex:
334 str(ex)
327 str(ex)
335
328
336
329
337 class TestShellGlob(object):
330 class TestShellGlob(object):
338
331
339 @classmethod
332 @classmethod
340 def setUpClass(cls):
333 def setUpClass(cls):
341 cls.filenames_start_with_a = ['a0', 'a1', 'a2']
334 cls.filenames_start_with_a = ['a0', 'a1', 'a2']
342 cls.filenames_end_with_b = ['0b', '1b', '2b']
335 cls.filenames_end_with_b = ['0b', '1b', '2b']
343 cls.filenames = cls.filenames_start_with_a + cls.filenames_end_with_b
336 cls.filenames = cls.filenames_start_with_a + cls.filenames_end_with_b
344 cls.tempdir = TemporaryDirectory()
337 cls.tempdir = TemporaryDirectory()
345 td = cls.tempdir.name
338 td = cls.tempdir.name
346
339
347 with cls.in_tempdir():
340 with cls.in_tempdir():
348 # Create empty files
341 # Create empty files
349 for fname in cls.filenames:
342 for fname in cls.filenames:
350 open(os.path.join(td, fname), 'w').close()
343 open(os.path.join(td, fname), 'w').close()
351
344
352 @classmethod
345 @classmethod
353 def tearDownClass(cls):
346 def tearDownClass(cls):
354 cls.tempdir.cleanup()
347 cls.tempdir.cleanup()
355
348
356 @classmethod
349 @classmethod
357 @contextmanager
350 @contextmanager
358 def in_tempdir(cls):
351 def in_tempdir(cls):
359 save = os.getcwd()
352 save = os.getcwd()
360 try:
353 try:
361 os.chdir(cls.tempdir.name)
354 os.chdir(cls.tempdir.name)
362 yield
355 yield
363 finally:
356 finally:
364 os.chdir(save)
357 os.chdir(save)
365
358
366 def check_match(self, patterns, matches):
359 def check_match(self, patterns, matches):
367 with self.in_tempdir():
360 with self.in_tempdir():
368 # glob returns unordered list. that's why sorted is required.
361 # glob returns unordered list. that's why sorted is required.
369 nt.assert_equal(sorted(path.shellglob(patterns)),
362 nt.assert_equal(sorted(path.shellglob(patterns)),
370 sorted(matches))
363 sorted(matches))
371
364
372 def common_cases(self):
365 def common_cases(self):
373 return [
366 return [
374 (['*'], self.filenames),
367 (['*'], self.filenames),
375 (['a*'], self.filenames_start_with_a),
368 (['a*'], self.filenames_start_with_a),
376 (['*c'], ['*c']),
369 (['*c'], ['*c']),
377 (['*', 'a*', '*b', '*c'], self.filenames
370 (['*', 'a*', '*b', '*c'], self.filenames
378 + self.filenames_start_with_a
371 + self.filenames_start_with_a
379 + self.filenames_end_with_b
372 + self.filenames_end_with_b
380 + ['*c']),
373 + ['*c']),
381 (['a[012]'], self.filenames_start_with_a),
374 (['a[012]'], self.filenames_start_with_a),
382 ]
375 ]
383
376
384 @skip_win32
377 @skip_win32
385 def test_match_posix(self):
378 def test_match_posix(self):
386 for (patterns, matches) in self.common_cases() + [
379 for (patterns, matches) in self.common_cases() + [
387 ([r'\*'], ['*']),
380 ([r'\*'], ['*']),
388 ([r'a\*', 'a*'], ['a*'] + self.filenames_start_with_a),
381 ([r'a\*', 'a*'], ['a*'] + self.filenames_start_with_a),
389 ([r'a\[012]'], ['a[012]']),
382 ([r'a\[012]'], ['a[012]']),
390 ]:
383 ]:
391 yield (self.check_match, patterns, matches)
384 yield (self.check_match, patterns, matches)
392
385
393 @skip_if_not_win32
386 @skip_if_not_win32
394 def test_match_windows(self):
387 def test_match_windows(self):
395 for (patterns, matches) in self.common_cases() + [
388 for (patterns, matches) in self.common_cases() + [
396 # In windows, backslash is interpreted as path
389 # In windows, backslash is interpreted as path
397 # separator. Therefore, you can't escape glob
390 # separator. Therefore, you can't escape glob
398 # using it.
391 # using it.
399 ([r'a\*', 'a*'], [r'a\*'] + self.filenames_start_with_a),
392 ([r'a\*', 'a*'], [r'a\*'] + self.filenames_start_with_a),
400 ([r'a\[012]'], [r'a\[012]']),
393 ([r'a\[012]'], [r'a\[012]']),
401 ]:
394 ]:
402 yield (self.check_match, patterns, matches)
395 yield (self.check_match, patterns, matches)
403
396
404
397
405 def test_unescape_glob():
398 def test_unescape_glob():
406 nt.assert_equal(path.unescape_glob(r'\*\[\!\]\?'), '*[!]?')
399 nt.assert_equal(path.unescape_glob(r'\*\[\!\]\?'), '*[!]?')
407 nt.assert_equal(path.unescape_glob(r'\\*'), r'\*')
400 nt.assert_equal(path.unescape_glob(r'\\*'), r'\*')
408 nt.assert_equal(path.unescape_glob(r'\\\*'), r'\*')
401 nt.assert_equal(path.unescape_glob(r'\\\*'), r'\*')
409 nt.assert_equal(path.unescape_glob(r'\\a'), r'\a')
402 nt.assert_equal(path.unescape_glob(r'\\a'), r'\a')
410 nt.assert_equal(path.unescape_glob(r'\a'), r'\a')
403 nt.assert_equal(path.unescape_glob(r'\a'), r'\a')
411
404
412
405
413 def test_ensure_dir_exists():
406 def test_ensure_dir_exists():
414 with TemporaryDirectory() as td:
407 with TemporaryDirectory() as td:
415 d = os.path.join(td, u'βˆ‚ir')
408 d = os.path.join(td, u'βˆ‚ir')
416 path.ensure_dir_exists(d) # create it
409 path.ensure_dir_exists(d) # create it
417 assert os.path.isdir(d)
410 assert os.path.isdir(d)
418 path.ensure_dir_exists(d) # no-op
411 path.ensure_dir_exists(d) # no-op
419 f = os.path.join(td, u'Ζ’ile')
412 f = os.path.join(td, u'Ζ’ile')
420 open(f, 'w').close() # touch
413 open(f, 'w').close() # touch
421 with nt.assert_raises(IOError):
414 with nt.assert_raises(IOError):
422 path.ensure_dir_exists(f)
415 path.ensure_dir_exists(f)
423
416
424 class TestLinkOrCopy(object):
417 class TestLinkOrCopy(object):
425 def setUp(self):
418 def setUp(self):
426 self.tempdir = TemporaryDirectory()
419 self.tempdir = TemporaryDirectory()
427 self.src = self.dst("src")
420 self.src = self.dst("src")
428 with open(self.src, "w") as f:
421 with open(self.src, "w") as f:
429 f.write("Hello, world!")
422 f.write("Hello, world!")
430
423
431 def tearDown(self):
424 def tearDown(self):
432 self.tempdir.cleanup()
425 self.tempdir.cleanup()
433
426
434 def dst(self, *args):
427 def dst(self, *args):
435 return os.path.join(self.tempdir.name, *args)
428 return os.path.join(self.tempdir.name, *args)
436
429
437 def assert_inode_not_equal(self, a, b):
430 def assert_inode_not_equal(self, a, b):
438 nt.assert_not_equal(os.stat(a).st_ino, os.stat(b).st_ino,
431 nt.assert_not_equal(os.stat(a).st_ino, os.stat(b).st_ino,
439 "%r and %r do reference the same indoes" %(a, b))
432 "%r and %r do reference the same indoes" %(a, b))
440
433
441 def assert_inode_equal(self, a, b):
434 def assert_inode_equal(self, a, b):
442 nt.assert_equal(os.stat(a).st_ino, os.stat(b).st_ino,
435 nt.assert_equal(os.stat(a).st_ino, os.stat(b).st_ino,
443 "%r and %r do not reference the same indoes" %(a, b))
436 "%r and %r do not reference the same indoes" %(a, b))
444
437
445 def assert_content_equal(self, a, b):
438 def assert_content_equal(self, a, b):
446 with open(a) as a_f:
439 with open(a) as a_f:
447 with open(b) as b_f:
440 with open(b) as b_f:
448 nt.assert_equal(a_f.read(), b_f.read())
441 nt.assert_equal(a_f.read(), b_f.read())
449
442
450 @skip_win32
443 @skip_win32
451 def test_link_successful(self):
444 def test_link_successful(self):
452 dst = self.dst("target")
445 dst = self.dst("target")
453 path.link_or_copy(self.src, dst)
446 path.link_or_copy(self.src, dst)
454 self.assert_inode_equal(self.src, dst)
447 self.assert_inode_equal(self.src, dst)
455
448
456 @skip_win32
449 @skip_win32
457 def test_link_into_dir(self):
450 def test_link_into_dir(self):
458 dst = self.dst("some_dir")
451 dst = self.dst("some_dir")
459 os.mkdir(dst)
452 os.mkdir(dst)
460 path.link_or_copy(self.src, dst)
453 path.link_or_copy(self.src, dst)
461 expected_dst = self.dst("some_dir", os.path.basename(self.src))
454 expected_dst = self.dst("some_dir", os.path.basename(self.src))
462 self.assert_inode_equal(self.src, expected_dst)
455 self.assert_inode_equal(self.src, expected_dst)
463
456
464 @skip_win32
457 @skip_win32
465 def test_target_exists(self):
458 def test_target_exists(self):
466 dst = self.dst("target")
459 dst = self.dst("target")
467 open(dst, "w").close()
460 open(dst, "w").close()
468 path.link_or_copy(self.src, dst)
461 path.link_or_copy(self.src, dst)
469 self.assert_inode_equal(self.src, dst)
462 self.assert_inode_equal(self.src, dst)
470
463
471 @skip_win32
464 @skip_win32
472 def test_no_link(self):
465 def test_no_link(self):
473 real_link = os.link
466 real_link = os.link
474 try:
467 try:
475 del os.link
468 del os.link
476 dst = self.dst("target")
469 dst = self.dst("target")
477 path.link_or_copy(self.src, dst)
470 path.link_or_copy(self.src, dst)
478 self.assert_content_equal(self.src, dst)
471 self.assert_content_equal(self.src, dst)
479 self.assert_inode_not_equal(self.src, dst)
472 self.assert_inode_not_equal(self.src, dst)
480 finally:
473 finally:
481 os.link = real_link
474 os.link = real_link
482
475
483 @skip_if_not_win32
476 @skip_if_not_win32
484 def test_windows(self):
477 def test_windows(self):
485 dst = self.dst("target")
478 dst = self.dst("target")
486 path.link_or_copy(self.src, dst)
479 path.link_or_copy(self.src, dst)
487 self.assert_content_equal(self.src, dst)
480 self.assert_content_equal(self.src, dst)
488
481
489 def test_link_twice(self):
482 def test_link_twice(self):
490 # Linking the same file twice shouldn't leave duplicates around.
483 # Linking the same file twice shouldn't leave duplicates around.
491 # See https://github.com/ipython/ipython/issues/6450
484 # See https://github.com/ipython/ipython/issues/6450
492 dst = self.dst('target')
485 dst = self.dst('target')
493 path.link_or_copy(self.src, dst)
486 path.link_or_copy(self.src, dst)
494 path.link_or_copy(self.src, dst)
487 path.link_or_copy(self.src, dst)
495 self.assert_inode_equal(self.src, dst)
488 self.assert_inode_equal(self.src, dst)
496 nt.assert_equal(sorted(os.listdir(self.tempdir.name)), ['src', 'target'])
489 nt.assert_equal(sorted(os.listdir(self.tempdir.name)), ['src', 'target'])
General Comments 0
You need to be logged in to leave comments. Login now