##// END OF EJS Templates
Tweak tests for PyPy and add CI runner...
Nikita Kniazev -
Show More
@@ -1,60 +1,70 b''
1 name: Run tests
1 name: Run tests
2
2
3 on:
3 on:
4 push:
4 push:
5 pull_request:
5 pull_request:
6 # Run weekly on Monday at 1:23 UTC
6 # Run weekly on Monday at 1:23 UTC
7 schedule:
7 schedule:
8 - cron: '23 1 * * 1'
8 - cron: '23 1 * * 1'
9 workflow_dispatch:
9 workflow_dispatch:
10
10
11
11
12 jobs:
12 jobs:
13 test:
13 test:
14 runs-on: ${{ matrix.os }}
14 runs-on: ${{ matrix.os }}
15 strategy:
15 strategy:
16 matrix:
16 matrix:
17 os: [ubuntu-latest, windows-latest]
17 os: [ubuntu-latest, windows-latest]
18 python-version: ["3.8", "3.9", "3.10"]
18 python-version: ["3.8", "3.9", "3.10"]
19 deps: [test_extra]
19 deps: [test_extra]
20 # Test all on ubuntu, test ends on macos
20 # Test all on ubuntu, test ends on macos
21 include:
21 include:
22 - os: macos-latest
22 - os: macos-latest
23 python-version: "3.8"
23 python-version: "3.8"
24 deps: test_extra
24 deps: test_extra
25 - os: macos-latest
25 - os: macos-latest
26 python-version: "3.10"
26 python-version: "3.10"
27 deps: test_extra
27 deps: test_extra
28 # Tests minimal dependencies set
28 # Tests minimal dependencies set
29 - os: ubuntu-latest
29 - os: ubuntu-latest
30 python-version: "3.10"
30 python-version: "3.10"
31 deps: test
31 deps: test
32 # Tests latest development Python version
32 # Tests latest development Python version
33 - os: ubuntu-latest
33 - os: ubuntu-latest
34 python-version: "3.11-dev"
34 python-version: "3.11-dev"
35 deps: test
35 deps: test
36 # Installing optional dependencies stuff takes ages on PyPy
37 - os: ubuntu-latest
38 python-version: "pypy-3.8"
39 deps: test
40 - os: windows-latest
41 python-version: "pypy-3.8"
42 deps: test
43 - os: macos-latest
44 python-version: "pypy-3.8"
45 deps: test
36
46
37 steps:
47 steps:
38 - uses: actions/checkout@v2
48 - uses: actions/checkout@v2
39 - name: Set up Python ${{ matrix.python-version }}
49 - name: Set up Python ${{ matrix.python-version }}
40 uses: actions/setup-python@v2
50 uses: actions/setup-python@v2
41 with:
51 with:
42 python-version: ${{ matrix.python-version }}
52 python-version: ${{ matrix.python-version }}
43 - name: Install latex
53 - name: Install latex
44 if: runner.os == 'Linux' && matrix.deps == 'test_extra'
54 if: runner.os == 'Linux' && matrix.deps == 'test_extra'
45 run: sudo apt-get -yq -o Acquire::Retries=3 --no-install-suggests --no-install-recommends install texlive dvipng
55 run: sudo apt-get -yq -o Acquire::Retries=3 --no-install-suggests --no-install-recommends install texlive dvipng
46 - name: Install and update Python dependencies
56 - name: Install and update Python dependencies
47 run: |
57 run: |
48 python -m pip install --upgrade pip setuptools wheel
58 python -m pip install --upgrade pip setuptools wheel
49 python -m pip install --upgrade -e .[${{ matrix.deps }}]
59 python -m pip install --upgrade -e .[${{ matrix.deps }}]
50 python -m pip install --upgrade check-manifest pytest-cov
60 python -m pip install --upgrade check-manifest pytest-cov
51 - name: Check manifest
61 - name: Check manifest
52 if: runner.os != 'Windows' # setup.py does not support sdist on Windows
62 if: runner.os != 'Windows' # setup.py does not support sdist on Windows
53 run: check-manifest
63 run: check-manifest
54 - name: pytest
64 - name: pytest
55 env:
65 env:
56 COLUMNS: 120
66 COLUMNS: 120
57 run: |
67 run: |
58 pytest --color=yes -raXxs --cov --cov-report=xml
68 pytest --color=yes -raXxs --cov --cov-report=xml
59 - name: Upload coverage to Codecov
69 - name: Upload coverage to Codecov
60 uses: codecov/codecov-action@v2
70 uses: codecov/codecov-action@v2
@@ -1,315 +1,316 b''
1 """
1 """
2 Test for async helpers.
2 Test for async helpers.
3
3
4 Should only trigger on python 3.5+ or will have syntax errors.
4 Should only trigger on python 3.5+ or will have syntax errors.
5 """
5 """
6 import platform
6 from itertools import chain, repeat
7 from itertools import chain, repeat
7 from textwrap import dedent, indent
8 from textwrap import dedent, indent
8 from unittest import TestCase
9 from unittest import TestCase
9 from IPython.testing.decorators import skip_without
10 from IPython.testing.decorators import skip_without
10 import sys
11 import sys
11 from typing import TYPE_CHECKING
12 from typing import TYPE_CHECKING
12
13
13 if TYPE_CHECKING:
14 if TYPE_CHECKING:
14 from IPython import get_ipython
15 from IPython import get_ipython
15
16
16 ip = get_ipython()
17 ip = get_ipython()
17
18
18
19
19 iprc = lambda x: ip.run_cell(dedent(x)).raise_error()
20 iprc = lambda x: ip.run_cell(dedent(x)).raise_error()
20 iprc_nr = lambda x: ip.run_cell(dedent(x))
21 iprc_nr = lambda x: ip.run_cell(dedent(x))
21
22
22 from IPython.core.async_helpers import _should_be_async
23 from IPython.core.async_helpers import _should_be_async
23
24
24 class AsyncTest(TestCase):
25 class AsyncTest(TestCase):
25 def test_should_be_async(self):
26 def test_should_be_async(self):
26 self.assertFalse(_should_be_async("False"))
27 self.assertFalse(_should_be_async("False"))
27 self.assertTrue(_should_be_async("await bar()"))
28 self.assertTrue(_should_be_async("await bar()"))
28 self.assertTrue(_should_be_async("x = await bar()"))
29 self.assertTrue(_should_be_async("x = await bar()"))
29 self.assertFalse(
30 self.assertFalse(
30 _should_be_async(
31 _should_be_async(
31 dedent(
32 dedent(
32 """
33 """
33 async def awaitable():
34 async def awaitable():
34 pass
35 pass
35 """
36 """
36 )
37 )
37 )
38 )
38 )
39 )
39
40
40 def _get_top_level_cases(self):
41 def _get_top_level_cases(self):
41 # These are test cases that should be valid in a function
42 # These are test cases that should be valid in a function
42 # but invalid outside of a function.
43 # but invalid outside of a function.
43 test_cases = []
44 test_cases = []
44 test_cases.append(('basic', "{val}"))
45 test_cases.append(('basic', "{val}"))
45
46
46 # Note, in all conditional cases, I use True instead of
47 # Note, in all conditional cases, I use True instead of
47 # False so that the peephole optimizer won't optimize away
48 # False so that the peephole optimizer won't optimize away
48 # the return, so CPython will see this as a syntax error:
49 # the return, so CPython will see this as a syntax error:
49 #
50 #
50 # while True:
51 # while True:
51 # break
52 # break
52 # return
53 # return
53 #
54 #
54 # But not this:
55 # But not this:
55 #
56 #
56 # while False:
57 # while False:
57 # return
58 # return
58 #
59 #
59 # See https://bugs.python.org/issue1875
60 # See https://bugs.python.org/issue1875
60
61
61 test_cases.append(('if', dedent("""
62 test_cases.append(('if', dedent("""
62 if True:
63 if True:
63 {val}
64 {val}
64 """)))
65 """)))
65
66
66 test_cases.append(('while', dedent("""
67 test_cases.append(('while', dedent("""
67 while True:
68 while True:
68 {val}
69 {val}
69 break
70 break
70 """)))
71 """)))
71
72
72 test_cases.append(('try', dedent("""
73 test_cases.append(('try', dedent("""
73 try:
74 try:
74 {val}
75 {val}
75 except:
76 except:
76 pass
77 pass
77 """)))
78 """)))
78
79
79 test_cases.append(('except', dedent("""
80 test_cases.append(('except', dedent("""
80 try:
81 try:
81 pass
82 pass
82 except:
83 except:
83 {val}
84 {val}
84 """)))
85 """)))
85
86
86 test_cases.append(('finally', dedent("""
87 test_cases.append(('finally', dedent("""
87 try:
88 try:
88 pass
89 pass
89 except:
90 except:
90 pass
91 pass
91 finally:
92 finally:
92 {val}
93 {val}
93 """)))
94 """)))
94
95
95 test_cases.append(('for', dedent("""
96 test_cases.append(('for', dedent("""
96 for _ in range(4):
97 for _ in range(4):
97 {val}
98 {val}
98 """)))
99 """)))
99
100
100
101
101 test_cases.append(('nested', dedent("""
102 test_cases.append(('nested', dedent("""
102 if True:
103 if True:
103 while True:
104 while True:
104 {val}
105 {val}
105 break
106 break
106 """)))
107 """)))
107
108
108 test_cases.append(('deep-nested', dedent("""
109 test_cases.append(('deep-nested', dedent("""
109 if True:
110 if True:
110 while True:
111 while True:
111 break
112 break
112 for x in range(3):
113 for x in range(3):
113 if True:
114 if True:
114 while True:
115 while True:
115 for x in range(3):
116 for x in range(3):
116 {val}
117 {val}
117 """)))
118 """)))
118
119
119 return test_cases
120 return test_cases
120
121
121 def _get_ry_syntax_errors(self):
122 def _get_ry_syntax_errors(self):
122 # This is a mix of tests that should be a syntax error if
123 # This is a mix of tests that should be a syntax error if
123 # return or yield whether or not they are in a function
124 # return or yield whether or not they are in a function
124
125
125 test_cases = []
126 test_cases = []
126
127
127 test_cases.append(('class', dedent("""
128 test_cases.append(('class', dedent("""
128 class V:
129 class V:
129 {val}
130 {val}
130 """)))
131 """)))
131
132
132 test_cases.append(('nested-class', dedent("""
133 test_cases.append(('nested-class', dedent("""
133 class V:
134 class V:
134 class C:
135 class C:
135 {val}
136 {val}
136 """)))
137 """)))
137
138
138 return test_cases
139 return test_cases
139
140
140
141
141 def test_top_level_return_error(self):
142 def test_top_level_return_error(self):
142 tl_err_test_cases = self._get_top_level_cases()
143 tl_err_test_cases = self._get_top_level_cases()
143 tl_err_test_cases.extend(self._get_ry_syntax_errors())
144 tl_err_test_cases.extend(self._get_ry_syntax_errors())
144
145
145 vals = ('return', 'yield', 'yield from (_ for _ in range(3))',
146 vals = ('return', 'yield', 'yield from (_ for _ in range(3))',
146 dedent('''
147 dedent('''
147 def f():
148 def f():
148 pass
149 pass
149 return
150 return
150 '''),
151 '''),
151 )
152 )
152
153
153 for test_name, test_case in tl_err_test_cases:
154 for test_name, test_case in tl_err_test_cases:
154 # This example should work if 'pass' is used as the value
155 # This example should work if 'pass' is used as the value
155 with self.subTest((test_name, 'pass')):
156 with self.subTest((test_name, 'pass')):
156 iprc(test_case.format(val='pass'))
157 iprc(test_case.format(val='pass'))
157
158
158 # It should fail with all the values
159 # It should fail with all the values
159 for val in vals:
160 for val in vals:
160 with self.subTest((test_name, val)):
161 with self.subTest((test_name, val)):
161 msg = "Syntax error not raised for %s, %s" % (test_name, val)
162 msg = "Syntax error not raised for %s, %s" % (test_name, val)
162 with self.assertRaises(SyntaxError, msg=msg):
163 with self.assertRaises(SyntaxError, msg=msg):
163 iprc(test_case.format(val=val))
164 iprc(test_case.format(val=val))
164
165
165 def test_in_func_no_error(self):
166 def test_in_func_no_error(self):
166 # Test that the implementation of top-level return/yield
167 # Test that the implementation of top-level return/yield
167 # detection isn't *too* aggressive, and works inside a function
168 # detection isn't *too* aggressive, and works inside a function
168 func_contexts = []
169 func_contexts = []
169
170
170 func_contexts.append(('func', False, dedent("""
171 func_contexts.append(('func', False, dedent("""
171 def f():""")))
172 def f():""")))
172
173
173 func_contexts.append(('method', False, dedent("""
174 func_contexts.append(('method', False, dedent("""
174 class MyClass:
175 class MyClass:
175 def __init__(self):
176 def __init__(self):
176 """)))
177 """)))
177
178
178 func_contexts.append(('async-func', True, dedent("""
179 func_contexts.append(('async-func', True, dedent("""
179 async def f():""")))
180 async def f():""")))
180
181
181 func_contexts.append(('async-method', True, dedent("""
182 func_contexts.append(('async-method', True, dedent("""
182 class MyClass:
183 class MyClass:
183 async def f(self):""")))
184 async def f(self):""")))
184
185
185 func_contexts.append(('closure', False, dedent("""
186 func_contexts.append(('closure', False, dedent("""
186 def f():
187 def f():
187 def g():
188 def g():
188 """)))
189 """)))
189
190
190 def nest_case(context, case):
191 def nest_case(context, case):
191 # Detect indentation
192 # Detect indentation
192 lines = context.strip().splitlines()
193 lines = context.strip().splitlines()
193 prefix_len = 0
194 prefix_len = 0
194 for c in lines[-1]:
195 for c in lines[-1]:
195 if c != ' ':
196 if c != ' ':
196 break
197 break
197 prefix_len += 1
198 prefix_len += 1
198
199
199 indented_case = indent(case, ' ' * (prefix_len + 4))
200 indented_case = indent(case, ' ' * (prefix_len + 4))
200 return context + '\n' + indented_case
201 return context + '\n' + indented_case
201
202
202 # Gather and run the tests
203 # Gather and run the tests
203
204
204 # yield is allowed in async functions, starting in Python 3.6,
205 # yield is allowed in async functions, starting in Python 3.6,
205 # and yield from is not allowed in any version
206 # and yield from is not allowed in any version
206 vals = ('return', 'yield', 'yield from (_ for _ in range(3))')
207 vals = ('return', 'yield', 'yield from (_ for _ in range(3))')
207
208
208 success_tests = zip(self._get_top_level_cases(), repeat(False))
209 success_tests = zip(self._get_top_level_cases(), repeat(False))
209 failure_tests = zip(self._get_ry_syntax_errors(), repeat(True))
210 failure_tests = zip(self._get_ry_syntax_errors(), repeat(True))
210
211
211 tests = chain(success_tests, failure_tests)
212 tests = chain(success_tests, failure_tests)
212
213
213 for context_name, async_func, context in func_contexts:
214 for context_name, async_func, context in func_contexts:
214 for (test_name, test_case), should_fail in tests:
215 for (test_name, test_case), should_fail in tests:
215 nested_case = nest_case(context, test_case)
216 nested_case = nest_case(context, test_case)
216
217
217 for val in vals:
218 for val in vals:
218 test_id = (context_name, test_name, val)
219 test_id = (context_name, test_name, val)
219 cell = nested_case.format(val=val)
220 cell = nested_case.format(val=val)
220
221
221 with self.subTest(test_id):
222 with self.subTest(test_id):
222 if should_fail:
223 if should_fail:
223 msg = ("SyntaxError not raised for %s" %
224 msg = ("SyntaxError not raised for %s" %
224 str(test_id))
225 str(test_id))
225 with self.assertRaises(SyntaxError, msg=msg):
226 with self.assertRaises(SyntaxError, msg=msg):
226 iprc(cell)
227 iprc(cell)
227
228
228 print(cell)
229 print(cell)
229 else:
230 else:
230 iprc(cell)
231 iprc(cell)
231
232
232 def test_nonlocal(self):
233 def test_nonlocal(self):
233 # fails if outer scope is not a function scope or if var not defined
234 # fails if outer scope is not a function scope or if var not defined
234 with self.assertRaises(SyntaxError):
235 with self.assertRaises(SyntaxError):
235 iprc("nonlocal x")
236 iprc("nonlocal x")
236 iprc("""
237 iprc("""
237 x = 1
238 x = 1
238 def f():
239 def f():
239 nonlocal x
240 nonlocal x
240 x = 10000
241 x = 10000
241 yield x
242 yield x
242 """)
243 """)
243 iprc("""
244 iprc("""
244 def f():
245 def f():
245 def g():
246 def g():
246 nonlocal x
247 nonlocal x
247 x = 10000
248 x = 10000
248 yield x
249 yield x
249 """)
250 """)
250
251
251 # works if outer scope is a function scope and var exists
252 # works if outer scope is a function scope and var exists
252 iprc("""
253 iprc("""
253 def f():
254 def f():
254 x = 20
255 x = 20
255 def g():
256 def g():
256 nonlocal x
257 nonlocal x
257 x = 10000
258 x = 10000
258 yield x
259 yield x
259 """)
260 """)
260
261
261
262
262 def test_execute(self):
263 def test_execute(self):
263 iprc("""
264 iprc("""
264 import asyncio
265 import asyncio
265 await asyncio.sleep(0.001)
266 await asyncio.sleep(0.001)
266 """
267 """
267 )
268 )
268
269
269 def test_autoawait(self):
270 def test_autoawait(self):
270 iprc("%autoawait False")
271 iprc("%autoawait False")
271 iprc("%autoawait True")
272 iprc("%autoawait True")
272 iprc("""
273 iprc("""
273 from asyncio import sleep
274 from asyncio import sleep
274 await sleep(0.1)
275 await sleep(0.1)
275 """
276 """
276 )
277 )
277
278
278 if sys.version_info < (3,9):
279 if sys.version_info < (3, 9) and platform.python_implementation() != "PyPy":
279 # new pgen parser in 3.9 does not raise MemoryError on too many nested
280 # new pgen parser in 3.9 does not raise MemoryError on too many nested
280 # parens anymore
281 # parens anymore
281 def test_memory_error(self):
282 def test_memory_error(self):
282 with self.assertRaises(MemoryError):
283 with self.assertRaises(MemoryError):
283 iprc("(" * 200 + ")" * 200)
284 iprc("(" * 200 + ")" * 200)
284
285
285 @skip_without('curio')
286 @skip_without('curio')
286 def test_autoawait_curio(self):
287 def test_autoawait_curio(self):
287 iprc("%autoawait curio")
288 iprc("%autoawait curio")
288
289
289 @skip_without('trio')
290 @skip_without('trio')
290 def test_autoawait_trio(self):
291 def test_autoawait_trio(self):
291 iprc("%autoawait trio")
292 iprc("%autoawait trio")
292
293
293 @skip_without('trio')
294 @skip_without('trio')
294 def test_autoawait_trio_wrong_sleep(self):
295 def test_autoawait_trio_wrong_sleep(self):
295 iprc("%autoawait trio")
296 iprc("%autoawait trio")
296 res = iprc_nr("""
297 res = iprc_nr("""
297 import asyncio
298 import asyncio
298 await asyncio.sleep(0)
299 await asyncio.sleep(0)
299 """)
300 """)
300 with self.assertRaises(TypeError):
301 with self.assertRaises(TypeError):
301 res.raise_error()
302 res.raise_error()
302
303
303 @skip_without('trio')
304 @skip_without('trio')
304 def test_autoawait_asyncio_wrong_sleep(self):
305 def test_autoawait_asyncio_wrong_sleep(self):
305 iprc("%autoawait asyncio")
306 iprc("%autoawait asyncio")
306 res = iprc_nr("""
307 res = iprc_nr("""
307 import trio
308 import trio
308 await trio.sleep(0)
309 await trio.sleep(0)
309 """)
310 """)
310 with self.assertRaises(RuntimeError):
311 with self.assertRaises(RuntimeError):
311 res.raise_error()
312 res.raise_error()
312
313
313
314
314 def tearDown(self):
315 def tearDown(self):
315 ip.loop_runner = "asyncio"
316 ip.loop_runner = "asyncio"
@@ -1,559 +1,563 b''
1 """Tests for debugging machinery.
1 """Tests for debugging machinery.
2 """
2 """
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 bdb
7 import bdb
8 import builtins
8 import builtins
9 import os
9 import os
10 import sys
10 import sys
11
11
12 from tempfile import NamedTemporaryFile
12 from tempfile import NamedTemporaryFile
13 from textwrap import dedent
13 from textwrap import dedent
14 from unittest.mock import patch
14 from unittest.mock import patch
15
15
16 from IPython.core import debugger
16 from IPython.core import debugger
17 from IPython.testing import IPYTHON_TESTING_TIMEOUT_SCALE
17 from IPython.testing import IPYTHON_TESTING_TIMEOUT_SCALE
18 from IPython.testing.decorators import skip_win32
18 from IPython.testing.decorators import skip_win32
19
19
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21 # Helper classes, from CPython's Pdb test suite
21 # Helper classes, from CPython's Pdb test suite
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23
23
24 class _FakeInput(object):
24 class _FakeInput(object):
25 """
25 """
26 A fake input stream for pdb's interactive debugger. Whenever a
26 A fake input stream for pdb's interactive debugger. Whenever a
27 line is read, print it (to simulate the user typing it), and then
27 line is read, print it (to simulate the user typing it), and then
28 return it. The set of lines to return is specified in the
28 return it. The set of lines to return is specified in the
29 constructor; they should not have trailing newlines.
29 constructor; they should not have trailing newlines.
30 """
30 """
31 def __init__(self, lines):
31 def __init__(self, lines):
32 self.lines = iter(lines)
32 self.lines = iter(lines)
33
33
34 def readline(self):
34 def readline(self):
35 line = next(self.lines)
35 line = next(self.lines)
36 print(line)
36 print(line)
37 return line+'\n'
37 return line+'\n'
38
38
39 class PdbTestInput(object):
39 class PdbTestInput(object):
40 """Context manager that makes testing Pdb in doctests easier."""
40 """Context manager that makes testing Pdb in doctests easier."""
41
41
42 def __init__(self, input):
42 def __init__(self, input):
43 self.input = input
43 self.input = input
44
44
45 def __enter__(self):
45 def __enter__(self):
46 self.real_stdin = sys.stdin
46 self.real_stdin = sys.stdin
47 sys.stdin = _FakeInput(self.input)
47 sys.stdin = _FakeInput(self.input)
48
48
49 def __exit__(self, *exc):
49 def __exit__(self, *exc):
50 sys.stdin = self.real_stdin
50 sys.stdin = self.real_stdin
51
51
52 #-----------------------------------------------------------------------------
52 #-----------------------------------------------------------------------------
53 # Tests
53 # Tests
54 #-----------------------------------------------------------------------------
54 #-----------------------------------------------------------------------------
55
55
56 def test_ipdb_magics():
56 def test_ipdb_magics():
57 '''Test calling some IPython magics from ipdb.
57 '''Test calling some IPython magics from ipdb.
58
58
59 First, set up some test functions and classes which we can inspect.
59 First, set up some test functions and classes which we can inspect.
60
60
61 >>> class ExampleClass(object):
61 >>> class ExampleClass(object):
62 ... """Docstring for ExampleClass."""
62 ... """Docstring for ExampleClass."""
63 ... def __init__(self):
63 ... def __init__(self):
64 ... """Docstring for ExampleClass.__init__"""
64 ... """Docstring for ExampleClass.__init__"""
65 ... pass
65 ... pass
66 ... def __str__(self):
66 ... def __str__(self):
67 ... return "ExampleClass()"
67 ... return "ExampleClass()"
68
68
69 >>> def example_function(x, y, z="hello"):
69 >>> def example_function(x, y, z="hello"):
70 ... """Docstring for example_function."""
70 ... """Docstring for example_function."""
71 ... pass
71 ... pass
72
72
73 >>> old_trace = sys.gettrace()
73 >>> old_trace = sys.gettrace()
74
74
75 Create a function which triggers ipdb.
75 Create a function which triggers ipdb.
76
76
77 >>> def trigger_ipdb():
77 >>> def trigger_ipdb():
78 ... a = ExampleClass()
78 ... a = ExampleClass()
79 ... debugger.Pdb().set_trace()
79 ... debugger.Pdb().set_trace()
80
80
81 >>> with PdbTestInput([
81 >>> with PdbTestInput([
82 ... 'pdef example_function',
82 ... 'pdef example_function',
83 ... 'pdoc ExampleClass',
83 ... 'pdoc ExampleClass',
84 ... 'up',
84 ... 'up',
85 ... 'down',
85 ... 'down',
86 ... 'list',
86 ... 'list',
87 ... 'pinfo a',
87 ... 'pinfo a',
88 ... 'll',
88 ... 'll',
89 ... 'continue',
89 ... 'continue',
90 ... ]):
90 ... ]):
91 ... trigger_ipdb()
91 ... trigger_ipdb()
92 --Return--
92 --Return--
93 None
93 None
94 > <doctest ...>(3)trigger_ipdb()
94 > <doctest ...>(3)trigger_ipdb()
95 1 def trigger_ipdb():
95 1 def trigger_ipdb():
96 2 a = ExampleClass()
96 2 a = ExampleClass()
97 ----> 3 debugger.Pdb().set_trace()
97 ----> 3 debugger.Pdb().set_trace()
98 <BLANKLINE>
98 <BLANKLINE>
99 ipdb> pdef example_function
99 ipdb> pdef example_function
100 example_function(x, y, z='hello')
100 example_function(x, y, z='hello')
101 ipdb> pdoc ExampleClass
101 ipdb> pdoc ExampleClass
102 Class docstring:
102 Class docstring:
103 Docstring for ExampleClass.
103 Docstring for ExampleClass.
104 Init docstring:
104 Init docstring:
105 Docstring for ExampleClass.__init__
105 Docstring for ExampleClass.__init__
106 ipdb> up
106 ipdb> up
107 > <doctest ...>(11)<module>()
107 > <doctest ...>(11)<module>()
108 7 'pinfo a',
108 7 'pinfo a',
109 8 'll',
109 8 'll',
110 9 'continue',
110 9 'continue',
111 10 ]):
111 10 ]):
112 ---> 11 trigger_ipdb()
112 ---> 11 trigger_ipdb()
113 <BLANKLINE>
113 <BLANKLINE>
114 ipdb> down
114 ipdb> down
115 None
115 None
116 > <doctest ...>(3)trigger_ipdb()
116 > <doctest ...>(3)trigger_ipdb()
117 1 def trigger_ipdb():
117 1 def trigger_ipdb():
118 2 a = ExampleClass()
118 2 a = ExampleClass()
119 ----> 3 debugger.Pdb().set_trace()
119 ----> 3 debugger.Pdb().set_trace()
120 <BLANKLINE>
120 <BLANKLINE>
121 ipdb> list
121 ipdb> list
122 1 def trigger_ipdb():
122 1 def trigger_ipdb():
123 2 a = ExampleClass()
123 2 a = ExampleClass()
124 ----> 3 debugger.Pdb().set_trace()
124 ----> 3 debugger.Pdb().set_trace()
125 <BLANKLINE>
125 <BLANKLINE>
126 ipdb> pinfo a
126 ipdb> pinfo a
127 Type: ExampleClass
127 Type: ExampleClass
128 String form: ExampleClass()
128 String form: ExampleClass()
129 Namespace: Local...
129 Namespace: Local...
130 Docstring: Docstring for ExampleClass.
130 Docstring: Docstring for ExampleClass.
131 Init docstring: Docstring for ExampleClass.__init__
131 Init docstring: Docstring for ExampleClass.__init__
132 ipdb> ll
132 ipdb> ll
133 1 def trigger_ipdb():
133 1 def trigger_ipdb():
134 2 a = ExampleClass()
134 2 a = ExampleClass()
135 ----> 3 debugger.Pdb().set_trace()
135 ----> 3 debugger.Pdb().set_trace()
136 <BLANKLINE>
136 <BLANKLINE>
137 ipdb> continue
137 ipdb> continue
138
138
139 Restore previous trace function, e.g. for coverage.py
139 Restore previous trace function, e.g. for coverage.py
140
140
141 >>> sys.settrace(old_trace)
141 >>> sys.settrace(old_trace)
142 '''
142 '''
143
143
144 def test_ipdb_magics2():
144 def test_ipdb_magics2():
145 '''Test ipdb with a very short function.
145 '''Test ipdb with a very short function.
146
146
147 >>> old_trace = sys.gettrace()
147 >>> old_trace = sys.gettrace()
148
148
149 >>> def bar():
149 >>> def bar():
150 ... pass
150 ... pass
151
151
152 Run ipdb.
152 Run ipdb.
153
153
154 >>> with PdbTestInput([
154 >>> with PdbTestInput([
155 ... 'continue',
155 ... 'continue',
156 ... ]):
156 ... ]):
157 ... debugger.Pdb().runcall(bar)
157 ... debugger.Pdb().runcall(bar)
158 > <doctest ...>(2)bar()
158 > <doctest ...>(2)bar()
159 1 def bar():
159 1 def bar():
160 ----> 2 pass
160 ----> 2 pass
161 <BLANKLINE>
161 <BLANKLINE>
162 ipdb> continue
162 ipdb> continue
163
163
164 Restore previous trace function, e.g. for coverage.py
164 Restore previous trace function, e.g. for coverage.py
165
165
166 >>> sys.settrace(old_trace)
166 >>> sys.settrace(old_trace)
167 '''
167 '''
168
168
169 def can_quit():
169 def can_quit():
170 '''Test that quit work in ipydb
170 '''Test that quit work in ipydb
171
171
172 >>> old_trace = sys.gettrace()
172 >>> old_trace = sys.gettrace()
173
173
174 >>> def bar():
174 >>> def bar():
175 ... pass
175 ... pass
176
176
177 >>> with PdbTestInput([
177 >>> with PdbTestInput([
178 ... 'quit',
178 ... 'quit',
179 ... ]):
179 ... ]):
180 ... debugger.Pdb().runcall(bar)
180 ... debugger.Pdb().runcall(bar)
181 > <doctest ...>(2)bar()
181 > <doctest ...>(2)bar()
182 1 def bar():
182 1 def bar():
183 ----> 2 pass
183 ----> 2 pass
184 <BLANKLINE>
184 <BLANKLINE>
185 ipdb> quit
185 ipdb> quit
186
186
187 Restore previous trace function, e.g. for coverage.py
187 Restore previous trace function, e.g. for coverage.py
188
188
189 >>> sys.settrace(old_trace)
189 >>> sys.settrace(old_trace)
190 '''
190 '''
191
191
192
192
193 def can_exit():
193 def can_exit():
194 '''Test that quit work in ipydb
194 '''Test that quit work in ipydb
195
195
196 >>> old_trace = sys.gettrace()
196 >>> old_trace = sys.gettrace()
197
197
198 >>> def bar():
198 >>> def bar():
199 ... pass
199 ... pass
200
200
201 >>> with PdbTestInput([
201 >>> with PdbTestInput([
202 ... 'exit',
202 ... 'exit',
203 ... ]):
203 ... ]):
204 ... debugger.Pdb().runcall(bar)
204 ... debugger.Pdb().runcall(bar)
205 > <doctest ...>(2)bar()
205 > <doctest ...>(2)bar()
206 1 def bar():
206 1 def bar():
207 ----> 2 pass
207 ----> 2 pass
208 <BLANKLINE>
208 <BLANKLINE>
209 ipdb> exit
209 ipdb> exit
210
210
211 Restore previous trace function, e.g. for coverage.py
211 Restore previous trace function, e.g. for coverage.py
212
212
213 >>> sys.settrace(old_trace)
213 >>> sys.settrace(old_trace)
214 '''
214 '''
215
215
216
216
217 def test_interruptible_core_debugger():
217 def test_interruptible_core_debugger():
218 """The debugger can be interrupted.
218 """The debugger can be interrupted.
219
219
220 The presumption is there is some mechanism that causes a KeyboardInterrupt
220 The presumption is there is some mechanism that causes a KeyboardInterrupt
221 (this is implemented in ipykernel). We want to ensure the
221 (this is implemented in ipykernel). We want to ensure the
222 KeyboardInterrupt cause debugging to cease.
222 KeyboardInterrupt cause debugging to cease.
223 """
223 """
224 def raising_input(msg="", called=[0]):
224 def raising_input(msg="", called=[0]):
225 called[0] += 1
225 called[0] += 1
226 assert called[0] == 1, "input() should only be called once!"
226 assert called[0] == 1, "input() should only be called once!"
227 raise KeyboardInterrupt()
227 raise KeyboardInterrupt()
228
228
229 tracer_orig = sys.gettrace()
229 tracer_orig = sys.gettrace()
230 try:
230 try:
231 with patch.object(builtins, "input", raising_input):
231 with patch.object(builtins, "input", raising_input):
232 debugger.InterruptiblePdb().set_trace()
232 debugger.InterruptiblePdb().set_trace()
233 # The way this test will fail is by set_trace() never exiting,
233 # The way this test will fail is by set_trace() never exiting,
234 # resulting in a timeout by the test runner. The alternative
234 # resulting in a timeout by the test runner. The alternative
235 # implementation would involve a subprocess, but that adds issues
235 # implementation would involve a subprocess, but that adds issues
236 # with interrupting subprocesses that are rather complex, so it's
236 # with interrupting subprocesses that are rather complex, so it's
237 # simpler just to do it this way.
237 # simpler just to do it this way.
238 finally:
238 finally:
239 # restore the original trace function
239 # restore the original trace function
240 sys.settrace(tracer_orig)
240 sys.settrace(tracer_orig)
241
241
242
242
243 @skip_win32
243 @skip_win32
244 def test_xmode_skip():
244 def test_xmode_skip():
245 """that xmode skip frames
245 """that xmode skip frames
246
246
247 Not as a doctest as pytest does not run doctests.
247 Not as a doctest as pytest does not run doctests.
248 """
248 """
249 import pexpect
249 import pexpect
250 env = os.environ.copy()
250 env = os.environ.copy()
251 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
251 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
252
252
253 child = pexpect.spawn(
253 child = pexpect.spawn(
254 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
254 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
255 )
255 )
256 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
256 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
257
257
258 child.expect("IPython")
258 child.expect("IPython")
259 child.expect("\n")
259 child.expect("\n")
260 child.expect_exact("In [1]")
260 child.expect_exact("In [1]")
261
261
262 block = dedent(
262 block = dedent(
263 """
263 """
264 def f():
264 def f():
265 __tracebackhide__ = True
265 __tracebackhide__ = True
266 g()
266 g()
267
267
268 def g():
268 def g():
269 raise ValueError
269 raise ValueError
270
270
271 f()
271 f()
272 """
272 """
273 )
273 )
274
274
275 for line in block.splitlines():
275 for line in block.splitlines():
276 child.sendline(line)
276 child.sendline(line)
277 child.expect_exact(line)
277 child.expect_exact(line)
278 child.expect_exact("skipping")
278 child.expect_exact("skipping")
279
279
280 block = dedent(
280 block = dedent(
281 """
281 """
282 def f():
282 def f():
283 __tracebackhide__ = True
283 __tracebackhide__ = True
284 g()
284 g()
285
285
286 def g():
286 def g():
287 from IPython.core.debugger import set_trace
287 from IPython.core.debugger import set_trace
288 set_trace()
288 set_trace()
289
289
290 f()
290 f()
291 """
291 """
292 )
292 )
293
293
294 for line in block.splitlines():
294 for line in block.splitlines():
295 child.sendline(line)
295 child.sendline(line)
296 child.expect_exact(line)
296 child.expect_exact(line)
297
297
298 child.expect("ipdb>")
298 child.expect("ipdb>")
299 child.sendline("w")
299 child.sendline("w")
300 child.expect("hidden")
300 child.expect("hidden")
301 child.expect("ipdb>")
301 child.expect("ipdb>")
302 child.sendline("skip_hidden false")
302 child.sendline("skip_hidden false")
303 child.sendline("w")
303 child.sendline("w")
304 child.expect("__traceba")
304 child.expect("__traceba")
305 child.expect("ipdb>")
305 child.expect("ipdb>")
306
306
307 child.close()
307 child.close()
308
308
309
309
310 skip_decorators_blocks = (
310 skip_decorators_blocks = (
311 """
311 """
312 def helpers_helper():
312 def helpers_helper():
313 pass # should not stop here except breakpoint
313 pass # should not stop here except breakpoint
314 """,
314 """,
315 """
315 """
316 def helper_1():
316 def helper_1():
317 helpers_helper() # should not stop here
317 helpers_helper() # should not stop here
318 """,
318 """,
319 """
319 """
320 def helper_2():
320 def helper_2():
321 pass # should not stop here
321 pass # should not stop here
322 """,
322 """,
323 """
323 """
324 def pdb_skipped_decorator2(function):
324 def pdb_skipped_decorator2(function):
325 def wrapped_fn(*args, **kwargs):
325 def wrapped_fn(*args, **kwargs):
326 __debuggerskip__ = True
326 __debuggerskip__ = True
327 helper_2()
327 helper_2()
328 __debuggerskip__ = False
328 __debuggerskip__ = False
329 result = function(*args, **kwargs)
329 result = function(*args, **kwargs)
330 __debuggerskip__ = True
330 __debuggerskip__ = True
331 helper_2()
331 helper_2()
332 return result
332 return result
333 return wrapped_fn
333 return wrapped_fn
334 """,
334 """,
335 """
335 """
336 def pdb_skipped_decorator(function):
336 def pdb_skipped_decorator(function):
337 def wrapped_fn(*args, **kwargs):
337 def wrapped_fn(*args, **kwargs):
338 __debuggerskip__ = True
338 __debuggerskip__ = True
339 helper_1()
339 helper_1()
340 __debuggerskip__ = False
340 __debuggerskip__ = False
341 result = function(*args, **kwargs)
341 result = function(*args, **kwargs)
342 __debuggerskip__ = True
342 __debuggerskip__ = True
343 helper_2()
343 helper_2()
344 return result
344 return result
345 return wrapped_fn
345 return wrapped_fn
346 """,
346 """,
347 """
347 """
348 @pdb_skipped_decorator
348 @pdb_skipped_decorator
349 @pdb_skipped_decorator2
349 @pdb_skipped_decorator2
350 def bar(x, y):
350 def bar(x, y):
351 return x * y
351 return x * y
352 """,
352 """,
353 """import IPython.terminal.debugger as ipdb""",
353 """import IPython.terminal.debugger as ipdb""",
354 """
354 """
355 def f():
355 def f():
356 ipdb.set_trace()
356 ipdb.set_trace()
357 bar(3, 4)
357 bar(3, 4)
358 """,
358 """,
359 """
359 """
360 f()
360 f()
361 """,
361 """,
362 )
362 )
363
363
364
364
365 def _decorator_skip_setup():
365 def _decorator_skip_setup():
366 import pexpect
366 import pexpect
367
367
368 env = os.environ.copy()
368 env = os.environ.copy()
369 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
369 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
370
370
371 child = pexpect.spawn(
371 child = pexpect.spawn(
372 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
372 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
373 )
373 )
374 child.timeout = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
374 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
375
375
376 child.expect("IPython")
376 child.expect("IPython")
377 child.expect("\n")
377 child.expect("\n")
378
378
379 child.timeout = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
380
379 dedented_blocks = [dedent(b).strip() for b in skip_decorators_blocks]
381 dedented_blocks = [dedent(b).strip() for b in skip_decorators_blocks]
380 in_prompt_number = 1
382 in_prompt_number = 1
381 for cblock in dedented_blocks:
383 for cblock in dedented_blocks:
382 child.expect_exact(f"In [{in_prompt_number}]:")
384 child.expect_exact(f"In [{in_prompt_number}]:")
383 in_prompt_number += 1
385 in_prompt_number += 1
384 for line in cblock.splitlines():
386 for line in cblock.splitlines():
385 child.sendline(line)
387 child.sendline(line)
386 child.expect_exact(line)
388 child.expect_exact(line)
387 child.sendline("")
389 child.sendline("")
388 return child
390 return child
389
391
390
392
391 @skip_win32
393 @skip_win32
392 def test_decorator_skip():
394 def test_decorator_skip():
393 """test that decorator frames can be skipped."""
395 """test that decorator frames can be skipped."""
394
396
395 child = _decorator_skip_setup()
397 child = _decorator_skip_setup()
396
398
397 child.expect_exact("3 bar(3, 4)")
399 child.expect_exact("3 bar(3, 4)")
398 child.expect("ipdb>")
400 child.expect("ipdb>")
399
401
400 child.expect("ipdb>")
402 child.expect("ipdb>")
401 child.sendline("step")
403 child.sendline("step")
402 child.expect_exact("step")
404 child.expect_exact("step")
403
405
404 child.expect_exact("1 @pdb_skipped_decorator")
406 child.expect_exact("1 @pdb_skipped_decorator")
405
407
406 child.sendline("s")
408 child.sendline("s")
407 child.expect_exact("return x * y")
409 child.expect_exact("return x * y")
408
410
409 child.close()
411 child.close()
410
412
411
413
412 @skip_win32
414 @skip_win32
413 def test_decorator_skip_disabled():
415 def test_decorator_skip_disabled():
414 """test that decorator frame skipping can be disabled"""
416 """test that decorator frame skipping can be disabled"""
415
417
416 child = _decorator_skip_setup()
418 child = _decorator_skip_setup()
417
419
418 child.expect_exact("3 bar(3, 4)")
420 child.expect_exact("3 bar(3, 4)")
419
421
420 for input_, expected in [
422 for input_, expected in [
421 ("skip_predicates debuggerskip False", ""),
423 ("skip_predicates debuggerskip False", ""),
422 ("skip_predicates", "debuggerskip : False"),
424 ("skip_predicates", "debuggerskip : False"),
423 ("step", "---> 2 def wrapped_fn"),
425 ("step", "---> 2 def wrapped_fn"),
424 ("step", "----> 3 __debuggerskip__"),
426 ("step", "----> 3 __debuggerskip__"),
425 ("step", "----> 4 helper_1()"),
427 ("step", "----> 4 helper_1()"),
426 ("step", "---> 1 def helper_1():"),
428 ("step", "---> 1 def helper_1():"),
427 ("next", "----> 2 helpers_helper()"),
429 ("next", "----> 2 helpers_helper()"),
428 ("next", "--Return--"),
430 ("next", "--Return--"),
429 ("next", "----> 5 __debuggerskip__ = False"),
431 ("next", "----> 5 __debuggerskip__ = False"),
430 ]:
432 ]:
431 child.expect("ipdb>")
433 child.expect("ipdb>")
432 child.sendline(input_)
434 child.sendline(input_)
433 child.expect_exact(input_)
435 child.expect_exact(input_)
434 child.expect_exact(expected)
436 child.expect_exact(expected)
435
437
436 child.close()
438 child.close()
437
439
438
440
439 @skip_win32
441 @skip_win32
440 def test_decorator_skip_with_breakpoint():
442 def test_decorator_skip_with_breakpoint():
441 """test that decorator frame skipping can be disabled"""
443 """test that decorator frame skipping can be disabled"""
442
444
443 import pexpect
445 import pexpect
444
446
445 env = os.environ.copy()
447 env = os.environ.copy()
446 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
448 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
447
449
448 child = pexpect.spawn(
450 child = pexpect.spawn(
449 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
451 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
450 )
452 )
451 child.timeout = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
453 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
452
454
453 child.expect("IPython")
455 child.expect("IPython")
454 child.expect("\n")
456 child.expect("\n")
455
457
458 child.timeout = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
459
456 ### we need a filename, so we need to exec the full block with a filename
460 ### we need a filename, so we need to exec the full block with a filename
457 with NamedTemporaryFile(suffix=".py", dir=".", delete=True) as tf:
461 with NamedTemporaryFile(suffix=".py", dir=".", delete=True) as tf:
458
462
459 name = tf.name[:-3].split("/")[-1]
463 name = tf.name[:-3].split("/")[-1]
460 tf.write("\n".join([dedent(x) for x in skip_decorators_blocks[:-1]]).encode())
464 tf.write("\n".join([dedent(x) for x in skip_decorators_blocks[:-1]]).encode())
461 tf.flush()
465 tf.flush()
462 codeblock = f"from {name} import f"
466 codeblock = f"from {name} import f"
463
467
464 dedented_blocks = [
468 dedented_blocks = [
465 codeblock,
469 codeblock,
466 "f()",
470 "f()",
467 ]
471 ]
468
472
469 in_prompt_number = 1
473 in_prompt_number = 1
470 for cblock in dedented_blocks:
474 for cblock in dedented_blocks:
471 child.expect_exact(f"In [{in_prompt_number}]:")
475 child.expect_exact(f"In [{in_prompt_number}]:")
472 in_prompt_number += 1
476 in_prompt_number += 1
473 for line in cblock.splitlines():
477 for line in cblock.splitlines():
474 child.sendline(line)
478 child.sendline(line)
475 child.expect_exact(line)
479 child.expect_exact(line)
476 child.sendline("")
480 child.sendline("")
477
481
478 # as the filename does not exists, we'll rely on the filename prompt
482 # as the filename does not exists, we'll rely on the filename prompt
479 child.expect_exact("47 bar(3, 4)")
483 child.expect_exact("47 bar(3, 4)")
480
484
481 for input_, expected in [
485 for input_, expected in [
482 (f"b {name}.py:3", ""),
486 (f"b {name}.py:3", ""),
483 ("step", "1---> 3 pass # should not stop here except"),
487 ("step", "1---> 3 pass # should not stop here except"),
484 ("step", "---> 38 @pdb_skipped_decorator"),
488 ("step", "---> 38 @pdb_skipped_decorator"),
485 ("continue", ""),
489 ("continue", ""),
486 ]:
490 ]:
487 child.expect("ipdb>")
491 child.expect("ipdb>")
488 child.sendline(input_)
492 child.sendline(input_)
489 child.expect_exact(input_)
493 child.expect_exact(input_)
490 child.expect_exact(expected)
494 child.expect_exact(expected)
491
495
492 child.close()
496 child.close()
493
497
494
498
495 @skip_win32
499 @skip_win32
496 def test_where_erase_value():
500 def test_where_erase_value():
497 """Test that `where` does not access f_locals and erase values."""
501 """Test that `where` does not access f_locals and erase values."""
498 import pexpect
502 import pexpect
499
503
500 env = os.environ.copy()
504 env = os.environ.copy()
501 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
505 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
502
506
503 child = pexpect.spawn(
507 child = pexpect.spawn(
504 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
508 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
505 )
509 )
506 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
510 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
507
511
508 child.expect("IPython")
512 child.expect("IPython")
509 child.expect("\n")
513 child.expect("\n")
510 child.expect_exact("In [1]")
514 child.expect_exact("In [1]")
511
515
512 block = dedent(
516 block = dedent(
513 """
517 """
514 def simple_f():
518 def simple_f():
515 myvar = 1
519 myvar = 1
516 print(myvar)
520 print(myvar)
517 1/0
521 1/0
518 print(myvar)
522 print(myvar)
519 simple_f() """
523 simple_f() """
520 )
524 )
521
525
522 for line in block.splitlines():
526 for line in block.splitlines():
523 child.sendline(line)
527 child.sendline(line)
524 child.expect_exact(line)
528 child.expect_exact(line)
525 child.expect_exact("ZeroDivisionError")
529 child.expect_exact("ZeroDivisionError")
526 child.expect_exact("In [2]:")
530 child.expect_exact("In [2]:")
527
531
528 child.sendline("%debug")
532 child.sendline("%debug")
529
533
530 ##
534 ##
531 child.expect("ipdb>")
535 child.expect("ipdb>")
532
536
533 child.sendline("myvar")
537 child.sendline("myvar")
534 child.expect("1")
538 child.expect("1")
535
539
536 ##
540 ##
537 child.expect("ipdb>")
541 child.expect("ipdb>")
538
542
539 child.sendline("myvar = 2")
543 child.sendline("myvar = 2")
540
544
541 ##
545 ##
542 child.expect_exact("ipdb>")
546 child.expect_exact("ipdb>")
543
547
544 child.sendline("myvar")
548 child.sendline("myvar")
545
549
546 child.expect_exact("2")
550 child.expect_exact("2")
547
551
548 ##
552 ##
549 child.expect("ipdb>")
553 child.expect("ipdb>")
550 child.sendline("where")
554 child.sendline("where")
551
555
552 ##
556 ##
553 child.expect("ipdb>")
557 child.expect("ipdb>")
554 child.sendline("myvar")
558 child.sendline("myvar")
555
559
556 child.expect_exact("2")
560 child.expect_exact("2")
557 child.expect("ipdb>")
561 child.expect("ipdb>")
558
562
559 child.close()
563 child.close()
@@ -1,1358 +1,1360 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tests for various magic functions."""
2 """Tests for various magic functions."""
3
3
4 import asyncio
4 import asyncio
5 import gc
5 import io
6 import io
6 import os
7 import os
7 import re
8 import re
8 import shlex
9 import shlex
9 import sys
10 import sys
10 import warnings
11 import warnings
11 from importlib import invalidate_caches
12 from importlib import invalidate_caches
12 from io import StringIO
13 from io import StringIO
13 from pathlib import Path
14 from pathlib import Path
14 from textwrap import dedent
15 from textwrap import dedent
15 from unittest import TestCase, mock
16 from unittest import TestCase, mock
16
17
17 import pytest
18 import pytest
18
19
19 from IPython import get_ipython
20 from IPython import get_ipython
20 from IPython.core import magic
21 from IPython.core import magic
21 from IPython.core.error import UsageError
22 from IPython.core.error import UsageError
22 from IPython.core.magic import (
23 from IPython.core.magic import (
23 Magics,
24 Magics,
24 cell_magic,
25 cell_magic,
25 line_magic,
26 line_magic,
26 magics_class,
27 magics_class,
27 register_cell_magic,
28 register_cell_magic,
28 register_line_magic,
29 register_line_magic,
29 )
30 )
30 from IPython.core.magics import code, execution, logging, osm, script
31 from IPython.core.magics import code, execution, logging, osm, script
31 from IPython.testing import decorators as dec
32 from IPython.testing import decorators as dec
32 from IPython.testing import tools as tt
33 from IPython.testing import tools as tt
33 from IPython.utils.io import capture_output
34 from IPython.utils.io import capture_output
34 from IPython.utils.process import find_cmd
35 from IPython.utils.process import find_cmd
35 from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
36 from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
36
37
37 from .test_debugger import PdbTestInput
38 from .test_debugger import PdbTestInput
38
39
39
40
40 @magic.magics_class
41 @magic.magics_class
41 class DummyMagics(magic.Magics): pass
42 class DummyMagics(magic.Magics): pass
42
43
43 def test_extract_code_ranges():
44 def test_extract_code_ranges():
44 instr = "1 3 5-6 7-9 10:15 17: :10 10- -13 :"
45 instr = "1 3 5-6 7-9 10:15 17: :10 10- -13 :"
45 expected = [
46 expected = [
46 (0, 1),
47 (0, 1),
47 (2, 3),
48 (2, 3),
48 (4, 6),
49 (4, 6),
49 (6, 9),
50 (6, 9),
50 (9, 14),
51 (9, 14),
51 (16, None),
52 (16, None),
52 (None, 9),
53 (None, 9),
53 (9, None),
54 (9, None),
54 (None, 13),
55 (None, 13),
55 (None, None),
56 (None, None),
56 ]
57 ]
57 actual = list(code.extract_code_ranges(instr))
58 actual = list(code.extract_code_ranges(instr))
58 assert actual == expected
59 assert actual == expected
59
60
60 def test_extract_symbols():
61 def test_extract_symbols():
61 source = """import foo\na = 10\ndef b():\n return 42\n\n\nclass A: pass\n\n\n"""
62 source = """import foo\na = 10\ndef b():\n return 42\n\n\nclass A: pass\n\n\n"""
62 symbols_args = ["a", "b", "A", "A,b", "A,a", "z"]
63 symbols_args = ["a", "b", "A", "A,b", "A,a", "z"]
63 expected = [([], ['a']),
64 expected = [([], ['a']),
64 (["def b():\n return 42\n"], []),
65 (["def b():\n return 42\n"], []),
65 (["class A: pass\n"], []),
66 (["class A: pass\n"], []),
66 (["class A: pass\n", "def b():\n return 42\n"], []),
67 (["class A: pass\n", "def b():\n return 42\n"], []),
67 (["class A: pass\n"], ['a']),
68 (["class A: pass\n"], ['a']),
68 ([], ['z'])]
69 ([], ['z'])]
69 for symbols, exp in zip(symbols_args, expected):
70 for symbols, exp in zip(symbols_args, expected):
70 assert code.extract_symbols(source, symbols) == exp
71 assert code.extract_symbols(source, symbols) == exp
71
72
72
73
73 def test_extract_symbols_raises_exception_with_non_python_code():
74 def test_extract_symbols_raises_exception_with_non_python_code():
74 source = ("=begin A Ruby program :)=end\n"
75 source = ("=begin A Ruby program :)=end\n"
75 "def hello\n"
76 "def hello\n"
76 "puts 'Hello world'\n"
77 "puts 'Hello world'\n"
77 "end")
78 "end")
78 with pytest.raises(SyntaxError):
79 with pytest.raises(SyntaxError):
79 code.extract_symbols(source, "hello")
80 code.extract_symbols(source, "hello")
80
81
81
82
82 def test_magic_not_found():
83 def test_magic_not_found():
83 # magic not found raises UsageError
84 # magic not found raises UsageError
84 with pytest.raises(UsageError):
85 with pytest.raises(UsageError):
85 _ip.magic('doesntexist')
86 _ip.magic('doesntexist')
86
87
87 # ensure result isn't success when a magic isn't found
88 # ensure result isn't success when a magic isn't found
88 result = _ip.run_cell('%doesntexist')
89 result = _ip.run_cell('%doesntexist')
89 assert isinstance(result.error_in_exec, UsageError)
90 assert isinstance(result.error_in_exec, UsageError)
90
91
91
92
92 def test_cell_magic_not_found():
93 def test_cell_magic_not_found():
93 # magic not found raises UsageError
94 # magic not found raises UsageError
94 with pytest.raises(UsageError):
95 with pytest.raises(UsageError):
95 _ip.run_cell_magic('doesntexist', 'line', 'cell')
96 _ip.run_cell_magic('doesntexist', 'line', 'cell')
96
97
97 # ensure result isn't success when a magic isn't found
98 # ensure result isn't success when a magic isn't found
98 result = _ip.run_cell('%%doesntexist')
99 result = _ip.run_cell('%%doesntexist')
99 assert isinstance(result.error_in_exec, UsageError)
100 assert isinstance(result.error_in_exec, UsageError)
100
101
101
102
102 def test_magic_error_status():
103 def test_magic_error_status():
103 def fail(shell):
104 def fail(shell):
104 1/0
105 1/0
105 _ip.register_magic_function(fail)
106 _ip.register_magic_function(fail)
106 result = _ip.run_cell('%fail')
107 result = _ip.run_cell('%fail')
107 assert isinstance(result.error_in_exec, ZeroDivisionError)
108 assert isinstance(result.error_in_exec, ZeroDivisionError)
108
109
109
110
110 def test_config():
111 def test_config():
111 """ test that config magic does not raise
112 """ test that config magic does not raise
112 can happen if Configurable init is moved too early into
113 can happen if Configurable init is moved too early into
113 Magics.__init__ as then a Config object will be registered as a
114 Magics.__init__ as then a Config object will be registered as a
114 magic.
115 magic.
115 """
116 """
116 ## should not raise.
117 ## should not raise.
117 _ip.magic('config')
118 _ip.magic('config')
118
119
119 def test_config_available_configs():
120 def test_config_available_configs():
120 """ test that config magic prints available configs in unique and
121 """ test that config magic prints available configs in unique and
121 sorted order. """
122 sorted order. """
122 with capture_output() as captured:
123 with capture_output() as captured:
123 _ip.magic('config')
124 _ip.magic('config')
124
125
125 stdout = captured.stdout
126 stdout = captured.stdout
126 config_classes = stdout.strip().split('\n')[1:]
127 config_classes = stdout.strip().split('\n')[1:]
127 assert config_classes == sorted(set(config_classes))
128 assert config_classes == sorted(set(config_classes))
128
129
129 def test_config_print_class():
130 def test_config_print_class():
130 """ test that config with a classname prints the class's options. """
131 """ test that config with a classname prints the class's options. """
131 with capture_output() as captured:
132 with capture_output() as captured:
132 _ip.magic('config TerminalInteractiveShell')
133 _ip.magic('config TerminalInteractiveShell')
133
134
134 stdout = captured.stdout
135 stdout = captured.stdout
135 assert re.match(
136 assert re.match(
136 "TerminalInteractiveShell.* options", stdout.splitlines()[0]
137 "TerminalInteractiveShell.* options", stdout.splitlines()[0]
137 ), f"{stdout}\n\n1st line of stdout not like 'TerminalInteractiveShell.* options'"
138 ), f"{stdout}\n\n1st line of stdout not like 'TerminalInteractiveShell.* options'"
138
139
139
140
140 def test_rehashx():
141 def test_rehashx():
141 # clear up everything
142 # clear up everything
142 _ip.alias_manager.clear_aliases()
143 _ip.alias_manager.clear_aliases()
143 del _ip.db['syscmdlist']
144 del _ip.db['syscmdlist']
144
145
145 _ip.magic('rehashx')
146 _ip.magic('rehashx')
146 # Practically ALL ipython development systems will have more than 10 aliases
147 # Practically ALL ipython development systems will have more than 10 aliases
147
148
148 assert len(_ip.alias_manager.aliases) > 10
149 assert len(_ip.alias_manager.aliases) > 10
149 for name, cmd in _ip.alias_manager.aliases:
150 for name, cmd in _ip.alias_manager.aliases:
150 # we must strip dots from alias names
151 # we must strip dots from alias names
151 assert "." not in name
152 assert "." not in name
152
153
153 # rehashx must fill up syscmdlist
154 # rehashx must fill up syscmdlist
154 scoms = _ip.db['syscmdlist']
155 scoms = _ip.db['syscmdlist']
155 assert len(scoms) > 10
156 assert len(scoms) > 10
156
157
157
158
158 def test_magic_parse_options():
159 def test_magic_parse_options():
159 """Test that we don't mangle paths when parsing magic options."""
160 """Test that we don't mangle paths when parsing magic options."""
160 ip = get_ipython()
161 ip = get_ipython()
161 path = 'c:\\x'
162 path = 'c:\\x'
162 m = DummyMagics(ip)
163 m = DummyMagics(ip)
163 opts = m.parse_options('-f %s' % path,'f:')[0]
164 opts = m.parse_options('-f %s' % path,'f:')[0]
164 # argv splitting is os-dependent
165 # argv splitting is os-dependent
165 if os.name == 'posix':
166 if os.name == 'posix':
166 expected = 'c:x'
167 expected = 'c:x'
167 else:
168 else:
168 expected = path
169 expected = path
169 assert opts["f"] == expected
170 assert opts["f"] == expected
170
171
171
172
172 def test_magic_parse_long_options():
173 def test_magic_parse_long_options():
173 """Magic.parse_options can handle --foo=bar long options"""
174 """Magic.parse_options can handle --foo=bar long options"""
174 ip = get_ipython()
175 ip = get_ipython()
175 m = DummyMagics(ip)
176 m = DummyMagics(ip)
176 opts, _ = m.parse_options("--foo --bar=bubble", "a", "foo", "bar=")
177 opts, _ = m.parse_options("--foo --bar=bubble", "a", "foo", "bar=")
177 assert "foo" in opts
178 assert "foo" in opts
178 assert "bar" in opts
179 assert "bar" in opts
179 assert opts["bar"] == "bubble"
180 assert opts["bar"] == "bubble"
180
181
181
182
182 def doctest_hist_f():
183 def doctest_hist_f():
183 """Test %hist -f with temporary filename.
184 """Test %hist -f with temporary filename.
184
185
185 In [9]: import tempfile
186 In [9]: import tempfile
186
187
187 In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-')
188 In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-')
188
189
189 In [11]: %hist -nl -f $tfile 3
190 In [11]: %hist -nl -f $tfile 3
190
191
191 In [13]: import os; os.unlink(tfile)
192 In [13]: import os; os.unlink(tfile)
192 """
193 """
193
194
194
195
195 def doctest_hist_op():
196 def doctest_hist_op():
196 """Test %hist -op
197 """Test %hist -op
197
198
198 In [1]: class b(float):
199 In [1]: class b(float):
199 ...: pass
200 ...: pass
200 ...:
201 ...:
201
202
202 In [2]: class s(object):
203 In [2]: class s(object):
203 ...: def __str__(self):
204 ...: def __str__(self):
204 ...: return 's'
205 ...: return 's'
205 ...:
206 ...:
206
207
207 In [3]:
208 In [3]:
208
209
209 In [4]: class r(b):
210 In [4]: class r(b):
210 ...: def __repr__(self):
211 ...: def __repr__(self):
211 ...: return 'r'
212 ...: return 'r'
212 ...:
213 ...:
213
214
214 In [5]: class sr(s,r): pass
215 In [5]: class sr(s,r): pass
215 ...:
216 ...:
216
217
217 In [6]:
218 In [6]:
218
219
219 In [7]: bb=b()
220 In [7]: bb=b()
220
221
221 In [8]: ss=s()
222 In [8]: ss=s()
222
223
223 In [9]: rr=r()
224 In [9]: rr=r()
224
225
225 In [10]: ssrr=sr()
226 In [10]: ssrr=sr()
226
227
227 In [11]: 4.5
228 In [11]: 4.5
228 Out[11]: 4.5
229 Out[11]: 4.5
229
230
230 In [12]: str(ss)
231 In [12]: str(ss)
231 Out[12]: 's'
232 Out[12]: 's'
232
233
233 In [13]:
234 In [13]:
234
235
235 In [14]: %hist -op
236 In [14]: %hist -op
236 >>> class b:
237 >>> class b:
237 ... pass
238 ... pass
238 ...
239 ...
239 >>> class s(b):
240 >>> class s(b):
240 ... def __str__(self):
241 ... def __str__(self):
241 ... return 's'
242 ... return 's'
242 ...
243 ...
243 >>>
244 >>>
244 >>> class r(b):
245 >>> class r(b):
245 ... def __repr__(self):
246 ... def __repr__(self):
246 ... return 'r'
247 ... return 'r'
247 ...
248 ...
248 >>> class sr(s,r): pass
249 >>> class sr(s,r): pass
249 >>>
250 >>>
250 >>> bb=b()
251 >>> bb=b()
251 >>> ss=s()
252 >>> ss=s()
252 >>> rr=r()
253 >>> rr=r()
253 >>> ssrr=sr()
254 >>> ssrr=sr()
254 >>> 4.5
255 >>> 4.5
255 4.5
256 4.5
256 >>> str(ss)
257 >>> str(ss)
257 's'
258 's'
258 >>>
259 >>>
259 """
260 """
260
261
261 def test_hist_pof():
262 def test_hist_pof():
262 ip = get_ipython()
263 ip = get_ipython()
263 ip.run_cell("1+2", store_history=True)
264 ip.run_cell("1+2", store_history=True)
264 #raise Exception(ip.history_manager.session_number)
265 #raise Exception(ip.history_manager.session_number)
265 #raise Exception(list(ip.history_manager._get_range_session()))
266 #raise Exception(list(ip.history_manager._get_range_session()))
266 with TemporaryDirectory() as td:
267 with TemporaryDirectory() as td:
267 tf = os.path.join(td, 'hist.py')
268 tf = os.path.join(td, 'hist.py')
268 ip.run_line_magic('history', '-pof %s' % tf)
269 ip.run_line_magic('history', '-pof %s' % tf)
269 assert os.path.isfile(tf)
270 assert os.path.isfile(tf)
270
271
271
272
272 def test_macro():
273 def test_macro():
273 ip = get_ipython()
274 ip = get_ipython()
274 ip.history_manager.reset() # Clear any existing history.
275 ip.history_manager.reset() # Clear any existing history.
275 cmds = ["a=1", "def b():\n return a**2", "print(a,b())"]
276 cmds = ["a=1", "def b():\n return a**2", "print(a,b())"]
276 for i, cmd in enumerate(cmds, start=1):
277 for i, cmd in enumerate(cmds, start=1):
277 ip.history_manager.store_inputs(i, cmd)
278 ip.history_manager.store_inputs(i, cmd)
278 ip.magic("macro test 1-3")
279 ip.magic("macro test 1-3")
279 assert ip.user_ns["test"].value == "\n".join(cmds) + "\n"
280 assert ip.user_ns["test"].value == "\n".join(cmds) + "\n"
280
281
281 # List macros
282 # List macros
282 assert "test" in ip.magic("macro")
283 assert "test" in ip.magic("macro")
283
284
284
285
285 def test_macro_run():
286 def test_macro_run():
286 """Test that we can run a multi-line macro successfully."""
287 """Test that we can run a multi-line macro successfully."""
287 ip = get_ipython()
288 ip = get_ipython()
288 ip.history_manager.reset()
289 ip.history_manager.reset()
289 cmds = ["a=10", "a+=1", "print(a)", "%macro test 2-3"]
290 cmds = ["a=10", "a+=1", "print(a)", "%macro test 2-3"]
290 for cmd in cmds:
291 for cmd in cmds:
291 ip.run_cell(cmd, store_history=True)
292 ip.run_cell(cmd, store_history=True)
292 assert ip.user_ns["test"].value == "a+=1\nprint(a)\n"
293 assert ip.user_ns["test"].value == "a+=1\nprint(a)\n"
293 with tt.AssertPrints("12"):
294 with tt.AssertPrints("12"):
294 ip.run_cell("test")
295 ip.run_cell("test")
295 with tt.AssertPrints("13"):
296 with tt.AssertPrints("13"):
296 ip.run_cell("test")
297 ip.run_cell("test")
297
298
298
299
299 def test_magic_magic():
300 def test_magic_magic():
300 """Test %magic"""
301 """Test %magic"""
301 ip = get_ipython()
302 ip = get_ipython()
302 with capture_output() as captured:
303 with capture_output() as captured:
303 ip.magic("magic")
304 ip.magic("magic")
304
305
305 stdout = captured.stdout
306 stdout = captured.stdout
306 assert "%magic" in stdout
307 assert "%magic" in stdout
307 assert "IPython" in stdout
308 assert "IPython" in stdout
308 assert "Available" in stdout
309 assert "Available" in stdout
309
310
310
311
311 @dec.skipif_not_numpy
312 @dec.skipif_not_numpy
312 def test_numpy_reset_array_undec():
313 def test_numpy_reset_array_undec():
313 "Test '%reset array' functionality"
314 "Test '%reset array' functionality"
314 _ip.ex("import numpy as np")
315 _ip.ex("import numpy as np")
315 _ip.ex("a = np.empty(2)")
316 _ip.ex("a = np.empty(2)")
316 assert "a" in _ip.user_ns
317 assert "a" in _ip.user_ns
317 _ip.magic("reset -f array")
318 _ip.magic("reset -f array")
318 assert "a" not in _ip.user_ns
319 assert "a" not in _ip.user_ns
319
320
320
321
321 def test_reset_out():
322 def test_reset_out():
322 "Test '%reset out' magic"
323 "Test '%reset out' magic"
323 _ip.run_cell("parrot = 'dead'", store_history=True)
324 _ip.run_cell("parrot = 'dead'", store_history=True)
324 # test '%reset -f out', make an Out prompt
325 # test '%reset -f out', make an Out prompt
325 _ip.run_cell("parrot", store_history=True)
326 _ip.run_cell("parrot", store_history=True)
326 assert "dead" in [_ip.user_ns[x] for x in ("_", "__", "___")]
327 assert "dead" in [_ip.user_ns[x] for x in ("_", "__", "___")]
327 _ip.magic("reset -f out")
328 _ip.magic("reset -f out")
328 assert "dead" not in [_ip.user_ns[x] for x in ("_", "__", "___")]
329 assert "dead" not in [_ip.user_ns[x] for x in ("_", "__", "___")]
329 assert len(_ip.user_ns["Out"]) == 0
330 assert len(_ip.user_ns["Out"]) == 0
330
331
331
332
332 def test_reset_in():
333 def test_reset_in():
333 "Test '%reset in' magic"
334 "Test '%reset in' magic"
334 # test '%reset -f in'
335 # test '%reset -f in'
335 _ip.run_cell("parrot", store_history=True)
336 _ip.run_cell("parrot", store_history=True)
336 assert "parrot" in [_ip.user_ns[x] for x in ("_i", "_ii", "_iii")]
337 assert "parrot" in [_ip.user_ns[x] for x in ("_i", "_ii", "_iii")]
337 _ip.magic("%reset -f in")
338 _ip.magic("%reset -f in")
338 assert "parrot" not in [_ip.user_ns[x] for x in ("_i", "_ii", "_iii")]
339 assert "parrot" not in [_ip.user_ns[x] for x in ("_i", "_ii", "_iii")]
339 assert len(set(_ip.user_ns["In"])) == 1
340 assert len(set(_ip.user_ns["In"])) == 1
340
341
341
342
342 def test_reset_dhist():
343 def test_reset_dhist():
343 "Test '%reset dhist' magic"
344 "Test '%reset dhist' magic"
344 _ip.run_cell("tmp = [d for d in _dh]") # copy before clearing
345 _ip.run_cell("tmp = [d for d in _dh]") # copy before clearing
345 _ip.magic("cd " + os.path.dirname(pytest.__file__))
346 _ip.magic("cd " + os.path.dirname(pytest.__file__))
346 _ip.magic("cd -")
347 _ip.magic("cd -")
347 assert len(_ip.user_ns["_dh"]) > 0
348 assert len(_ip.user_ns["_dh"]) > 0
348 _ip.magic("reset -f dhist")
349 _ip.magic("reset -f dhist")
349 assert len(_ip.user_ns["_dh"]) == 0
350 assert len(_ip.user_ns["_dh"]) == 0
350 _ip.run_cell("_dh = [d for d in tmp]") # restore
351 _ip.run_cell("_dh = [d for d in tmp]") # restore
351
352
352
353
353 def test_reset_in_length():
354 def test_reset_in_length():
354 "Test that '%reset in' preserves In[] length"
355 "Test that '%reset in' preserves In[] length"
355 _ip.run_cell("print 'foo'")
356 _ip.run_cell("print 'foo'")
356 _ip.run_cell("reset -f in")
357 _ip.run_cell("reset -f in")
357 assert len(_ip.user_ns["In"]) == _ip.displayhook.prompt_count + 1
358 assert len(_ip.user_ns["In"]) == _ip.displayhook.prompt_count + 1
358
359
359
360
360 class TestResetErrors(TestCase):
361 class TestResetErrors(TestCase):
361
362
362 def test_reset_redefine(self):
363 def test_reset_redefine(self):
363
364
364 @magics_class
365 @magics_class
365 class KernelMagics(Magics):
366 class KernelMagics(Magics):
366 @line_magic
367 @line_magic
367 def less(self, shell): pass
368 def less(self, shell): pass
368
369
369 _ip.register_magics(KernelMagics)
370 _ip.register_magics(KernelMagics)
370
371
371 with self.assertLogs() as cm:
372 with self.assertLogs() as cm:
372 # hack, we want to just capture logs, but assertLogs fails if not
373 # hack, we want to just capture logs, but assertLogs fails if not
373 # logs get produce.
374 # logs get produce.
374 # so log one things we ignore.
375 # so log one things we ignore.
375 import logging as log_mod
376 import logging as log_mod
376 log = log_mod.getLogger()
377 log = log_mod.getLogger()
377 log.info('Nothing')
378 log.info('Nothing')
378 # end hack.
379 # end hack.
379 _ip.run_cell("reset -f")
380 _ip.run_cell("reset -f")
380
381
381 assert len(cm.output) == 1
382 assert len(cm.output) == 1
382 for out in cm.output:
383 for out in cm.output:
383 assert "Invalid alias" not in out
384 assert "Invalid alias" not in out
384
385
385 def test_tb_syntaxerror():
386 def test_tb_syntaxerror():
386 """test %tb after a SyntaxError"""
387 """test %tb after a SyntaxError"""
387 ip = get_ipython()
388 ip = get_ipython()
388 ip.run_cell("for")
389 ip.run_cell("for")
389
390
390 # trap and validate stdout
391 # trap and validate stdout
391 save_stdout = sys.stdout
392 save_stdout = sys.stdout
392 try:
393 try:
393 sys.stdout = StringIO()
394 sys.stdout = StringIO()
394 ip.run_cell("%tb")
395 ip.run_cell("%tb")
395 out = sys.stdout.getvalue()
396 out = sys.stdout.getvalue()
396 finally:
397 finally:
397 sys.stdout = save_stdout
398 sys.stdout = save_stdout
398 # trim output, and only check the last line
399 # trim output, and only check the last line
399 last_line = out.rstrip().splitlines()[-1].strip()
400 last_line = out.rstrip().splitlines()[-1].strip()
400 assert last_line == "SyntaxError: invalid syntax"
401 assert last_line == "SyntaxError: invalid syntax"
401
402
402
403
403 def test_time():
404 def test_time():
404 ip = get_ipython()
405 ip = get_ipython()
405
406
406 with tt.AssertPrints("Wall time: "):
407 with tt.AssertPrints("Wall time: "):
407 ip.run_cell("%time None")
408 ip.run_cell("%time None")
408
409
409 ip.run_cell("def f(kmjy):\n"
410 ip.run_cell("def f(kmjy):\n"
410 " %time print (2*kmjy)")
411 " %time print (2*kmjy)")
411
412
412 with tt.AssertPrints("Wall time: "):
413 with tt.AssertPrints("Wall time: "):
413 with tt.AssertPrints("hihi", suppress=False):
414 with tt.AssertPrints("hihi", suppress=False):
414 ip.run_cell("f('hi')")
415 ip.run_cell("f('hi')")
415
416
416 def test_time_last_not_expression():
417 def test_time_last_not_expression():
417 ip.run_cell("%%time\n"
418 ip.run_cell("%%time\n"
418 "var_1 = 1\n"
419 "var_1 = 1\n"
419 "var_2 = 2\n")
420 "var_2 = 2\n")
420 assert ip.user_ns['var_1'] == 1
421 assert ip.user_ns['var_1'] == 1
421 del ip.user_ns['var_1']
422 del ip.user_ns['var_1']
422 assert ip.user_ns['var_2'] == 2
423 assert ip.user_ns['var_2'] == 2
423 del ip.user_ns['var_2']
424 del ip.user_ns['var_2']
424
425
425
426
426 @dec.skip_win32
427 @dec.skip_win32
427 def test_time2():
428 def test_time2():
428 ip = get_ipython()
429 ip = get_ipython()
429
430
430 with tt.AssertPrints("CPU times: user "):
431 with tt.AssertPrints("CPU times: user "):
431 ip.run_cell("%time None")
432 ip.run_cell("%time None")
432
433
433 def test_time3():
434 def test_time3():
434 """Erroneous magic function calls, issue gh-3334"""
435 """Erroneous magic function calls, issue gh-3334"""
435 ip = get_ipython()
436 ip = get_ipython()
436 ip.user_ns.pop('run', None)
437 ip.user_ns.pop('run', None)
437
438
438 with tt.AssertNotPrints("not found", channel='stderr'):
439 with tt.AssertNotPrints("not found", channel='stderr'):
439 ip.run_cell("%%time\n"
440 ip.run_cell("%%time\n"
440 "run = 0\n"
441 "run = 0\n"
441 "run += 1")
442 "run += 1")
442
443
443 def test_multiline_time():
444 def test_multiline_time():
444 """Make sure last statement from time return a value."""
445 """Make sure last statement from time return a value."""
445 ip = get_ipython()
446 ip = get_ipython()
446 ip.user_ns.pop('run', None)
447 ip.user_ns.pop('run', None)
447
448
448 ip.run_cell(dedent("""\
449 ip.run_cell(dedent("""\
449 %%time
450 %%time
450 a = "ho"
451 a = "ho"
451 b = "hey"
452 b = "hey"
452 a+b
453 a+b
453 """
454 """
454 )
455 )
455 )
456 )
456 assert ip.user_ns_hidden["_"] == "hohey"
457 assert ip.user_ns_hidden["_"] == "hohey"
457
458
458
459
459 def test_time_local_ns():
460 def test_time_local_ns():
460 """
461 """
461 Test that local_ns is actually global_ns when running a cell magic
462 Test that local_ns is actually global_ns when running a cell magic
462 """
463 """
463 ip = get_ipython()
464 ip = get_ipython()
464 ip.run_cell("%%time\n" "myvar = 1")
465 ip.run_cell("%%time\n" "myvar = 1")
465 assert ip.user_ns["myvar"] == 1
466 assert ip.user_ns["myvar"] == 1
466 del ip.user_ns["myvar"]
467 del ip.user_ns["myvar"]
467
468
468
469
469 def test_doctest_mode():
470 def test_doctest_mode():
470 "Toggle doctest_mode twice, it should be a no-op and run without error"
471 "Toggle doctest_mode twice, it should be a no-op and run without error"
471 _ip.magic('doctest_mode')
472 _ip.magic('doctest_mode')
472 _ip.magic('doctest_mode')
473 _ip.magic('doctest_mode')
473
474
474
475
475 def test_parse_options():
476 def test_parse_options():
476 """Tests for basic options parsing in magics."""
477 """Tests for basic options parsing in magics."""
477 # These are only the most minimal of tests, more should be added later. At
478 # These are only the most minimal of tests, more should be added later. At
478 # the very least we check that basic text/unicode calls work OK.
479 # the very least we check that basic text/unicode calls work OK.
479 m = DummyMagics(_ip)
480 m = DummyMagics(_ip)
480 assert m.parse_options("foo", "")[1] == "foo"
481 assert m.parse_options("foo", "")[1] == "foo"
481 assert m.parse_options("foo", "")[1] == "foo"
482 assert m.parse_options("foo", "")[1] == "foo"
482
483
483
484
484 def test_parse_options_preserve_non_option_string():
485 def test_parse_options_preserve_non_option_string():
485 """Test to assert preservation of non-option part of magic-block, while parsing magic options."""
486 """Test to assert preservation of non-option part of magic-block, while parsing magic options."""
486 m = DummyMagics(_ip)
487 m = DummyMagics(_ip)
487 opts, stmt = m.parse_options(
488 opts, stmt = m.parse_options(
488 " -n1 -r 13 _ = 314 + foo", "n:r:", preserve_non_opts=True
489 " -n1 -r 13 _ = 314 + foo", "n:r:", preserve_non_opts=True
489 )
490 )
490 assert opts == {"n": "1", "r": "13"}
491 assert opts == {"n": "1", "r": "13"}
491 assert stmt == "_ = 314 + foo"
492 assert stmt == "_ = 314 + foo"
492
493
493
494
494 def test_run_magic_preserve_code_block():
495 def test_run_magic_preserve_code_block():
495 """Test to assert preservation of non-option part of magic-block, while running magic."""
496 """Test to assert preservation of non-option part of magic-block, while running magic."""
496 _ip.user_ns["spaces"] = []
497 _ip.user_ns["spaces"] = []
497 _ip.magic("timeit -n1 -r1 spaces.append([s.count(' ') for s in ['document']])")
498 _ip.magic("timeit -n1 -r1 spaces.append([s.count(' ') for s in ['document']])")
498 assert _ip.user_ns["spaces"] == [[0]]
499 assert _ip.user_ns["spaces"] == [[0]]
499
500
500
501
501 def test_dirops():
502 def test_dirops():
502 """Test various directory handling operations."""
503 """Test various directory handling operations."""
503 # curpath = lambda :os.path.splitdrive(os.getcwd())[1].replace('\\','/')
504 # curpath = lambda :os.path.splitdrive(os.getcwd())[1].replace('\\','/')
504 curpath = os.getcwd
505 curpath = os.getcwd
505 startdir = os.getcwd()
506 startdir = os.getcwd()
506 ipdir = os.path.realpath(_ip.ipython_dir)
507 ipdir = os.path.realpath(_ip.ipython_dir)
507 try:
508 try:
508 _ip.magic('cd "%s"' % ipdir)
509 _ip.magic('cd "%s"' % ipdir)
509 assert curpath() == ipdir
510 assert curpath() == ipdir
510 _ip.magic('cd -')
511 _ip.magic('cd -')
511 assert curpath() == startdir
512 assert curpath() == startdir
512 _ip.magic('pushd "%s"' % ipdir)
513 _ip.magic('pushd "%s"' % ipdir)
513 assert curpath() == ipdir
514 assert curpath() == ipdir
514 _ip.magic('popd')
515 _ip.magic('popd')
515 assert curpath() == startdir
516 assert curpath() == startdir
516 finally:
517 finally:
517 os.chdir(startdir)
518 os.chdir(startdir)
518
519
519
520
520 def test_cd_force_quiet():
521 def test_cd_force_quiet():
521 """Test OSMagics.cd_force_quiet option"""
522 """Test OSMagics.cd_force_quiet option"""
522 _ip.config.OSMagics.cd_force_quiet = True
523 _ip.config.OSMagics.cd_force_quiet = True
523 osmagics = osm.OSMagics(shell=_ip)
524 osmagics = osm.OSMagics(shell=_ip)
524
525
525 startdir = os.getcwd()
526 startdir = os.getcwd()
526 ipdir = os.path.realpath(_ip.ipython_dir)
527 ipdir = os.path.realpath(_ip.ipython_dir)
527
528
528 try:
529 try:
529 with tt.AssertNotPrints(ipdir):
530 with tt.AssertNotPrints(ipdir):
530 osmagics.cd('"%s"' % ipdir)
531 osmagics.cd('"%s"' % ipdir)
531 with tt.AssertNotPrints(startdir):
532 with tt.AssertNotPrints(startdir):
532 osmagics.cd('-')
533 osmagics.cd('-')
533 finally:
534 finally:
534 os.chdir(startdir)
535 os.chdir(startdir)
535
536
536
537
537 def test_xmode():
538 def test_xmode():
538 # Calling xmode three times should be a no-op
539 # Calling xmode three times should be a no-op
539 xmode = _ip.InteractiveTB.mode
540 xmode = _ip.InteractiveTB.mode
540 for i in range(4):
541 for i in range(4):
541 _ip.magic("xmode")
542 _ip.magic("xmode")
542 assert _ip.InteractiveTB.mode == xmode
543 assert _ip.InteractiveTB.mode == xmode
543
544
544 def test_reset_hard():
545 def test_reset_hard():
545 monitor = []
546 monitor = []
546 class A(object):
547 class A(object):
547 def __del__(self):
548 def __del__(self):
548 monitor.append(1)
549 monitor.append(1)
549 def __repr__(self):
550 def __repr__(self):
550 return "<A instance>"
551 return "<A instance>"
551
552
552 _ip.user_ns["a"] = A()
553 _ip.user_ns["a"] = A()
553 _ip.run_cell("a")
554 _ip.run_cell("a")
554
555
555 assert monitor == []
556 assert monitor == []
556 _ip.magic("reset -f")
557 _ip.magic("reset -f")
557 assert monitor == [1]
558 assert monitor == [1]
558
559
559 class TestXdel(tt.TempFileMixin):
560 class TestXdel(tt.TempFileMixin):
560 def test_xdel(self):
561 def test_xdel(self):
561 """Test that references from %run are cleared by xdel."""
562 """Test that references from %run are cleared by xdel."""
562 src = ("class A(object):\n"
563 src = ("class A(object):\n"
563 " monitor = []\n"
564 " monitor = []\n"
564 " def __del__(self):\n"
565 " def __del__(self):\n"
565 " self.monitor.append(1)\n"
566 " self.monitor.append(1)\n"
566 "a = A()\n")
567 "a = A()\n")
567 self.mktmp(src)
568 self.mktmp(src)
568 # %run creates some hidden references...
569 # %run creates some hidden references...
569 _ip.magic("run %s" % self.fname)
570 _ip.magic("run %s" % self.fname)
570 # ... as does the displayhook.
571 # ... as does the displayhook.
571 _ip.run_cell("a")
572 _ip.run_cell("a")
572
573
573 monitor = _ip.user_ns["A"].monitor
574 monitor = _ip.user_ns["A"].monitor
574 assert monitor == []
575 assert monitor == []
575
576
576 _ip.magic("xdel a")
577 _ip.magic("xdel a")
577
578
578 # Check that a's __del__ method has been called.
579 # Check that a's __del__ method has been called.
580 gc.collect(0)
579 assert monitor == [1]
581 assert monitor == [1]
580
582
581 def doctest_who():
583 def doctest_who():
582 """doctest for %who
584 """doctest for %who
583
585
584 In [1]: %reset -sf
586 In [1]: %reset -sf
585
587
586 In [2]: alpha = 123
588 In [2]: alpha = 123
587
589
588 In [3]: beta = 'beta'
590 In [3]: beta = 'beta'
589
591
590 In [4]: %who int
592 In [4]: %who int
591 alpha
593 alpha
592
594
593 In [5]: %who str
595 In [5]: %who str
594 beta
596 beta
595
597
596 In [6]: %whos
598 In [6]: %whos
597 Variable Type Data/Info
599 Variable Type Data/Info
598 ----------------------------
600 ----------------------------
599 alpha int 123
601 alpha int 123
600 beta str beta
602 beta str beta
601
603
602 In [7]: %who_ls
604 In [7]: %who_ls
603 Out[7]: ['alpha', 'beta']
605 Out[7]: ['alpha', 'beta']
604 """
606 """
605
607
606 def test_whos():
608 def test_whos():
607 """Check that whos is protected against objects where repr() fails."""
609 """Check that whos is protected against objects where repr() fails."""
608 class A(object):
610 class A(object):
609 def __repr__(self):
611 def __repr__(self):
610 raise Exception()
612 raise Exception()
611 _ip.user_ns['a'] = A()
613 _ip.user_ns['a'] = A()
612 _ip.magic("whos")
614 _ip.magic("whos")
613
615
614 def doctest_precision():
616 def doctest_precision():
615 """doctest for %precision
617 """doctest for %precision
616
618
617 In [1]: f = get_ipython().display_formatter.formatters['text/plain']
619 In [1]: f = get_ipython().display_formatter.formatters['text/plain']
618
620
619 In [2]: %precision 5
621 In [2]: %precision 5
620 Out[2]: '%.5f'
622 Out[2]: '%.5f'
621
623
622 In [3]: f.float_format
624 In [3]: f.float_format
623 Out[3]: '%.5f'
625 Out[3]: '%.5f'
624
626
625 In [4]: %precision %e
627 In [4]: %precision %e
626 Out[4]: '%e'
628 Out[4]: '%e'
627
629
628 In [5]: f(3.1415927)
630 In [5]: f(3.1415927)
629 Out[5]: '3.141593e+00'
631 Out[5]: '3.141593e+00'
630 """
632 """
631
633
632 def test_debug_magic():
634 def test_debug_magic():
633 """Test debugging a small code with %debug
635 """Test debugging a small code with %debug
634
636
635 In [1]: with PdbTestInput(['c']):
637 In [1]: with PdbTestInput(['c']):
636 ...: %debug print("a b") #doctest: +ELLIPSIS
638 ...: %debug print("a b") #doctest: +ELLIPSIS
637 ...:
639 ...:
638 ...
640 ...
639 ipdb> c
641 ipdb> c
640 a b
642 a b
641 In [2]:
643 In [2]:
642 """
644 """
643
645
644 def test_psearch():
646 def test_psearch():
645 with tt.AssertPrints("dict.fromkeys"):
647 with tt.AssertPrints("dict.fromkeys"):
646 _ip.run_cell("dict.fr*?")
648 _ip.run_cell("dict.fr*?")
647 with tt.AssertPrints("π.is_integer"):
649 with tt.AssertPrints("π.is_integer"):
648 _ip.run_cell("π = 3.14;\nπ.is_integ*?")
650 _ip.run_cell("π = 3.14;\nπ.is_integ*?")
649
651
650 def test_timeit_shlex():
652 def test_timeit_shlex():
651 """test shlex issues with timeit (#1109)"""
653 """test shlex issues with timeit (#1109)"""
652 _ip.ex("def f(*a,**kw): pass")
654 _ip.ex("def f(*a,**kw): pass")
653 _ip.magic('timeit -n1 "this is a bug".count(" ")')
655 _ip.magic('timeit -n1 "this is a bug".count(" ")')
654 _ip.magic('timeit -r1 -n1 f(" ", 1)')
656 _ip.magic('timeit -r1 -n1 f(" ", 1)')
655 _ip.magic('timeit -r1 -n1 f(" ", 1, " ", 2, " ")')
657 _ip.magic('timeit -r1 -n1 f(" ", 1, " ", 2, " ")')
656 _ip.magic('timeit -r1 -n1 ("a " + "b")')
658 _ip.magic('timeit -r1 -n1 ("a " + "b")')
657 _ip.magic('timeit -r1 -n1 f("a " + "b")')
659 _ip.magic('timeit -r1 -n1 f("a " + "b")')
658 _ip.magic('timeit -r1 -n1 f("a " + "b ")')
660 _ip.magic('timeit -r1 -n1 f("a " + "b ")')
659
661
660
662
661 def test_timeit_special_syntax():
663 def test_timeit_special_syntax():
662 "Test %%timeit with IPython special syntax"
664 "Test %%timeit with IPython special syntax"
663 @register_line_magic
665 @register_line_magic
664 def lmagic(line):
666 def lmagic(line):
665 ip = get_ipython()
667 ip = get_ipython()
666 ip.user_ns['lmagic_out'] = line
668 ip.user_ns['lmagic_out'] = line
667
669
668 # line mode test
670 # line mode test
669 _ip.run_line_magic("timeit", "-n1 -r1 %lmagic my line")
671 _ip.run_line_magic("timeit", "-n1 -r1 %lmagic my line")
670 assert _ip.user_ns["lmagic_out"] == "my line"
672 assert _ip.user_ns["lmagic_out"] == "my line"
671 # cell mode test
673 # cell mode test
672 _ip.run_cell_magic("timeit", "-n1 -r1", "%lmagic my line2")
674 _ip.run_cell_magic("timeit", "-n1 -r1", "%lmagic my line2")
673 assert _ip.user_ns["lmagic_out"] == "my line2"
675 assert _ip.user_ns["lmagic_out"] == "my line2"
674
676
675
677
676 def test_timeit_return():
678 def test_timeit_return():
677 """
679 """
678 test whether timeit -o return object
680 test whether timeit -o return object
679 """
681 """
680
682
681 res = _ip.run_line_magic('timeit','-n10 -r10 -o 1')
683 res = _ip.run_line_magic('timeit','-n10 -r10 -o 1')
682 assert(res is not None)
684 assert(res is not None)
683
685
684 def test_timeit_quiet():
686 def test_timeit_quiet():
685 """
687 """
686 test quiet option of timeit magic
688 test quiet option of timeit magic
687 """
689 """
688 with tt.AssertNotPrints("loops"):
690 with tt.AssertNotPrints("loops"):
689 _ip.run_cell("%timeit -n1 -r1 -q 1")
691 _ip.run_cell("%timeit -n1 -r1 -q 1")
690
692
691 def test_timeit_return_quiet():
693 def test_timeit_return_quiet():
692 with tt.AssertNotPrints("loops"):
694 with tt.AssertNotPrints("loops"):
693 res = _ip.run_line_magic('timeit', '-n1 -r1 -q -o 1')
695 res = _ip.run_line_magic('timeit', '-n1 -r1 -q -o 1')
694 assert (res is not None)
696 assert (res is not None)
695
697
696 def test_timeit_invalid_return():
698 def test_timeit_invalid_return():
697 with pytest.raises(SyntaxError):
699 with pytest.raises(SyntaxError):
698 _ip.run_line_magic('timeit', 'return')
700 _ip.run_line_magic('timeit', 'return')
699
701
700 @dec.skipif(execution.profile is None)
702 @dec.skipif(execution.profile is None)
701 def test_prun_special_syntax():
703 def test_prun_special_syntax():
702 "Test %%prun with IPython special syntax"
704 "Test %%prun with IPython special syntax"
703 @register_line_magic
705 @register_line_magic
704 def lmagic(line):
706 def lmagic(line):
705 ip = get_ipython()
707 ip = get_ipython()
706 ip.user_ns['lmagic_out'] = line
708 ip.user_ns['lmagic_out'] = line
707
709
708 # line mode test
710 # line mode test
709 _ip.run_line_magic("prun", "-q %lmagic my line")
711 _ip.run_line_magic("prun", "-q %lmagic my line")
710 assert _ip.user_ns["lmagic_out"] == "my line"
712 assert _ip.user_ns["lmagic_out"] == "my line"
711 # cell mode test
713 # cell mode test
712 _ip.run_cell_magic("prun", "-q", "%lmagic my line2")
714 _ip.run_cell_magic("prun", "-q", "%lmagic my line2")
713 assert _ip.user_ns["lmagic_out"] == "my line2"
715 assert _ip.user_ns["lmagic_out"] == "my line2"
714
716
715
717
716 @dec.skipif(execution.profile is None)
718 @dec.skipif(execution.profile is None)
717 def test_prun_quotes():
719 def test_prun_quotes():
718 "Test that prun does not clobber string escapes (GH #1302)"
720 "Test that prun does not clobber string escapes (GH #1302)"
719 _ip.magic(r"prun -q x = '\t'")
721 _ip.magic(r"prun -q x = '\t'")
720 assert _ip.user_ns["x"] == "\t"
722 assert _ip.user_ns["x"] == "\t"
721
723
722
724
723 def test_extension():
725 def test_extension():
724 # Debugging information for failures of this test
726 # Debugging information for failures of this test
725 print('sys.path:')
727 print('sys.path:')
726 for p in sys.path:
728 for p in sys.path:
727 print(' ', p)
729 print(' ', p)
728 print('CWD', os.getcwd())
730 print('CWD', os.getcwd())
729
731
730 pytest.raises(ImportError, _ip.magic, "load_ext daft_extension")
732 pytest.raises(ImportError, _ip.magic, "load_ext daft_extension")
731 daft_path = os.path.join(os.path.dirname(__file__), "daft_extension")
733 daft_path = os.path.join(os.path.dirname(__file__), "daft_extension")
732 sys.path.insert(0, daft_path)
734 sys.path.insert(0, daft_path)
733 try:
735 try:
734 _ip.user_ns.pop('arq', None)
736 _ip.user_ns.pop('arq', None)
735 invalidate_caches() # Clear import caches
737 invalidate_caches() # Clear import caches
736 _ip.magic("load_ext daft_extension")
738 _ip.magic("load_ext daft_extension")
737 assert _ip.user_ns["arq"] == 185
739 assert _ip.user_ns["arq"] == 185
738 _ip.magic("unload_ext daft_extension")
740 _ip.magic("unload_ext daft_extension")
739 assert 'arq' not in _ip.user_ns
741 assert 'arq' not in _ip.user_ns
740 finally:
742 finally:
741 sys.path.remove(daft_path)
743 sys.path.remove(daft_path)
742
744
743
745
744 def test_notebook_export_json():
746 def test_notebook_export_json():
745 pytest.importorskip("nbformat")
747 pytest.importorskip("nbformat")
746 _ip = get_ipython()
748 _ip = get_ipython()
747 _ip.history_manager.reset() # Clear any existing history.
749 _ip.history_manager.reset() # Clear any existing history.
748 cmds = ["a=1", "def b():\n return a**2", "print('noël, été', b())"]
750 cmds = ["a=1", "def b():\n return a**2", "print('noël, été', b())"]
749 for i, cmd in enumerate(cmds, start=1):
751 for i, cmd in enumerate(cmds, start=1):
750 _ip.history_manager.store_inputs(i, cmd)
752 _ip.history_manager.store_inputs(i, cmd)
751 with TemporaryDirectory() as td:
753 with TemporaryDirectory() as td:
752 outfile = os.path.join(td, "nb.ipynb")
754 outfile = os.path.join(td, "nb.ipynb")
753 _ip.magic("notebook %s" % outfile)
755 _ip.magic("notebook %s" % outfile)
754
756
755
757
756 class TestEnv(TestCase):
758 class TestEnv(TestCase):
757
759
758 def test_env(self):
760 def test_env(self):
759 env = _ip.magic("env")
761 env = _ip.magic("env")
760 self.assertTrue(isinstance(env, dict))
762 self.assertTrue(isinstance(env, dict))
761
763
762 def test_env_secret(self):
764 def test_env_secret(self):
763 env = _ip.magic("env")
765 env = _ip.magic("env")
764 hidden = "<hidden>"
766 hidden = "<hidden>"
765 with mock.patch.dict(
767 with mock.patch.dict(
766 os.environ,
768 os.environ,
767 {
769 {
768 "API_KEY": "abc123",
770 "API_KEY": "abc123",
769 "SECRET_THING": "ssshhh",
771 "SECRET_THING": "ssshhh",
770 "JUPYTER_TOKEN": "",
772 "JUPYTER_TOKEN": "",
771 "VAR": "abc"
773 "VAR": "abc"
772 }
774 }
773 ):
775 ):
774 env = _ip.magic("env")
776 env = _ip.magic("env")
775 assert env["API_KEY"] == hidden
777 assert env["API_KEY"] == hidden
776 assert env["SECRET_THING"] == hidden
778 assert env["SECRET_THING"] == hidden
777 assert env["JUPYTER_TOKEN"] == hidden
779 assert env["JUPYTER_TOKEN"] == hidden
778 assert env["VAR"] == "abc"
780 assert env["VAR"] == "abc"
779
781
780 def test_env_get_set_simple(self):
782 def test_env_get_set_simple(self):
781 env = _ip.magic("env var val1")
783 env = _ip.magic("env var val1")
782 self.assertEqual(env, None)
784 self.assertEqual(env, None)
783 self.assertEqual(os.environ['var'], 'val1')
785 self.assertEqual(os.environ['var'], 'val1')
784 self.assertEqual(_ip.magic("env var"), 'val1')
786 self.assertEqual(_ip.magic("env var"), 'val1')
785 env = _ip.magic("env var=val2")
787 env = _ip.magic("env var=val2")
786 self.assertEqual(env, None)
788 self.assertEqual(env, None)
787 self.assertEqual(os.environ['var'], 'val2')
789 self.assertEqual(os.environ['var'], 'val2')
788
790
789 def test_env_get_set_complex(self):
791 def test_env_get_set_complex(self):
790 env = _ip.magic("env var 'val1 '' 'val2")
792 env = _ip.magic("env var 'val1 '' 'val2")
791 self.assertEqual(env, None)
793 self.assertEqual(env, None)
792 self.assertEqual(os.environ['var'], "'val1 '' 'val2")
794 self.assertEqual(os.environ['var'], "'val1 '' 'val2")
793 self.assertEqual(_ip.magic("env var"), "'val1 '' 'val2")
795 self.assertEqual(_ip.magic("env var"), "'val1 '' 'val2")
794 env = _ip.magic('env var=val2 val3="val4')
796 env = _ip.magic('env var=val2 val3="val4')
795 self.assertEqual(env, None)
797 self.assertEqual(env, None)
796 self.assertEqual(os.environ['var'], 'val2 val3="val4')
798 self.assertEqual(os.environ['var'], 'val2 val3="val4')
797
799
798 def test_env_set_bad_input(self):
800 def test_env_set_bad_input(self):
799 self.assertRaises(UsageError, lambda: _ip.magic("set_env var"))
801 self.assertRaises(UsageError, lambda: _ip.magic("set_env var"))
800
802
801 def test_env_set_whitespace(self):
803 def test_env_set_whitespace(self):
802 self.assertRaises(UsageError, lambda: _ip.magic("env var A=B"))
804 self.assertRaises(UsageError, lambda: _ip.magic("env var A=B"))
803
805
804
806
805 class CellMagicTestCase(TestCase):
807 class CellMagicTestCase(TestCase):
806
808
807 def check_ident(self, magic):
809 def check_ident(self, magic):
808 # Manually called, we get the result
810 # Manually called, we get the result
809 out = _ip.run_cell_magic(magic, "a", "b")
811 out = _ip.run_cell_magic(magic, "a", "b")
810 assert out == ("a", "b")
812 assert out == ("a", "b")
811 # Via run_cell, it goes into the user's namespace via displayhook
813 # Via run_cell, it goes into the user's namespace via displayhook
812 _ip.run_cell("%%" + magic + " c\nd\n")
814 _ip.run_cell("%%" + magic + " c\nd\n")
813 assert _ip.user_ns["_"] == ("c", "d\n")
815 assert _ip.user_ns["_"] == ("c", "d\n")
814
816
815 def test_cell_magic_func_deco(self):
817 def test_cell_magic_func_deco(self):
816 "Cell magic using simple decorator"
818 "Cell magic using simple decorator"
817 @register_cell_magic
819 @register_cell_magic
818 def cellm(line, cell):
820 def cellm(line, cell):
819 return line, cell
821 return line, cell
820
822
821 self.check_ident('cellm')
823 self.check_ident('cellm')
822
824
823 def test_cell_magic_reg(self):
825 def test_cell_magic_reg(self):
824 "Cell magic manually registered"
826 "Cell magic manually registered"
825 def cellm(line, cell):
827 def cellm(line, cell):
826 return line, cell
828 return line, cell
827
829
828 _ip.register_magic_function(cellm, 'cell', 'cellm2')
830 _ip.register_magic_function(cellm, 'cell', 'cellm2')
829 self.check_ident('cellm2')
831 self.check_ident('cellm2')
830
832
831 def test_cell_magic_class(self):
833 def test_cell_magic_class(self):
832 "Cell magics declared via a class"
834 "Cell magics declared via a class"
833 @magics_class
835 @magics_class
834 class MyMagics(Magics):
836 class MyMagics(Magics):
835
837
836 @cell_magic
838 @cell_magic
837 def cellm3(self, line, cell):
839 def cellm3(self, line, cell):
838 return line, cell
840 return line, cell
839
841
840 _ip.register_magics(MyMagics)
842 _ip.register_magics(MyMagics)
841 self.check_ident('cellm3')
843 self.check_ident('cellm3')
842
844
843 def test_cell_magic_class2(self):
845 def test_cell_magic_class2(self):
844 "Cell magics declared via a class, #2"
846 "Cell magics declared via a class, #2"
845 @magics_class
847 @magics_class
846 class MyMagics2(Magics):
848 class MyMagics2(Magics):
847
849
848 @cell_magic('cellm4')
850 @cell_magic('cellm4')
849 def cellm33(self, line, cell):
851 def cellm33(self, line, cell):
850 return line, cell
852 return line, cell
851
853
852 _ip.register_magics(MyMagics2)
854 _ip.register_magics(MyMagics2)
853 self.check_ident('cellm4')
855 self.check_ident('cellm4')
854 # Check that nothing is registered as 'cellm33'
856 # Check that nothing is registered as 'cellm33'
855 c33 = _ip.find_cell_magic('cellm33')
857 c33 = _ip.find_cell_magic('cellm33')
856 assert c33 == None
858 assert c33 == None
857
859
858 def test_file():
860 def test_file():
859 """Basic %%writefile"""
861 """Basic %%writefile"""
860 ip = get_ipython()
862 ip = get_ipython()
861 with TemporaryDirectory() as td:
863 with TemporaryDirectory() as td:
862 fname = os.path.join(td, 'file1')
864 fname = os.path.join(td, 'file1')
863 ip.run_cell_magic("writefile", fname, u'\n'.join([
865 ip.run_cell_magic("writefile", fname, u'\n'.join([
864 'line1',
866 'line1',
865 'line2',
867 'line2',
866 ]))
868 ]))
867 s = Path(fname).read_text()
869 s = Path(fname).read_text()
868 assert "line1\n" in s
870 assert "line1\n" in s
869 assert "line2" in s
871 assert "line2" in s
870
872
871
873
872 @dec.skip_win32
874 @dec.skip_win32
873 def test_file_single_quote():
875 def test_file_single_quote():
874 """Basic %%writefile with embedded single quotes"""
876 """Basic %%writefile with embedded single quotes"""
875 ip = get_ipython()
877 ip = get_ipython()
876 with TemporaryDirectory() as td:
878 with TemporaryDirectory() as td:
877 fname = os.path.join(td, '\'file1\'')
879 fname = os.path.join(td, '\'file1\'')
878 ip.run_cell_magic("writefile", fname, u'\n'.join([
880 ip.run_cell_magic("writefile", fname, u'\n'.join([
879 'line1',
881 'line1',
880 'line2',
882 'line2',
881 ]))
883 ]))
882 s = Path(fname).read_text()
884 s = Path(fname).read_text()
883 assert "line1\n" in s
885 assert "line1\n" in s
884 assert "line2" in s
886 assert "line2" in s
885
887
886
888
887 @dec.skip_win32
889 @dec.skip_win32
888 def test_file_double_quote():
890 def test_file_double_quote():
889 """Basic %%writefile with embedded double quotes"""
891 """Basic %%writefile with embedded double quotes"""
890 ip = get_ipython()
892 ip = get_ipython()
891 with TemporaryDirectory() as td:
893 with TemporaryDirectory() as td:
892 fname = os.path.join(td, '"file1"')
894 fname = os.path.join(td, '"file1"')
893 ip.run_cell_magic("writefile", fname, u'\n'.join([
895 ip.run_cell_magic("writefile", fname, u'\n'.join([
894 'line1',
896 'line1',
895 'line2',
897 'line2',
896 ]))
898 ]))
897 s = Path(fname).read_text()
899 s = Path(fname).read_text()
898 assert "line1\n" in s
900 assert "line1\n" in s
899 assert "line2" in s
901 assert "line2" in s
900
902
901
903
902 def test_file_var_expand():
904 def test_file_var_expand():
903 """%%writefile $filename"""
905 """%%writefile $filename"""
904 ip = get_ipython()
906 ip = get_ipython()
905 with TemporaryDirectory() as td:
907 with TemporaryDirectory() as td:
906 fname = os.path.join(td, 'file1')
908 fname = os.path.join(td, 'file1')
907 ip.user_ns['filename'] = fname
909 ip.user_ns['filename'] = fname
908 ip.run_cell_magic("writefile", '$filename', u'\n'.join([
910 ip.run_cell_magic("writefile", '$filename', u'\n'.join([
909 'line1',
911 'line1',
910 'line2',
912 'line2',
911 ]))
913 ]))
912 s = Path(fname).read_text()
914 s = Path(fname).read_text()
913 assert "line1\n" in s
915 assert "line1\n" in s
914 assert "line2" in s
916 assert "line2" in s
915
917
916
918
917 def test_file_unicode():
919 def test_file_unicode():
918 """%%writefile with unicode cell"""
920 """%%writefile with unicode cell"""
919 ip = get_ipython()
921 ip = get_ipython()
920 with TemporaryDirectory() as td:
922 with TemporaryDirectory() as td:
921 fname = os.path.join(td, 'file1')
923 fname = os.path.join(td, 'file1')
922 ip.run_cell_magic("writefile", fname, u'\n'.join([
924 ip.run_cell_magic("writefile", fname, u'\n'.join([
923 u'liné1',
925 u'liné1',
924 u'liné2',
926 u'liné2',
925 ]))
927 ]))
926 with io.open(fname, encoding='utf-8') as f:
928 with io.open(fname, encoding='utf-8') as f:
927 s = f.read()
929 s = f.read()
928 assert "liné1\n" in s
930 assert "liné1\n" in s
929 assert "liné2" in s
931 assert "liné2" in s
930
932
931
933
932 def test_file_amend():
934 def test_file_amend():
933 """%%writefile -a amends files"""
935 """%%writefile -a amends files"""
934 ip = get_ipython()
936 ip = get_ipython()
935 with TemporaryDirectory() as td:
937 with TemporaryDirectory() as td:
936 fname = os.path.join(td, 'file2')
938 fname = os.path.join(td, 'file2')
937 ip.run_cell_magic("writefile", fname, u'\n'.join([
939 ip.run_cell_magic("writefile", fname, u'\n'.join([
938 'line1',
940 'line1',
939 'line2',
941 'line2',
940 ]))
942 ]))
941 ip.run_cell_magic("writefile", "-a %s" % fname, u'\n'.join([
943 ip.run_cell_magic("writefile", "-a %s" % fname, u'\n'.join([
942 'line3',
944 'line3',
943 'line4',
945 'line4',
944 ]))
946 ]))
945 s = Path(fname).read_text()
947 s = Path(fname).read_text()
946 assert "line1\n" in s
948 assert "line1\n" in s
947 assert "line3\n" in s
949 assert "line3\n" in s
948
950
949
951
950 def test_file_spaces():
952 def test_file_spaces():
951 """%%file with spaces in filename"""
953 """%%file with spaces in filename"""
952 ip = get_ipython()
954 ip = get_ipython()
953 with TemporaryWorkingDirectory() as td:
955 with TemporaryWorkingDirectory() as td:
954 fname = "file name"
956 fname = "file name"
955 ip.run_cell_magic("file", '"%s"'%fname, u'\n'.join([
957 ip.run_cell_magic("file", '"%s"'%fname, u'\n'.join([
956 'line1',
958 'line1',
957 'line2',
959 'line2',
958 ]))
960 ]))
959 s = Path(fname).read_text()
961 s = Path(fname).read_text()
960 assert "line1\n" in s
962 assert "line1\n" in s
961 assert "line2" in s
963 assert "line2" in s
962
964
963
965
964 def test_script_config():
966 def test_script_config():
965 ip = get_ipython()
967 ip = get_ipython()
966 ip.config.ScriptMagics.script_magics = ['whoda']
968 ip.config.ScriptMagics.script_magics = ['whoda']
967 sm = script.ScriptMagics(shell=ip)
969 sm = script.ScriptMagics(shell=ip)
968 assert "whoda" in sm.magics["cell"]
970 assert "whoda" in sm.magics["cell"]
969
971
970
972
971 @pytest.fixture
973 @pytest.fixture
972 def event_loop():
974 def event_loop():
973 policy = asyncio.get_event_loop_policy()
975 policy = asyncio.get_event_loop_policy()
974 loop = policy.new_event_loop()
976 loop = policy.new_event_loop()
975 policy.set_event_loop(loop)
977 policy.set_event_loop(loop)
976 yield loop
978 yield loop
977 loop.close()
979 loop.close()
978
980
979
981
980 @dec.skip_win32
982 @dec.skip_win32
981 @pytest.mark.skipif(
983 @pytest.mark.skipif(
982 sys.platform == "win32", reason="This test does not run under Windows"
984 sys.platform == "win32", reason="This test does not run under Windows"
983 )
985 )
984 def test_script_out(event_loop):
986 def test_script_out(event_loop):
985 assert event_loop.is_running() is False
987 assert event_loop.is_running() is False
986
988
987 ip = get_ipython()
989 ip = get_ipython()
988 ip.run_cell_magic("script", "--out output sh", "echo 'hi'")
990 ip.run_cell_magic("script", "--out output sh", "echo 'hi'")
989 assert event_loop.is_running() is False
991 assert event_loop.is_running() is False
990 assert ip.user_ns["output"] == "hi\n"
992 assert ip.user_ns["output"] == "hi\n"
991
993
992
994
993 @dec.skip_win32
995 @dec.skip_win32
994 @pytest.mark.skipif(
996 @pytest.mark.skipif(
995 sys.platform == "win32", reason="This test does not run under Windows"
997 sys.platform == "win32", reason="This test does not run under Windows"
996 )
998 )
997 def test_script_err(event_loop):
999 def test_script_err(event_loop):
998 ip = get_ipython()
1000 ip = get_ipython()
999 assert event_loop.is_running() is False
1001 assert event_loop.is_running() is False
1000 ip.run_cell_magic("script", "--err error sh", "echo 'hello' >&2")
1002 ip.run_cell_magic("script", "--err error sh", "echo 'hello' >&2")
1001 assert event_loop.is_running() is False
1003 assert event_loop.is_running() is False
1002 assert ip.user_ns["error"] == "hello\n"
1004 assert ip.user_ns["error"] == "hello\n"
1003
1005
1004
1006
1005 @dec.skip_win32
1007 @dec.skip_win32
1006 @pytest.mark.skipif(
1008 @pytest.mark.skipif(
1007 sys.platform == "win32", reason="This test does not run under Windows"
1009 sys.platform == "win32", reason="This test does not run under Windows"
1008 )
1010 )
1009 def test_script_out_err():
1011 def test_script_out_err():
1010
1012
1011 ip = get_ipython()
1013 ip = get_ipython()
1012 ip.run_cell_magic(
1014 ip.run_cell_magic(
1013 "script", "--out output --err error sh", "echo 'hi'\necho 'hello' >&2"
1015 "script", "--out output --err error sh", "echo 'hi'\necho 'hello' >&2"
1014 )
1016 )
1015 assert ip.user_ns["output"] == "hi\n"
1017 assert ip.user_ns["output"] == "hi\n"
1016 assert ip.user_ns["error"] == "hello\n"
1018 assert ip.user_ns["error"] == "hello\n"
1017
1019
1018
1020
1019 @dec.skip_win32
1021 @dec.skip_win32
1020 @pytest.mark.skipif(
1022 @pytest.mark.skipif(
1021 sys.platform == "win32", reason="This test does not run under Windows"
1023 sys.platform == "win32", reason="This test does not run under Windows"
1022 )
1024 )
1023 async def test_script_bg_out(event_loop):
1025 async def test_script_bg_out(event_loop):
1024 ip = get_ipython()
1026 ip = get_ipython()
1025 ip.run_cell_magic("script", "--bg --out output sh", "echo 'hi'")
1027 ip.run_cell_magic("script", "--bg --out output sh", "echo 'hi'")
1026 assert (await ip.user_ns["output"].read()) == b"hi\n"
1028 assert (await ip.user_ns["output"].read()) == b"hi\n"
1027 ip.user_ns["output"].close()
1029 ip.user_ns["output"].close()
1028 event_loop.stop()
1030 event_loop.stop()
1029
1031
1030
1032
1031 @dec.skip_win32
1033 @dec.skip_win32
1032 @pytest.mark.skipif(
1034 @pytest.mark.skipif(
1033 sys.platform == "win32", reason="This test does not run under Windows"
1035 sys.platform == "win32", reason="This test does not run under Windows"
1034 )
1036 )
1035 async def test_script_bg_err():
1037 async def test_script_bg_err():
1036 ip = get_ipython()
1038 ip = get_ipython()
1037 ip.run_cell_magic("script", "--bg --err error sh", "echo 'hello' >&2")
1039 ip.run_cell_magic("script", "--bg --err error sh", "echo 'hello' >&2")
1038 assert (await ip.user_ns["error"].read()) == b"hello\n"
1040 assert (await ip.user_ns["error"].read()) == b"hello\n"
1039 ip.user_ns["error"].close()
1041 ip.user_ns["error"].close()
1040
1042
1041
1043
1042 @dec.skip_win32
1044 @dec.skip_win32
1043 @pytest.mark.skipif(
1045 @pytest.mark.skipif(
1044 sys.platform == "win32", reason="This test does not run under Windows"
1046 sys.platform == "win32", reason="This test does not run under Windows"
1045 )
1047 )
1046 async def test_script_bg_out_err():
1048 async def test_script_bg_out_err():
1047 ip = get_ipython()
1049 ip = get_ipython()
1048 ip.run_cell_magic(
1050 ip.run_cell_magic(
1049 "script", "--bg --out output --err error sh", "echo 'hi'\necho 'hello' >&2"
1051 "script", "--bg --out output --err error sh", "echo 'hi'\necho 'hello' >&2"
1050 )
1052 )
1051 assert (await ip.user_ns["output"].read()) == b"hi\n"
1053 assert (await ip.user_ns["output"].read()) == b"hi\n"
1052 assert (await ip.user_ns["error"].read()) == b"hello\n"
1054 assert (await ip.user_ns["error"].read()) == b"hello\n"
1053 ip.user_ns["output"].close()
1055 ip.user_ns["output"].close()
1054 ip.user_ns["error"].close()
1056 ip.user_ns["error"].close()
1055
1057
1056
1058
1057 def test_script_defaults():
1059 def test_script_defaults():
1058 ip = get_ipython()
1060 ip = get_ipython()
1059 for cmd in ['sh', 'bash', 'perl', 'ruby']:
1061 for cmd in ['sh', 'bash', 'perl', 'ruby']:
1060 try:
1062 try:
1061 find_cmd(cmd)
1063 find_cmd(cmd)
1062 except Exception:
1064 except Exception:
1063 pass
1065 pass
1064 else:
1066 else:
1065 assert cmd in ip.magics_manager.magics["cell"]
1067 assert cmd in ip.magics_manager.magics["cell"]
1066
1068
1067
1069
1068 @magics_class
1070 @magics_class
1069 class FooFoo(Magics):
1071 class FooFoo(Magics):
1070 """class with both %foo and %%foo magics"""
1072 """class with both %foo and %%foo magics"""
1071 @line_magic('foo')
1073 @line_magic('foo')
1072 def line_foo(self, line):
1074 def line_foo(self, line):
1073 "I am line foo"
1075 "I am line foo"
1074 pass
1076 pass
1075
1077
1076 @cell_magic("foo")
1078 @cell_magic("foo")
1077 def cell_foo(self, line, cell):
1079 def cell_foo(self, line, cell):
1078 "I am cell foo, not line foo"
1080 "I am cell foo, not line foo"
1079 pass
1081 pass
1080
1082
1081 def test_line_cell_info():
1083 def test_line_cell_info():
1082 """%%foo and %foo magics are distinguishable to inspect"""
1084 """%%foo and %foo magics are distinguishable to inspect"""
1083 ip = get_ipython()
1085 ip = get_ipython()
1084 ip.magics_manager.register(FooFoo)
1086 ip.magics_manager.register(FooFoo)
1085 oinfo = ip.object_inspect("foo")
1087 oinfo = ip.object_inspect("foo")
1086 assert oinfo["found"] is True
1088 assert oinfo["found"] is True
1087 assert oinfo["ismagic"] is True
1089 assert oinfo["ismagic"] is True
1088
1090
1089 oinfo = ip.object_inspect("%%foo")
1091 oinfo = ip.object_inspect("%%foo")
1090 assert oinfo["found"] is True
1092 assert oinfo["found"] is True
1091 assert oinfo["ismagic"] is True
1093 assert oinfo["ismagic"] is True
1092 assert oinfo["docstring"] == FooFoo.cell_foo.__doc__
1094 assert oinfo["docstring"] == FooFoo.cell_foo.__doc__
1093
1095
1094 oinfo = ip.object_inspect("%foo")
1096 oinfo = ip.object_inspect("%foo")
1095 assert oinfo["found"] is True
1097 assert oinfo["found"] is True
1096 assert oinfo["ismagic"] is True
1098 assert oinfo["ismagic"] is True
1097 assert oinfo["docstring"] == FooFoo.line_foo.__doc__
1099 assert oinfo["docstring"] == FooFoo.line_foo.__doc__
1098
1100
1099
1101
1100 def test_multiple_magics():
1102 def test_multiple_magics():
1101 ip = get_ipython()
1103 ip = get_ipython()
1102 foo1 = FooFoo(ip)
1104 foo1 = FooFoo(ip)
1103 foo2 = FooFoo(ip)
1105 foo2 = FooFoo(ip)
1104 mm = ip.magics_manager
1106 mm = ip.magics_manager
1105 mm.register(foo1)
1107 mm.register(foo1)
1106 assert mm.magics["line"]["foo"].__self__ is foo1
1108 assert mm.magics["line"]["foo"].__self__ is foo1
1107 mm.register(foo2)
1109 mm.register(foo2)
1108 assert mm.magics["line"]["foo"].__self__ is foo2
1110 assert mm.magics["line"]["foo"].__self__ is foo2
1109
1111
1110
1112
1111 def test_alias_magic():
1113 def test_alias_magic():
1112 """Test %alias_magic."""
1114 """Test %alias_magic."""
1113 ip = get_ipython()
1115 ip = get_ipython()
1114 mm = ip.magics_manager
1116 mm = ip.magics_manager
1115
1117
1116 # Basic operation: both cell and line magics are created, if possible.
1118 # Basic operation: both cell and line magics are created, if possible.
1117 ip.run_line_magic("alias_magic", "timeit_alias timeit")
1119 ip.run_line_magic("alias_magic", "timeit_alias timeit")
1118 assert "timeit_alias" in mm.magics["line"]
1120 assert "timeit_alias" in mm.magics["line"]
1119 assert "timeit_alias" in mm.magics["cell"]
1121 assert "timeit_alias" in mm.magics["cell"]
1120
1122
1121 # --cell is specified, line magic not created.
1123 # --cell is specified, line magic not created.
1122 ip.run_line_magic("alias_magic", "--cell timeit_cell_alias timeit")
1124 ip.run_line_magic("alias_magic", "--cell timeit_cell_alias timeit")
1123 assert "timeit_cell_alias" not in mm.magics["line"]
1125 assert "timeit_cell_alias" not in mm.magics["line"]
1124 assert "timeit_cell_alias" in mm.magics["cell"]
1126 assert "timeit_cell_alias" in mm.magics["cell"]
1125
1127
1126 # Test that line alias is created successfully.
1128 # Test that line alias is created successfully.
1127 ip.run_line_magic("alias_magic", "--line env_alias env")
1129 ip.run_line_magic("alias_magic", "--line env_alias env")
1128 assert ip.run_line_magic("env", "") == ip.run_line_magic("env_alias", "")
1130 assert ip.run_line_magic("env", "") == ip.run_line_magic("env_alias", "")
1129
1131
1130 # Test that line alias with parameters passed in is created successfully.
1132 # Test that line alias with parameters passed in is created successfully.
1131 ip.run_line_magic(
1133 ip.run_line_magic(
1132 "alias_magic", "--line history_alias history --params " + shlex.quote("3")
1134 "alias_magic", "--line history_alias history --params " + shlex.quote("3")
1133 )
1135 )
1134 assert "history_alias" in mm.magics["line"]
1136 assert "history_alias" in mm.magics["line"]
1135
1137
1136
1138
1137 def test_save():
1139 def test_save():
1138 """Test %save."""
1140 """Test %save."""
1139 ip = get_ipython()
1141 ip = get_ipython()
1140 ip.history_manager.reset() # Clear any existing history.
1142 ip.history_manager.reset() # Clear any existing history.
1141 cmds = ["a=1", "def b():\n return a**2", "print(a, b())"]
1143 cmds = ["a=1", "def b():\n return a**2", "print(a, b())"]
1142 for i, cmd in enumerate(cmds, start=1):
1144 for i, cmd in enumerate(cmds, start=1):
1143 ip.history_manager.store_inputs(i, cmd)
1145 ip.history_manager.store_inputs(i, cmd)
1144 with TemporaryDirectory() as tmpdir:
1146 with TemporaryDirectory() as tmpdir:
1145 file = os.path.join(tmpdir, "testsave.py")
1147 file = os.path.join(tmpdir, "testsave.py")
1146 ip.run_line_magic("save", "%s 1-10" % file)
1148 ip.run_line_magic("save", "%s 1-10" % file)
1147 content = Path(file).read_text()
1149 content = Path(file).read_text()
1148 assert content.count(cmds[0]) == 1
1150 assert content.count(cmds[0]) == 1
1149 assert "coding: utf-8" in content
1151 assert "coding: utf-8" in content
1150 ip.run_line_magic("save", "-a %s 1-10" % file)
1152 ip.run_line_magic("save", "-a %s 1-10" % file)
1151 content = Path(file).read_text()
1153 content = Path(file).read_text()
1152 assert content.count(cmds[0]) == 2
1154 assert content.count(cmds[0]) == 2
1153 assert "coding: utf-8" in content
1155 assert "coding: utf-8" in content
1154
1156
1155
1157
1156 def test_save_with_no_args():
1158 def test_save_with_no_args():
1157 ip = get_ipython()
1159 ip = get_ipython()
1158 ip.history_manager.reset() # Clear any existing history.
1160 ip.history_manager.reset() # Clear any existing history.
1159 cmds = ["a=1", "def b():\n return a**2", "print(a, b())", "%save"]
1161 cmds = ["a=1", "def b():\n return a**2", "print(a, b())", "%save"]
1160 for i, cmd in enumerate(cmds, start=1):
1162 for i, cmd in enumerate(cmds, start=1):
1161 ip.history_manager.store_inputs(i, cmd)
1163 ip.history_manager.store_inputs(i, cmd)
1162
1164
1163 with TemporaryDirectory() as tmpdir:
1165 with TemporaryDirectory() as tmpdir:
1164 path = os.path.join(tmpdir, "testsave.py")
1166 path = os.path.join(tmpdir, "testsave.py")
1165 ip.run_line_magic("save", path)
1167 ip.run_line_magic("save", path)
1166 content = Path(path).read_text()
1168 content = Path(path).read_text()
1167 expected_content = dedent(
1169 expected_content = dedent(
1168 """\
1170 """\
1169 # coding: utf-8
1171 # coding: utf-8
1170 a=1
1172 a=1
1171 def b():
1173 def b():
1172 return a**2
1174 return a**2
1173 print(a, b())
1175 print(a, b())
1174 """
1176 """
1175 )
1177 )
1176 assert content == expected_content
1178 assert content == expected_content
1177
1179
1178
1180
1179 def test_store():
1181 def test_store():
1180 """Test %store."""
1182 """Test %store."""
1181 ip = get_ipython()
1183 ip = get_ipython()
1182 ip.run_line_magic('load_ext', 'storemagic')
1184 ip.run_line_magic('load_ext', 'storemagic')
1183
1185
1184 # make sure the storage is empty
1186 # make sure the storage is empty
1185 ip.run_line_magic("store", "-z")
1187 ip.run_line_magic("store", "-z")
1186 ip.user_ns["var"] = 42
1188 ip.user_ns["var"] = 42
1187 ip.run_line_magic("store", "var")
1189 ip.run_line_magic("store", "var")
1188 ip.user_ns["var"] = 39
1190 ip.user_ns["var"] = 39
1189 ip.run_line_magic("store", "-r")
1191 ip.run_line_magic("store", "-r")
1190 assert ip.user_ns["var"] == 42
1192 assert ip.user_ns["var"] == 42
1191
1193
1192 ip.run_line_magic("store", "-d var")
1194 ip.run_line_magic("store", "-d var")
1193 ip.user_ns["var"] = 39
1195 ip.user_ns["var"] = 39
1194 ip.run_line_magic("store", "-r")
1196 ip.run_line_magic("store", "-r")
1195 assert ip.user_ns["var"] == 39
1197 assert ip.user_ns["var"] == 39
1196
1198
1197
1199
1198 def _run_edit_test(arg_s, exp_filename=None,
1200 def _run_edit_test(arg_s, exp_filename=None,
1199 exp_lineno=-1,
1201 exp_lineno=-1,
1200 exp_contents=None,
1202 exp_contents=None,
1201 exp_is_temp=None):
1203 exp_is_temp=None):
1202 ip = get_ipython()
1204 ip = get_ipython()
1203 M = code.CodeMagics(ip)
1205 M = code.CodeMagics(ip)
1204 last_call = ['','']
1206 last_call = ['','']
1205 opts,args = M.parse_options(arg_s,'prxn:')
1207 opts,args = M.parse_options(arg_s,'prxn:')
1206 filename, lineno, is_temp = M._find_edit_target(ip, args, opts, last_call)
1208 filename, lineno, is_temp = M._find_edit_target(ip, args, opts, last_call)
1207
1209
1208 if exp_filename is not None:
1210 if exp_filename is not None:
1209 assert exp_filename == filename
1211 assert exp_filename == filename
1210 if exp_contents is not None:
1212 if exp_contents is not None:
1211 with io.open(filename, 'r', encoding='utf-8') as f:
1213 with io.open(filename, 'r', encoding='utf-8') as f:
1212 contents = f.read()
1214 contents = f.read()
1213 assert exp_contents == contents
1215 assert exp_contents == contents
1214 if exp_lineno != -1:
1216 if exp_lineno != -1:
1215 assert exp_lineno == lineno
1217 assert exp_lineno == lineno
1216 if exp_is_temp is not None:
1218 if exp_is_temp is not None:
1217 assert exp_is_temp == is_temp
1219 assert exp_is_temp == is_temp
1218
1220
1219
1221
1220 def test_edit_interactive():
1222 def test_edit_interactive():
1221 """%edit on interactively defined objects"""
1223 """%edit on interactively defined objects"""
1222 ip = get_ipython()
1224 ip = get_ipython()
1223 n = ip.execution_count
1225 n = ip.execution_count
1224 ip.run_cell("def foo(): return 1", store_history=True)
1226 ip.run_cell("def foo(): return 1", store_history=True)
1225
1227
1226 with pytest.raises(code.InteractivelyDefined) as e:
1228 with pytest.raises(code.InteractivelyDefined) as e:
1227 _run_edit_test("foo")
1229 _run_edit_test("foo")
1228 assert e.value.index == n
1230 assert e.value.index == n
1229
1231
1230
1232
1231 def test_edit_cell():
1233 def test_edit_cell():
1232 """%edit [cell id]"""
1234 """%edit [cell id]"""
1233 ip = get_ipython()
1235 ip = get_ipython()
1234
1236
1235 ip.run_cell("def foo(): return 1", store_history=True)
1237 ip.run_cell("def foo(): return 1", store_history=True)
1236
1238
1237 # test
1239 # test
1238 _run_edit_test("1", exp_contents=ip.user_ns['In'][1], exp_is_temp=True)
1240 _run_edit_test("1", exp_contents=ip.user_ns['In'][1], exp_is_temp=True)
1239
1241
1240 def test_edit_fname():
1242 def test_edit_fname():
1241 """%edit file"""
1243 """%edit file"""
1242 # test
1244 # test
1243 _run_edit_test("test file.py", exp_filename="test file.py")
1245 _run_edit_test("test file.py", exp_filename="test file.py")
1244
1246
1245 def test_bookmark():
1247 def test_bookmark():
1246 ip = get_ipython()
1248 ip = get_ipython()
1247 ip.run_line_magic('bookmark', 'bmname')
1249 ip.run_line_magic('bookmark', 'bmname')
1248 with tt.AssertPrints('bmname'):
1250 with tt.AssertPrints('bmname'):
1249 ip.run_line_magic('bookmark', '-l')
1251 ip.run_line_magic('bookmark', '-l')
1250 ip.run_line_magic('bookmark', '-d bmname')
1252 ip.run_line_magic('bookmark', '-d bmname')
1251
1253
1252 def test_ls_magic():
1254 def test_ls_magic():
1253 ip = get_ipython()
1255 ip = get_ipython()
1254 json_formatter = ip.display_formatter.formatters['application/json']
1256 json_formatter = ip.display_formatter.formatters['application/json']
1255 json_formatter.enabled = True
1257 json_formatter.enabled = True
1256 lsmagic = ip.magic('lsmagic')
1258 lsmagic = ip.magic('lsmagic')
1257 with warnings.catch_warnings(record=True) as w:
1259 with warnings.catch_warnings(record=True) as w:
1258 j = json_formatter(lsmagic)
1260 j = json_formatter(lsmagic)
1259 assert sorted(j) == ["cell", "line"]
1261 assert sorted(j) == ["cell", "line"]
1260 assert w == [] # no warnings
1262 assert w == [] # no warnings
1261
1263
1262
1264
1263 def test_strip_initial_indent():
1265 def test_strip_initial_indent():
1264 def sii(s):
1266 def sii(s):
1265 lines = s.splitlines()
1267 lines = s.splitlines()
1266 return '\n'.join(code.strip_initial_indent(lines))
1268 return '\n'.join(code.strip_initial_indent(lines))
1267
1269
1268 assert sii(" a = 1\nb = 2") == "a = 1\nb = 2"
1270 assert sii(" a = 1\nb = 2") == "a = 1\nb = 2"
1269 assert sii(" a\n b\nc") == "a\n b\nc"
1271 assert sii(" a\n b\nc") == "a\n b\nc"
1270 assert sii("a\n b") == "a\n b"
1272 assert sii("a\n b") == "a\n b"
1271
1273
1272 def test_logging_magic_quiet_from_arg():
1274 def test_logging_magic_quiet_from_arg():
1273 _ip.config.LoggingMagics.quiet = False
1275 _ip.config.LoggingMagics.quiet = False
1274 lm = logging.LoggingMagics(shell=_ip)
1276 lm = logging.LoggingMagics(shell=_ip)
1275 with TemporaryDirectory() as td:
1277 with TemporaryDirectory() as td:
1276 try:
1278 try:
1277 with tt.AssertNotPrints(re.compile("Activating.*")):
1279 with tt.AssertNotPrints(re.compile("Activating.*")):
1278 lm.logstart('-q {}'.format(
1280 lm.logstart('-q {}'.format(
1279 os.path.join(td, "quiet_from_arg.log")))
1281 os.path.join(td, "quiet_from_arg.log")))
1280 finally:
1282 finally:
1281 _ip.logger.logstop()
1283 _ip.logger.logstop()
1282
1284
1283 def test_logging_magic_quiet_from_config():
1285 def test_logging_magic_quiet_from_config():
1284 _ip.config.LoggingMagics.quiet = True
1286 _ip.config.LoggingMagics.quiet = True
1285 lm = logging.LoggingMagics(shell=_ip)
1287 lm = logging.LoggingMagics(shell=_ip)
1286 with TemporaryDirectory() as td:
1288 with TemporaryDirectory() as td:
1287 try:
1289 try:
1288 with tt.AssertNotPrints(re.compile("Activating.*")):
1290 with tt.AssertNotPrints(re.compile("Activating.*")):
1289 lm.logstart(os.path.join(td, "quiet_from_config.log"))
1291 lm.logstart(os.path.join(td, "quiet_from_config.log"))
1290 finally:
1292 finally:
1291 _ip.logger.logstop()
1293 _ip.logger.logstop()
1292
1294
1293
1295
1294 def test_logging_magic_not_quiet():
1296 def test_logging_magic_not_quiet():
1295 _ip.config.LoggingMagics.quiet = False
1297 _ip.config.LoggingMagics.quiet = False
1296 lm = logging.LoggingMagics(shell=_ip)
1298 lm = logging.LoggingMagics(shell=_ip)
1297 with TemporaryDirectory() as td:
1299 with TemporaryDirectory() as td:
1298 try:
1300 try:
1299 with tt.AssertPrints(re.compile("Activating.*")):
1301 with tt.AssertPrints(re.compile("Activating.*")):
1300 lm.logstart(os.path.join(td, "not_quiet.log"))
1302 lm.logstart(os.path.join(td, "not_quiet.log"))
1301 finally:
1303 finally:
1302 _ip.logger.logstop()
1304 _ip.logger.logstop()
1303
1305
1304
1306
1305 def test_time_no_var_expand():
1307 def test_time_no_var_expand():
1306 _ip.user_ns['a'] = 5
1308 _ip.user_ns['a'] = 5
1307 _ip.user_ns['b'] = []
1309 _ip.user_ns['b'] = []
1308 _ip.magic('time b.append("{a}")')
1310 _ip.magic('time b.append("{a}")')
1309 assert _ip.user_ns['b'] == ['{a}']
1311 assert _ip.user_ns['b'] == ['{a}']
1310
1312
1311
1313
1312 # this is slow, put at the end for local testing.
1314 # this is slow, put at the end for local testing.
1313 def test_timeit_arguments():
1315 def test_timeit_arguments():
1314 "Test valid timeit arguments, should not cause SyntaxError (GH #1269)"
1316 "Test valid timeit arguments, should not cause SyntaxError (GH #1269)"
1315 _ip.magic("timeit -n1 -r1 a=('#')")
1317 _ip.magic("timeit -n1 -r1 a=('#')")
1316
1318
1317
1319
1318 TEST_MODULE = """
1320 TEST_MODULE = """
1319 print('Loaded my_tmp')
1321 print('Loaded my_tmp')
1320 if __name__ == "__main__":
1322 if __name__ == "__main__":
1321 print('I just ran a script')
1323 print('I just ran a script')
1322 """
1324 """
1323
1325
1324
1326
1325 def test_run_module_from_import_hook():
1327 def test_run_module_from_import_hook():
1326 "Test that a module can be loaded via an import hook"
1328 "Test that a module can be loaded via an import hook"
1327 with TemporaryDirectory() as tmpdir:
1329 with TemporaryDirectory() as tmpdir:
1328 fullpath = os.path.join(tmpdir, 'my_tmp.py')
1330 fullpath = os.path.join(tmpdir, 'my_tmp.py')
1329 Path(fullpath).write_text(TEST_MODULE)
1331 Path(fullpath).write_text(TEST_MODULE)
1330
1332
1331 import importlib.abc
1333 import importlib.abc
1332 import importlib.util
1334 import importlib.util
1333
1335
1334 class MyTempImporter(importlib.abc.MetaPathFinder, importlib.abc.SourceLoader):
1336 class MyTempImporter(importlib.abc.MetaPathFinder, importlib.abc.SourceLoader):
1335 def find_spec(self, fullname, path, target=None):
1337 def find_spec(self, fullname, path, target=None):
1336 if fullname == "my_tmp":
1338 if fullname == "my_tmp":
1337 return importlib.util.spec_from_loader(fullname, self)
1339 return importlib.util.spec_from_loader(fullname, self)
1338
1340
1339 def get_filename(self, fullname):
1341 def get_filename(self, fullname):
1340 if fullname != "my_tmp":
1342 if fullname != "my_tmp":
1341 raise ImportError(f"unexpected module name '{fullname}'")
1343 raise ImportError(f"unexpected module name '{fullname}'")
1342 return fullpath
1344 return fullpath
1343
1345
1344 def get_data(self, path):
1346 def get_data(self, path):
1345 if not Path(path).samefile(fullpath):
1347 if not Path(path).samefile(fullpath):
1346 raise OSError(f"expected path '{fullpath}', got '{path}'")
1348 raise OSError(f"expected path '{fullpath}', got '{path}'")
1347 return Path(fullpath).read_text()
1349 return Path(fullpath).read_text()
1348
1350
1349 sys.meta_path.insert(0, MyTempImporter())
1351 sys.meta_path.insert(0, MyTempImporter())
1350
1352
1351 with capture_output() as captured:
1353 with capture_output() as captured:
1352 _ip.magic("run -m my_tmp")
1354 _ip.magic("run -m my_tmp")
1353 _ip.run_cell("import my_tmp")
1355 _ip.run_cell("import my_tmp")
1354
1356
1355 output = "Loaded my_tmp\nI just ran a script\nLoaded my_tmp\n"
1357 output = "Loaded my_tmp\nI just ran a script\nLoaded my_tmp\n"
1356 assert output == captured.stdout
1358 assert output == captured.stdout
1357
1359
1358 sys.meta_path.pop(0)
1360 sys.meta_path.pop(0)
@@ -1,601 +1,612 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 Note that any test using `run -i` should make sure to do a `reset` afterwards,
10 Note that any test using `run -i` should make sure to do a `reset` afterwards,
11 as otherwise it may influence later tests.
11 as otherwise it may influence later tests.
12 """
12 """
13
13
14 # Copyright (c) IPython Development Team.
14 # Copyright (c) IPython Development Team.
15 # Distributed under the terms of the Modified BSD License.
15 # Distributed under the terms of the Modified BSD License.
16
16
17
17
18
18
19 import functools
19 import functools
20 import os
20 import os
21 import platform
21 from os.path import join as pjoin
22 from os.path import join as pjoin
22 import random
23 import random
23 import string
24 import string
24 import sys
25 import sys
25 import textwrap
26 import textwrap
26 import unittest
27 import unittest
27 from unittest.mock import patch
28 from unittest.mock import patch
28
29
29 import pytest
30 import pytest
30
31
31 from IPython.testing import decorators as dec
32 from IPython.testing import decorators as dec
32 from IPython.testing import tools as tt
33 from IPython.testing import tools as tt
33 from IPython.utils.io import capture_output
34 from IPython.utils.io import capture_output
34 from IPython.utils.tempdir import TemporaryDirectory
35 from IPython.utils.tempdir import TemporaryDirectory
35 from IPython.core import debugger
36 from IPython.core import debugger
36
37
37 def doctest_refbug():
38 def doctest_refbug():
38 """Very nasty problem with references held by multiple runs of a script.
39 """Very nasty problem with references held by multiple runs of a script.
39 See: https://github.com/ipython/ipython/issues/141
40 See: https://github.com/ipython/ipython/issues/141
40
41
41 In [1]: _ip.clear_main_mod_cache()
42 In [1]: _ip.clear_main_mod_cache()
42 # random
43 # random
43
44
44 In [2]: %run refbug
45 In [2]: %run refbug
45
46
46 In [3]: call_f()
47 In [3]: call_f()
47 lowercased: hello
48 lowercased: hello
48
49
49 In [4]: %run refbug
50 In [4]: %run refbug
50
51
51 In [5]: call_f()
52 In [5]: call_f()
52 lowercased: hello
53 lowercased: hello
53 lowercased: hello
54 lowercased: hello
54 """
55 """
55
56
56
57
57 def doctest_run_builtins():
58 def doctest_run_builtins():
58 r"""Check that %run doesn't damage __builtins__.
59 r"""Check that %run doesn't damage __builtins__.
59
60
60 In [1]: import tempfile
61 In [1]: import tempfile
61
62
62 In [2]: bid1 = id(__builtins__)
63 In [2]: bid1 = id(__builtins__)
63
64
64 In [3]: fname = tempfile.mkstemp('.py')[1]
65 In [3]: fname = tempfile.mkstemp('.py')[1]
65
66
66 In [3]: f = open(fname,'w')
67 In [3]: f = open(fname,'w')
67
68
68 In [4]: dummy= f.write('pass\n')
69 In [4]: dummy= f.write('pass\n')
69
70
70 In [5]: f.flush()
71 In [5]: f.flush()
71
72
72 In [6]: t1 = type(__builtins__)
73 In [6]: t1 = type(__builtins__)
73
74
74 In [7]: %run $fname
75 In [7]: %run $fname
75
76
76 In [7]: f.close()
77 In [7]: f.close()
77
78
78 In [8]: bid2 = id(__builtins__)
79 In [8]: bid2 = id(__builtins__)
79
80
80 In [9]: t2 = type(__builtins__)
81 In [9]: t2 = type(__builtins__)
81
82
82 In [10]: t1 == t2
83 In [10]: t1 == t2
83 Out[10]: True
84 Out[10]: True
84
85
85 In [10]: bid1 == bid2
86 In [10]: bid1 == bid2
86 Out[10]: True
87 Out[10]: True
87
88
88 In [12]: try:
89 In [12]: try:
89 ....: os.unlink(fname)
90 ....: os.unlink(fname)
90 ....: except:
91 ....: except:
91 ....: pass
92 ....: pass
92 ....:
93 ....:
93 """
94 """
94
95
95
96
96 def doctest_run_option_parser():
97 def doctest_run_option_parser():
97 r"""Test option parser in %run.
98 r"""Test option parser in %run.
98
99
99 In [1]: %run print_argv.py
100 In [1]: %run print_argv.py
100 []
101 []
101
102
102 In [2]: %run print_argv.py print*.py
103 In [2]: %run print_argv.py print*.py
103 ['print_argv.py']
104 ['print_argv.py']
104
105
105 In [3]: %run -G print_argv.py print*.py
106 In [3]: %run -G print_argv.py print*.py
106 ['print*.py']
107 ['print*.py']
107
108
108 """
109 """
109
110
110
111
111 @dec.skip_win32
112 @dec.skip_win32
112 def doctest_run_option_parser_for_posix():
113 def doctest_run_option_parser_for_posix():
113 r"""Test option parser in %run (Linux/OSX specific).
114 r"""Test option parser in %run (Linux/OSX specific).
114
115
115 You need double quote to escape glob in POSIX systems:
116 You need double quote to escape glob in POSIX systems:
116
117
117 In [1]: %run print_argv.py print\\*.py
118 In [1]: %run print_argv.py print\\*.py
118 ['print*.py']
119 ['print*.py']
119
120
120 You can't use quote to escape glob in POSIX systems:
121 You can't use quote to escape glob in POSIX systems:
121
122
122 In [2]: %run print_argv.py 'print*.py'
123 In [2]: %run print_argv.py 'print*.py'
123 ['print_argv.py']
124 ['print_argv.py']
124
125
125 """
126 """
126
127
127
128
128 doctest_run_option_parser_for_posix.__skip_doctest__ = sys.platform == "win32"
129 doctest_run_option_parser_for_posix.__skip_doctest__ = sys.platform == "win32"
129
130
130
131
131 @dec.skip_if_not_win32
132 @dec.skip_if_not_win32
132 def doctest_run_option_parser_for_windows():
133 def doctest_run_option_parser_for_windows():
133 r"""Test option parser in %run (Windows specific).
134 r"""Test option parser in %run (Windows specific).
134
135
135 In Windows, you can't escape ``*` `by backslash:
136 In Windows, you can't escape ``*` `by backslash:
136
137
137 In [1]: %run print_argv.py print\\*.py
138 In [1]: %run print_argv.py print\\*.py
138 ['print\\\\*.py']
139 ['print\\\\*.py']
139
140
140 You can use quote to escape glob:
141 You can use quote to escape glob:
141
142
142 In [2]: %run print_argv.py 'print*.py'
143 In [2]: %run print_argv.py 'print*.py'
143 ["'print*.py'"]
144 ["'print*.py'"]
144
145
145 """
146 """
146
147
147
148
148 doctest_run_option_parser_for_windows.__skip_doctest__ = sys.platform != "win32"
149 doctest_run_option_parser_for_windows.__skip_doctest__ = sys.platform != "win32"
149
150
150
151
151 def doctest_reset_del():
152 def doctest_reset_del():
152 """Test that resetting doesn't cause errors in __del__ methods.
153 """Test that resetting doesn't cause errors in __del__ methods.
153
154
154 In [2]: class A(object):
155 In [2]: class A(object):
155 ...: def __del__(self):
156 ...: def __del__(self):
156 ...: print(str("Hi"))
157 ...: print(str("Hi"))
157 ...:
158 ...:
158
159
159 In [3]: a = A()
160 In [3]: a = A()
160
161
161 In [4]: get_ipython().reset()
162 In [4]: get_ipython().reset(); import gc; x = gc.collect(0)
162 Hi
163 Hi
163
164
164 In [5]: 1+1
165 In [5]: 1+1
165 Out[5]: 2
166 Out[5]: 2
166 """
167 """
167
168
168 # For some tests, it will be handy to organize them in a class with a common
169 # For some tests, it will be handy to organize them in a class with a common
169 # setup that makes a temp file
170 # setup that makes a temp file
170
171
171 class TestMagicRunPass(tt.TempFileMixin):
172 class TestMagicRunPass(tt.TempFileMixin):
172
173
173 def setUp(self):
174 def setUp(self):
174 content = "a = [1,2,3]\nb = 1"
175 content = "a = [1,2,3]\nb = 1"
175 self.mktmp(content)
176 self.mktmp(content)
176
177
177 def run_tmpfile(self):
178 def run_tmpfile(self):
178 _ip = get_ipython()
179 _ip = get_ipython()
179 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
180 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
180 # See below and ticket https://bugs.launchpad.net/bugs/366353
181 # See below and ticket https://bugs.launchpad.net/bugs/366353
181 _ip.magic('run %s' % self.fname)
182 _ip.magic('run %s' % self.fname)
182
183
183 def run_tmpfile_p(self):
184 def run_tmpfile_p(self):
184 _ip = get_ipython()
185 _ip = get_ipython()
185 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
186 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
186 # See below and ticket https://bugs.launchpad.net/bugs/366353
187 # See below and ticket https://bugs.launchpad.net/bugs/366353
187 _ip.magic('run -p %s' % self.fname)
188 _ip.magic('run -p %s' % self.fname)
188
189
189 def test_builtins_id(self):
190 def test_builtins_id(self):
190 """Check that %run doesn't damage __builtins__ """
191 """Check that %run doesn't damage __builtins__ """
191 _ip = get_ipython()
192 _ip = get_ipython()
192 # Test that the id of __builtins__ is not modified by %run
193 # Test that the id of __builtins__ is not modified by %run
193 bid1 = id(_ip.user_ns['__builtins__'])
194 bid1 = id(_ip.user_ns['__builtins__'])
194 self.run_tmpfile()
195 self.run_tmpfile()
195 bid2 = id(_ip.user_ns['__builtins__'])
196 bid2 = id(_ip.user_ns['__builtins__'])
196 assert bid1 == bid2
197 assert bid1 == bid2
197
198
198 def test_builtins_type(self):
199 def test_builtins_type(self):
199 """Check that the type of __builtins__ doesn't change with %run.
200 """Check that the type of __builtins__ doesn't change with %run.
200
201
201 However, the above could pass if __builtins__ was already modified to
202 However, the above could pass if __builtins__ was already modified to
202 be a dict (it should be a module) by a previous use of %run. So we
203 be a dict (it should be a module) by a previous use of %run. So we
203 also check explicitly that it really is a module:
204 also check explicitly that it really is a module:
204 """
205 """
205 _ip = get_ipython()
206 _ip = get_ipython()
206 self.run_tmpfile()
207 self.run_tmpfile()
207 assert type(_ip.user_ns["__builtins__"]) == type(sys)
208 assert type(_ip.user_ns["__builtins__"]) == type(sys)
208
209
209 def test_run_profile(self):
210 def test_run_profile(self):
210 """Test that the option -p, which invokes the profiler, do not
211 """Test that the option -p, which invokes the profiler, do not
211 crash by invoking execfile"""
212 crash by invoking execfile"""
212 self.run_tmpfile_p()
213 self.run_tmpfile_p()
213
214
214 def test_run_debug_twice(self):
215 def test_run_debug_twice(self):
215 # https://github.com/ipython/ipython/issues/10028
216 # https://github.com/ipython/ipython/issues/10028
216 _ip = get_ipython()
217 _ip = get_ipython()
217 with tt.fake_input(['c']):
218 with tt.fake_input(['c']):
218 _ip.magic('run -d %s' % self.fname)
219 _ip.magic('run -d %s' % self.fname)
219 with tt.fake_input(['c']):
220 with tt.fake_input(['c']):
220 _ip.magic('run -d %s' % self.fname)
221 _ip.magic('run -d %s' % self.fname)
221
222
222 def test_run_debug_twice_with_breakpoint(self):
223 def test_run_debug_twice_with_breakpoint(self):
223 """Make a valid python temp file."""
224 """Make a valid python temp file."""
224 _ip = get_ipython()
225 _ip = get_ipython()
225 with tt.fake_input(['b 2', 'c', 'c']):
226 with tt.fake_input(['b 2', 'c', 'c']):
226 _ip.magic('run -d %s' % self.fname)
227 _ip.magic('run -d %s' % self.fname)
227
228
228 with tt.fake_input(['c']):
229 with tt.fake_input(['c']):
229 with tt.AssertNotPrints('KeyError'):
230 with tt.AssertNotPrints('KeyError'):
230 _ip.magic('run -d %s' % self.fname)
231 _ip.magic('run -d %s' % self.fname)
231
232
232
233
233 class TestMagicRunSimple(tt.TempFileMixin):
234 class TestMagicRunSimple(tt.TempFileMixin):
234
235
235 def test_simpledef(self):
236 def test_simpledef(self):
236 """Test that simple class definitions work."""
237 """Test that simple class definitions work."""
237 src = ("class foo: pass\n"
238 src = ("class foo: pass\n"
238 "def f(): return foo()")
239 "def f(): return foo()")
239 self.mktmp(src)
240 self.mktmp(src)
240 _ip.magic("run %s" % self.fname)
241 _ip.magic("run %s" % self.fname)
241 _ip.run_cell("t = isinstance(f(), foo)")
242 _ip.run_cell("t = isinstance(f(), foo)")
242 assert _ip.user_ns["t"] is True
243 assert _ip.user_ns["t"] is True
243
244
245 @pytest.mark.xfail(
246 platform.python_implementation() == "PyPy",
247 reason="expecting __del__ call on exit is unreliable and doesn't happen on PyPy",
248 )
244 def test_obj_del(self):
249 def test_obj_del(self):
245 """Test that object's __del__ methods are called on exit."""
250 """Test that object's __del__ methods are called on exit."""
246 src = ("class A(object):\n"
251 src = ("class A(object):\n"
247 " def __del__(self):\n"
252 " def __del__(self):\n"
248 " print('object A deleted')\n"
253 " print('object A deleted')\n"
249 "a = A()\n")
254 "a = A()\n")
250 self.mktmp(src)
255 self.mktmp(src)
251 err = None
256 err = None
252 tt.ipexec_validate(self.fname, 'object A deleted', err)
257 tt.ipexec_validate(self.fname, 'object A deleted', err)
253
258
254 def test_aggressive_namespace_cleanup(self):
259 def test_aggressive_namespace_cleanup(self):
255 """Test that namespace cleanup is not too aggressive GH-238
260 """Test that namespace cleanup is not too aggressive GH-238
256
261
257 Returning from another run magic deletes the namespace"""
262 Returning from another run magic deletes the namespace"""
258 # see ticket https://github.com/ipython/ipython/issues/238
263 # see ticket https://github.com/ipython/ipython/issues/238
259
264
260 with tt.TempFileMixin() as empty:
265 with tt.TempFileMixin() as empty:
261 empty.mktmp("")
266 empty.mktmp("")
262 # On Windows, the filename will have \users in it, so we need to use the
267 # On Windows, the filename will have \users in it, so we need to use the
263 # repr so that the \u becomes \\u.
268 # repr so that the \u becomes \\u.
264 src = (
269 src = (
265 "ip = get_ipython()\n"
270 "ip = get_ipython()\n"
266 "for i in range(5):\n"
271 "for i in range(5):\n"
267 " try:\n"
272 " try:\n"
268 " ip.magic(%r)\n"
273 " ip.magic(%r)\n"
269 " except NameError as e:\n"
274 " except NameError as e:\n"
270 " print(i)\n"
275 " print(i)\n"
271 " break\n" % ("run " + empty.fname)
276 " break\n" % ("run " + empty.fname)
272 )
277 )
273 self.mktmp(src)
278 self.mktmp(src)
274 _ip.magic("run %s" % self.fname)
279 _ip.magic("run %s" % self.fname)
275 _ip.run_cell("ip == get_ipython()")
280 _ip.run_cell("ip == get_ipython()")
276 assert _ip.user_ns["i"] == 4
281 assert _ip.user_ns["i"] == 4
277
282
278 def test_run_second(self):
283 def test_run_second(self):
279 """Test that running a second file doesn't clobber the first, gh-3547"""
284 """Test that running a second file doesn't clobber the first, gh-3547"""
280 self.mktmp("avar = 1\n" "def afunc():\n" " return avar\n")
285 self.mktmp("avar = 1\n" "def afunc():\n" " return avar\n")
281
286
282 with tt.TempFileMixin() as empty:
287 with tt.TempFileMixin() as empty:
283 empty.mktmp("")
288 empty.mktmp("")
284
289
285 _ip.magic("run %s" % self.fname)
290 _ip.magic("run %s" % self.fname)
286 _ip.magic("run %s" % empty.fname)
291 _ip.magic("run %s" % empty.fname)
287 assert _ip.user_ns["afunc"]() == 1
292 assert _ip.user_ns["afunc"]() == 1
288
293
289 @dec.skip_win32
290 def test_tclass(self):
294 def test_tclass(self):
291 mydir = os.path.dirname(__file__)
295 mydir = os.path.dirname(__file__)
292 tc = os.path.join(mydir, 'tclass')
296 tc = os.path.join(mydir, "tclass")
293 src = ("%%run '%s' C-first\n"
297 src = f"""\
294 "%%run '%s' C-second\n"
298 import gc
295 "%%run '%s' C-third\n") % (tc, tc, tc)
299 %run "{tc}" C-first
296 self.mktmp(src, '.ipy')
300 gc.collect(0)
301 %run "{tc}" C-second
302 gc.collect(0)
303 %run "{tc}" C-third
304 gc.collect(0)
305 %reset -f
306 """
307 self.mktmp(src, ".ipy")
297 out = """\
308 out = """\
298 ARGV 1-: ['C-first']
309 ARGV 1-: ['C-first']
299 ARGV 1-: ['C-second']
310 ARGV 1-: ['C-second']
300 tclass.py: deleting object: C-first
311 tclass.py: deleting object: C-first
301 ARGV 1-: ['C-third']
312 ARGV 1-: ['C-third']
302 tclass.py: deleting object: C-second
313 tclass.py: deleting object: C-second
303 tclass.py: deleting object: C-third
314 tclass.py: deleting object: C-third
304 """
315 """
305 err = None
316 err = None
306 tt.ipexec_validate(self.fname, out, err)
317 tt.ipexec_validate(self.fname, out, err)
307
318
308 def test_run_i_after_reset(self):
319 def test_run_i_after_reset(self):
309 """Check that %run -i still works after %reset (gh-693)"""
320 """Check that %run -i still works after %reset (gh-693)"""
310 src = "yy = zz\n"
321 src = "yy = zz\n"
311 self.mktmp(src)
322 self.mktmp(src)
312 _ip.run_cell("zz = 23")
323 _ip.run_cell("zz = 23")
313 try:
324 try:
314 _ip.magic("run -i %s" % self.fname)
325 _ip.magic("run -i %s" % self.fname)
315 assert _ip.user_ns["yy"] == 23
326 assert _ip.user_ns["yy"] == 23
316 finally:
327 finally:
317 _ip.magic('reset -f')
328 _ip.magic('reset -f')
318
329
319 _ip.run_cell("zz = 23")
330 _ip.run_cell("zz = 23")
320 try:
331 try:
321 _ip.magic("run -i %s" % self.fname)
332 _ip.magic("run -i %s" % self.fname)
322 assert _ip.user_ns["yy"] == 23
333 assert _ip.user_ns["yy"] == 23
323 finally:
334 finally:
324 _ip.magic('reset -f')
335 _ip.magic('reset -f')
325
336
326 def test_unicode(self):
337 def test_unicode(self):
327 """Check that files in odd encodings are accepted."""
338 """Check that files in odd encodings are accepted."""
328 mydir = os.path.dirname(__file__)
339 mydir = os.path.dirname(__file__)
329 na = os.path.join(mydir, 'nonascii.py')
340 na = os.path.join(mydir, 'nonascii.py')
330 _ip.magic('run "%s"' % na)
341 _ip.magic('run "%s"' % na)
331 assert _ip.user_ns["u"] == "Ўт№Ф"
342 assert _ip.user_ns["u"] == "Ўт№Ф"
332
343
333 def test_run_py_file_attribute(self):
344 def test_run_py_file_attribute(self):
334 """Test handling of `__file__` attribute in `%run <file>.py`."""
345 """Test handling of `__file__` attribute in `%run <file>.py`."""
335 src = "t = __file__\n"
346 src = "t = __file__\n"
336 self.mktmp(src)
347 self.mktmp(src)
337 _missing = object()
348 _missing = object()
338 file1 = _ip.user_ns.get('__file__', _missing)
349 file1 = _ip.user_ns.get('__file__', _missing)
339 _ip.magic('run %s' % self.fname)
350 _ip.magic('run %s' % self.fname)
340 file2 = _ip.user_ns.get('__file__', _missing)
351 file2 = _ip.user_ns.get('__file__', _missing)
341
352
342 # Check that __file__ was equal to the filename in the script's
353 # Check that __file__ was equal to the filename in the script's
343 # namespace.
354 # namespace.
344 assert _ip.user_ns["t"] == self.fname
355 assert _ip.user_ns["t"] == self.fname
345
356
346 # Check that __file__ was not leaked back into user_ns.
357 # Check that __file__ was not leaked back into user_ns.
347 assert file1 == file2
358 assert file1 == file2
348
359
349 def test_run_ipy_file_attribute(self):
360 def test_run_ipy_file_attribute(self):
350 """Test handling of `__file__` attribute in `%run <file.ipy>`."""
361 """Test handling of `__file__` attribute in `%run <file.ipy>`."""
351 src = "t = __file__\n"
362 src = "t = __file__\n"
352 self.mktmp(src, ext='.ipy')
363 self.mktmp(src, ext='.ipy')
353 _missing = object()
364 _missing = object()
354 file1 = _ip.user_ns.get('__file__', _missing)
365 file1 = _ip.user_ns.get('__file__', _missing)
355 _ip.magic('run %s' % self.fname)
366 _ip.magic('run %s' % self.fname)
356 file2 = _ip.user_ns.get('__file__', _missing)
367 file2 = _ip.user_ns.get('__file__', _missing)
357
368
358 # Check that __file__ was equal to the filename in the script's
369 # Check that __file__ was equal to the filename in the script's
359 # namespace.
370 # namespace.
360 assert _ip.user_ns["t"] == self.fname
371 assert _ip.user_ns["t"] == self.fname
361
372
362 # Check that __file__ was not leaked back into user_ns.
373 # Check that __file__ was not leaked back into user_ns.
363 assert file1 == file2
374 assert file1 == file2
364
375
365 def test_run_formatting(self):
376 def test_run_formatting(self):
366 """ Test that %run -t -N<N> does not raise a TypeError for N > 1."""
377 """ Test that %run -t -N<N> does not raise a TypeError for N > 1."""
367 src = "pass"
378 src = "pass"
368 self.mktmp(src)
379 self.mktmp(src)
369 _ip.magic('run -t -N 1 %s' % self.fname)
380 _ip.magic('run -t -N 1 %s' % self.fname)
370 _ip.magic('run -t -N 10 %s' % self.fname)
381 _ip.magic('run -t -N 10 %s' % self.fname)
371
382
372 def test_ignore_sys_exit(self):
383 def test_ignore_sys_exit(self):
373 """Test the -e option to ignore sys.exit()"""
384 """Test the -e option to ignore sys.exit()"""
374 src = "import sys; sys.exit(1)"
385 src = "import sys; sys.exit(1)"
375 self.mktmp(src)
386 self.mktmp(src)
376 with tt.AssertPrints('SystemExit'):
387 with tt.AssertPrints('SystemExit'):
377 _ip.magic('run %s' % self.fname)
388 _ip.magic('run %s' % self.fname)
378
389
379 with tt.AssertNotPrints('SystemExit'):
390 with tt.AssertNotPrints('SystemExit'):
380 _ip.magic('run -e %s' % self.fname)
391 _ip.magic('run -e %s' % self.fname)
381
392
382 def test_run_nb(self):
393 def test_run_nb(self):
383 """Test %run notebook.ipynb"""
394 """Test %run notebook.ipynb"""
384 pytest.importorskip("nbformat")
395 pytest.importorskip("nbformat")
385 from nbformat import v4, writes
396 from nbformat import v4, writes
386 nb = v4.new_notebook(
397 nb = v4.new_notebook(
387 cells=[
398 cells=[
388 v4.new_markdown_cell("The Ultimate Question of Everything"),
399 v4.new_markdown_cell("The Ultimate Question of Everything"),
389 v4.new_code_cell("answer=42")
400 v4.new_code_cell("answer=42")
390 ]
401 ]
391 )
402 )
392 src = writes(nb, version=4)
403 src = writes(nb, version=4)
393 self.mktmp(src, ext='.ipynb')
404 self.mktmp(src, ext='.ipynb')
394
405
395 _ip.magic("run %s" % self.fname)
406 _ip.magic("run %s" % self.fname)
396
407
397 assert _ip.user_ns["answer"] == 42
408 assert _ip.user_ns["answer"] == 42
398
409
399 def test_run_nb_error(self):
410 def test_run_nb_error(self):
400 """Test %run notebook.ipynb error"""
411 """Test %run notebook.ipynb error"""
401 pytest.importorskip("nbformat")
412 pytest.importorskip("nbformat")
402 from nbformat import v4, writes
413 from nbformat import v4, writes
403 # %run when a file name isn't provided
414 # %run when a file name isn't provided
404 pytest.raises(Exception, _ip.magic, "run")
415 pytest.raises(Exception, _ip.magic, "run")
405
416
406 # %run when a file doesn't exist
417 # %run when a file doesn't exist
407 pytest.raises(Exception, _ip.magic, "run foobar.ipynb")
418 pytest.raises(Exception, _ip.magic, "run foobar.ipynb")
408
419
409 # %run on a notebook with an error
420 # %run on a notebook with an error
410 nb = v4.new_notebook(
421 nb = v4.new_notebook(
411 cells=[
422 cells=[
412 v4.new_code_cell("0/0")
423 v4.new_code_cell("0/0")
413 ]
424 ]
414 )
425 )
415 src = writes(nb, version=4)
426 src = writes(nb, version=4)
416 self.mktmp(src, ext='.ipynb')
427 self.mktmp(src, ext='.ipynb')
417 pytest.raises(Exception, _ip.magic, "run %s" % self.fname)
428 pytest.raises(Exception, _ip.magic, "run %s" % self.fname)
418
429
419 def test_file_options(self):
430 def test_file_options(self):
420 src = ('import sys\n'
431 src = ('import sys\n'
421 'a = " ".join(sys.argv[1:])\n')
432 'a = " ".join(sys.argv[1:])\n')
422 self.mktmp(src)
433 self.mktmp(src)
423 test_opts = "-x 3 --verbose"
434 test_opts = "-x 3 --verbose"
424 _ip.run_line_magic("run", "{0} {1}".format(self.fname, test_opts))
435 _ip.run_line_magic("run", "{0} {1}".format(self.fname, test_opts))
425 assert _ip.user_ns["a"] == test_opts
436 assert _ip.user_ns["a"] == test_opts
426
437
427
438
428 class TestMagicRunWithPackage(unittest.TestCase):
439 class TestMagicRunWithPackage(unittest.TestCase):
429
440
430 def writefile(self, name, content):
441 def writefile(self, name, content):
431 path = os.path.join(self.tempdir.name, name)
442 path = os.path.join(self.tempdir.name, name)
432 d = os.path.dirname(path)
443 d = os.path.dirname(path)
433 if not os.path.isdir(d):
444 if not os.path.isdir(d):
434 os.makedirs(d)
445 os.makedirs(d)
435 with open(path, 'w') as f:
446 with open(path, 'w') as f:
436 f.write(textwrap.dedent(content))
447 f.write(textwrap.dedent(content))
437
448
438 def setUp(self):
449 def setUp(self):
439 self.package = package = 'tmp{0}'.format(''.join([random.choice(string.ascii_letters) for i in range(10)]))
450 self.package = package = 'tmp{0}'.format(''.join([random.choice(string.ascii_letters) for i in range(10)]))
440 """Temporary (probably) valid python package name."""
451 """Temporary (probably) valid python package name."""
441
452
442 self.value = int(random.random() * 10000)
453 self.value = int(random.random() * 10000)
443
454
444 self.tempdir = TemporaryDirectory()
455 self.tempdir = TemporaryDirectory()
445 self.__orig_cwd = os.getcwd()
456 self.__orig_cwd = os.getcwd()
446 sys.path.insert(0, self.tempdir.name)
457 sys.path.insert(0, self.tempdir.name)
447
458
448 self.writefile(os.path.join(package, '__init__.py'), '')
459 self.writefile(os.path.join(package, '__init__.py'), '')
449 self.writefile(os.path.join(package, 'sub.py'), """
460 self.writefile(os.path.join(package, 'sub.py'), """
450 x = {0!r}
461 x = {0!r}
451 """.format(self.value))
462 """.format(self.value))
452 self.writefile(os.path.join(package, 'relative.py'), """
463 self.writefile(os.path.join(package, 'relative.py'), """
453 from .sub import x
464 from .sub import x
454 """)
465 """)
455 self.writefile(os.path.join(package, 'absolute.py'), """
466 self.writefile(os.path.join(package, 'absolute.py'), """
456 from {0}.sub import x
467 from {0}.sub import x
457 """.format(package))
468 """.format(package))
458 self.writefile(os.path.join(package, 'args.py'), """
469 self.writefile(os.path.join(package, 'args.py'), """
459 import sys
470 import sys
460 a = " ".join(sys.argv[1:])
471 a = " ".join(sys.argv[1:])
461 """.format(package))
472 """.format(package))
462
473
463 def tearDown(self):
474 def tearDown(self):
464 os.chdir(self.__orig_cwd)
475 os.chdir(self.__orig_cwd)
465 sys.path[:] = [p for p in sys.path if p != self.tempdir.name]
476 sys.path[:] = [p for p in sys.path if p != self.tempdir.name]
466 self.tempdir.cleanup()
477 self.tempdir.cleanup()
467
478
468 def check_run_submodule(self, submodule, opts=''):
479 def check_run_submodule(self, submodule, opts=''):
469 _ip.user_ns.pop('x', None)
480 _ip.user_ns.pop('x', None)
470 _ip.magic('run {2} -m {0}.{1}'.format(self.package, submodule, opts))
481 _ip.magic('run {2} -m {0}.{1}'.format(self.package, submodule, opts))
471 self.assertEqual(_ip.user_ns['x'], self.value,
482 self.assertEqual(_ip.user_ns['x'], self.value,
472 'Variable `x` is not loaded from module `{0}`.'
483 'Variable `x` is not loaded from module `{0}`.'
473 .format(submodule))
484 .format(submodule))
474
485
475 def test_run_submodule_with_absolute_import(self):
486 def test_run_submodule_with_absolute_import(self):
476 self.check_run_submodule('absolute')
487 self.check_run_submodule('absolute')
477
488
478 def test_run_submodule_with_relative_import(self):
489 def test_run_submodule_with_relative_import(self):
479 """Run submodule that has a relative import statement (#2727)."""
490 """Run submodule that has a relative import statement (#2727)."""
480 self.check_run_submodule('relative')
491 self.check_run_submodule('relative')
481
492
482 def test_prun_submodule_with_absolute_import(self):
493 def test_prun_submodule_with_absolute_import(self):
483 self.check_run_submodule('absolute', '-p')
494 self.check_run_submodule('absolute', '-p')
484
495
485 def test_prun_submodule_with_relative_import(self):
496 def test_prun_submodule_with_relative_import(self):
486 self.check_run_submodule('relative', '-p')
497 self.check_run_submodule('relative', '-p')
487
498
488 def with_fake_debugger(func):
499 def with_fake_debugger(func):
489 @functools.wraps(func)
500 @functools.wraps(func)
490 def wrapper(*args, **kwds):
501 def wrapper(*args, **kwds):
491 with patch.object(debugger.Pdb, 'run', staticmethod(eval)):
502 with patch.object(debugger.Pdb, 'run', staticmethod(eval)):
492 return func(*args, **kwds)
503 return func(*args, **kwds)
493 return wrapper
504 return wrapper
494
505
495 @with_fake_debugger
506 @with_fake_debugger
496 def test_debug_run_submodule_with_absolute_import(self):
507 def test_debug_run_submodule_with_absolute_import(self):
497 self.check_run_submodule('absolute', '-d')
508 self.check_run_submodule('absolute', '-d')
498
509
499 @with_fake_debugger
510 @with_fake_debugger
500 def test_debug_run_submodule_with_relative_import(self):
511 def test_debug_run_submodule_with_relative_import(self):
501 self.check_run_submodule('relative', '-d')
512 self.check_run_submodule('relative', '-d')
502
513
503 def test_module_options(self):
514 def test_module_options(self):
504 _ip.user_ns.pop("a", None)
515 _ip.user_ns.pop("a", None)
505 test_opts = "-x abc -m test"
516 test_opts = "-x abc -m test"
506 _ip.run_line_magic("run", "-m {0}.args {1}".format(self.package, test_opts))
517 _ip.run_line_magic("run", "-m {0}.args {1}".format(self.package, test_opts))
507 assert _ip.user_ns["a"] == test_opts
518 assert _ip.user_ns["a"] == test_opts
508
519
509 def test_module_options_with_separator(self):
520 def test_module_options_with_separator(self):
510 _ip.user_ns.pop("a", None)
521 _ip.user_ns.pop("a", None)
511 test_opts = "-x abc -m test"
522 test_opts = "-x abc -m test"
512 _ip.run_line_magic("run", "-m {0}.args -- {1}".format(self.package, test_opts))
523 _ip.run_line_magic("run", "-m {0}.args -- {1}".format(self.package, test_opts))
513 assert _ip.user_ns["a"] == test_opts
524 assert _ip.user_ns["a"] == test_opts
514
525
515
526
516 def test_run__name__():
527 def test_run__name__():
517 with TemporaryDirectory() as td:
528 with TemporaryDirectory() as td:
518 path = pjoin(td, 'foo.py')
529 path = pjoin(td, 'foo.py')
519 with open(path, 'w') as f:
530 with open(path, 'w') as f:
520 f.write("q = __name__")
531 f.write("q = __name__")
521
532
522 _ip.user_ns.pop("q", None)
533 _ip.user_ns.pop("q", None)
523 _ip.magic("run {}".format(path))
534 _ip.magic("run {}".format(path))
524 assert _ip.user_ns.pop("q") == "__main__"
535 assert _ip.user_ns.pop("q") == "__main__"
525
536
526 _ip.magic("run -n {}".format(path))
537 _ip.magic("run -n {}".format(path))
527 assert _ip.user_ns.pop("q") == "foo"
538 assert _ip.user_ns.pop("q") == "foo"
528
539
529 try:
540 try:
530 _ip.magic("run -i -n {}".format(path))
541 _ip.magic("run -i -n {}".format(path))
531 assert _ip.user_ns.pop("q") == "foo"
542 assert _ip.user_ns.pop("q") == "foo"
532 finally:
543 finally:
533 _ip.magic('reset -f')
544 _ip.magic('reset -f')
534
545
535
546
536 def test_run_tb():
547 def test_run_tb():
537 """Test traceback offset in %run"""
548 """Test traceback offset in %run"""
538 with TemporaryDirectory() as td:
549 with TemporaryDirectory() as td:
539 path = pjoin(td, 'foo.py')
550 path = pjoin(td, 'foo.py')
540 with open(path, 'w') as f:
551 with open(path, 'w') as f:
541 f.write('\n'.join([
552 f.write('\n'.join([
542 "def foo():",
553 "def foo():",
543 " return bar()",
554 " return bar()",
544 "def bar():",
555 "def bar():",
545 " raise RuntimeError('hello!')",
556 " raise RuntimeError('hello!')",
546 "foo()",
557 "foo()",
547 ]))
558 ]))
548 with capture_output() as io:
559 with capture_output() as io:
549 _ip.magic('run {}'.format(path))
560 _ip.magic('run {}'.format(path))
550 out = io.stdout
561 out = io.stdout
551 assert "execfile" not in out
562 assert "execfile" not in out
552 assert "RuntimeError" in out
563 assert "RuntimeError" in out
553 assert out.count("---->") == 3
564 assert out.count("---->") == 3
554 del ip.user_ns['bar']
565 del ip.user_ns['bar']
555 del ip.user_ns['foo']
566 del ip.user_ns['foo']
556
567
557
568
558 def test_multiprocessing_run():
569 def test_multiprocessing_run():
559 """Set we can run mutiprocesgin without messing up up main namespace
570 """Set we can run mutiprocesgin without messing up up main namespace
560
571
561 Note that import `nose.tools as nt` mdify the value s
572 Note that import `nose.tools as nt` mdify the value s
562 sys.module['__mp_main__'] so we need to temporarily set it to None to test
573 sys.module['__mp_main__'] so we need to temporarily set it to None to test
563 the issue.
574 the issue.
564 """
575 """
565 with TemporaryDirectory() as td:
576 with TemporaryDirectory() as td:
566 mpm = sys.modules.get('__mp_main__')
577 mpm = sys.modules.get('__mp_main__')
567 sys.modules['__mp_main__'] = None
578 sys.modules['__mp_main__'] = None
568 try:
579 try:
569 path = pjoin(td, 'test.py')
580 path = pjoin(td, 'test.py')
570 with open(path, 'w') as f:
581 with open(path, 'w') as f:
571 f.write("import multiprocessing\nprint('hoy')")
582 f.write("import multiprocessing\nprint('hoy')")
572 with capture_output() as io:
583 with capture_output() as io:
573 _ip.run_line_magic('run', path)
584 _ip.run_line_magic('run', path)
574 _ip.run_cell("i_m_undefined")
585 _ip.run_cell("i_m_undefined")
575 out = io.stdout
586 out = io.stdout
576 assert "hoy" in out
587 assert "hoy" in out
577 assert "AttributeError" not in out
588 assert "AttributeError" not in out
578 assert "NameError" in out
589 assert "NameError" in out
579 assert out.count("---->") == 1
590 assert out.count("---->") == 1
580 except:
591 except:
581 raise
592 raise
582 finally:
593 finally:
583 sys.modules['__mp_main__'] = mpm
594 sys.modules['__mp_main__'] = mpm
584
595
585
596
586 def test_script_tb():
597 def test_script_tb():
587 """Test traceback offset in `ipython script.py`"""
598 """Test traceback offset in `ipython script.py`"""
588 with TemporaryDirectory() as td:
599 with TemporaryDirectory() as td:
589 path = pjoin(td, 'foo.py')
600 path = pjoin(td, 'foo.py')
590 with open(path, 'w') as f:
601 with open(path, 'w') as f:
591 f.write('\n'.join([
602 f.write('\n'.join([
592 "def foo():",
603 "def foo():",
593 " return bar()",
604 " return bar()",
594 "def bar():",
605 "def bar():",
595 " raise RuntimeError('hello!')",
606 " raise RuntimeError('hello!')",
596 "foo()",
607 "foo()",
597 ]))
608 ]))
598 out, err = tt.ipexec(path)
609 out, err = tt.ipexec(path)
599 assert "execfile" not in out
610 assert "execfile" not in out
600 assert "RuntimeError" in out
611 assert "RuntimeError" in out
601 assert out.count("---->") == 3
612 assert out.count("---->") == 3
@@ -1,412 +1,414 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 logging
5 import logging
6 import platform
6 import re
7 import re
7 import sys
8 import sys
8 import os.path
9 import os.path
9 from textwrap import dedent
10 from textwrap import dedent
10 import traceback
11 import traceback
11 import unittest
12 import unittest
12
13
13 from IPython.core.ultratb import ColorTB, VerboseTB
14 from IPython.core.ultratb import ColorTB, VerboseTB
14
15
15
16
16 from IPython.testing import tools as tt
17 from IPython.testing import tools as tt
17 from IPython.testing.decorators import onlyif_unicode_paths
18 from IPython.testing.decorators import onlyif_unicode_paths
18 from IPython.utils.syspathcontext import prepended_to_syspath
19 from IPython.utils.syspathcontext import prepended_to_syspath
19 from IPython.utils.tempdir import TemporaryDirectory
20 from IPython.utils.tempdir import TemporaryDirectory
20
21
21 file_1 = """1
22 file_1 = """1
22 2
23 2
23 3
24 3
24 def f():
25 def f():
25 1/0
26 1/0
26 """
27 """
27
28
28 file_2 = """def f():
29 file_2 = """def f():
29 1/0
30 1/0
30 """
31 """
31
32
32
33
33 def recursionlimit(frames):
34 def recursionlimit(frames):
34 """
35 """
35 decorator to set the recursion limit temporarily
36 decorator to set the recursion limit temporarily
36 """
37 """
37
38
38 def inner(test_function):
39 def inner(test_function):
39 def wrapper(*args, **kwargs):
40 def wrapper(*args, **kwargs):
40 rl = sys.getrecursionlimit()
41 rl = sys.getrecursionlimit()
41 sys.setrecursionlimit(frames)
42 sys.setrecursionlimit(frames)
42 try:
43 try:
43 return test_function(*args, **kwargs)
44 return test_function(*args, **kwargs)
44 finally:
45 finally:
45 sys.setrecursionlimit(rl)
46 sys.setrecursionlimit(rl)
46
47
47 return wrapper
48 return wrapper
48
49
49 return inner
50 return inner
50
51
51
52
52 class ChangedPyFileTest(unittest.TestCase):
53 class ChangedPyFileTest(unittest.TestCase):
53 def test_changing_py_file(self):
54 def test_changing_py_file(self):
54 """Traceback produced if the line where the error occurred is missing?
55 """Traceback produced if the line where the error occurred is missing?
55
56
56 https://github.com/ipython/ipython/issues/1456
57 https://github.com/ipython/ipython/issues/1456
57 """
58 """
58 with TemporaryDirectory() as td:
59 with TemporaryDirectory() as td:
59 fname = os.path.join(td, "foo.py")
60 fname = os.path.join(td, "foo.py")
60 with open(fname, "w") as f:
61 with open(fname, "w") as f:
61 f.write(file_1)
62 f.write(file_1)
62
63
63 with prepended_to_syspath(td):
64 with prepended_to_syspath(td):
64 ip.run_cell("import foo")
65 ip.run_cell("import foo")
65
66
66 with tt.AssertPrints("ZeroDivisionError"):
67 with tt.AssertPrints("ZeroDivisionError"):
67 ip.run_cell("foo.f()")
68 ip.run_cell("foo.f()")
68
69
69 # Make the file shorter, so the line of the error is missing.
70 # Make the file shorter, so the line of the error is missing.
70 with open(fname, "w") as f:
71 with open(fname, "w") as f:
71 f.write(file_2)
72 f.write(file_2)
72
73
73 # For some reason, this was failing on the *second* call after
74 # For some reason, this was failing on the *second* call after
74 # changing the file, so we call f() twice.
75 # changing the file, so we call f() twice.
75 with tt.AssertNotPrints("Internal Python error", channel='stderr'):
76 with tt.AssertNotPrints("Internal Python error", channel='stderr'):
76 with tt.AssertPrints("ZeroDivisionError"):
77 with tt.AssertPrints("ZeroDivisionError"):
77 ip.run_cell("foo.f()")
78 ip.run_cell("foo.f()")
78 with tt.AssertPrints("ZeroDivisionError"):
79 with tt.AssertPrints("ZeroDivisionError"):
79 ip.run_cell("foo.f()")
80 ip.run_cell("foo.f()")
80
81
81 iso_8859_5_file = u'''# coding: iso-8859-5
82 iso_8859_5_file = u'''# coding: iso-8859-5
82
83
83 def fail():
84 def fail():
84 """дбИЖ"""
85 """дбИЖ"""
85 1/0 # дбИЖ
86 1/0 # дбИЖ
86 '''
87 '''
87
88
88 class NonAsciiTest(unittest.TestCase):
89 class NonAsciiTest(unittest.TestCase):
89 @onlyif_unicode_paths
90 @onlyif_unicode_paths
90 def test_nonascii_path(self):
91 def test_nonascii_path(self):
91 # Non-ascii directory name as well.
92 # Non-ascii directory name as well.
92 with TemporaryDirectory(suffix=u'é') as td:
93 with TemporaryDirectory(suffix=u'é') as td:
93 fname = os.path.join(td, u"fooé.py")
94 fname = os.path.join(td, u"fooé.py")
94 with open(fname, "w") as f:
95 with open(fname, "w") as f:
95 f.write(file_1)
96 f.write(file_1)
96
97
97 with prepended_to_syspath(td):
98 with prepended_to_syspath(td):
98 ip.run_cell("import foo")
99 ip.run_cell("import foo")
99
100
100 with tt.AssertPrints("ZeroDivisionError"):
101 with tt.AssertPrints("ZeroDivisionError"):
101 ip.run_cell("foo.f()")
102 ip.run_cell("foo.f()")
102
103
103 def test_iso8859_5(self):
104 def test_iso8859_5(self):
104 with TemporaryDirectory() as td:
105 with TemporaryDirectory() as td:
105 fname = os.path.join(td, 'dfghjkl.py')
106 fname = os.path.join(td, 'dfghjkl.py')
106
107
107 with io.open(fname, 'w', encoding='iso-8859-5') as f:
108 with io.open(fname, 'w', encoding='iso-8859-5') as f:
108 f.write(iso_8859_5_file)
109 f.write(iso_8859_5_file)
109
110
110 with prepended_to_syspath(td):
111 with prepended_to_syspath(td):
111 ip.run_cell("from dfghjkl import fail")
112 ip.run_cell("from dfghjkl import fail")
112
113
113 with tt.AssertPrints("ZeroDivisionError"):
114 with tt.AssertPrints("ZeroDivisionError"):
114 with tt.AssertPrints(u'дбИЖ', suppress=False):
115 with tt.AssertPrints(u'дбИЖ', suppress=False):
115 ip.run_cell('fail()')
116 ip.run_cell('fail()')
116
117
117 def test_nonascii_msg(self):
118 def test_nonascii_msg(self):
118 cell = u"raise Exception('é')"
119 cell = u"raise Exception('é')"
119 expected = u"Exception('é')"
120 expected = u"Exception('é')"
120 ip.run_cell("%xmode plain")
121 ip.run_cell("%xmode plain")
121 with tt.AssertPrints(expected):
122 with tt.AssertPrints(expected):
122 ip.run_cell(cell)
123 ip.run_cell(cell)
123
124
124 ip.run_cell("%xmode verbose")
125 ip.run_cell("%xmode verbose")
125 with tt.AssertPrints(expected):
126 with tt.AssertPrints(expected):
126 ip.run_cell(cell)
127 ip.run_cell(cell)
127
128
128 ip.run_cell("%xmode context")
129 ip.run_cell("%xmode context")
129 with tt.AssertPrints(expected):
130 with tt.AssertPrints(expected):
130 ip.run_cell(cell)
131 ip.run_cell(cell)
131
132
132 ip.run_cell("%xmode minimal")
133 ip.run_cell("%xmode minimal")
133 with tt.AssertPrints(u"Exception: é"):
134 with tt.AssertPrints(u"Exception: é"):
134 ip.run_cell(cell)
135 ip.run_cell(cell)
135
136
136 # Put this back into Context mode for later tests.
137 # Put this back into Context mode for later tests.
137 ip.run_cell("%xmode context")
138 ip.run_cell("%xmode context")
138
139
139 class NestedGenExprTestCase(unittest.TestCase):
140 class NestedGenExprTestCase(unittest.TestCase):
140 """
141 """
141 Regression test for the following issues:
142 Regression test for the following issues:
142 https://github.com/ipython/ipython/issues/8293
143 https://github.com/ipython/ipython/issues/8293
143 https://github.com/ipython/ipython/issues/8205
144 https://github.com/ipython/ipython/issues/8205
144 """
145 """
145 def test_nested_genexpr(self):
146 def test_nested_genexpr(self):
146 code = dedent(
147 code = dedent(
147 """\
148 """\
148 class SpecificException(Exception):
149 class SpecificException(Exception):
149 pass
150 pass
150
151
151 def foo(x):
152 def foo(x):
152 raise SpecificException("Success!")
153 raise SpecificException("Success!")
153
154
154 sum(sum(foo(x) for _ in [0]) for x in [0])
155 sum(sum(foo(x) for _ in [0]) for x in [0])
155 """
156 """
156 )
157 )
157 with tt.AssertPrints('SpecificException: Success!', suppress=False):
158 with tt.AssertPrints('SpecificException: Success!', suppress=False):
158 ip.run_cell(code)
159 ip.run_cell(code)
159
160
160
161
161 indentationerror_file = """if True:
162 indentationerror_file = """if True:
162 zoon()
163 zoon()
163 """
164 """
164
165
165 class IndentationErrorTest(unittest.TestCase):
166 class IndentationErrorTest(unittest.TestCase):
166 def test_indentationerror_shows_line(self):
167 def test_indentationerror_shows_line(self):
167 # See issue gh-2398
168 # See issue gh-2398
168 with tt.AssertPrints("IndentationError"):
169 with tt.AssertPrints("IndentationError"):
169 with tt.AssertPrints("zoon()", suppress=False):
170 with tt.AssertPrints("zoon()", suppress=False):
170 ip.run_cell(indentationerror_file)
171 ip.run_cell(indentationerror_file)
171
172
172 with TemporaryDirectory() as td:
173 with TemporaryDirectory() as td:
173 fname = os.path.join(td, "foo.py")
174 fname = os.path.join(td, "foo.py")
174 with open(fname, "w") as f:
175 with open(fname, "w") as f:
175 f.write(indentationerror_file)
176 f.write(indentationerror_file)
176
177
177 with tt.AssertPrints("IndentationError"):
178 with tt.AssertPrints("IndentationError"):
178 with tt.AssertPrints("zoon()", suppress=False):
179 with tt.AssertPrints("zoon()", suppress=False):
179 ip.magic('run %s' % fname)
180 ip.magic('run %s' % fname)
180
181
181 se_file_1 = """1
182 se_file_1 = """1
182 2
183 2
183 7/
184 7/
184 """
185 """
185
186
186 se_file_2 = """7/
187 se_file_2 = """7/
187 """
188 """
188
189
189 class SyntaxErrorTest(unittest.TestCase):
190 class SyntaxErrorTest(unittest.TestCase):
190 def test_syntaxerror_without_lineno(self):
191 def test_syntaxerror_without_lineno(self):
191 with tt.AssertNotPrints("TypeError"):
192 with tt.AssertNotPrints("TypeError"):
192 with tt.AssertPrints("line unknown"):
193 with tt.AssertPrints("line unknown"):
193 ip.run_cell("raise SyntaxError()")
194 ip.run_cell("raise SyntaxError()")
194
195
195 def test_syntaxerror_no_stacktrace_at_compile_time(self):
196 def test_syntaxerror_no_stacktrace_at_compile_time(self):
196 syntax_error_at_compile_time = """
197 syntax_error_at_compile_time = """
197 def foo():
198 def foo():
198 ..
199 ..
199 """
200 """
200 with tt.AssertPrints("SyntaxError"):
201 with tt.AssertPrints("SyntaxError"):
201 ip.run_cell(syntax_error_at_compile_time)
202 ip.run_cell(syntax_error_at_compile_time)
202
203
203 with tt.AssertNotPrints("foo()"):
204 with tt.AssertNotPrints("foo()"):
204 ip.run_cell(syntax_error_at_compile_time)
205 ip.run_cell(syntax_error_at_compile_time)
205
206
206 def test_syntaxerror_stacktrace_when_running_compiled_code(self):
207 def test_syntaxerror_stacktrace_when_running_compiled_code(self):
207 syntax_error_at_runtime = """
208 syntax_error_at_runtime = """
208 def foo():
209 def foo():
209 eval("..")
210 eval("..")
210
211
211 def bar():
212 def bar():
212 foo()
213 foo()
213
214
214 bar()
215 bar()
215 """
216 """
216 with tt.AssertPrints("SyntaxError"):
217 with tt.AssertPrints("SyntaxError"):
217 ip.run_cell(syntax_error_at_runtime)
218 ip.run_cell(syntax_error_at_runtime)
218 # Assert syntax error during runtime generate stacktrace
219 # Assert syntax error during runtime generate stacktrace
219 with tt.AssertPrints(["foo()", "bar()"]):
220 with tt.AssertPrints(["foo()", "bar()"]):
220 ip.run_cell(syntax_error_at_runtime)
221 ip.run_cell(syntax_error_at_runtime)
221 del ip.user_ns['bar']
222 del ip.user_ns['bar']
222 del ip.user_ns['foo']
223 del ip.user_ns['foo']
223
224
224 def test_changing_py_file(self):
225 def test_changing_py_file(self):
225 with TemporaryDirectory() as td:
226 with TemporaryDirectory() as td:
226 fname = os.path.join(td, "foo.py")
227 fname = os.path.join(td, "foo.py")
227 with open(fname, 'w') as f:
228 with open(fname, 'w') as f:
228 f.write(se_file_1)
229 f.write(se_file_1)
229
230
230 with tt.AssertPrints(["7/", "SyntaxError"]):
231 with tt.AssertPrints(["7/", "SyntaxError"]):
231 ip.magic("run " + fname)
232 ip.magic("run " + fname)
232
233
233 # Modify the file
234 # Modify the file
234 with open(fname, 'w') as f:
235 with open(fname, 'w') as f:
235 f.write(se_file_2)
236 f.write(se_file_2)
236
237
237 # The SyntaxError should point to the correct line
238 # The SyntaxError should point to the correct line
238 with tt.AssertPrints(["7/", "SyntaxError"]):
239 with tt.AssertPrints(["7/", "SyntaxError"]):
239 ip.magic("run " + fname)
240 ip.magic("run " + fname)
240
241
241 def test_non_syntaxerror(self):
242 def test_non_syntaxerror(self):
242 # SyntaxTB may be called with an error other than a SyntaxError
243 # SyntaxTB may be called with an error other than a SyntaxError
243 # See e.g. gh-4361
244 # See e.g. gh-4361
244 try:
245 try:
245 raise ValueError('QWERTY')
246 raise ValueError('QWERTY')
246 except ValueError:
247 except ValueError:
247 with tt.AssertPrints('QWERTY'):
248 with tt.AssertPrints('QWERTY'):
248 ip.showsyntaxerror()
249 ip.showsyntaxerror()
249
250
250 import sys
251 import sys
251 if sys.version_info < (3,9):
252
253 if sys.version_info < (3, 9) and platform.python_implementation() != "PyPy":
252 """
254 """
253 New 3.9 Pgen Parser does not raise Memory error, except on failed malloc.
255 New 3.9 Pgen Parser does not raise Memory error, except on failed malloc.
254 """
256 """
255 class MemoryErrorTest(unittest.TestCase):
257 class MemoryErrorTest(unittest.TestCase):
256 def test_memoryerror(self):
258 def test_memoryerror(self):
257 memoryerror_code = "(" * 200 + ")" * 200
259 memoryerror_code = "(" * 200 + ")" * 200
258 with tt.AssertPrints("MemoryError"):
260 with tt.AssertPrints("MemoryError"):
259 ip.run_cell(memoryerror_code)
261 ip.run_cell(memoryerror_code)
260
262
261
263
262 class Python3ChainedExceptionsTest(unittest.TestCase):
264 class Python3ChainedExceptionsTest(unittest.TestCase):
263 DIRECT_CAUSE_ERROR_CODE = """
265 DIRECT_CAUSE_ERROR_CODE = """
264 try:
266 try:
265 x = 1 + 2
267 x = 1 + 2
266 print(not_defined_here)
268 print(not_defined_here)
267 except Exception as e:
269 except Exception as e:
268 x += 55
270 x += 55
269 x - 1
271 x - 1
270 y = {}
272 y = {}
271 raise KeyError('uh') from e
273 raise KeyError('uh') from e
272 """
274 """
273
275
274 EXCEPTION_DURING_HANDLING_CODE = """
276 EXCEPTION_DURING_HANDLING_CODE = """
275 try:
277 try:
276 x = 1 + 2
278 x = 1 + 2
277 print(not_defined_here)
279 print(not_defined_here)
278 except Exception as e:
280 except Exception as e:
279 x += 55
281 x += 55
280 x - 1
282 x - 1
281 y = {}
283 y = {}
282 raise KeyError('uh')
284 raise KeyError('uh')
283 """
285 """
284
286
285 SUPPRESS_CHAINING_CODE = """
287 SUPPRESS_CHAINING_CODE = """
286 try:
288 try:
287 1/0
289 1/0
288 except Exception:
290 except Exception:
289 raise ValueError("Yikes") from None
291 raise ValueError("Yikes") from None
290 """
292 """
291
293
292 def test_direct_cause_error(self):
294 def test_direct_cause_error(self):
293 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
295 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
294 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
296 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
295
297
296 def test_exception_during_handling_error(self):
298 def test_exception_during_handling_error(self):
297 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
299 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
298 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
300 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
299
301
300 def test_suppress_exception_chaining(self):
302 def test_suppress_exception_chaining(self):
301 with tt.AssertNotPrints("ZeroDivisionError"), \
303 with tt.AssertNotPrints("ZeroDivisionError"), \
302 tt.AssertPrints("ValueError", suppress=False):
304 tt.AssertPrints("ValueError", suppress=False):
303 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
305 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
304
306
305 def test_plain_direct_cause_error(self):
307 def test_plain_direct_cause_error(self):
306 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
308 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
307 ip.run_cell("%xmode Plain")
309 ip.run_cell("%xmode Plain")
308 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
310 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
309 ip.run_cell("%xmode Verbose")
311 ip.run_cell("%xmode Verbose")
310
312
311 def test_plain_exception_during_handling_error(self):
313 def test_plain_exception_during_handling_error(self):
312 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
314 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
313 ip.run_cell("%xmode Plain")
315 ip.run_cell("%xmode Plain")
314 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
316 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
315 ip.run_cell("%xmode Verbose")
317 ip.run_cell("%xmode Verbose")
316
318
317 def test_plain_suppress_exception_chaining(self):
319 def test_plain_suppress_exception_chaining(self):
318 with tt.AssertNotPrints("ZeroDivisionError"), \
320 with tt.AssertNotPrints("ZeroDivisionError"), \
319 tt.AssertPrints("ValueError", suppress=False):
321 tt.AssertPrints("ValueError", suppress=False):
320 ip.run_cell("%xmode Plain")
322 ip.run_cell("%xmode Plain")
321 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
323 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
322 ip.run_cell("%xmode Verbose")
324 ip.run_cell("%xmode Verbose")
323
325
324
326
325 class RecursionTest(unittest.TestCase):
327 class RecursionTest(unittest.TestCase):
326 DEFINITIONS = """
328 DEFINITIONS = """
327 def non_recurs():
329 def non_recurs():
328 1/0
330 1/0
329
331
330 def r1():
332 def r1():
331 r1()
333 r1()
332
334
333 def r3a():
335 def r3a():
334 r3b()
336 r3b()
335
337
336 def r3b():
338 def r3b():
337 r3c()
339 r3c()
338
340
339 def r3c():
341 def r3c():
340 r3a()
342 r3a()
341
343
342 def r3o1():
344 def r3o1():
343 r3a()
345 r3a()
344
346
345 def r3o2():
347 def r3o2():
346 r3o1()
348 r3o1()
347 """
349 """
348 def setUp(self):
350 def setUp(self):
349 ip.run_cell(self.DEFINITIONS)
351 ip.run_cell(self.DEFINITIONS)
350
352
351 def test_no_recursion(self):
353 def test_no_recursion(self):
352 with tt.AssertNotPrints("skipping similar frames"):
354 with tt.AssertNotPrints("skipping similar frames"):
353 ip.run_cell("non_recurs()")
355 ip.run_cell("non_recurs()")
354
356
355 @recursionlimit(200)
357 @recursionlimit(200)
356 def test_recursion_one_frame(self):
358 def test_recursion_one_frame(self):
357 with tt.AssertPrints(re.compile(
359 with tt.AssertPrints(re.compile(
358 r"\[\.\.\. skipping similar frames: r1 at line 5 \(\d{2,3} times\)\]")
360 r"\[\.\.\. skipping similar frames: r1 at line 5 \(\d{2,3} times\)\]")
359 ):
361 ):
360 ip.run_cell("r1()")
362 ip.run_cell("r1()")
361
363
362 @recursionlimit(200)
364 @recursionlimit(160)
363 def test_recursion_three_frames(self):
365 def test_recursion_three_frames(self):
364 with tt.AssertPrints("[... skipping similar frames: "), \
366 with tt.AssertPrints("[... skipping similar frames: "), \
365 tt.AssertPrints(re.compile(r"r3a at line 8 \(\d{2} times\)"), suppress=False), \
367 tt.AssertPrints(re.compile(r"r3a at line 8 \(\d{2} times\)"), suppress=False), \
366 tt.AssertPrints(re.compile(r"r3b at line 11 \(\d{2} times\)"), suppress=False), \
368 tt.AssertPrints(re.compile(r"r3b at line 11 \(\d{2} times\)"), suppress=False), \
367 tt.AssertPrints(re.compile(r"r3c at line 14 \(\d{2} times\)"), suppress=False):
369 tt.AssertPrints(re.compile(r"r3c at line 14 \(\d{2} times\)"), suppress=False):
368 ip.run_cell("r3o2()")
370 ip.run_cell("r3o2()")
369
371
370
372
371 #----------------------------------------------------------------------------
373 #----------------------------------------------------------------------------
372
374
373 # module testing (minimal)
375 # module testing (minimal)
374 def test_handlers():
376 def test_handlers():
375 def spam(c, d_e):
377 def spam(c, d_e):
376 (d, e) = d_e
378 (d, e) = d_e
377 x = c + d
379 x = c + d
378 y = c * d
380 y = c * d
379 foo(x, y)
381 foo(x, y)
380
382
381 def foo(a, b, bar=1):
383 def foo(a, b, bar=1):
382 eggs(a, b + bar)
384 eggs(a, b + bar)
383
385
384 def eggs(f, g, z=globals()):
386 def eggs(f, g, z=globals()):
385 h = f + g
387 h = f + g
386 i = f - g
388 i = f - g
387 return h / i
389 return h / i
388
390
389 buff = io.StringIO()
391 buff = io.StringIO()
390
392
391 buff.write('')
393 buff.write('')
392 buff.write('*** Before ***')
394 buff.write('*** Before ***')
393 try:
395 try:
394 buff.write(spam(1, (2, 3)))
396 buff.write(spam(1, (2, 3)))
395 except:
397 except:
396 traceback.print_exc(file=buff)
398 traceback.print_exc(file=buff)
397
399
398 handler = ColorTB(ostream=buff)
400 handler = ColorTB(ostream=buff)
399 buff.write('*** ColorTB ***')
401 buff.write('*** ColorTB ***')
400 try:
402 try:
401 buff.write(spam(1, (2, 3)))
403 buff.write(spam(1, (2, 3)))
402 except:
404 except:
403 handler(*sys.exc_info())
405 handler(*sys.exc_info())
404 buff.write('')
406 buff.write('')
405
407
406 handler = VerboseTB(ostream=buff)
408 handler = VerboseTB(ostream=buff)
407 buff.write('*** VerboseTB ***')
409 buff.write('*** VerboseTB ***')
408 try:
410 try:
409 buff.write(spam(1, (2, 3)))
411 buff.write(spam(1, (2, 3)))
410 except:
412 except:
411 handler(*sys.exc_info())
413 handler(*sys.exc_info())
412 buff.write('')
414 buff.write('')
@@ -1,554 +1,562 b''
1 """Tests for autoreload extension.
1 """Tests for autoreload extension.
2 """
2 """
3 # -----------------------------------------------------------------------------
3 # -----------------------------------------------------------------------------
4 # Copyright (c) 2012 IPython Development Team.
4 # Copyright (c) 2012 IPython Development Team.
5 #
5 #
6 # Distributed under the terms of the Modified BSD License.
6 # Distributed under the terms of the Modified BSD License.
7 #
7 #
8 # The full license is in the file COPYING.txt, distributed with this software.
8 # The full license is in the file COPYING.txt, distributed with this software.
9 # -----------------------------------------------------------------------------
9 # -----------------------------------------------------------------------------
10
10
11 # -----------------------------------------------------------------------------
11 # -----------------------------------------------------------------------------
12 # Imports
12 # Imports
13 # -----------------------------------------------------------------------------
13 # -----------------------------------------------------------------------------
14
14
15 import os
15 import os
16 import platform
17 import pytest
16 import sys
18 import sys
17 import tempfile
19 import tempfile
18 import textwrap
20 import textwrap
19 import shutil
21 import shutil
20 import random
22 import random
21 import time
23 import time
22 from io import StringIO
24 from io import StringIO
23
25
24 import IPython.testing.tools as tt
26 import IPython.testing.tools as tt
25
27
26 from unittest import TestCase
28 from unittest import TestCase
27
29
28 from IPython.extensions.autoreload import AutoreloadMagics
30 from IPython.extensions.autoreload import AutoreloadMagics
29 from IPython.core.events import EventManager, pre_run_cell
31 from IPython.core.events import EventManager, pre_run_cell
30
32
33 if platform.python_implementation() == "PyPy":
34 pytest.skip(
35 "Current autoreload implementation is extremly slow on PyPy",
36 allow_module_level=True,
37 )
38
31 # -----------------------------------------------------------------------------
39 # -----------------------------------------------------------------------------
32 # Test fixture
40 # Test fixture
33 # -----------------------------------------------------------------------------
41 # -----------------------------------------------------------------------------
34
42
35 noop = lambda *a, **kw: None
43 noop = lambda *a, **kw: None
36
44
37
45
38 class FakeShell:
46 class FakeShell:
39 def __init__(self):
47 def __init__(self):
40 self.ns = {}
48 self.ns = {}
41 self.user_ns = self.ns
49 self.user_ns = self.ns
42 self.user_ns_hidden = {}
50 self.user_ns_hidden = {}
43 self.events = EventManager(self, {"pre_run_cell", pre_run_cell})
51 self.events = EventManager(self, {"pre_run_cell", pre_run_cell})
44 self.auto_magics = AutoreloadMagics(shell=self)
52 self.auto_magics = AutoreloadMagics(shell=self)
45 self.events.register("pre_run_cell", self.auto_magics.pre_run_cell)
53 self.events.register("pre_run_cell", self.auto_magics.pre_run_cell)
46
54
47 register_magics = set_hook = noop
55 register_magics = set_hook = noop
48
56
49 def run_code(self, code):
57 def run_code(self, code):
50 self.events.trigger("pre_run_cell")
58 self.events.trigger("pre_run_cell")
51 exec(code, self.user_ns)
59 exec(code, self.user_ns)
52 self.auto_magics.post_execute_hook()
60 self.auto_magics.post_execute_hook()
53
61
54 def push(self, items):
62 def push(self, items):
55 self.ns.update(items)
63 self.ns.update(items)
56
64
57 def magic_autoreload(self, parameter):
65 def magic_autoreload(self, parameter):
58 self.auto_magics.autoreload(parameter)
66 self.auto_magics.autoreload(parameter)
59
67
60 def magic_aimport(self, parameter, stream=None):
68 def magic_aimport(self, parameter, stream=None):
61 self.auto_magics.aimport(parameter, stream=stream)
69 self.auto_magics.aimport(parameter, stream=stream)
62 self.auto_magics.post_execute_hook()
70 self.auto_magics.post_execute_hook()
63
71
64
72
65 class Fixture(TestCase):
73 class Fixture(TestCase):
66 """Fixture for creating test module files"""
74 """Fixture for creating test module files"""
67
75
68 test_dir = None
76 test_dir = None
69 old_sys_path = None
77 old_sys_path = None
70 filename_chars = "abcdefghijklmopqrstuvwxyz0123456789"
78 filename_chars = "abcdefghijklmopqrstuvwxyz0123456789"
71
79
72 def setUp(self):
80 def setUp(self):
73 self.test_dir = tempfile.mkdtemp()
81 self.test_dir = tempfile.mkdtemp()
74 self.old_sys_path = list(sys.path)
82 self.old_sys_path = list(sys.path)
75 sys.path.insert(0, self.test_dir)
83 sys.path.insert(0, self.test_dir)
76 self.shell = FakeShell()
84 self.shell = FakeShell()
77
85
78 def tearDown(self):
86 def tearDown(self):
79 shutil.rmtree(self.test_dir)
87 shutil.rmtree(self.test_dir)
80 sys.path = self.old_sys_path
88 sys.path = self.old_sys_path
81
89
82 self.test_dir = None
90 self.test_dir = None
83 self.old_sys_path = None
91 self.old_sys_path = None
84 self.shell = None
92 self.shell = None
85
93
86 def get_module(self):
94 def get_module(self):
87 module_name = "tmpmod_" + "".join(random.sample(self.filename_chars, 20))
95 module_name = "tmpmod_" + "".join(random.sample(self.filename_chars, 20))
88 if module_name in sys.modules:
96 if module_name in sys.modules:
89 del sys.modules[module_name]
97 del sys.modules[module_name]
90 file_name = os.path.join(self.test_dir, module_name + ".py")
98 file_name = os.path.join(self.test_dir, module_name + ".py")
91 return module_name, file_name
99 return module_name, file_name
92
100
93 def write_file(self, filename, content):
101 def write_file(self, filename, content):
94 """
102 """
95 Write a file, and force a timestamp difference of at least one second
103 Write a file, and force a timestamp difference of at least one second
96
104
97 Notes
105 Notes
98 -----
106 -----
99 Python's .pyc files record the timestamp of their compilation
107 Python's .pyc files record the timestamp of their compilation
100 with a time resolution of one second.
108 with a time resolution of one second.
101
109
102 Therefore, we need to force a timestamp difference between .py
110 Therefore, we need to force a timestamp difference between .py
103 and .pyc, without having the .py file be timestamped in the
111 and .pyc, without having the .py file be timestamped in the
104 future, and without changing the timestamp of the .pyc file
112 future, and without changing the timestamp of the .pyc file
105 (because that is stored in the file). The only reliable way
113 (because that is stored in the file). The only reliable way
106 to achieve this seems to be to sleep.
114 to achieve this seems to be to sleep.
107 """
115 """
108 content = textwrap.dedent(content)
116 content = textwrap.dedent(content)
109 # Sleep one second + eps
117 # Sleep one second + eps
110 time.sleep(1.05)
118 time.sleep(1.05)
111
119
112 # Write
120 # Write
113 with open(filename, "w") as f:
121 with open(filename, "w") as f:
114 f.write(content)
122 f.write(content)
115
123
116 def new_module(self, code):
124 def new_module(self, code):
117 code = textwrap.dedent(code)
125 code = textwrap.dedent(code)
118 mod_name, mod_fn = self.get_module()
126 mod_name, mod_fn = self.get_module()
119 with open(mod_fn, "w") as f:
127 with open(mod_fn, "w") as f:
120 f.write(code)
128 f.write(code)
121 return mod_name, mod_fn
129 return mod_name, mod_fn
122
130
123
131
124 # -----------------------------------------------------------------------------
132 # -----------------------------------------------------------------------------
125 # Test automatic reloading
133 # Test automatic reloading
126 # -----------------------------------------------------------------------------
134 # -----------------------------------------------------------------------------
127
135
128
136
129 def pickle_get_current_class(obj):
137 def pickle_get_current_class(obj):
130 """
138 """
131 Original issue comes from pickle; hence the name.
139 Original issue comes from pickle; hence the name.
132 """
140 """
133 name = obj.__class__.__name__
141 name = obj.__class__.__name__
134 module_name = getattr(obj, "__module__", None)
142 module_name = getattr(obj, "__module__", None)
135 obj2 = sys.modules[module_name]
143 obj2 = sys.modules[module_name]
136 for subpath in name.split("."):
144 for subpath in name.split("."):
137 obj2 = getattr(obj2, subpath)
145 obj2 = getattr(obj2, subpath)
138 return obj2
146 return obj2
139
147
140
148
141 class TestAutoreload(Fixture):
149 class TestAutoreload(Fixture):
142 def test_reload_enums(self):
150 def test_reload_enums(self):
143 mod_name, mod_fn = self.new_module(
151 mod_name, mod_fn = self.new_module(
144 textwrap.dedent(
152 textwrap.dedent(
145 """
153 """
146 from enum import Enum
154 from enum import Enum
147 class MyEnum(Enum):
155 class MyEnum(Enum):
148 A = 'A'
156 A = 'A'
149 B = 'B'
157 B = 'B'
150 """
158 """
151 )
159 )
152 )
160 )
153 self.shell.magic_autoreload("2")
161 self.shell.magic_autoreload("2")
154 self.shell.magic_aimport(mod_name)
162 self.shell.magic_aimport(mod_name)
155 self.write_file(
163 self.write_file(
156 mod_fn,
164 mod_fn,
157 textwrap.dedent(
165 textwrap.dedent(
158 """
166 """
159 from enum import Enum
167 from enum import Enum
160 class MyEnum(Enum):
168 class MyEnum(Enum):
161 A = 'A'
169 A = 'A'
162 B = 'B'
170 B = 'B'
163 C = 'C'
171 C = 'C'
164 """
172 """
165 ),
173 ),
166 )
174 )
167 with tt.AssertNotPrints(
175 with tt.AssertNotPrints(
168 ("[autoreload of %s failed:" % mod_name), channel="stderr"
176 ("[autoreload of %s failed:" % mod_name), channel="stderr"
169 ):
177 ):
170 self.shell.run_code("pass") # trigger another reload
178 self.shell.run_code("pass") # trigger another reload
171
179
172 def test_reload_class_type(self):
180 def test_reload_class_type(self):
173 self.shell.magic_autoreload("2")
181 self.shell.magic_autoreload("2")
174 mod_name, mod_fn = self.new_module(
182 mod_name, mod_fn = self.new_module(
175 """
183 """
176 class Test():
184 class Test():
177 def meth(self):
185 def meth(self):
178 return "old"
186 return "old"
179 """
187 """
180 )
188 )
181 assert "test" not in self.shell.ns
189 assert "test" not in self.shell.ns
182 assert "result" not in self.shell.ns
190 assert "result" not in self.shell.ns
183
191
184 self.shell.run_code("from %s import Test" % mod_name)
192 self.shell.run_code("from %s import Test" % mod_name)
185 self.shell.run_code("test = Test()")
193 self.shell.run_code("test = Test()")
186
194
187 self.write_file(
195 self.write_file(
188 mod_fn,
196 mod_fn,
189 """
197 """
190 class Test():
198 class Test():
191 def meth(self):
199 def meth(self):
192 return "new"
200 return "new"
193 """,
201 """,
194 )
202 )
195
203
196 test_object = self.shell.ns["test"]
204 test_object = self.shell.ns["test"]
197
205
198 # important to trigger autoreload logic !
206 # important to trigger autoreload logic !
199 self.shell.run_code("pass")
207 self.shell.run_code("pass")
200
208
201 test_class = pickle_get_current_class(test_object)
209 test_class = pickle_get_current_class(test_object)
202 assert isinstance(test_object, test_class)
210 assert isinstance(test_object, test_class)
203
211
204 # extra check.
212 # extra check.
205 self.shell.run_code("import pickle")
213 self.shell.run_code("import pickle")
206 self.shell.run_code("p = pickle.dumps(test)")
214 self.shell.run_code("p = pickle.dumps(test)")
207
215
208 def test_reload_class_attributes(self):
216 def test_reload_class_attributes(self):
209 self.shell.magic_autoreload("2")
217 self.shell.magic_autoreload("2")
210 mod_name, mod_fn = self.new_module(
218 mod_name, mod_fn = self.new_module(
211 textwrap.dedent(
219 textwrap.dedent(
212 """
220 """
213 class MyClass:
221 class MyClass:
214
222
215 def __init__(self, a=10):
223 def __init__(self, a=10):
216 self.a = a
224 self.a = a
217 self.b = 22
225 self.b = 22
218 # self.toto = 33
226 # self.toto = 33
219
227
220 def square(self):
228 def square(self):
221 print('compute square')
229 print('compute square')
222 return self.a*self.a
230 return self.a*self.a
223 """
231 """
224 )
232 )
225 )
233 )
226 self.shell.run_code("from %s import MyClass" % mod_name)
234 self.shell.run_code("from %s import MyClass" % mod_name)
227 self.shell.run_code("first = MyClass(5)")
235 self.shell.run_code("first = MyClass(5)")
228 self.shell.run_code("first.square()")
236 self.shell.run_code("first.square()")
229 with self.assertRaises(AttributeError):
237 with self.assertRaises(AttributeError):
230 self.shell.run_code("first.cube()")
238 self.shell.run_code("first.cube()")
231 with self.assertRaises(AttributeError):
239 with self.assertRaises(AttributeError):
232 self.shell.run_code("first.power(5)")
240 self.shell.run_code("first.power(5)")
233 self.shell.run_code("first.b")
241 self.shell.run_code("first.b")
234 with self.assertRaises(AttributeError):
242 with self.assertRaises(AttributeError):
235 self.shell.run_code("first.toto")
243 self.shell.run_code("first.toto")
236
244
237 # remove square, add power
245 # remove square, add power
238
246
239 self.write_file(
247 self.write_file(
240 mod_fn,
248 mod_fn,
241 textwrap.dedent(
249 textwrap.dedent(
242 """
250 """
243 class MyClass:
251 class MyClass:
244
252
245 def __init__(self, a=10):
253 def __init__(self, a=10):
246 self.a = a
254 self.a = a
247 self.b = 11
255 self.b = 11
248
256
249 def power(self, p):
257 def power(self, p):
250 print('compute power '+str(p))
258 print('compute power '+str(p))
251 return self.a**p
259 return self.a**p
252 """
260 """
253 ),
261 ),
254 )
262 )
255
263
256 self.shell.run_code("second = MyClass(5)")
264 self.shell.run_code("second = MyClass(5)")
257
265
258 for object_name in {"first", "second"}:
266 for object_name in {"first", "second"}:
259 self.shell.run_code(f"{object_name}.power(5)")
267 self.shell.run_code(f"{object_name}.power(5)")
260 with self.assertRaises(AttributeError):
268 with self.assertRaises(AttributeError):
261 self.shell.run_code(f"{object_name}.cube()")
269 self.shell.run_code(f"{object_name}.cube()")
262 with self.assertRaises(AttributeError):
270 with self.assertRaises(AttributeError):
263 self.shell.run_code(f"{object_name}.square()")
271 self.shell.run_code(f"{object_name}.square()")
264 self.shell.run_code(f"{object_name}.b")
272 self.shell.run_code(f"{object_name}.b")
265 self.shell.run_code(f"{object_name}.a")
273 self.shell.run_code(f"{object_name}.a")
266 with self.assertRaises(AttributeError):
274 with self.assertRaises(AttributeError):
267 self.shell.run_code(f"{object_name}.toto")
275 self.shell.run_code(f"{object_name}.toto")
268
276
269 def test_autoload_newly_added_objects(self):
277 def test_autoload_newly_added_objects(self):
270 self.shell.magic_autoreload("3")
278 self.shell.magic_autoreload("3")
271 mod_code = """
279 mod_code = """
272 def func1(): pass
280 def func1(): pass
273 """
281 """
274 mod_name, mod_fn = self.new_module(textwrap.dedent(mod_code))
282 mod_name, mod_fn = self.new_module(textwrap.dedent(mod_code))
275 self.shell.run_code(f"from {mod_name} import *")
283 self.shell.run_code(f"from {mod_name} import *")
276 self.shell.run_code("func1()")
284 self.shell.run_code("func1()")
277 with self.assertRaises(NameError):
285 with self.assertRaises(NameError):
278 self.shell.run_code("func2()")
286 self.shell.run_code("func2()")
279 with self.assertRaises(NameError):
287 with self.assertRaises(NameError):
280 self.shell.run_code("t = Test()")
288 self.shell.run_code("t = Test()")
281 with self.assertRaises(NameError):
289 with self.assertRaises(NameError):
282 self.shell.run_code("number")
290 self.shell.run_code("number")
283
291
284 # ----------- TEST NEW OBJ LOADED --------------------------
292 # ----------- TEST NEW OBJ LOADED --------------------------
285
293
286 new_code = """
294 new_code = """
287 def func1(): pass
295 def func1(): pass
288 def func2(): pass
296 def func2(): pass
289 class Test: pass
297 class Test: pass
290 number = 0
298 number = 0
291 from enum import Enum
299 from enum import Enum
292 class TestEnum(Enum):
300 class TestEnum(Enum):
293 A = 'a'
301 A = 'a'
294 """
302 """
295 self.write_file(mod_fn, textwrap.dedent(new_code))
303 self.write_file(mod_fn, textwrap.dedent(new_code))
296
304
297 # test function now exists in shell's namespace namespace
305 # test function now exists in shell's namespace namespace
298 self.shell.run_code("func2()")
306 self.shell.run_code("func2()")
299 # test function now exists in module's dict
307 # test function now exists in module's dict
300 self.shell.run_code(f"import sys; sys.modules['{mod_name}'].func2()")
308 self.shell.run_code(f"import sys; sys.modules['{mod_name}'].func2()")
301 # test class now exists
309 # test class now exists
302 self.shell.run_code("t = Test()")
310 self.shell.run_code("t = Test()")
303 # test global built-in var now exists
311 # test global built-in var now exists
304 self.shell.run_code("number")
312 self.shell.run_code("number")
305 # test the enumerations gets loaded successfully
313 # test the enumerations gets loaded successfully
306 self.shell.run_code("TestEnum.A")
314 self.shell.run_code("TestEnum.A")
307
315
308 # ----------- TEST NEW OBJ CAN BE CHANGED --------------------
316 # ----------- TEST NEW OBJ CAN BE CHANGED --------------------
309
317
310 new_code = """
318 new_code = """
311 def func1(): return 'changed'
319 def func1(): return 'changed'
312 def func2(): return 'changed'
320 def func2(): return 'changed'
313 class Test:
321 class Test:
314 def new_func(self):
322 def new_func(self):
315 return 'changed'
323 return 'changed'
316 number = 1
324 number = 1
317 from enum import Enum
325 from enum import Enum
318 class TestEnum(Enum):
326 class TestEnum(Enum):
319 A = 'a'
327 A = 'a'
320 B = 'added'
328 B = 'added'
321 """
329 """
322 self.write_file(mod_fn, textwrap.dedent(new_code))
330 self.write_file(mod_fn, textwrap.dedent(new_code))
323 self.shell.run_code("assert func1() == 'changed'")
331 self.shell.run_code("assert func1() == 'changed'")
324 self.shell.run_code("assert func2() == 'changed'")
332 self.shell.run_code("assert func2() == 'changed'")
325 self.shell.run_code("t = Test(); assert t.new_func() == 'changed'")
333 self.shell.run_code("t = Test(); assert t.new_func() == 'changed'")
326 self.shell.run_code("assert number == 1")
334 self.shell.run_code("assert number == 1")
327 self.shell.run_code("assert TestEnum.B.value == 'added'")
335 self.shell.run_code("assert TestEnum.B.value == 'added'")
328
336
329 # ----------- TEST IMPORT FROM MODULE --------------------------
337 # ----------- TEST IMPORT FROM MODULE --------------------------
330
338
331 new_mod_code = """
339 new_mod_code = """
332 from enum import Enum
340 from enum import Enum
333 class Ext(Enum):
341 class Ext(Enum):
334 A = 'ext'
342 A = 'ext'
335 def ext_func():
343 def ext_func():
336 return 'ext'
344 return 'ext'
337 class ExtTest:
345 class ExtTest:
338 def meth(self):
346 def meth(self):
339 return 'ext'
347 return 'ext'
340 ext_int = 2
348 ext_int = 2
341 """
349 """
342 new_mod_name, new_mod_fn = self.new_module(textwrap.dedent(new_mod_code))
350 new_mod_name, new_mod_fn = self.new_module(textwrap.dedent(new_mod_code))
343 current_mod_code = f"""
351 current_mod_code = f"""
344 from {new_mod_name} import *
352 from {new_mod_name} import *
345 """
353 """
346 self.write_file(mod_fn, textwrap.dedent(current_mod_code))
354 self.write_file(mod_fn, textwrap.dedent(current_mod_code))
347 self.shell.run_code("assert Ext.A.value == 'ext'")
355 self.shell.run_code("assert Ext.A.value == 'ext'")
348 self.shell.run_code("assert ext_func() == 'ext'")
356 self.shell.run_code("assert ext_func() == 'ext'")
349 self.shell.run_code("t = ExtTest(); assert t.meth() == 'ext'")
357 self.shell.run_code("t = ExtTest(); assert t.meth() == 'ext'")
350 self.shell.run_code("assert ext_int == 2")
358 self.shell.run_code("assert ext_int == 2")
351
359
352 def _check_smoketest(self, use_aimport=True):
360 def _check_smoketest(self, use_aimport=True):
353 """
361 """
354 Functional test for the automatic reloader using either
362 Functional test for the automatic reloader using either
355 '%autoreload 1' or '%autoreload 2'
363 '%autoreload 1' or '%autoreload 2'
356 """
364 """
357
365
358 mod_name, mod_fn = self.new_module(
366 mod_name, mod_fn = self.new_module(
359 """
367 """
360 x = 9
368 x = 9
361
369
362 z = 123 # this item will be deleted
370 z = 123 # this item will be deleted
363
371
364 def foo(y):
372 def foo(y):
365 return y + 3
373 return y + 3
366
374
367 class Baz(object):
375 class Baz(object):
368 def __init__(self, x):
376 def __init__(self, x):
369 self.x = x
377 self.x = x
370 def bar(self, y):
378 def bar(self, y):
371 return self.x + y
379 return self.x + y
372 @property
380 @property
373 def quux(self):
381 def quux(self):
374 return 42
382 return 42
375 def zzz(self):
383 def zzz(self):
376 '''This method will be deleted below'''
384 '''This method will be deleted below'''
377 return 99
385 return 99
378
386
379 class Bar: # old-style class: weakref doesn't work for it on Python < 2.7
387 class Bar: # old-style class: weakref doesn't work for it on Python < 2.7
380 def foo(self):
388 def foo(self):
381 return 1
389 return 1
382 """
390 """
383 )
391 )
384
392
385 #
393 #
386 # Import module, and mark for reloading
394 # Import module, and mark for reloading
387 #
395 #
388 if use_aimport:
396 if use_aimport:
389 self.shell.magic_autoreload("1")
397 self.shell.magic_autoreload("1")
390 self.shell.magic_aimport(mod_name)
398 self.shell.magic_aimport(mod_name)
391 stream = StringIO()
399 stream = StringIO()
392 self.shell.magic_aimport("", stream=stream)
400 self.shell.magic_aimport("", stream=stream)
393 self.assertIn(("Modules to reload:\n%s" % mod_name), stream.getvalue())
401 self.assertIn(("Modules to reload:\n%s" % mod_name), stream.getvalue())
394
402
395 with self.assertRaises(ImportError):
403 with self.assertRaises(ImportError):
396 self.shell.magic_aimport("tmpmod_as318989e89ds")
404 self.shell.magic_aimport("tmpmod_as318989e89ds")
397 else:
405 else:
398 self.shell.magic_autoreload("2")
406 self.shell.magic_autoreload("2")
399 self.shell.run_code("import %s" % mod_name)
407 self.shell.run_code("import %s" % mod_name)
400 stream = StringIO()
408 stream = StringIO()
401 self.shell.magic_aimport("", stream=stream)
409 self.shell.magic_aimport("", stream=stream)
402 self.assertTrue(
410 self.assertTrue(
403 "Modules to reload:\nall-except-skipped" in stream.getvalue()
411 "Modules to reload:\nall-except-skipped" in stream.getvalue()
404 )
412 )
405 self.assertIn(mod_name, self.shell.ns)
413 self.assertIn(mod_name, self.shell.ns)
406
414
407 mod = sys.modules[mod_name]
415 mod = sys.modules[mod_name]
408
416
409 #
417 #
410 # Test module contents
418 # Test module contents
411 #
419 #
412 old_foo = mod.foo
420 old_foo = mod.foo
413 old_obj = mod.Baz(9)
421 old_obj = mod.Baz(9)
414 old_obj2 = mod.Bar()
422 old_obj2 = mod.Bar()
415
423
416 def check_module_contents():
424 def check_module_contents():
417 self.assertEqual(mod.x, 9)
425 self.assertEqual(mod.x, 9)
418 self.assertEqual(mod.z, 123)
426 self.assertEqual(mod.z, 123)
419
427
420 self.assertEqual(old_foo(0), 3)
428 self.assertEqual(old_foo(0), 3)
421 self.assertEqual(mod.foo(0), 3)
429 self.assertEqual(mod.foo(0), 3)
422
430
423 obj = mod.Baz(9)
431 obj = mod.Baz(9)
424 self.assertEqual(old_obj.bar(1), 10)
432 self.assertEqual(old_obj.bar(1), 10)
425 self.assertEqual(obj.bar(1), 10)
433 self.assertEqual(obj.bar(1), 10)
426 self.assertEqual(obj.quux, 42)
434 self.assertEqual(obj.quux, 42)
427 self.assertEqual(obj.zzz(), 99)
435 self.assertEqual(obj.zzz(), 99)
428
436
429 obj2 = mod.Bar()
437 obj2 = mod.Bar()
430 self.assertEqual(old_obj2.foo(), 1)
438 self.assertEqual(old_obj2.foo(), 1)
431 self.assertEqual(obj2.foo(), 1)
439 self.assertEqual(obj2.foo(), 1)
432
440
433 check_module_contents()
441 check_module_contents()
434
442
435 #
443 #
436 # Simulate a failed reload: no reload should occur and exactly
444 # Simulate a failed reload: no reload should occur and exactly
437 # one error message should be printed
445 # one error message should be printed
438 #
446 #
439 self.write_file(
447 self.write_file(
440 mod_fn,
448 mod_fn,
441 """
449 """
442 a syntax error
450 a syntax error
443 """,
451 """,
444 )
452 )
445
453
446 with tt.AssertPrints(
454 with tt.AssertPrints(
447 ("[autoreload of %s failed:" % mod_name), channel="stderr"
455 ("[autoreload of %s failed:" % mod_name), channel="stderr"
448 ):
456 ):
449 self.shell.run_code("pass") # trigger reload
457 self.shell.run_code("pass") # trigger reload
450 with tt.AssertNotPrints(
458 with tt.AssertNotPrints(
451 ("[autoreload of %s failed:" % mod_name), channel="stderr"
459 ("[autoreload of %s failed:" % mod_name), channel="stderr"
452 ):
460 ):
453 self.shell.run_code("pass") # trigger another reload
461 self.shell.run_code("pass") # trigger another reload
454 check_module_contents()
462 check_module_contents()
455
463
456 #
464 #
457 # Rewrite module (this time reload should succeed)
465 # Rewrite module (this time reload should succeed)
458 #
466 #
459 self.write_file(
467 self.write_file(
460 mod_fn,
468 mod_fn,
461 """
469 """
462 x = 10
470 x = 10
463
471
464 def foo(y):
472 def foo(y):
465 return y + 4
473 return y + 4
466
474
467 class Baz(object):
475 class Baz(object):
468 def __init__(self, x):
476 def __init__(self, x):
469 self.x = x
477 self.x = x
470 def bar(self, y):
478 def bar(self, y):
471 return self.x + y + 1
479 return self.x + y + 1
472 @property
480 @property
473 def quux(self):
481 def quux(self):
474 return 43
482 return 43
475
483
476 class Bar: # old-style class
484 class Bar: # old-style class
477 def foo(self):
485 def foo(self):
478 return 2
486 return 2
479 """,
487 """,
480 )
488 )
481
489
482 def check_module_contents():
490 def check_module_contents():
483 self.assertEqual(mod.x, 10)
491 self.assertEqual(mod.x, 10)
484 self.assertFalse(hasattr(mod, "z"))
492 self.assertFalse(hasattr(mod, "z"))
485
493
486 self.assertEqual(old_foo(0), 4) # superreload magic!
494 self.assertEqual(old_foo(0), 4) # superreload magic!
487 self.assertEqual(mod.foo(0), 4)
495 self.assertEqual(mod.foo(0), 4)
488
496
489 obj = mod.Baz(9)
497 obj = mod.Baz(9)
490 self.assertEqual(old_obj.bar(1), 11) # superreload magic!
498 self.assertEqual(old_obj.bar(1), 11) # superreload magic!
491 self.assertEqual(obj.bar(1), 11)
499 self.assertEqual(obj.bar(1), 11)
492
500
493 self.assertEqual(old_obj.quux, 43)
501 self.assertEqual(old_obj.quux, 43)
494 self.assertEqual(obj.quux, 43)
502 self.assertEqual(obj.quux, 43)
495
503
496 self.assertFalse(hasattr(old_obj, "zzz"))
504 self.assertFalse(hasattr(old_obj, "zzz"))
497 self.assertFalse(hasattr(obj, "zzz"))
505 self.assertFalse(hasattr(obj, "zzz"))
498
506
499 obj2 = mod.Bar()
507 obj2 = mod.Bar()
500 self.assertEqual(old_obj2.foo(), 2)
508 self.assertEqual(old_obj2.foo(), 2)
501 self.assertEqual(obj2.foo(), 2)
509 self.assertEqual(obj2.foo(), 2)
502
510
503 self.shell.run_code("pass") # trigger reload
511 self.shell.run_code("pass") # trigger reload
504 check_module_contents()
512 check_module_contents()
505
513
506 #
514 #
507 # Another failure case: deleted file (shouldn't reload)
515 # Another failure case: deleted file (shouldn't reload)
508 #
516 #
509 os.unlink(mod_fn)
517 os.unlink(mod_fn)
510
518
511 self.shell.run_code("pass") # trigger reload
519 self.shell.run_code("pass") # trigger reload
512 check_module_contents()
520 check_module_contents()
513
521
514 #
522 #
515 # Disable autoreload and rewrite module: no reload should occur
523 # Disable autoreload and rewrite module: no reload should occur
516 #
524 #
517 if use_aimport:
525 if use_aimport:
518 self.shell.magic_aimport("-" + mod_name)
526 self.shell.magic_aimport("-" + mod_name)
519 stream = StringIO()
527 stream = StringIO()
520 self.shell.magic_aimport("", stream=stream)
528 self.shell.magic_aimport("", stream=stream)
521 self.assertTrue(("Modules to skip:\n%s" % mod_name) in stream.getvalue())
529 self.assertTrue(("Modules to skip:\n%s" % mod_name) in stream.getvalue())
522
530
523 # This should succeed, although no such module exists
531 # This should succeed, although no such module exists
524 self.shell.magic_aimport("-tmpmod_as318989e89ds")
532 self.shell.magic_aimport("-tmpmod_as318989e89ds")
525 else:
533 else:
526 self.shell.magic_autoreload("0")
534 self.shell.magic_autoreload("0")
527
535
528 self.write_file(
536 self.write_file(
529 mod_fn,
537 mod_fn,
530 """
538 """
531 x = -99
539 x = -99
532 """,
540 """,
533 )
541 )
534
542
535 self.shell.run_code("pass") # trigger reload
543 self.shell.run_code("pass") # trigger reload
536 self.shell.run_code("pass")
544 self.shell.run_code("pass")
537 check_module_contents()
545 check_module_contents()
538
546
539 #
547 #
540 # Re-enable autoreload: reload should now occur
548 # Re-enable autoreload: reload should now occur
541 #
549 #
542 if use_aimport:
550 if use_aimport:
543 self.shell.magic_aimport(mod_name)
551 self.shell.magic_aimport(mod_name)
544 else:
552 else:
545 self.shell.magic_autoreload("")
553 self.shell.magic_autoreload("")
546
554
547 self.shell.run_code("pass") # trigger reload
555 self.shell.run_code("pass") # trigger reload
548 self.assertEqual(mod.x, -99)
556 self.assertEqual(mod.x, -99)
549
557
550 def test_smoketest_aimport(self):
558 def test_smoketest_aimport(self):
551 self._check_smoketest(use_aimport=True)
559 self._check_smoketest(use_aimport=True)
552
560
553 def test_smoketest_autoreload(self):
561 def test_smoketest_autoreload(self):
554 self._check_smoketest(use_aimport=False)
562 self._check_smoketest(use_aimport=False)
@@ -1,78 +1,80 b''
1 """Test embedding of IPython"""
1 """Test embedding of IPython"""
2
2
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2013 The IPython Development Team
4 # Copyright (C) 2013 The IPython Development Team
5 #
5 #
6 # Distributed under the terms of the BSD License. The full license is in
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING, distributed as part of this software.
7 # the file COPYING, distributed as part of this software.
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Imports
11 # Imports
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 import os
14 import os
15 import sys
15 import sys
16 from IPython.testing.decorators import skip_win32
16 from IPython.testing.decorators import skip_win32
17 from IPython.testing import IPYTHON_TESTING_TIMEOUT_SCALE
17 from IPython.testing import IPYTHON_TESTING_TIMEOUT_SCALE
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Tests
20 # Tests
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22
22
23 @skip_win32
23 @skip_win32
24 def test_debug_magic_passes_through_generators():
24 def test_debug_magic_passes_through_generators():
25 """
25 """
26 This test that we can correctly pass through frames of a generator post-mortem.
26 This test that we can correctly pass through frames of a generator post-mortem.
27 """
27 """
28 import pexpect
28 import pexpect
29 import re
29 import re
30 in_prompt = re.compile(br'In ?\[\d+\]:')
30 in_prompt = re.compile(br'In ?\[\d+\]:')
31 ipdb_prompt = 'ipdb>'
31 ipdb_prompt = 'ipdb>'
32 env = os.environ.copy()
32 env = os.environ.copy()
33 child = pexpect.spawn(sys.executable, ['-m', 'IPython', '--colors=nocolor', '--simple-prompt'],
33 child = pexpect.spawn(sys.executable, ['-m', 'IPython', '--colors=nocolor', '--simple-prompt'],
34 env=env)
34 env=env)
35 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
35 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
36
36
37 child.expect(in_prompt)
37 child.expect(in_prompt)
38
38
39 child.timeout = 2 * IPYTHON_TESTING_TIMEOUT_SCALE
39 child.timeout = 2 * IPYTHON_TESTING_TIMEOUT_SCALE
40
40
41 child.sendline("def f(x):")
41 child.sendline("def f(x):")
42 child.sendline(" raise Exception")
42 child.sendline(" raise Exception")
43 child.sendline("")
43 child.sendline("")
44
44
45 child.expect(in_prompt)
45 child.expect(in_prompt)
46 child.sendline("gen = (f(x) for x in [0])")
46 child.sendline("gen = (f(x) for x in [0])")
47 child.sendline("")
47 child.sendline("")
48
48
49 child.expect(in_prompt)
49 child.expect(in_prompt)
50 child.sendline("for x in gen:")
50 child.sendline("for x in gen:")
51 child.sendline(" pass")
51 child.sendline(" pass")
52 child.sendline("")
52 child.sendline("")
53
53
54 child.timeout = 10 * IPYTHON_TESTING_TIMEOUT_SCALE
55
54 child.expect('Exception:')
56 child.expect('Exception:')
55
57
56 child.expect(in_prompt)
58 child.expect(in_prompt)
57 child.sendline(r'%debug')
59 child.sendline(r'%debug')
58 child.expect('----> 2 raise Exception')
60 child.expect('----> 2 raise Exception')
59
61
60 child.expect(ipdb_prompt)
62 child.expect(ipdb_prompt)
61 child.sendline('u')
63 child.sendline('u')
62 child.expect_exact(r'----> 1 gen = (f(x) for x in [0])')
64 child.expect_exact(r'----> 1 gen = (f(x) for x in [0])')
63
65
64 child.expect(ipdb_prompt)
66 child.expect(ipdb_prompt)
65 child.sendline('u')
67 child.sendline('u')
66 child.expect_exact('----> 1 for x in gen:')
68 child.expect_exact('----> 1 for x in gen:')
67
69
68 child.expect(ipdb_prompt)
70 child.expect(ipdb_prompt)
69 child.sendline('u')
71 child.sendline('u')
70 child.expect_exact('*** Oldest frame')
72 child.expect_exact('*** Oldest frame')
71
73
72 child.expect(ipdb_prompt)
74 child.expect(ipdb_prompt)
73 child.sendline('exit')
75 child.sendline('exit')
74
76
75 child.expect(in_prompt)
77 child.expect(in_prompt)
76 child.sendline('exit')
78 child.sendline('exit')
77
79
78 child.close()
80 child.close()
@@ -1,137 +1,138 b''
1 """Test embedding of IPython"""
1 """Test embedding of IPython"""
2
2
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2013 The IPython Development Team
4 # Copyright (C) 2013 The IPython Development Team
5 #
5 #
6 # Distributed under the terms of the BSD License. The full license is in
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING, distributed as part of this software.
7 # the file COPYING, distributed as part of this software.
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Imports
11 # Imports
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 import os
14 import os
15 import subprocess
15 import subprocess
16 import sys
16 import sys
17
17
18 from IPython.utils.tempdir import NamedFileInTemporaryDirectory
18 from IPython.utils.tempdir import NamedFileInTemporaryDirectory
19 from IPython.testing.decorators import skip_win32
19 from IPython.testing.decorators import skip_win32
20 from IPython.testing import IPYTHON_TESTING_TIMEOUT_SCALE
20 from IPython.testing import IPYTHON_TESTING_TIMEOUT_SCALE
21
21
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23 # Tests
23 # Tests
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25
25
26
26
27 _sample_embed = b"""
27 _sample_embed = b"""
28 import IPython
28 import IPython
29
29
30 a = 3
30 a = 3
31 b = 14
31 b = 14
32 print(a, '.', b)
32 print(a, '.', b)
33
33
34 IPython.embed()
34 IPython.embed()
35
35
36 print('bye!')
36 print('bye!')
37 """
37 """
38
38
39 _exit = b"exit\r"
39 _exit = b"exit\r"
40
40
41 def test_ipython_embed():
41 def test_ipython_embed():
42 """test that `IPython.embed()` works"""
42 """test that `IPython.embed()` works"""
43 with NamedFileInTemporaryDirectory('file_with_embed.py') as f:
43 with NamedFileInTemporaryDirectory('file_with_embed.py') as f:
44 f.write(_sample_embed)
44 f.write(_sample_embed)
45 f.flush()
45 f.flush()
46 f.close() # otherwise msft won't be able to read the file
46 f.close() # otherwise msft won't be able to read the file
47
47
48 # run `python file_with_embed.py`
48 # run `python file_with_embed.py`
49 cmd = [sys.executable, f.name]
49 cmd = [sys.executable, f.name]
50 env = os.environ.copy()
50 env = os.environ.copy()
51 env['IPY_TEST_SIMPLE_PROMPT'] = '1'
51 env['IPY_TEST_SIMPLE_PROMPT'] = '1'
52
52
53 p = subprocess.Popen(cmd, env=env, stdin=subprocess.PIPE,
53 p = subprocess.Popen(cmd, env=env, stdin=subprocess.PIPE,
54 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
54 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
55 out, err = p.communicate(_exit)
55 out, err = p.communicate(_exit)
56 std = out.decode('UTF-8')
56 std = out.decode('UTF-8')
57
57
58 assert p.returncode == 0
58 assert p.returncode == 0
59 assert "3 . 14" in std
59 assert "3 . 14" in std
60 if os.name != "nt":
60 if os.name != "nt":
61 # TODO: Fix up our different stdout references, see issue gh-14
61 # TODO: Fix up our different stdout references, see issue gh-14
62 assert "IPython" in std
62 assert "IPython" in std
63 assert "bye!" in std
63 assert "bye!" in std
64
64
65
65
66 @skip_win32
66 @skip_win32
67 def test_nest_embed():
67 def test_nest_embed():
68 """test that `IPython.embed()` is nestable"""
68 """test that `IPython.embed()` is nestable"""
69 import pexpect
69 import pexpect
70 ipy_prompt = r']:' #ansi color codes give problems matching beyond this
70 ipy_prompt = r']:' #ansi color codes give problems matching beyond this
71 env = os.environ.copy()
71 env = os.environ.copy()
72 env['IPY_TEST_SIMPLE_PROMPT'] = '1'
72 env['IPY_TEST_SIMPLE_PROMPT'] = '1'
73
73
74
74
75 child = pexpect.spawn(sys.executable, ['-m', 'IPython', '--colors=nocolor'],
75 child = pexpect.spawn(sys.executable, ['-m', 'IPython', '--colors=nocolor'],
76 env=env)
76 env=env)
77 child.timeout = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
77 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
78 child.expect(ipy_prompt)
78 child.expect(ipy_prompt)
79 child.timeout = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
79 child.sendline("import IPython")
80 child.sendline("import IPython")
80 child.expect(ipy_prompt)
81 child.expect(ipy_prompt)
81 child.sendline("ip0 = get_ipython()")
82 child.sendline("ip0 = get_ipython()")
82 #enter first nested embed
83 #enter first nested embed
83 child.sendline("IPython.embed()")
84 child.sendline("IPython.embed()")
84 #skip the banner until we get to a prompt
85 #skip the banner until we get to a prompt
85 try:
86 try:
86 prompted = -1
87 prompted = -1
87 while prompted != 0:
88 while prompted != 0:
88 prompted = child.expect([ipy_prompt, '\r\n'])
89 prompted = child.expect([ipy_prompt, '\r\n'])
89 except pexpect.TIMEOUT as e:
90 except pexpect.TIMEOUT as e:
90 print(e)
91 print(e)
91 #child.interact()
92 #child.interact()
92 child.sendline("embed1 = get_ipython()")
93 child.sendline("embed1 = get_ipython()")
93 child.expect(ipy_prompt)
94 child.expect(ipy_prompt)
94 child.sendline("print('true' if embed1 is not ip0 else 'false')")
95 child.sendline("print('true' if embed1 is not ip0 else 'false')")
95 assert(child.expect(['true\r\n', 'false\r\n']) == 0)
96 assert(child.expect(['true\r\n', 'false\r\n']) == 0)
96 child.expect(ipy_prompt)
97 child.expect(ipy_prompt)
97 child.sendline("print('true' if IPython.get_ipython() is embed1 else 'false')")
98 child.sendline("print('true' if IPython.get_ipython() is embed1 else 'false')")
98 assert(child.expect(['true\r\n', 'false\r\n']) == 0)
99 assert(child.expect(['true\r\n', 'false\r\n']) == 0)
99 child.expect(ipy_prompt)
100 child.expect(ipy_prompt)
100 #enter second nested embed
101 #enter second nested embed
101 child.sendline("IPython.embed()")
102 child.sendline("IPython.embed()")
102 #skip the banner until we get to a prompt
103 #skip the banner until we get to a prompt
103 try:
104 try:
104 prompted = -1
105 prompted = -1
105 while prompted != 0:
106 while prompted != 0:
106 prompted = child.expect([ipy_prompt, '\r\n'])
107 prompted = child.expect([ipy_prompt, '\r\n'])
107 except pexpect.TIMEOUT as e:
108 except pexpect.TIMEOUT as e:
108 print(e)
109 print(e)
109 #child.interact()
110 #child.interact()
110 child.sendline("embed2 = get_ipython()")
111 child.sendline("embed2 = get_ipython()")
111 child.expect(ipy_prompt)
112 child.expect(ipy_prompt)
112 child.sendline("print('true' if embed2 is not embed1 else 'false')")
113 child.sendline("print('true' if embed2 is not embed1 else 'false')")
113 assert(child.expect(['true\r\n', 'false\r\n']) == 0)
114 assert(child.expect(['true\r\n', 'false\r\n']) == 0)
114 child.expect(ipy_prompt)
115 child.expect(ipy_prompt)
115 child.sendline("print('true' if embed2 is IPython.get_ipython() else 'false')")
116 child.sendline("print('true' if embed2 is IPython.get_ipython() else 'false')")
116 assert(child.expect(['true\r\n', 'false\r\n']) == 0)
117 assert(child.expect(['true\r\n', 'false\r\n']) == 0)
117 child.expect(ipy_prompt)
118 child.expect(ipy_prompt)
118 child.sendline('exit')
119 child.sendline('exit')
119 #back at first embed
120 #back at first embed
120 child.expect(ipy_prompt)
121 child.expect(ipy_prompt)
121 child.sendline("print('true' if get_ipython() is embed1 else 'false')")
122 child.sendline("print('true' if get_ipython() is embed1 else 'false')")
122 assert(child.expect(['true\r\n', 'false\r\n']) == 0)
123 assert(child.expect(['true\r\n', 'false\r\n']) == 0)
123 child.expect(ipy_prompt)
124 child.expect(ipy_prompt)
124 child.sendline("print('true' if IPython.get_ipython() is embed1 else 'false')")
125 child.sendline("print('true' if IPython.get_ipython() is embed1 else 'false')")
125 assert(child.expect(['true\r\n', 'false\r\n']) == 0)
126 assert(child.expect(['true\r\n', 'false\r\n']) == 0)
126 child.expect(ipy_prompt)
127 child.expect(ipy_prompt)
127 child.sendline('exit')
128 child.sendline('exit')
128 #back at launching scope
129 #back at launching scope
129 child.expect(ipy_prompt)
130 child.expect(ipy_prompt)
130 child.sendline("print('true' if get_ipython() is ip0 else 'false')")
131 child.sendline("print('true' if get_ipython() is ip0 else 'false')")
131 assert(child.expect(['true\r\n', 'false\r\n']) == 0)
132 assert(child.expect(['true\r\n', 'false\r\n']) == 0)
132 child.expect(ipy_prompt)
133 child.expect(ipy_prompt)
133 child.sendline("print('true' if IPython.get_ipython() is ip0 else 'false')")
134 child.sendline("print('true' if IPython.get_ipython() is ip0 else 'false')")
134 assert(child.expect(['true\r\n', 'false\r\n']) == 0)
135 assert(child.expect(['true\r\n', 'false\r\n']) == 0)
135 child.expect(ipy_prompt)
136 child.expect(ipy_prompt)
136 child.sendline('exit')
137 child.sendline('exit')
137 child.close()
138 child.close()
@@ -1,46 +1,39 b''
1 """Some simple tests for the plugin while running scripts.
1 """Some simple tests for the plugin while running scripts.
2 """
2 """
3 # Module imports
3 # Module imports
4 # Std lib
4 # Std lib
5 import inspect
5 import inspect
6
6
7 # Our own
7 # Our own
8
8
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Testing functions
10 # Testing functions
11
11
12 def test_trivial():
12 def test_trivial():
13 """A trivial passing test."""
13 """A trivial passing test."""
14 pass
14 pass
15
15
16 def doctest_run():
16 def doctest_run():
17 """Test running a trivial script.
17 """Test running a trivial script.
18
18
19 In [13]: run simplevars.py
19 In [13]: run simplevars.py
20 x is: 1
20 x is: 1
21 """
21 """
22
22
23 def doctest_runvars():
23 def doctest_runvars():
24 """Test that variables defined in scripts get loaded correctly via %run.
24 """Test that variables defined in scripts get loaded correctly via %run.
25
25
26 In [13]: run simplevars.py
26 In [13]: run simplevars.py
27 x is: 1
27 x is: 1
28
28
29 In [14]: x
29 In [14]: x
30 Out[14]: 1
30 Out[14]: 1
31 """
31 """
32
32
33 def doctest_ivars():
33 def doctest_ivars():
34 """Test that variables defined interactively are picked up.
34 """Test that variables defined interactively are picked up.
35 In [5]: zz=1
35 In [5]: zz=1
36
36
37 In [6]: zz
37 In [6]: zz
38 Out[6]: 1
38 Out[6]: 1
39 """
39 """
40
41 def doctest_refs():
42 """DocTest reference holding issues when running scripts.
43
44 In [32]: run show_refs.py
45 c referrers: [<... 'dict'>]
46 """
@@ -1,379 +1,379 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """A dict subclass that supports attribute style access.
2 """A dict subclass that supports attribute style access.
3
3
4 Authors:
4 Authors:
5
5
6 * Fernando Perez (original)
6 * Fernando Perez (original)
7 * Brian Granger (refactoring to a dict subclass)
7 * Brian Granger (refactoring to a dict subclass)
8 """
8 """
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Copyright (C) 2008-2011 The IPython Development Team
11 # Copyright (C) 2008-2011 The IPython Development Team
12 #
12 #
13 # Distributed under the terms of the BSD License. The full license is in
13 # Distributed under the terms of the BSD License. The full license is in
14 # the file COPYING, distributed as part of this software.
14 # the file COPYING, distributed as part of this software.
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18 # Imports
18 # Imports
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20
20
21 __all__ = ['Struct']
21 __all__ = ['Struct']
22
22
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24 # Code
24 # Code
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26
26
27
27
28 class Struct(dict):
28 class Struct(dict):
29 """A dict subclass with attribute style access.
29 """A dict subclass with attribute style access.
30
30
31 This dict subclass has a a few extra features:
31 This dict subclass has a a few extra features:
32
32
33 * Attribute style access.
33 * Attribute style access.
34 * Protection of class members (like keys, items) when using attribute
34 * Protection of class members (like keys, items) when using attribute
35 style access.
35 style access.
36 * The ability to restrict assignment to only existing keys.
36 * The ability to restrict assignment to only existing keys.
37 * Intelligent merging.
37 * Intelligent merging.
38 * Overloaded operators.
38 * Overloaded operators.
39 """
39 """
40 _allownew = True
40 _allownew = True
41 def __init__(self, *args, **kw):
41 def __init__(self, *args, **kw):
42 """Initialize with a dictionary, another Struct, or data.
42 """Initialize with a dictionary, another Struct, or data.
43
43
44 Parameters
44 Parameters
45 ----------
45 ----------
46 *args : dict, Struct
46 *args : dict, Struct
47 Initialize with one dict or Struct
47 Initialize with one dict or Struct
48 **kw : dict
48 **kw : dict
49 Initialize with key, value pairs.
49 Initialize with key, value pairs.
50
50
51 Examples
51 Examples
52 --------
52 --------
53 >>> s = Struct(a=10,b=30)
53 >>> s = Struct(a=10,b=30)
54 >>> s.a
54 >>> s.a
55 10
55 10
56 >>> s.b
56 >>> s.b
57 30
57 30
58 >>> s2 = Struct(s,c=30)
58 >>> s2 = Struct(s,c=30)
59 >>> sorted(s2.keys())
59 >>> sorted(s2.keys())
60 ['a', 'b', 'c']
60 ['a', 'b', 'c']
61 """
61 """
62 object.__setattr__(self, '_allownew', True)
62 object.__setattr__(self, '_allownew', True)
63 dict.__init__(self, *args, **kw)
63 dict.__init__(self, *args, **kw)
64
64
65 def __setitem__(self, key, value):
65 def __setitem__(self, key, value):
66 """Set an item with check for allownew.
66 """Set an item with check for allownew.
67
67
68 Examples
68 Examples
69 --------
69 --------
70 >>> s = Struct()
70 >>> s = Struct()
71 >>> s['a'] = 10
71 >>> s['a'] = 10
72 >>> s.allow_new_attr(False)
72 >>> s.allow_new_attr(False)
73 >>> s['a'] = 10
73 >>> s['a'] = 10
74 >>> s['a']
74 >>> s['a']
75 10
75 10
76 >>> try:
76 >>> try:
77 ... s['b'] = 20
77 ... s['b'] = 20
78 ... except KeyError:
78 ... except KeyError:
79 ... print('this is not allowed')
79 ... print('this is not allowed')
80 ...
80 ...
81 this is not allowed
81 this is not allowed
82 """
82 """
83 if not self._allownew and key not in self:
83 if not self._allownew and key not in self:
84 raise KeyError(
84 raise KeyError(
85 "can't create new attribute %s when allow_new_attr(False)" % key)
85 "can't create new attribute %s when allow_new_attr(False)" % key)
86 dict.__setitem__(self, key, value)
86 dict.__setitem__(self, key, value)
87
87
88 def __setattr__(self, key, value):
88 def __setattr__(self, key, value):
89 """Set an attr with protection of class members.
89 """Set an attr with protection of class members.
90
90
91 This calls :meth:`self.__setitem__` but convert :exc:`KeyError` to
91 This calls :meth:`self.__setitem__` but convert :exc:`KeyError` to
92 :exc:`AttributeError`.
92 :exc:`AttributeError`.
93
93
94 Examples
94 Examples
95 --------
95 --------
96 >>> s = Struct()
96 >>> s = Struct()
97 >>> s.a = 10
97 >>> s.a = 10
98 >>> s.a
98 >>> s.a
99 10
99 10
100 >>> try:
100 >>> try:
101 ... s.get = 10
101 ... s.get = 10
102 ... except AttributeError:
102 ... except AttributeError:
103 ... print("you can't set a class member")
103 ... print("you can't set a class member")
104 ...
104 ...
105 you can't set a class member
105 you can't set a class member
106 """
106 """
107 # If key is an str it might be a class member or instance var
107 # If key is an str it might be a class member or instance var
108 if isinstance(key, str):
108 if isinstance(key, str):
109 # I can't simply call hasattr here because it calls getattr, which
109 # I can't simply call hasattr here because it calls getattr, which
110 # calls self.__getattr__, which returns True for keys in
110 # calls self.__getattr__, which returns True for keys in
111 # self._data. But I only want keys in the class and in
111 # self._data. But I only want keys in the class and in
112 # self.__dict__
112 # self.__dict__
113 if key in self.__dict__ or hasattr(Struct, key):
113 if key in self.__dict__ or hasattr(Struct, key):
114 raise AttributeError(
114 raise AttributeError(
115 'attr %s is a protected member of class Struct.' % key
115 'attr %s is a protected member of class Struct.' % key
116 )
116 )
117 try:
117 try:
118 self.__setitem__(key, value)
118 self.__setitem__(key, value)
119 except KeyError as e:
119 except KeyError as e:
120 raise AttributeError(e) from e
120 raise AttributeError(e) from e
121
121
122 def __getattr__(self, key):
122 def __getattr__(self, key):
123 """Get an attr by calling :meth:`dict.__getitem__`.
123 """Get an attr by calling :meth:`dict.__getitem__`.
124
124
125 Like :meth:`__setattr__`, this method converts :exc:`KeyError` to
125 Like :meth:`__setattr__`, this method converts :exc:`KeyError` to
126 :exc:`AttributeError`.
126 :exc:`AttributeError`.
127
127
128 Examples
128 Examples
129 --------
129 --------
130 >>> s = Struct(a=10)
130 >>> s = Struct(a=10)
131 >>> s.a
131 >>> s.a
132 10
132 10
133 >>> type(s.get)
133 >>> type(s.get)
134 <... 'builtin_function_or_method'>
134 <...method'>
135 >>> try:
135 >>> try:
136 ... s.b
136 ... s.b
137 ... except AttributeError:
137 ... except AttributeError:
138 ... print("I don't have that key")
138 ... print("I don't have that key")
139 ...
139 ...
140 I don't have that key
140 I don't have that key
141 """
141 """
142 try:
142 try:
143 result = self[key]
143 result = self[key]
144 except KeyError as e:
144 except KeyError as e:
145 raise AttributeError(key) from e
145 raise AttributeError(key) from e
146 else:
146 else:
147 return result
147 return result
148
148
149 def __iadd__(self, other):
149 def __iadd__(self, other):
150 """s += s2 is a shorthand for s.merge(s2).
150 """s += s2 is a shorthand for s.merge(s2).
151
151
152 Examples
152 Examples
153 --------
153 --------
154 >>> s = Struct(a=10,b=30)
154 >>> s = Struct(a=10,b=30)
155 >>> s2 = Struct(a=20,c=40)
155 >>> s2 = Struct(a=20,c=40)
156 >>> s += s2
156 >>> s += s2
157 >>> sorted(s.keys())
157 >>> sorted(s.keys())
158 ['a', 'b', 'c']
158 ['a', 'b', 'c']
159 """
159 """
160 self.merge(other)
160 self.merge(other)
161 return self
161 return self
162
162
163 def __add__(self,other):
163 def __add__(self,other):
164 """s + s2 -> New Struct made from s.merge(s2).
164 """s + s2 -> New Struct made from s.merge(s2).
165
165
166 Examples
166 Examples
167 --------
167 --------
168 >>> s1 = Struct(a=10,b=30)
168 >>> s1 = Struct(a=10,b=30)
169 >>> s2 = Struct(a=20,c=40)
169 >>> s2 = Struct(a=20,c=40)
170 >>> s = s1 + s2
170 >>> s = s1 + s2
171 >>> sorted(s.keys())
171 >>> sorted(s.keys())
172 ['a', 'b', 'c']
172 ['a', 'b', 'c']
173 """
173 """
174 sout = self.copy()
174 sout = self.copy()
175 sout.merge(other)
175 sout.merge(other)
176 return sout
176 return sout
177
177
178 def __sub__(self,other):
178 def __sub__(self,other):
179 """s1 - s2 -> remove keys in s2 from s1.
179 """s1 - s2 -> remove keys in s2 from s1.
180
180
181 Examples
181 Examples
182 --------
182 --------
183 >>> s1 = Struct(a=10,b=30)
183 >>> s1 = Struct(a=10,b=30)
184 >>> s2 = Struct(a=40)
184 >>> s2 = Struct(a=40)
185 >>> s = s1 - s2
185 >>> s = s1 - s2
186 >>> s
186 >>> s
187 {'b': 30}
187 {'b': 30}
188 """
188 """
189 sout = self.copy()
189 sout = self.copy()
190 sout -= other
190 sout -= other
191 return sout
191 return sout
192
192
193 def __isub__(self,other):
193 def __isub__(self,other):
194 """Inplace remove keys from self that are in other.
194 """Inplace remove keys from self that are in other.
195
195
196 Examples
196 Examples
197 --------
197 --------
198 >>> s1 = Struct(a=10,b=30)
198 >>> s1 = Struct(a=10,b=30)
199 >>> s2 = Struct(a=40)
199 >>> s2 = Struct(a=40)
200 >>> s1 -= s2
200 >>> s1 -= s2
201 >>> s1
201 >>> s1
202 {'b': 30}
202 {'b': 30}
203 """
203 """
204 for k in other.keys():
204 for k in other.keys():
205 if k in self:
205 if k in self:
206 del self[k]
206 del self[k]
207 return self
207 return self
208
208
209 def __dict_invert(self, data):
209 def __dict_invert(self, data):
210 """Helper function for merge.
210 """Helper function for merge.
211
211
212 Takes a dictionary whose values are lists and returns a dict with
212 Takes a dictionary whose values are lists and returns a dict with
213 the elements of each list as keys and the original keys as values.
213 the elements of each list as keys and the original keys as values.
214 """
214 """
215 outdict = {}
215 outdict = {}
216 for k,lst in data.items():
216 for k,lst in data.items():
217 if isinstance(lst, str):
217 if isinstance(lst, str):
218 lst = lst.split()
218 lst = lst.split()
219 for entry in lst:
219 for entry in lst:
220 outdict[entry] = k
220 outdict[entry] = k
221 return outdict
221 return outdict
222
222
223 def dict(self):
223 def dict(self):
224 return self
224 return self
225
225
226 def copy(self):
226 def copy(self):
227 """Return a copy as a Struct.
227 """Return a copy as a Struct.
228
228
229 Examples
229 Examples
230 --------
230 --------
231 >>> s = Struct(a=10,b=30)
231 >>> s = Struct(a=10,b=30)
232 >>> s2 = s.copy()
232 >>> s2 = s.copy()
233 >>> type(s2) is Struct
233 >>> type(s2) is Struct
234 True
234 True
235 """
235 """
236 return Struct(dict.copy(self))
236 return Struct(dict.copy(self))
237
237
238 def hasattr(self, key):
238 def hasattr(self, key):
239 """hasattr function available as a method.
239 """hasattr function available as a method.
240
240
241 Implemented like has_key.
241 Implemented like has_key.
242
242
243 Examples
243 Examples
244 --------
244 --------
245 >>> s = Struct(a=10)
245 >>> s = Struct(a=10)
246 >>> s.hasattr('a')
246 >>> s.hasattr('a')
247 True
247 True
248 >>> s.hasattr('b')
248 >>> s.hasattr('b')
249 False
249 False
250 >>> s.hasattr('get')
250 >>> s.hasattr('get')
251 False
251 False
252 """
252 """
253 return key in self
253 return key in self
254
254
255 def allow_new_attr(self, allow = True):
255 def allow_new_attr(self, allow = True):
256 """Set whether new attributes can be created in this Struct.
256 """Set whether new attributes can be created in this Struct.
257
257
258 This can be used to catch typos by verifying that the attribute user
258 This can be used to catch typos by verifying that the attribute user
259 tries to change already exists in this Struct.
259 tries to change already exists in this Struct.
260 """
260 """
261 object.__setattr__(self, '_allownew', allow)
261 object.__setattr__(self, '_allownew', allow)
262
262
263 def merge(self, __loc_data__=None, __conflict_solve=None, **kw):
263 def merge(self, __loc_data__=None, __conflict_solve=None, **kw):
264 """Merge two Structs with customizable conflict resolution.
264 """Merge two Structs with customizable conflict resolution.
265
265
266 This is similar to :meth:`update`, but much more flexible. First, a
266 This is similar to :meth:`update`, but much more flexible. First, a
267 dict is made from data+key=value pairs. When merging this dict with
267 dict is made from data+key=value pairs. When merging this dict with
268 the Struct S, the optional dictionary 'conflict' is used to decide
268 the Struct S, the optional dictionary 'conflict' is used to decide
269 what to do.
269 what to do.
270
270
271 If conflict is not given, the default behavior is to preserve any keys
271 If conflict is not given, the default behavior is to preserve any keys
272 with their current value (the opposite of the :meth:`update` method's
272 with their current value (the opposite of the :meth:`update` method's
273 behavior).
273 behavior).
274
274
275 Parameters
275 Parameters
276 ----------
276 ----------
277 __loc_data__ : dict, Struct
277 __loc_data__ : dict, Struct
278 The data to merge into self
278 The data to merge into self
279 __conflict_solve : dict
279 __conflict_solve : dict
280 The conflict policy dict. The keys are binary functions used to
280 The conflict policy dict. The keys are binary functions used to
281 resolve the conflict and the values are lists of strings naming
281 resolve the conflict and the values are lists of strings naming
282 the keys the conflict resolution function applies to. Instead of
282 the keys the conflict resolution function applies to. Instead of
283 a list of strings a space separated string can be used, like
283 a list of strings a space separated string can be used, like
284 'a b c'.
284 'a b c'.
285 **kw : dict
285 **kw : dict
286 Additional key, value pairs to merge in
286 Additional key, value pairs to merge in
287
287
288 Notes
288 Notes
289 -----
289 -----
290 The `__conflict_solve` dict is a dictionary of binary functions which will be used to
290 The `__conflict_solve` dict is a dictionary of binary functions which will be used to
291 solve key conflicts. Here is an example::
291 solve key conflicts. Here is an example::
292
292
293 __conflict_solve = dict(
293 __conflict_solve = dict(
294 func1=['a','b','c'],
294 func1=['a','b','c'],
295 func2=['d','e']
295 func2=['d','e']
296 )
296 )
297
297
298 In this case, the function :func:`func1` will be used to resolve
298 In this case, the function :func:`func1` will be used to resolve
299 keys 'a', 'b' and 'c' and the function :func:`func2` will be used for
299 keys 'a', 'b' and 'c' and the function :func:`func2` will be used for
300 keys 'd' and 'e'. This could also be written as::
300 keys 'd' and 'e'. This could also be written as::
301
301
302 __conflict_solve = dict(func1='a b c',func2='d e')
302 __conflict_solve = dict(func1='a b c',func2='d e')
303
303
304 These functions will be called for each key they apply to with the
304 These functions will be called for each key they apply to with the
305 form::
305 form::
306
306
307 func1(self['a'], other['a'])
307 func1(self['a'], other['a'])
308
308
309 The return value is used as the final merged value.
309 The return value is used as the final merged value.
310
310
311 As a convenience, merge() provides five (the most commonly needed)
311 As a convenience, merge() provides five (the most commonly needed)
312 pre-defined policies: preserve, update, add, add_flip and add_s. The
312 pre-defined policies: preserve, update, add, add_flip and add_s. The
313 easiest explanation is their implementation::
313 easiest explanation is their implementation::
314
314
315 preserve = lambda old,new: old
315 preserve = lambda old,new: old
316 update = lambda old,new: new
316 update = lambda old,new: new
317 add = lambda old,new: old + new
317 add = lambda old,new: old + new
318 add_flip = lambda old,new: new + old # note change of order!
318 add_flip = lambda old,new: new + old # note change of order!
319 add_s = lambda old,new: old + ' ' + new # only for str!
319 add_s = lambda old,new: old + ' ' + new # only for str!
320
320
321 You can use those four words (as strings) as keys instead
321 You can use those four words (as strings) as keys instead
322 of defining them as functions, and the merge method will substitute
322 of defining them as functions, and the merge method will substitute
323 the appropriate functions for you.
323 the appropriate functions for you.
324
324
325 For more complicated conflict resolution policies, you still need to
325 For more complicated conflict resolution policies, you still need to
326 construct your own functions.
326 construct your own functions.
327
327
328 Examples
328 Examples
329 --------
329 --------
330 This show the default policy:
330 This show the default policy:
331
331
332 >>> s = Struct(a=10,b=30)
332 >>> s = Struct(a=10,b=30)
333 >>> s2 = Struct(a=20,c=40)
333 >>> s2 = Struct(a=20,c=40)
334 >>> s.merge(s2)
334 >>> s.merge(s2)
335 >>> sorted(s.items())
335 >>> sorted(s.items())
336 [('a', 10), ('b', 30), ('c', 40)]
336 [('a', 10), ('b', 30), ('c', 40)]
337
337
338 Now, show how to specify a conflict dict:
338 Now, show how to specify a conflict dict:
339
339
340 >>> s = Struct(a=10,b=30)
340 >>> s = Struct(a=10,b=30)
341 >>> s2 = Struct(a=20,b=40)
341 >>> s2 = Struct(a=20,b=40)
342 >>> conflict = {'update':'a','add':'b'}
342 >>> conflict = {'update':'a','add':'b'}
343 >>> s.merge(s2,conflict)
343 >>> s.merge(s2,conflict)
344 >>> sorted(s.items())
344 >>> sorted(s.items())
345 [('a', 20), ('b', 70)]
345 [('a', 20), ('b', 70)]
346 """
346 """
347
347
348 data_dict = dict(__loc_data__,**kw)
348 data_dict = dict(__loc_data__,**kw)
349
349
350 # policies for conflict resolution: two argument functions which return
350 # policies for conflict resolution: two argument functions which return
351 # the value that will go in the new struct
351 # the value that will go in the new struct
352 preserve = lambda old,new: old
352 preserve = lambda old,new: old
353 update = lambda old,new: new
353 update = lambda old,new: new
354 add = lambda old,new: old + new
354 add = lambda old,new: old + new
355 add_flip = lambda old,new: new + old # note change of order!
355 add_flip = lambda old,new: new + old # note change of order!
356 add_s = lambda old,new: old + ' ' + new
356 add_s = lambda old,new: old + ' ' + new
357
357
358 # default policy is to keep current keys when there's a conflict
358 # default policy is to keep current keys when there's a conflict
359 conflict_solve = dict.fromkeys(self, preserve)
359 conflict_solve = dict.fromkeys(self, preserve)
360
360
361 # the conflict_solve dictionary is given by the user 'inverted': we
361 # the conflict_solve dictionary is given by the user 'inverted': we
362 # need a name-function mapping, it comes as a function -> names
362 # need a name-function mapping, it comes as a function -> names
363 # dict. Make a local copy (b/c we'll make changes), replace user
363 # dict. Make a local copy (b/c we'll make changes), replace user
364 # strings for the three builtin policies and invert it.
364 # strings for the three builtin policies and invert it.
365 if __conflict_solve:
365 if __conflict_solve:
366 inv_conflict_solve_user = __conflict_solve.copy()
366 inv_conflict_solve_user = __conflict_solve.copy()
367 for name, func in [('preserve',preserve), ('update',update),
367 for name, func in [('preserve',preserve), ('update',update),
368 ('add',add), ('add_flip',add_flip),
368 ('add',add), ('add_flip',add_flip),
369 ('add_s',add_s)]:
369 ('add_s',add_s)]:
370 if name in inv_conflict_solve_user.keys():
370 if name in inv_conflict_solve_user.keys():
371 inv_conflict_solve_user[func] = inv_conflict_solve_user[name]
371 inv_conflict_solve_user[func] = inv_conflict_solve_user[name]
372 del inv_conflict_solve_user[name]
372 del inv_conflict_solve_user[name]
373 conflict_solve.update(self.__dict_invert(inv_conflict_solve_user))
373 conflict_solve.update(self.__dict_invert(inv_conflict_solve_user))
374 for key in data_dict:
374 for key in data_dict:
375 if key not in self:
375 if key not in self:
376 self[key] = data_dict[key]
376 self[key] = data_dict[key]
377 else:
377 else:
378 self[key] = conflict_solve[key](self[key],data_dict[key])
378 self[key] = conflict_solve[key](self[key],data_dict[key])
379
379
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now