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