##// END OF EJS Templates
more fixes
M Bussonnier -
Show More
@@ -1,1556 +1,1556
1 1 # -*- coding: utf-8 -*-
2 2 """Tests for various magic functions."""
3 3
4 4 import gc
5 5 import io
6 6 import os
7 7 import re
8 8 import shlex
9 9 import sys
10 10 import warnings
11 11 from importlib import invalidate_caches
12 12 from io import StringIO
13 13 from pathlib import Path
14 14 from textwrap import dedent
15 15 from unittest import TestCase, mock
16 16
17 17 import pytest
18 18
19 19 from IPython import get_ipython
20 20 from IPython.core import magic
21 21 from IPython.core.error import UsageError
22 22 from IPython.core.magic import (
23 23 Magics,
24 24 cell_magic,
25 25 line_magic,
26 26 magics_class,
27 27 register_cell_magic,
28 28 register_line_magic,
29 29 )
30 30 from IPython.core.magics import code, execution, logging, osm, script
31 31 from IPython.testing import decorators as dec
32 32 from IPython.testing import tools as tt
33 33 from IPython.utils.io import capture_output
34 34 from IPython.utils.process import find_cmd
35 35 from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
36 36 from IPython.utils.syspathcontext import prepended_to_syspath
37 37
38 38 # import needed by doctest
39 39 from .test_debugger import PdbTestInput # noqa: F401
40 40
41 _ip = get_ipython()
41 42
42 43 @magic.magics_class
43 class DummyMagics(magic.Magics): pass
44 class DummyMagics(magic.Magics):
45 pass
44 46
45 47 def test_extract_code_ranges():
46 48 instr = "1 3 5-6 7-9 10:15 17: :10 10- -13 :"
47 49 expected = [
48 50 (0, 1),
49 51 (2, 3),
50 52 (4, 6),
51 53 (6, 9),
52 54 (9, 14),
53 55 (16, None),
54 56 (None, 9),
55 57 (9, None),
56 58 (None, 13),
57 59 (None, None),
58 60 ]
59 61 actual = list(code.extract_code_ranges(instr))
60 62 assert actual == expected
61 63
62 64 def test_extract_symbols():
63 65 source = """import foo\na = 10\ndef b():\n return 42\n\n\nclass A: pass\n\n\n"""
64 66 symbols_args = ["a", "b", "A", "A,b", "A,a", "z"]
65 67 expected = [([], ['a']),
66 68 (["def b():\n return 42\n"], []),
67 69 (["class A: pass\n"], []),
68 70 (["class A: pass\n", "def b():\n return 42\n"], []),
69 71 (["class A: pass\n"], ['a']),
70 72 ([], ['z'])]
71 73 for symbols, exp in zip(symbols_args, expected):
72 74 assert code.extract_symbols(source, symbols) == exp
73 75
74 76
75 77 def test_extract_symbols_raises_exception_with_non_python_code():
76 78 source = ("=begin A Ruby program :)=end\n"
77 79 "def hello\n"
78 80 "puts 'Hello world'\n"
79 81 "end")
80 82 with pytest.raises(SyntaxError):
81 83 code.extract_symbols(source, "hello")
82 84
83 85
84 86 def test_magic_not_found():
85 87 # magic not found raises UsageError
86 88 with pytest.raises(UsageError):
87 89 _ip.run_line_magic("doesntexist", "")
88 90
89 91 # ensure result isn't success when a magic isn't found
90 92 result = _ip.run_cell('%doesntexist')
91 93 assert isinstance(result.error_in_exec, UsageError)
92 94
93 95
94 96 def test_cell_magic_not_found():
95 97 # magic not found raises UsageError
96 98 with pytest.raises(UsageError):
97 99 _ip.run_cell_magic('doesntexist', 'line', 'cell')
98 100
99 101 # ensure result isn't success when a magic isn't found
100 102 result = _ip.run_cell('%%doesntexist')
101 103 assert isinstance(result.error_in_exec, UsageError)
102 104
103 105
104 106 def test_magic_error_status():
105 107 def fail(shell):
106 108 1/0
107 109 _ip.register_magic_function(fail)
108 110 result = _ip.run_cell('%fail')
109 111 assert isinstance(result.error_in_exec, ZeroDivisionError)
110 112
111 113
112 114 def test_config():
113 115 """ test that config magic does not raise
114 116 can happen if Configurable init is moved too early into
115 117 Magics.__init__ as then a Config object will be registered as a
116 118 magic.
117 119 """
118 120 ## should not raise.
119 121 _ip.run_line_magic("config", "")
120 122
121 123
122 124 def test_config_available_configs():
123 125 """ test that config magic prints available configs in unique and
124 126 sorted order. """
125 127 with capture_output() as captured:
126 128 _ip.run_line_magic("config", "")
127 129
128 130 stdout = captured.stdout
129 131 config_classes = stdout.strip().split('\n')[1:]
130 132 assert config_classes == sorted(set(config_classes))
131 133
132 134 def test_config_print_class():
133 135 """ test that config with a classname prints the class's options. """
134 136 with capture_output() as captured:
135 137 _ip.run_line_magic("config", "TerminalInteractiveShell")
136 138
137 139 stdout = captured.stdout
138 140 assert re.match(
139 141 "TerminalInteractiveShell.* options", stdout.splitlines()[0]
140 142 ), f"{stdout}\n\n1st line of stdout not like 'TerminalInteractiveShell.* options'"
141 143
142 144
143 145 def test_rehashx():
144 146 # clear up everything
145 147 _ip.alias_manager.clear_aliases()
146 148 del _ip.db['syscmdlist']
147 149
148 150 _ip.run_line_magic("rehashx", "")
149 151 # Practically ALL ipython development systems will have more than 10 aliases
150 152
151 153 assert len(_ip.alias_manager.aliases) > 10
152 154 for name, cmd in _ip.alias_manager.aliases:
153 155 # we must strip dots from alias names
154 156 assert "." not in name
155 157
156 158 # rehashx must fill up syscmdlist
157 159 scoms = _ip.db['syscmdlist']
158 160 assert len(scoms) > 10
159 161
160 162
161 163 def test_magic_parse_options():
162 164 """Test that we don't mangle paths when parsing magic options."""
163 165 ip = get_ipython()
164 166 path = 'c:\\x'
165 167 m = DummyMagics(ip)
166 168 opts = m.parse_options('-f %s' % path,'f:')[0]
167 169 # argv splitting is os-dependent
168 170 if os.name == 'posix':
169 171 expected = 'c:x'
170 172 else:
171 173 expected = path
172 174 assert opts["f"] == expected
173 175
174 176
175 177 def test_magic_parse_long_options():
176 178 """Magic.parse_options can handle --foo=bar long options"""
177 179 ip = get_ipython()
178 180 m = DummyMagics(ip)
179 181 opts, _ = m.parse_options("--foo --bar=bubble", "a", "foo", "bar=")
180 182 assert "foo" in opts
181 183 assert "bar" in opts
182 184 assert opts["bar"] == "bubble"
183 185
184 186
185 187 def doctest_hist_f():
186 188 """Test %hist -f with temporary filename.
187 189
188 190 In [9]: import tempfile
189 191
190 192 In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-')
191 193
192 194 In [11]: %hist -nl -f $tfile 3
193 195
194 196 In [13]: import os; os.unlink(tfile)
195 197 """
196 198
197 199
198 200 def doctest_hist_op():
199 201 """Test %hist -op
200 202
201 203 In [1]: class b(float):
202 204 ...: pass
203 205 ...:
204 206
205 207 In [2]: class s(object):
206 208 ...: def __str__(self):
207 209 ...: return 's'
208 210 ...:
209 211
210 212 In [3]:
211 213
212 214 In [4]: class r(b):
213 215 ...: def __repr__(self):
214 216 ...: return 'r'
215 217 ...:
216 218
217 219 In [5]: class sr(s,r): pass
218 220 ...:
219 221
220 222 In [6]:
221 223
222 224 In [7]: bb=b()
223 225
224 226 In [8]: ss=s()
225 227
226 228 In [9]: rr=r()
227 229
228 230 In [10]: ssrr=sr()
229 231
230 232 In [11]: 4.5
231 233 Out[11]: 4.5
232 234
233 235 In [12]: str(ss)
234 236 Out[12]: 's'
235 237
236 238 In [13]:
237 239
238 240 In [14]: %hist -op
239 241 >>> class b:
240 242 ... pass
241 243 ...
242 244 >>> class s(b):
243 245 ... def __str__(self):
244 246 ... return 's'
245 247 ...
246 248 >>>
247 249 >>> class r(b):
248 250 ... def __repr__(self):
249 251 ... return 'r'
250 252 ...
251 253 >>> class sr(s,r): pass
252 254 >>>
253 255 >>> bb=b()
254 256 >>> ss=s()
255 257 >>> rr=r()
256 258 >>> ssrr=sr()
257 259 >>> 4.5
258 260 4.5
259 261 >>> str(ss)
260 262 's'
261 263 >>>
262 264 """
263 265
264 266 def test_hist_pof():
265 267 ip = get_ipython()
266 268 ip.run_cell("1+2", store_history=True)
267 269 #raise Exception(ip.history_manager.session_number)
268 270 #raise Exception(list(ip.history_manager._get_range_session()))
269 271 with TemporaryDirectory() as td:
270 272 tf = os.path.join(td, 'hist.py')
271 273 ip.run_line_magic('history', '-pof %s' % tf)
272 274 assert os.path.isfile(tf)
273 275
274 276
275 277 def test_macro():
276 278 ip = get_ipython()
277 279 ip.history_manager.reset() # Clear any existing history.
278 280 cmds = ["a=1", "def b():\n return a**2", "print(a,b())"]
279 281 for i, cmd in enumerate(cmds, start=1):
280 282 ip.history_manager.store_inputs(i, cmd)
281 283 ip.run_line_magic("macro", "test 1-3")
282 284 assert ip.user_ns["test"].value == "\n".join(cmds) + "\n"
283 285
284 286 # List macros
285 287 assert "test" in ip.run_line_magic("macro", "")
286 288
287 289
288 290 def test_macro_run():
289 291 """Test that we can run a multi-line macro successfully."""
290 292 ip = get_ipython()
291 293 ip.history_manager.reset()
292 294 cmds = ["a=10", "a+=1", "print(a)", "%macro test 2-3"]
293 295 for cmd in cmds:
294 296 ip.run_cell(cmd, store_history=True)
295 297 assert ip.user_ns["test"].value == "a+=1\nprint(a)\n"
296 298 with tt.AssertPrints("12"):
297 299 ip.run_cell("test")
298 300 with tt.AssertPrints("13"):
299 301 ip.run_cell("test")
300 302
301 303
302 304 def test_magic_magic():
303 305 """Test %magic"""
304 306 ip = get_ipython()
305 307 with capture_output() as captured:
306 308 ip.run_line_magic("magic", "")
307 309
308 310 stdout = captured.stdout
309 311 assert "%magic" in stdout
310 312 assert "IPython" in stdout
311 313 assert "Available" in stdout
312 314
313 315
314 316 @dec.skipif_not_numpy
315 317 def test_numpy_reset_array_undec():
316 318 "Test '%reset array' functionality"
317 319 _ip.ex("import numpy as np")
318 320 _ip.ex("a = np.empty(2)")
319 321 assert "a" in _ip.user_ns
320 322 _ip.run_line_magic("reset", "-f array")
321 323 assert "a" not in _ip.user_ns
322 324
323 325
324 326 def test_reset_out():
325 327 "Test '%reset out' magic"
326 328 _ip.run_cell("parrot = 'dead'", store_history=True)
327 329 # test '%reset -f out', make an Out prompt
328 330 _ip.run_cell("parrot", store_history=True)
329 331 assert "dead" in [_ip.user_ns[x] for x in ("_", "__", "___")]
330 332 _ip.run_line_magic("reset", "-f out")
331 333 assert "dead" not in [_ip.user_ns[x] for x in ("_", "__", "___")]
332 334 assert len(_ip.user_ns["Out"]) == 0
333 335
334 336
335 337 def test_reset_in():
336 338 "Test '%reset in' magic"
337 339 # test '%reset -f in'
338 340 _ip.run_cell("parrot", store_history=True)
339 341 assert "parrot" in [_ip.user_ns[x] for x in ("_i", "_ii", "_iii")]
340 342 _ip.run_line_magic("reset", "-f in")
341 343 assert "parrot" not in [_ip.user_ns[x] for x in ("_i", "_ii", "_iii")]
342 344 assert len(set(_ip.user_ns["In"])) == 1
343 345
344 346
345 347 def test_reset_dhist():
346 348 "Test '%reset dhist' magic"
347 349 _ip.run_cell("tmp = [d for d in _dh]") # copy before clearing
348 350 _ip.run_line_magic("cd", os.path.dirname(pytest.__file__))
349 351 _ip.run_line_magic("cd", "-")
350 352 assert len(_ip.user_ns["_dh"]) > 0
351 353 _ip.run_line_magic("reset", "-f dhist")
352 354 assert len(_ip.user_ns["_dh"]) == 0
353 355 _ip.run_cell("_dh = [d for d in tmp]") # restore
354 356
355 357
356 358 def test_reset_in_length():
357 359 "Test that '%reset in' preserves In[] length"
358 360 _ip.run_cell("print 'foo'")
359 361 _ip.run_cell("reset -f in")
360 362 assert len(_ip.user_ns["In"]) == _ip.displayhook.prompt_count + 1
361 363
362 364
363 365 class TestResetErrors(TestCase):
364 366
365 367 def test_reset_redefine(self):
366 368
367 369 @magics_class
368 370 class KernelMagics(Magics):
369 371 @line_magic
370 372 def less(self, shell): pass
371 373
372 374 _ip.register_magics(KernelMagics)
373 375
374 376 with self.assertLogs() as cm:
375 377 # hack, we want to just capture logs, but assertLogs fails if not
376 378 # logs get produce.
377 379 # so log one things we ignore.
378 380 import logging as log_mod
379 381 log = log_mod.getLogger()
380 382 log.info('Nothing')
381 383 # end hack.
382 384 _ip.run_cell("reset -f")
383 385
384 386 assert len(cm.output) == 1
385 387 for out in cm.output:
386 388 assert "Invalid alias" not in out
387 389
388 390 def test_tb_syntaxerror():
389 391 """test %tb after a SyntaxError"""
390 392 ip = get_ipython()
391 393 ip.run_cell("for")
392 394
393 395 # trap and validate stdout
394 396 save_stdout = sys.stdout
395 397 try:
396 398 sys.stdout = StringIO()
397 399 ip.run_cell("%tb")
398 400 out = sys.stdout.getvalue()
399 401 finally:
400 402 sys.stdout = save_stdout
401 403 # trim output, and only check the last line
402 404 last_line = out.rstrip().splitlines()[-1].strip()
403 405 assert last_line == "SyntaxError: invalid syntax"
404 406
405 407
406 408 def test_time():
407 409 ip = get_ipython()
408 410
409 411 with tt.AssertPrints("Wall time: "):
410 412 ip.run_cell("%time None")
411 413
412 414 ip.run_cell("def f(kmjy):\n"
413 415 " %time print (2*kmjy)")
414 416
415 417 with tt.AssertPrints("Wall time: "):
416 418 with tt.AssertPrints("hihi", suppress=False):
417 419 ip.run_cell("f('hi')")
418 420
419 421
420 422 # ';' at the end of %time prevents instruction value to be printed.
421 423 # This tests fix for #13837.
422 424 def test_time_no_output_with_semicolon():
423 425 ip = get_ipython()
424 426
425 427 # Test %time cases
426 428 with tt.AssertPrints(" 123456"):
427 429 with tt.AssertPrints("Wall time: ", suppress=False):
428 430 with tt.AssertPrints("CPU times: ", suppress=False):
429 431 ip.run_cell("%time 123000+456")
430 432
431 433 with tt.AssertNotPrints(" 123456"):
432 434 with tt.AssertPrints("Wall time: ", suppress=False):
433 435 with tt.AssertPrints("CPU times: ", suppress=False):
434 436 ip.run_cell("%time 123000+456;")
435 437
436 438 with tt.AssertPrints(" 123456"):
437 439 with tt.AssertPrints("Wall time: ", suppress=False):
438 440 with tt.AssertPrints("CPU times: ", suppress=False):
439 441 ip.run_cell("%time 123000+456 # Comment")
440 442
441 443 with tt.AssertNotPrints(" 123456"):
442 444 with tt.AssertPrints("Wall time: ", suppress=False):
443 445 with tt.AssertPrints("CPU times: ", suppress=False):
444 446 ip.run_cell("%time 123000+456; # Comment")
445 447
446 448 with tt.AssertPrints(" 123456"):
447 449 with tt.AssertPrints("Wall time: ", suppress=False):
448 450 with tt.AssertPrints("CPU times: ", suppress=False):
449 451 ip.run_cell("%time 123000+456 # ;Comment")
450 452
451 453 # Test %%time cases
452 454 with tt.AssertPrints("123456"):
453 455 with tt.AssertPrints("Wall time: ", suppress=False):
454 456 with tt.AssertPrints("CPU times: ", suppress=False):
455 457 ip.run_cell("%%time\n123000+456\n\n\n")
456 458
457 459 with tt.AssertNotPrints("123456"):
458 460 with tt.AssertPrints("Wall time: ", suppress=False):
459 461 with tt.AssertPrints("CPU times: ", suppress=False):
460 462 ip.run_cell("%%time\n123000+456;\n\n\n")
461 463
462 464 with tt.AssertPrints("123456"):
463 465 with tt.AssertPrints("Wall time: ", suppress=False):
464 466 with tt.AssertPrints("CPU times: ", suppress=False):
465 467 ip.run_cell("%%time\n123000+456 # Comment\n\n\n")
466 468
467 469 with tt.AssertNotPrints("123456"):
468 470 with tt.AssertPrints("Wall time: ", suppress=False):
469 471 with tt.AssertPrints("CPU times: ", suppress=False):
470 472 ip.run_cell("%%time\n123000+456; # Comment\n\n\n")
471 473
472 474 with tt.AssertPrints("123456"):
473 475 with tt.AssertPrints("Wall time: ", suppress=False):
474 476 with tt.AssertPrints("CPU times: ", suppress=False):
475 477 ip.run_cell("%%time\n123000+456 # ;Comment\n\n\n")
476 478
477 479
478 480 def test_time_last_not_expression():
479 ip.run_cell("%%time\n"
481 _ip.run_cell("%%time\n"
480 482 "var_1 = 1\n"
481 483 "var_2 = 2\n")
482 assert ip.user_ns['var_1'] == 1
483 del ip.user_ns['var_1']
484 assert ip.user_ns['var_2'] == 2
485 del ip.user_ns['var_2']
484 assert _ip.user_ns['var_1'] == 1
485 del _ip.user_ns['var_1']
486 assert _ip.user_ns['var_2'] == 2
487 del _ip.user_ns['var_2']
486 488
487 489
488 490 @dec.skip_win32
489 491 def test_time2():
490 492 ip = get_ipython()
491 493
492 494 with tt.AssertPrints("CPU times: user "):
493 495 ip.run_cell("%time None")
494 496
495 497 def test_time3():
496 498 """Erroneous magic function calls, issue gh-3334"""
497 499 ip = get_ipython()
498 500 ip.user_ns.pop('run', None)
499 501
500 502 with tt.AssertNotPrints("not found", channel='stderr'):
501 503 ip.run_cell("%%time\n"
502 504 "run = 0\n"
503 505 "run += 1")
504 506
505 507 def test_multiline_time():
506 508 """Make sure last statement from time return a value."""
507 509 ip = get_ipython()
508 510 ip.user_ns.pop('run', None)
509 511
510 512 ip.run_cell(
511 513 dedent(
512 514 """\
513 515 %%time
514 516 a = "ho"
515 517 b = "hey"
516 518 a+b
517 519 """
518 520 )
519 521 )
520 522 assert ip.user_ns_hidden["_"] == "hohey"
521 523
522 524
523 525 def test_time_local_ns():
524 526 """
525 527 Test that local_ns is actually global_ns when running a cell magic
526 528 """
527 529 ip = get_ipython()
528 530 ip.run_cell("%%time\n" "myvar = 1")
529 531 assert ip.user_ns["myvar"] == 1
530 532 del ip.user_ns["myvar"]
531 533
532 534
533 535 def test_time_microseconds_display():
534 536 """Ensure ASCII is used when necessary"""
535 537 with mock.patch("sys.stdout", io.TextIOWrapper(StringIO(), encoding="utf-8")):
536 538 assert execution._format_time(0.000001) == "1 \u03bcs"
537 539 with mock.patch("sys.stdout", io.TextIOWrapper(StringIO(), encoding="ascii")):
538 540 assert execution._format_time(0.000001) == "1 us"
539 541
540 542
541 543 # Test %%capture magic. Added to test issue #13926
542 544 def test_capture():
543 545 ip = get_ipython()
544 546
545 547 # Test %%capture nominal case
546 548 ip.run_cell("%%capture abc\n1+2")
547 549 with tt.AssertPrints("True", suppress=False):
548 550 ip.run_cell("'abc' in locals()")
549 551 with tt.AssertPrints("True", suppress=False):
550 552 ip.run_cell("'outputs' in dir(abc)")
551 553 with tt.AssertPrints("3", suppress=False):
552 554 ip.run_cell("abc.outputs[0]")
553 555
554 556 # Test %%capture with ';' at end of expression
555 557 ip.run_cell("%%capture abc\n7+8;")
556 558 with tt.AssertPrints("False", suppress=False):
557 559 ip.run_cell("'abc' in locals()")
558 560
559 561
560 562 def test_doctest_mode():
561 563 "Toggle doctest_mode twice, it should be a no-op and run without error"
562 564 _ip.run_line_magic("doctest_mode", "")
563 565 _ip.run_line_magic("doctest_mode", "")
564 566
565 567
566 568 def test_parse_options():
567 569 """Tests for basic options parsing in magics."""
568 570 # These are only the most minimal of tests, more should be added later. At
569 571 # the very least we check that basic text/unicode calls work OK.
570 572 m = DummyMagics(_ip)
571 573 assert m.parse_options("foo", "")[1] == "foo"
572 574 assert m.parse_options("foo", "")[1] == "foo"
573 575
574 576
575 577 def test_parse_options_preserve_non_option_string():
576 578 """Test to assert preservation of non-option part of magic-block, while parsing magic options."""
577 579 m = DummyMagics(_ip)
578 580 opts, stmt = m.parse_options(
579 581 " -n1 -r 13 _ = 314 + foo", "n:r:", preserve_non_opts=True
580 582 )
581 583 assert opts == {"n": "1", "r": "13"}
582 584 assert stmt == "_ = 314 + foo"
583 585
584 586
585 587 def test_run_magic_preserve_code_block():
586 588 """Test to assert preservation of non-option part of magic-block, while running magic."""
587 589 _ip.user_ns["spaces"] = []
588 590 _ip.run_line_magic(
589 591 "timeit", "-n1 -r1 spaces.append([s.count(' ') for s in ['document']])"
590 592 )
591 593 assert _ip.user_ns["spaces"] == [[0]]
592 594
593 595
594 596 def test_dirops():
595 597 """Test various directory handling operations."""
596 598 # curpath = lambda :os.path.splitdrive(os.getcwd())[1].replace('\\','/')
597 599 curpath = os.getcwd
598 600 startdir = os.getcwd()
599 601 ipdir = os.path.realpath(_ip.ipython_dir)
600 602 try:
601 603 _ip.run_line_magic("cd", '"%s"' % ipdir)
602 604 assert curpath() == ipdir
603 605 _ip.run_line_magic("cd", "-")
604 606 assert curpath() == startdir
605 607 _ip.run_line_magic("pushd", '"%s"' % ipdir)
606 608 assert curpath() == ipdir
607 609 _ip.run_line_magic("popd", "")
608 610 assert curpath() == startdir
609 611 finally:
610 612 os.chdir(startdir)
611 613
612 614
613 615 def test_cd_force_quiet():
614 616 """Test OSMagics.cd_force_quiet option"""
615 617 _ip.config.OSMagics.cd_force_quiet = True
616 618 osmagics = osm.OSMagics(shell=_ip)
617 619
618 620 startdir = os.getcwd()
619 621 ipdir = os.path.realpath(_ip.ipython_dir)
620 622
621 623 try:
622 624 with tt.AssertNotPrints(ipdir):
623 625 osmagics.cd('"%s"' % ipdir)
624 626 with tt.AssertNotPrints(startdir):
625 627 osmagics.cd('-')
626 628 finally:
627 629 os.chdir(startdir)
628 630
629 631
630 632 def test_xmode():
631 633 # Calling xmode three times should be a no-op
632 634 xmode = _ip.InteractiveTB.mode
633 635 for i in range(4):
634 636 _ip.run_line_magic("xmode", "")
635 637 assert _ip.InteractiveTB.mode == xmode
636 638
637 639 def test_reset_hard():
638 640 monitor = []
639 641 class A(object):
640 642 def __del__(self):
641 643 monitor.append(1)
642 644 def __repr__(self):
643 645 return "<A instance>"
644 646
645 647 _ip.user_ns["a"] = A()
646 648 _ip.run_cell("a")
647 649
648 650 assert monitor == []
649 651 _ip.run_line_magic("reset", "-f")
650 652 assert monitor == [1]
651 653
652 654 class TestXdel(tt.TempFileMixin):
653 655 def test_xdel(self):
654 656 """Test that references from %run are cleared by xdel."""
655 657 src = ("class A(object):\n"
656 658 " monitor = []\n"
657 659 " def __del__(self):\n"
658 660 " self.monitor.append(1)\n"
659 661 "a = A()\n")
660 662 self.mktmp(src)
661 663 # %run creates some hidden references...
662 664 _ip.run_line_magic("run", "%s" % self.fname)
663 665 # ... as does the displayhook.
664 666 _ip.run_cell("a")
665 667
666 668 monitor = _ip.user_ns["A"].monitor
667 669 assert monitor == []
668 670
669 671 _ip.run_line_magic("xdel", "a")
670 672
671 673 # Check that a's __del__ method has been called.
672 674 gc.collect(0)
673 675 assert monitor == [1]
674 676
675 677 def doctest_who():
676 678 """doctest for %who
677 679
678 680 In [1]: %reset -sf
679 681
680 682 In [2]: alpha = 123
681 683
682 684 In [3]: beta = 'beta'
683 685
684 686 In [4]: %who int
685 687 alpha
686 688
687 689 In [5]: %who str
688 690 beta
689 691
690 692 In [6]: %whos
691 693 Variable Type Data/Info
692 694 ----------------------------
693 695 alpha int 123
694 696 beta str beta
695 697
696 698 In [7]: %who_ls
697 699 Out[7]: ['alpha', 'beta']
698 700 """
699 701
700 702 def test_whos():
701 703 """Check that whos is protected against objects where repr() fails."""
702 704 class A(object):
703 705 def __repr__(self):
704 706 raise Exception()
705 707 _ip.user_ns['a'] = A()
706 708 _ip.run_line_magic("whos", "")
707 709
708 710 def doctest_precision():
709 711 """doctest for %precision
710 712
711 713 In [1]: f = get_ipython().display_formatter.formatters['text/plain']
712 714
713 715 In [2]: %precision 5
714 716 Out[2]: '%.5f'
715 717
716 718 In [3]: f.float_format
717 719 Out[3]: '%.5f'
718 720
719 721 In [4]: %precision %e
720 722 Out[4]: '%e'
721 723
722 724 In [5]: f(3.1415927)
723 725 Out[5]: '3.141593e+00'
724 726 """
725 727
726 728
727 729 def test_debug_magic():
728 730 """Test debugging a small code with %debug
729 731
730 732 In [1]: with PdbTestInput(['c']):
731 733 ...: %debug print("a b") #doctest: +ELLIPSIS
732 734 ...:
733 735 ...
734 736 ipdb> c
735 737 a b
736 738 In [2]:
737 739 """
738 740
739 741
740 742 def test_debug_magic_locals():
741 743 """Test debugging a small code with %debug with locals
742 744
743 745 In [1]: with PdbTestInput(['c']):
744 746 ...: def fun():
745 747 ...: res = 1
746 748 ...: %debug print(res)
747 749 ...: fun()
748 750 ...:
749 751 ...
750 752 ipdb> c
751 753 1
752 754 In [2]:
753 755 """
754 756
755 757 def test_psearch():
756 758 with tt.AssertPrints("dict.fromkeys"):
757 759 _ip.run_cell("dict.fr*?")
758 760 with tt.AssertPrints("Ο€.is_integer"):
759 761 _ip.run_cell("Ο€ = 3.14;\nΟ€.is_integ*?")
760 762
761 763 def test_timeit_shlex():
762 764 """test shlex issues with timeit (#1109)"""
763 765 _ip.ex("def f(*a,**kw): pass")
764 766 _ip.run_line_magic("timeit", '-n1 "this is a bug".count(" ")')
765 767 _ip.run_line_magic("timeit", '-r1 -n1 f(" ", 1)')
766 768 _ip.run_line_magic("timeit", '-r1 -n1 f(" ", 1, " ", 2, " ")')
767 769 _ip.run_line_magic("timeit", '-r1 -n1 ("a " + "b")')
768 770 _ip.run_line_magic("timeit", '-r1 -n1 f("a " + "b")')
769 771 _ip.run_line_magic("timeit", '-r1 -n1 f("a " + "b ")')
770 772
771 773
772 774 def test_timeit_special_syntax():
773 775 "Test %%timeit with IPython special syntax"
774 776 @register_line_magic
775 777 def lmagic(line):
776 778 ip = get_ipython()
777 779 ip.user_ns['lmagic_out'] = line
778 780
779 781 # line mode test
780 782 _ip.run_line_magic("timeit", "-n1 -r1 %lmagic my line")
781 783 assert _ip.user_ns["lmagic_out"] == "my line"
782 784 # cell mode test
783 785 _ip.run_cell_magic("timeit", "-n1 -r1", "%lmagic my line2")
784 786 assert _ip.user_ns["lmagic_out"] == "my line2"
785 787
786 788
787 789 def test_timeit_return():
788 790 """
789 791 test whether timeit -o return object
790 792 """
791 793
792 794 res = _ip.run_line_magic('timeit','-n10 -r10 -o 1')
793 795 assert(res is not None)
794 796
795 797 def test_timeit_quiet():
796 798 """
797 799 test quiet option of timeit magic
798 800 """
799 801 with tt.AssertNotPrints("loops"):
800 802 _ip.run_cell("%timeit -n1 -r1 -q 1")
801 803
802 804 def test_timeit_return_quiet():
803 805 with tt.AssertNotPrints("loops"):
804 806 res = _ip.run_line_magic('timeit', '-n1 -r1 -q -o 1')
805 807 assert (res is not None)
806 808
807 809 def test_timeit_invalid_return():
808 810 with pytest.raises(SyntaxError):
809 811 _ip.run_line_magic('timeit', 'return')
810 812
811 813 @dec.skipif(execution.profile is None)
812 814 def test_prun_special_syntax():
813 815 "Test %%prun with IPython special syntax"
814 816 @register_line_magic
815 817 def lmagic(line):
816 818 ip = get_ipython()
817 819 ip.user_ns['lmagic_out'] = line
818 820
819 821 # line mode test
820 822 _ip.run_line_magic("prun", "-q %lmagic my line")
821 823 assert _ip.user_ns["lmagic_out"] == "my line"
822 824 # cell mode test
823 825 _ip.run_cell_magic("prun", "-q", "%lmagic my line2")
824 826 assert _ip.user_ns["lmagic_out"] == "my line2"
825 827
826 828
827 829 @dec.skipif(execution.profile is None)
828 830 def test_prun_quotes():
829 831 "Test that prun does not clobber string escapes (GH #1302)"
830 832 _ip.run_line_magic("prun", r"-q x = '\t'")
831 833 assert _ip.user_ns["x"] == "\t"
832 834
833 835
834 836 def test_extension():
835 837 # Debugging information for failures of this test
836 838 print('sys.path:')
837 839 for p in sys.path:
838 840 print(' ', p)
839 841 print('CWD', os.getcwd())
840 842
841 843 pytest.raises(ImportError, _ip.run_line_magic, "load_ext", "daft_extension")
842 844 daft_path = os.path.join(os.path.dirname(__file__), "daft_extension")
843 845 sys.path.insert(0, daft_path)
844 846 try:
845 847 _ip.user_ns.pop('arq', None)
846 848 invalidate_caches() # Clear import caches
847 849 _ip.run_line_magic("load_ext", "daft_extension")
848 850 assert _ip.user_ns["arq"] == 185
849 851 _ip.run_line_magic("unload_ext", "daft_extension")
850 852 assert 'arq' not in _ip.user_ns
851 853 finally:
852 854 sys.path.remove(daft_path)
853 855
854 856
855 857 def test_notebook_export_json():
856 858 pytest.importorskip("nbformat")
857 859 _ip = get_ipython()
858 860 _ip.history_manager.reset() # Clear any existing history.
859 861 cmds = ["a=1", "def b():\n return a**2", "print('noΓ«l, Γ©tΓ©', b())"]
860 862 for i, cmd in enumerate(cmds, start=1):
861 863 _ip.history_manager.store_inputs(i, cmd)
862 864 with TemporaryDirectory() as td:
863 865 outfile = os.path.join(td, "nb.ipynb")
864 866 _ip.run_line_magic("notebook", "%s" % outfile)
865 867
866 868
867 869 class TestEnv(TestCase):
868 870
869 871 def test_env(self):
870 872 env = _ip.run_line_magic("env", "")
871 873 self.assertTrue(isinstance(env, dict))
872 874
873 875 def test_env_secret(self):
874 876 env = _ip.run_line_magic("env", "")
875 877 hidden = "<hidden>"
876 878 with mock.patch.dict(
877 879 os.environ,
878 880 {
879 881 "API_KEY": "abc123",
880 882 "SECRET_THING": "ssshhh",
881 883 "JUPYTER_TOKEN": "",
882 884 "VAR": "abc"
883 885 }
884 886 ):
885 887 env = _ip.run_line_magic("env", "")
886 888 assert env["API_KEY"] == hidden
887 889 assert env["SECRET_THING"] == hidden
888 890 assert env["JUPYTER_TOKEN"] == hidden
889 891 assert env["VAR"] == "abc"
890 892
891 893 def test_env_get_set_simple(self):
892 894 env = _ip.run_line_magic("env", "var val1")
893 895 self.assertEqual(env, None)
894 896 self.assertEqual(os.environ["var"], "val1")
895 897 self.assertEqual(_ip.run_line_magic("env", "var"), "val1")
896 898 env = _ip.run_line_magic("env", "var=val2")
897 899 self.assertEqual(env, None)
898 900 self.assertEqual(os.environ['var'], 'val2')
899 901
900 902 def test_env_get_set_complex(self):
901 903 env = _ip.run_line_magic("env", "var 'val1 '' 'val2")
902 904 self.assertEqual(env, None)
903 905 self.assertEqual(os.environ['var'], "'val1 '' 'val2")
904 906 self.assertEqual(_ip.run_line_magic("env", "var"), "'val1 '' 'val2")
905 907 env = _ip.run_line_magic("env", 'var=val2 val3="val4')
906 908 self.assertEqual(env, None)
907 909 self.assertEqual(os.environ['var'], 'val2 val3="val4')
908 910
909 911 def test_env_set_bad_input(self):
910 912 self.assertRaises(UsageError, lambda: _ip.run_line_magic("set_env", "var"))
911 913
912 914 def test_env_set_whitespace(self):
913 915 self.assertRaises(UsageError, lambda: _ip.run_line_magic("env", "var A=B"))
914 916
915 917
916 918 class CellMagicTestCase(TestCase):
917 919
918 920 def check_ident(self, magic):
919 921 # Manually called, we get the result
920 922 out = _ip.run_cell_magic(magic, "a", "b")
921 923 assert out == ("a", "b")
922 924 # Via run_cell, it goes into the user's namespace via displayhook
923 925 _ip.run_cell("%%" + magic + " c\nd\n")
924 926 assert _ip.user_ns["_"] == ("c", "d\n")
925 927
926 928 def test_cell_magic_func_deco(self):
927 929 "Cell magic using simple decorator"
928 930 @register_cell_magic
929 931 def cellm(line, cell):
930 932 return line, cell
931 933
932 934 self.check_ident('cellm')
933 935
934 936 def test_cell_magic_reg(self):
935 937 "Cell magic manually registered"
936 938 def cellm(line, cell):
937 939 return line, cell
938 940
939 941 _ip.register_magic_function(cellm, 'cell', 'cellm2')
940 942 self.check_ident('cellm2')
941 943
942 944 def test_cell_magic_class(self):
943 945 "Cell magics declared via a class"
944 946 @magics_class
945 947 class MyMagics(Magics):
946 948
947 949 @cell_magic
948 950 def cellm3(self, line, cell):
949 951 return line, cell
950 952
951 953 _ip.register_magics(MyMagics)
952 954 self.check_ident('cellm3')
953 955
954 956 def test_cell_magic_class2(self):
955 957 "Cell magics declared via a class, #2"
956 958 @magics_class
957 959 class MyMagics2(Magics):
958 960
959 961 @cell_magic('cellm4')
960 962 def cellm33(self, line, cell):
961 963 return line, cell
962 964
963 965 _ip.register_magics(MyMagics2)
964 966 self.check_ident('cellm4')
965 967 # Check that nothing is registered as 'cellm33'
966 968 c33 = _ip.find_cell_magic('cellm33')
967 assert c33 == None
969 assert c33 is None
968 970
969 971 def test_file():
970 972 """Basic %%writefile"""
971 973 ip = get_ipython()
972 974 with TemporaryDirectory() as td:
973 975 fname = os.path.join(td, "file1")
974 976 ip.run_cell_magic(
975 977 "writefile",
976 978 fname,
977 979 "\n".join(
978 980 [
979 981 "line1",
980 982 "line2",
981 983 ]
982 984 ),
983 985 )
984 986 s = Path(fname).read_text(encoding="utf-8")
985 987 assert "line1\n" in s
986 988 assert "line2" in s
987 989
988 990
989 991 @dec.skip_win32
990 992 def test_file_single_quote():
991 993 """Basic %%writefile with embedded single quotes"""
992 994 ip = get_ipython()
993 995 with TemporaryDirectory() as td:
994 996 fname = os.path.join(td, "'file1'")
995 997 ip.run_cell_magic(
996 998 "writefile",
997 999 fname,
998 1000 "\n".join(
999 1001 [
1000 1002 "line1",
1001 1003 "line2",
1002 1004 ]
1003 1005 ),
1004 1006 )
1005 1007 s = Path(fname).read_text(encoding="utf-8")
1006 1008 assert "line1\n" in s
1007 1009 assert "line2" in s
1008 1010
1009 1011
1010 1012 @dec.skip_win32
1011 1013 def test_file_double_quote():
1012 1014 """Basic %%writefile with embedded double quotes"""
1013 1015 ip = get_ipython()
1014 1016 with TemporaryDirectory() as td:
1015 1017 fname = os.path.join(td, '"file1"')
1016 1018 ip.run_cell_magic(
1017 1019 "writefile",
1018 1020 fname,
1019 1021 "\n".join(
1020 1022 [
1021 1023 "line1",
1022 1024 "line2",
1023 1025 ]
1024 1026 ),
1025 1027 )
1026 1028 s = Path(fname).read_text(encoding="utf-8")
1027 1029 assert "line1\n" in s
1028 1030 assert "line2" in s
1029 1031
1030 1032
1031 1033 def test_file_var_expand():
1032 1034 """%%writefile $filename"""
1033 1035 ip = get_ipython()
1034 1036 with TemporaryDirectory() as td:
1035 1037 fname = os.path.join(td, "file1")
1036 1038 ip.user_ns["filename"] = fname
1037 1039 ip.run_cell_magic(
1038 1040 "writefile",
1039 1041 "$filename",
1040 1042 "\n".join(
1041 1043 [
1042 1044 "line1",
1043 1045 "line2",
1044 1046 ]
1045 1047 ),
1046 1048 )
1047 1049 s = Path(fname).read_text(encoding="utf-8")
1048 1050 assert "line1\n" in s
1049 1051 assert "line2" in s
1050 1052
1051 1053
1052 1054 def test_file_unicode():
1053 1055 """%%writefile with unicode cell"""
1054 1056 ip = get_ipython()
1055 1057 with TemporaryDirectory() as td:
1056 1058 fname = os.path.join(td, 'file1')
1057 1059 ip.run_cell_magic("writefile", fname, u'\n'.join([
1058 1060 u'linΓ©1',
1059 1061 u'linΓ©2',
1060 1062 ]))
1061 1063 with io.open(fname, encoding='utf-8') as f:
1062 1064 s = f.read()
1063 1065 assert "linΓ©1\n" in s
1064 1066 assert "linΓ©2" in s
1065 1067
1066 1068
1067 1069 def test_file_amend():
1068 1070 """%%writefile -a amends files"""
1069 1071 ip = get_ipython()
1070 1072 with TemporaryDirectory() as td:
1071 1073 fname = os.path.join(td, "file2")
1072 1074 ip.run_cell_magic(
1073 1075 "writefile",
1074 1076 fname,
1075 1077 "\n".join(
1076 1078 [
1077 1079 "line1",
1078 1080 "line2",
1079 1081 ]
1080 1082 ),
1081 1083 )
1082 1084 ip.run_cell_magic(
1083 1085 "writefile",
1084 1086 "-a %s" % fname,
1085 1087 "\n".join(
1086 1088 [
1087 1089 "line3",
1088 1090 "line4",
1089 1091 ]
1090 1092 ),
1091 1093 )
1092 1094 s = Path(fname).read_text(encoding="utf-8")
1093 1095 assert "line1\n" in s
1094 1096 assert "line3\n" in s
1095 1097
1096 1098
1097 1099 def test_file_spaces():
1098 1100 """%%file with spaces in filename"""
1099 1101 ip = get_ipython()
1100 with TemporaryWorkingDirectory() as td:
1102 with TemporaryWorkingDirectory():
1101 1103 fname = "file name"
1102 1104 ip.run_cell_magic(
1103 1105 "file",
1104 1106 '"%s"' % fname,
1105 1107 "\n".join(
1106 1108 [
1107 1109 "line1",
1108 1110 "line2",
1109 1111 ]
1110 1112 ),
1111 1113 )
1112 1114 s = Path(fname).read_text(encoding="utf-8")
1113 1115 assert "line1\n" in s
1114 1116 assert "line2" in s
1115 1117
1116 1118
1117 1119 def test_script_config():
1118 1120 ip = get_ipython()
1119 1121 ip.config.ScriptMagics.script_magics = ['whoda']
1120 1122 sm = script.ScriptMagics(shell=ip)
1121 1123 assert "whoda" in sm.magics["cell"]
1122 1124
1123 1125
1124 1126 def test_script_out():
1125 1127 ip = get_ipython()
1126 1128 ip.run_cell_magic("script", f"--out output {sys.executable}", "print('hi')")
1127 1129 assert ip.user_ns["output"].strip() == "hi"
1128 1130
1129 1131
1130 1132 def test_script_err():
1131 1133 ip = get_ipython()
1132 1134 ip.run_cell_magic(
1133 1135 "script",
1134 1136 f"--err error {sys.executable}",
1135 1137 "import sys; print('hello', file=sys.stderr)",
1136 1138 )
1137 1139 assert ip.user_ns["error"].strip() == "hello"
1138 1140
1139 1141
1140 1142 def test_script_out_err():
1141 1143 ip = get_ipython()
1142 1144 ip.run_cell_magic(
1143 1145 "script",
1144 1146 f"--out output --err error {sys.executable}",
1145 1147 "\n".join(
1146 1148 [
1147 1149 "import sys",
1148 1150 "print('hi')",
1149 1151 "print('hello', file=sys.stderr)",
1150 1152 ]
1151 1153 ),
1152 1154 )
1153 1155 assert ip.user_ns["output"].strip() == "hi"
1154 1156 assert ip.user_ns["error"].strip() == "hello"
1155 1157
1156 1158
1157 1159 async def test_script_bg_out():
1158 1160 ip = get_ipython()
1159 1161 ip.run_cell_magic("script", f"--bg --out output {sys.executable}", "print('hi')")
1160 1162 assert (await ip.user_ns["output"].read()).strip() == b"hi"
1161 1163 assert ip.user_ns["output"].at_eof()
1162 1164
1163 1165
1164 1166 async def test_script_bg_err():
1165 1167 ip = get_ipython()
1166 1168 ip.run_cell_magic(
1167 1169 "script",
1168 1170 f"--bg --err error {sys.executable}",
1169 1171 "import sys; print('hello', file=sys.stderr)",
1170 1172 )
1171 1173 assert (await ip.user_ns["error"].read()).strip() == b"hello"
1172 1174 assert ip.user_ns["error"].at_eof()
1173 1175
1174 1176
1175 1177 async def test_script_bg_out_err():
1176 1178 ip = get_ipython()
1177 1179 ip.run_cell_magic(
1178 1180 "script",
1179 1181 f"--bg --out output --err error {sys.executable}",
1180 1182 "\n".join(
1181 1183 [
1182 1184 "import sys",
1183 1185 "print('hi')",
1184 1186 "print('hello', file=sys.stderr)",
1185 1187 ]
1186 1188 ),
1187 1189 )
1188 1190 assert (await ip.user_ns["output"].read()).strip() == b"hi"
1189 1191 assert (await ip.user_ns["error"].read()).strip() == b"hello"
1190 1192 assert ip.user_ns["output"].at_eof()
1191 1193 assert ip.user_ns["error"].at_eof()
1192 1194
1193 1195
1194 1196 async def test_script_bg_proc():
1195 1197 ip = get_ipython()
1196 1198 ip.run_cell_magic(
1197 1199 "script",
1198 1200 f"--bg --out output --proc p {sys.executable}",
1199 1201 "\n".join(
1200 1202 [
1201 1203 "import sys",
1202 1204 "print('hi')",
1203 1205 "print('hello', file=sys.stderr)",
1204 1206 ]
1205 1207 ),
1206 1208 )
1207 1209 p = ip.user_ns["p"]
1208 1210 await p.wait()
1209 1211 assert p.returncode == 0
1210 1212 assert (await p.stdout.read()).strip() == b"hi"
1211 1213 # not captured, so empty
1212 1214 assert (await p.stderr.read()) == b""
1213 1215 assert p.stdout.at_eof()
1214 1216 assert p.stderr.at_eof()
1215 1217
1216 1218
1217 1219 def test_script_defaults():
1218 1220 ip = get_ipython()
1219 1221 for cmd in ['sh', 'bash', 'perl', 'ruby']:
1220 1222 try:
1221 1223 find_cmd(cmd)
1222 1224 except Exception:
1223 1225 pass
1224 1226 else:
1225 1227 assert cmd in ip.magics_manager.magics["cell"]
1226 1228
1227 1229
1228 1230 @magics_class
1229 1231 class FooFoo(Magics):
1230 1232 """class with both %foo and %%foo magics"""
1231 1233 @line_magic('foo')
1232 1234 def line_foo(self, line):
1233 1235 "I am line foo"
1234 1236 pass
1235 1237
1236 1238 @cell_magic("foo")
1237 1239 def cell_foo(self, line, cell):
1238 1240 "I am cell foo, not line foo"
1239 1241 pass
1240 1242
1241 1243 def test_line_cell_info():
1242 1244 """%%foo and %foo magics are distinguishable to inspect"""
1243 1245 ip = get_ipython()
1244 1246 ip.magics_manager.register(FooFoo)
1245 1247 oinfo = ip.object_inspect("foo")
1246 1248 assert oinfo["found"] is True
1247 1249 assert oinfo["ismagic"] is True
1248 1250
1249 1251 oinfo = ip.object_inspect("%%foo")
1250 1252 assert oinfo["found"] is True
1251 1253 assert oinfo["ismagic"] is True
1252 1254 assert oinfo["docstring"] == FooFoo.cell_foo.__doc__
1253 1255
1254 1256 oinfo = ip.object_inspect("%foo")
1255 1257 assert oinfo["found"] is True
1256 1258 assert oinfo["ismagic"] is True
1257 1259 assert oinfo["docstring"] == FooFoo.line_foo.__doc__
1258 1260
1259 1261
1260 1262 def test_multiple_magics():
1261 1263 ip = get_ipython()
1262 1264 foo1 = FooFoo(ip)
1263 1265 foo2 = FooFoo(ip)
1264 1266 mm = ip.magics_manager
1265 1267 mm.register(foo1)
1266 1268 assert mm.magics["line"]["foo"].__self__ is foo1
1267 1269 mm.register(foo2)
1268 1270 assert mm.magics["line"]["foo"].__self__ is foo2
1269 1271
1270 1272
1271 1273 def test_alias_magic():
1272 1274 """Test %alias_magic."""
1273 1275 ip = get_ipython()
1274 1276 mm = ip.magics_manager
1275 1277
1276 1278 # Basic operation: both cell and line magics are created, if possible.
1277 1279 ip.run_line_magic("alias_magic", "timeit_alias timeit")
1278 1280 assert "timeit_alias" in mm.magics["line"]
1279 1281 assert "timeit_alias" in mm.magics["cell"]
1280 1282
1281 1283 # --cell is specified, line magic not created.
1282 1284 ip.run_line_magic("alias_magic", "--cell timeit_cell_alias timeit")
1283 1285 assert "timeit_cell_alias" not in mm.magics["line"]
1284 1286 assert "timeit_cell_alias" in mm.magics["cell"]
1285 1287
1286 1288 # Test that line alias is created successfully.
1287 1289 ip.run_line_magic("alias_magic", "--line env_alias env")
1288 1290 assert ip.run_line_magic("env", "") == ip.run_line_magic("env_alias", "")
1289 1291
1290 1292 # Test that line alias with parameters passed in is created successfully.
1291 1293 ip.run_line_magic(
1292 1294 "alias_magic", "--line history_alias history --params " + shlex.quote("3")
1293 1295 )
1294 1296 assert "history_alias" in mm.magics["line"]
1295 1297
1296 1298
1297 1299 def test_save():
1298 1300 """Test %save."""
1299 1301 ip = get_ipython()
1300 1302 ip.history_manager.reset() # Clear any existing history.
1301 1303 cmds = ["a=1", "def b():\n return a**2", "print(a, b())"]
1302 1304 for i, cmd in enumerate(cmds, start=1):
1303 1305 ip.history_manager.store_inputs(i, cmd)
1304 1306 with TemporaryDirectory() as tmpdir:
1305 1307 file = os.path.join(tmpdir, "testsave.py")
1306 1308 ip.run_line_magic("save", "%s 1-10" % file)
1307 1309 content = Path(file).read_text(encoding="utf-8")
1308 1310 assert content.count(cmds[0]) == 1
1309 1311 assert "coding: utf-8" in content
1310 1312 ip.run_line_magic("save", "-a %s 1-10" % file)
1311 1313 content = Path(file).read_text(encoding="utf-8")
1312 1314 assert content.count(cmds[0]) == 2
1313 1315 assert "coding: utf-8" in content
1314 1316
1315 1317
1316 1318 def test_save_with_no_args():
1317 1319 ip = get_ipython()
1318 1320 ip.history_manager.reset() # Clear any existing history.
1319 1321 cmds = ["a=1", "def b():\n return a**2", "print(a, b())", "%save"]
1320 1322 for i, cmd in enumerate(cmds, start=1):
1321 1323 ip.history_manager.store_inputs(i, cmd)
1322 1324
1323 1325 with TemporaryDirectory() as tmpdir:
1324 1326 path = os.path.join(tmpdir, "testsave.py")
1325 1327 ip.run_line_magic("save", path)
1326 1328 content = Path(path).read_text(encoding="utf-8")
1327 1329 expected_content = dedent(
1328 1330 """\
1329 1331 # coding: utf-8
1330 1332 a=1
1331 1333 def b():
1332 1334 return a**2
1333 1335 print(a, b())
1334 1336 """
1335 1337 )
1336 1338 assert content == expected_content
1337 1339
1338 1340
1339 1341 def test_store():
1340 1342 """Test %store."""
1341 1343 ip = get_ipython()
1342 1344 ip.run_line_magic('load_ext', 'storemagic')
1343 1345
1344 1346 # make sure the storage is empty
1345 1347 ip.run_line_magic("store", "-z")
1346 1348 ip.user_ns["var"] = 42
1347 1349 ip.run_line_magic("store", "var")
1348 1350 ip.user_ns["var"] = 39
1349 1351 ip.run_line_magic("store", "-r")
1350 1352 assert ip.user_ns["var"] == 42
1351 1353
1352 1354 ip.run_line_magic("store", "-d var")
1353 1355 ip.user_ns["var"] = 39
1354 1356 ip.run_line_magic("store", "-r")
1355 1357 assert ip.user_ns["var"] == 39
1356 1358
1357 1359
1358 1360 def _run_edit_test(arg_s, exp_filename=None,
1359 1361 exp_lineno=-1,
1360 1362 exp_contents=None,
1361 1363 exp_is_temp=None):
1362 1364 ip = get_ipython()
1363 1365 M = code.CodeMagics(ip)
1364 1366 last_call = ['','']
1365 1367 opts,args = M.parse_options(arg_s,'prxn:')
1366 1368 filename, lineno, is_temp = M._find_edit_target(ip, args, opts, last_call)
1367 1369
1368 1370 if exp_filename is not None:
1369 1371 assert exp_filename == filename
1370 1372 if exp_contents is not None:
1371 1373 with io.open(filename, 'r', encoding='utf-8') as f:
1372 1374 contents = f.read()
1373 1375 assert exp_contents == contents
1374 1376 if exp_lineno != -1:
1375 1377 assert exp_lineno == lineno
1376 1378 if exp_is_temp is not None:
1377 1379 assert exp_is_temp == is_temp
1378 1380
1379 1381
1380 1382 def test_edit_interactive():
1381 1383 """%edit on interactively defined objects"""
1382 1384 ip = get_ipython()
1383 1385 n = ip.execution_count
1384 1386 ip.run_cell("def foo(): return 1", store_history=True)
1385 1387
1386 1388 with pytest.raises(code.InteractivelyDefined) as e:
1387 1389 _run_edit_test("foo")
1388 1390 assert e.value.index == n
1389 1391
1390 1392
1391 1393 def test_edit_cell():
1392 1394 """%edit [cell id]"""
1393 1395 ip = get_ipython()
1394 1396
1395 1397 ip.run_cell("def foo(): return 1", store_history=True)
1396 1398
1397 1399 # test
1398 1400 _run_edit_test("1", exp_contents=ip.user_ns['In'][1], exp_is_temp=True)
1399 1401
1400 1402 def test_edit_fname():
1401 1403 """%edit file"""
1402 1404 # test
1403 1405 _run_edit_test("test file.py", exp_filename="test file.py")
1404 1406
1405 1407 def test_bookmark():
1406 1408 ip = get_ipython()
1407 1409 ip.run_line_magic('bookmark', 'bmname')
1408 1410 with tt.AssertPrints('bmname'):
1409 1411 ip.run_line_magic('bookmark', '-l')
1410 1412 ip.run_line_magic('bookmark', '-d bmname')
1411 1413
1412 1414 def test_ls_magic():
1413 1415 ip = get_ipython()
1414 1416 json_formatter = ip.display_formatter.formatters['application/json']
1415 1417 json_formatter.enabled = True
1416 1418 lsmagic = ip.run_line_magic("lsmagic", "")
1417 1419 with warnings.catch_warnings(record=True) as w:
1418 1420 j = json_formatter(lsmagic)
1419 1421 assert sorted(j) == ["cell", "line"]
1420 1422 assert w == [] # no warnings
1421 1423
1422 1424
1423 1425 def test_strip_initial_indent():
1424 1426 def sii(s):
1425 1427 lines = s.splitlines()
1426 1428 return '\n'.join(code.strip_initial_indent(lines))
1427 1429
1428 1430 assert sii(" a = 1\nb = 2") == "a = 1\nb = 2"
1429 1431 assert sii(" a\n b\nc") == "a\n b\nc"
1430 1432 assert sii("a\n b") == "a\n b"
1431 1433
1432 1434 def test_logging_magic_quiet_from_arg():
1433 1435 _ip.config.LoggingMagics.quiet = False
1434 1436 lm = logging.LoggingMagics(shell=_ip)
1435 1437 with TemporaryDirectory() as td:
1436 1438 try:
1437 1439 with tt.AssertNotPrints(re.compile("Activating.*")):
1438 1440 lm.logstart('-q {}'.format(
1439 1441 os.path.join(td, "quiet_from_arg.log")))
1440 1442 finally:
1441 1443 _ip.logger.logstop()
1442 1444
1443 1445 def test_logging_magic_quiet_from_config():
1444 1446 _ip.config.LoggingMagics.quiet = True
1445 1447 lm = logging.LoggingMagics(shell=_ip)
1446 1448 with TemporaryDirectory() as td:
1447 1449 try:
1448 1450 with tt.AssertNotPrints(re.compile("Activating.*")):
1449 1451 lm.logstart(os.path.join(td, "quiet_from_config.log"))
1450 1452 finally:
1451 1453 _ip.logger.logstop()
1452 1454
1453 1455
1454 1456 def test_logging_magic_not_quiet():
1455 1457 _ip.config.LoggingMagics.quiet = False
1456 1458 lm = logging.LoggingMagics(shell=_ip)
1457 1459 with TemporaryDirectory() as td:
1458 1460 try:
1459 1461 with tt.AssertPrints(re.compile("Activating.*")):
1460 1462 lm.logstart(os.path.join(td, "not_quiet.log"))
1461 1463 finally:
1462 1464 _ip.logger.logstop()
1463 1465
1464 1466
1465 1467 def test_time_no_var_expand():
1466 1468 _ip.user_ns["a"] = 5
1467 1469 _ip.user_ns["b"] = []
1468 1470 _ip.run_line_magic("time", 'b.append("{a}")')
1469 1471 assert _ip.user_ns["b"] == ["{a}"]
1470 1472
1471 1473
1472 1474 # this is slow, put at the end for local testing.
1473 1475 def test_timeit_arguments():
1474 1476 "Test valid timeit arguments, should not cause SyntaxError (GH #1269)"
1475 1477 _ip.run_line_magic("timeit", "-n1 -r1 a=('#')")
1476 1478
1477 1479
1478 1480 MINIMAL_LAZY_MAGIC = """
1479 1481 from IPython.core.magic import (
1480 1482 Magics,
1481 1483 magics_class,
1482 1484 line_magic,
1483 1485 cell_magic,
1484 1486 )
1485 1487
1486 1488
1487 1489 @magics_class
1488 1490 class LazyMagics(Magics):
1489 1491 @line_magic
1490 1492 def lazy_line(self, line):
1491 1493 print("Lazy Line")
1492 1494
1493 1495 @cell_magic
1494 1496 def lazy_cell(self, line, cell):
1495 1497 print("Lazy Cell")
1496 1498
1497 1499
1498 1500 def load_ipython_extension(ipython):
1499 1501 ipython.register_magics(LazyMagics)
1500 1502 """
1501 1503
1502 1504
1503 1505 def test_lazy_magics():
1504 1506 with pytest.raises(UsageError):
1505 ip.run_line_magic("lazy_line", "")
1506
1507 startdir = os.getcwd()
1507 _ip.run_line_magic("lazy_line", "")
1508 1508
1509 1509 with TemporaryDirectory() as tmpdir:
1510 1510 with prepended_to_syspath(tmpdir):
1511 1511 ptempdir = Path(tmpdir)
1512 1512 tf = ptempdir / "lazy_magic_module.py"
1513 1513 tf.write_text(MINIMAL_LAZY_MAGIC)
1514 ip.magics_manager.register_lazy("lazy_line", Path(tf.name).name[:-3])
1514 _ip.magics_manager.register_lazy("lazy_line", Path(tf.name).name[:-3])
1515 1515 with tt.AssertPrints("Lazy Line"):
1516 ip.run_line_magic("lazy_line", "")
1516 _ip.run_line_magic("lazy_line", "")
1517 1517
1518 1518
1519 1519 TEST_MODULE = """
1520 1520 print('Loaded my_tmp')
1521 1521 if __name__ == "__main__":
1522 1522 print('I just ran a script')
1523 1523 """
1524 1524
1525 1525 def test_run_module_from_import_hook():
1526 1526 "Test that a module can be loaded via an import hook"
1527 1527 with TemporaryDirectory() as tmpdir:
1528 1528 fullpath = os.path.join(tmpdir, "my_tmp.py")
1529 1529 Path(fullpath).write_text(TEST_MODULE, encoding="utf-8")
1530 1530
1531 1531 import importlib.abc
1532 1532 import importlib.util
1533 1533
1534 1534 class MyTempImporter(importlib.abc.MetaPathFinder, importlib.abc.SourceLoader):
1535 1535 def find_spec(self, fullname, path, target=None):
1536 1536 if fullname == "my_tmp":
1537 1537 return importlib.util.spec_from_loader(fullname, self)
1538 1538
1539 1539 def get_filename(self, fullname):
1540 1540 assert fullname == "my_tmp"
1541 1541 return fullpath
1542 1542
1543 1543 def get_data(self, path):
1544 1544 assert Path(path).samefile(fullpath)
1545 1545 return Path(fullpath).read_text(encoding="utf-8")
1546 1546
1547 1547 sys.meta_path.insert(0, MyTempImporter())
1548 1548
1549 1549 with capture_output() as captured:
1550 1550 _ip.run_line_magic("run", "-m my_tmp")
1551 1551 _ip.run_cell("import my_tmp")
1552 1552
1553 1553 output = "Loaded my_tmp\nI just ran a script\nLoaded my_tmp\n"
1554 1554 assert output == captured.stdout
1555 1555
1556 1556 sys.meta_path.pop(0)
@@ -1,165 +1,166
1 1 """Define text roles for GitHub
2 2
3 3 * ghissue - Issue
4 4 * ghpull - Pull Request
5 5 * ghuser - User
6 6
7 7 Adapted from bitbucket example here:
8 8 https://bitbucket.org/birkenfeld/sphinx-contrib/src/tip/bitbucket/sphinxcontrib/bitbucket.py
9 9
10 10 Authors
11 11 -------
12 12
13 13 * Doug Hellmann
14 14 * Min RK
15 15 """
16 16 #
17 17 # Original Copyright (c) 2010 Doug Hellmann. All rights reserved.
18 18 #
19 19
20 20 from docutils import nodes, utils
21 21 from docutils.parsers.rst.roles import set_classes
22 22 from sphinx.util.logging import getLogger
23 23
24 24 info = getLogger(__name__).info
25 25
26 26 def make_link_node(rawtext, app, type, slug, options):
27 27 """Create a link to a github resource.
28 28
29 29 :param rawtext: Text being replaced with link node.
30 30 :param app: Sphinx application context
31 31 :param type: Link type (issues, changeset, etc.)
32 32 :param slug: ID of the thing to link to
33 33 :param options: Options dictionary passed to role func.
34 34 """
35 35
36 36 try:
37 37 base = app.config.github_project_url
38 38 if not base:
39 39 raise AttributeError
40 40 if not base.endswith('/'):
41 41 base += '/'
42 42 except AttributeError as err:
43 43 raise ValueError('github_project_url configuration value is not set (%s)' % str(err)) from err
44 44
45 45 ref = base + type + '/' + slug + '/'
46 46 set_classes(options)
47 47 prefix = "#"
48 48 if type == 'pull':
49 49 prefix = "PR " + prefix
50 50 node = nodes.reference(rawtext, prefix + utils.unescape(slug), refuri=ref,
51 51 **options)
52 52 return node
53 53
54 54 def ghissue_role(name, rawtext, text, lineno, inliner, options=None, content=None):
55 55 """Link to a GitHub issue.
56 56
57 57 Returns 2 part tuple containing list of nodes to insert into the
58 58 document and a list of system messages. Both are allowed to be
59 59 empty.
60 60
61 61 :param name: The role name used in the document.
62 62 :param rawtext: The entire markup snippet, with role.
63 63 :param text: The text marked with the role.
64 64 :param lineno: The line number where rawtext appears in the input.
65 65 :param inliner: The inliner instance that called us.
66 66 :param options: Directive options for customization.
67 67 :param content: The directive content for customization.
68 68 """
69 69 if options is None:
70 70 options = {}
71 71
72 72 try:
73 73 issue_num = int(text)
74 74 if issue_num <= 0:
75 75 raise ValueError
76 76 except ValueError:
77 77 msg = inliner.reporter.error(
78 78 'GitHub issue number must be a number greater than or equal to 1; '
79 79 '"%s" is invalid.' % text, line=lineno)
80 80 prb = inliner.problematic(rawtext, rawtext, msg)
81 81 return [prb], [msg]
82 82 #info('issue %r' % text)
83 83 if 'pull' in name.lower():
84 84 category = 'pull'
85 85 elif 'issue' in name.lower():
86 86 category = 'issues'
87 87 else:
88 88 msg = inliner.reporter.error(
89 89 'GitHub roles include "ghpull" and "ghissue", '
90 90 '"%s" is invalid.' % name, line=lineno)
91 91 prb = inliner.problematic(rawtext, rawtext, msg)
92 92 return [prb], [msg]
93 app = inliner.document.settings.env.app
93 94 node = make_link_node(rawtext, app, category, str(issue_num), options)
94 95 return [node], []
95 96
96 97 def ghuser_role(name, rawtext, text, lineno, inliner, options=None, content=None):
97 98 """Link to a GitHub user.
98 99
99 100 Returns 2 part tuple containing list of nodes to insert into the
100 101 document and a list of system messages. Both are allowed to be
101 102 empty.
102 103
103 104 :param name: The role name used in the document.
104 105 :param rawtext: The entire markup snippet, with role.
105 106 :param text: The text marked with the role.
106 107 :param lineno: The line number where rawtext appears in the input.
107 108 :param inliner: The inliner instance that called us.
108 109 :param options: Directive options for customization.
109 110 :param content: The directive content for customization.
110 111 """
111 112 if options is None:
112 113 options = {}
113 114
114 115 #info('user link %r' % text)
115 116 ref = 'https://www.github.com/' + text
116 117 node = nodes.reference(rawtext, text, refuri=ref, **options)
117 118 return [node], []
118 119
119 120 def ghcommit_role(name, rawtext, text, lineno, inliner, options=None, content=None):
120 121 """Link to a GitHub commit.
121 122
122 123 Returns 2 part tuple containing list of nodes to insert into the
123 124 document and a list of system messages. Both are allowed to be
124 125 empty.
125 126
126 127 :param name: The role name used in the document.
127 128 :param rawtext: The entire markup snippet, with role.
128 129 :param text: The text marked with the role.
129 130 :param lineno: The line number where rawtext appears in the input.
130 131 :param inliner: The inliner instance that called us.
131 132 :param options: Directive options for customization.
132 133 :param content: The directive content for customization.
133 134 """
134 135 if options is None:
135 136 options = {}
136 137
137 138 #info('user link %r' % text)
138 139 try:
139 140 base = app.config.github_project_url
140 141 if not base:
141 142 raise AttributeError
142 143 if not base.endswith('/'):
143 144 base += '/'
144 145 except AttributeError as err:
145 146 raise ValueError('github_project_url configuration value is not set (%s)' % str(err)) from err
146 147
147 148 ref = base + text
148 149 node = nodes.reference(rawtext, text[:6], refuri=ref, **options)
149 150 return [node], []
150 151
151 152
152 153 def setup(app):
153 154 """Install the plugin.
154 155
155 156 :param app: Sphinx application context.
156 157 """
157 158 info('Initializing GitHub plugin')
158 159 app.add_role('ghissue', ghissue_role)
159 160 app.add_role('ghpull', ghissue_role)
160 161 app.add_role('ghuser', ghuser_role)
161 162 app.add_role('ghcommit', ghcommit_role)
162 163 app.add_config_value('github_project_url', None, 'env')
163 164
164 165 metadata = {'parallel_read_safe': True, 'parallel_write_safe': True}
165 166 return metadata
General Comments 0
You need to be logged in to leave comments. Login now