##// END OF EJS Templates
do not use .magic(...) that has been deprecated for a decade (#14566)
M Bussonnier -
r28950:ac9ed9c1 merge
parent child Browse files
Show More
@@ -1,666 +1,666
1 1 """Implementation of basic magic functions."""
2 2
3 3
4 4 from logging import error
5 5 import io
6 6 import os
7 7 from pprint import pformat
8 8 import sys
9 9 from warnings import warn
10 10
11 11 from traitlets.utils.importstring import import_item
12 12 from IPython.core import magic_arguments, page
13 13 from IPython.core.error import UsageError
14 14 from IPython.core.magic import Magics, magics_class, line_magic, magic_escapes
15 15 from IPython.utils.text import format_screen, dedent, indent
16 16 from IPython.testing.skipdoctest import skip_doctest
17 17 from IPython.utils.ipstruct import Struct
18 18
19 19
20 20 class MagicsDisplay(object):
21 21 def __init__(self, magics_manager, ignore=None):
22 22 self.ignore = ignore if ignore else []
23 23 self.magics_manager = magics_manager
24 24
25 25 def _lsmagic(self):
26 26 """The main implementation of the %lsmagic"""
27 27 mesc = magic_escapes['line']
28 28 cesc = magic_escapes['cell']
29 29 mman = self.magics_manager
30 30 magics = mman.lsmagic()
31 31 out = ['Available line magics:',
32 32 mesc + (' '+mesc).join(sorted([m for m,v in magics['line'].items() if (v not in self.ignore)])),
33 33 '',
34 34 'Available cell magics:',
35 35 cesc + (' '+cesc).join(sorted([m for m,v in magics['cell'].items() if (v not in self.ignore)])),
36 36 '',
37 37 mman.auto_status()]
38 38 return '\n'.join(out)
39 39
40 40 def _repr_pretty_(self, p, cycle):
41 41 p.text(self._lsmagic())
42 42
43 43 def __repr__(self):
44 44 return self.__str__()
45 45
46 46 def __str__(self):
47 47 return self._lsmagic()
48 48
49 49 def _jsonable(self):
50 50 """turn magics dict into jsonable dict of the same structure
51 51
52 52 replaces object instances with their class names as strings
53 53 """
54 54 magic_dict = {}
55 55 mman = self.magics_manager
56 56 magics = mman.lsmagic()
57 57 for key, subdict in magics.items():
58 58 d = {}
59 59 magic_dict[key] = d
60 60 for name, obj in subdict.items():
61 61 try:
62 62 classname = obj.__self__.__class__.__name__
63 63 except AttributeError:
64 64 classname = 'Other'
65 65
66 66 d[name] = classname
67 67 return magic_dict
68 68
69 69 def _repr_json_(self):
70 70 return self._jsonable()
71 71
72 72
73 73 @magics_class
74 74 class BasicMagics(Magics):
75 75 """Magics that provide central IPython functionality.
76 76
77 77 These are various magics that don't fit into specific categories but that
78 78 are all part of the base 'IPython experience'."""
79 79
80 80 @skip_doctest
81 81 @magic_arguments.magic_arguments()
82 82 @magic_arguments.argument(
83 83 '-l', '--line', action='store_true',
84 84 help="""Create a line magic alias."""
85 85 )
86 86 @magic_arguments.argument(
87 87 '-c', '--cell', action='store_true',
88 88 help="""Create a cell magic alias."""
89 89 )
90 90 @magic_arguments.argument(
91 91 'name',
92 92 help="""Name of the magic to be created."""
93 93 )
94 94 @magic_arguments.argument(
95 95 'target',
96 96 help="""Name of the existing line or cell magic."""
97 97 )
98 98 @magic_arguments.argument(
99 99 '-p', '--params', default=None,
100 100 help="""Parameters passed to the magic function."""
101 101 )
102 102 @line_magic
103 103 def alias_magic(self, line=''):
104 104 """Create an alias for an existing line or cell magic.
105 105
106 106 Examples
107 107 --------
108 108 ::
109 109
110 110 In [1]: %alias_magic t timeit
111 111 Created `%t` as an alias for `%timeit`.
112 112 Created `%%t` as an alias for `%%timeit`.
113 113
114 114 In [2]: %t -n1 pass
115 115 107 ns Β± 43.6 ns per loop (mean Β± std. dev. of 7 runs, 1 loop each)
116 116
117 117 In [3]: %%t -n1
118 118 ...: pass
119 119 ...:
120 120 107 ns Β± 58.3 ns per loop (mean Β± std. dev. of 7 runs, 1 loop each)
121 121
122 122 In [4]: %alias_magic --cell whereami pwd
123 123 UsageError: Cell magic function `%%pwd` not found.
124 124 In [5]: %alias_magic --line whereami pwd
125 125 Created `%whereami` as an alias for `%pwd`.
126 126
127 127 In [6]: %whereami
128 128 Out[6]: '/home/testuser'
129 129
130 130 In [7]: %alias_magic h history -p "-l 30" --line
131 131 Created `%h` as an alias for `%history -l 30`.
132 132 """
133 133
134 134 args = magic_arguments.parse_argstring(self.alias_magic, line)
135 135 shell = self.shell
136 136 mman = self.shell.magics_manager
137 137 escs = ''.join(magic_escapes.values())
138 138
139 139 target = args.target.lstrip(escs)
140 140 name = args.name.lstrip(escs)
141 141
142 142 params = args.params
143 143 if (params and
144 144 ((params.startswith('"') and params.endswith('"'))
145 145 or (params.startswith("'") and params.endswith("'")))):
146 146 params = params[1:-1]
147 147
148 148 # Find the requested magics.
149 149 m_line = shell.find_magic(target, 'line')
150 150 m_cell = shell.find_magic(target, 'cell')
151 151 if args.line and m_line is None:
152 152 raise UsageError('Line magic function `%s%s` not found.' %
153 153 (magic_escapes['line'], target))
154 154 if args.cell and m_cell is None:
155 155 raise UsageError('Cell magic function `%s%s` not found.' %
156 156 (magic_escapes['cell'], target))
157 157
158 158 # If --line and --cell are not specified, default to the ones
159 159 # that are available.
160 160 if not args.line and not args.cell:
161 161 if not m_line and not m_cell:
162 162 raise UsageError(
163 163 'No line or cell magic with name `%s` found.' % target
164 164 )
165 165 args.line = bool(m_line)
166 166 args.cell = bool(m_cell)
167 167
168 168 params_str = "" if params is None else " " + params
169 169
170 170 if args.line:
171 171 mman.register_alias(name, target, 'line', params)
172 172 print('Created `%s%s` as an alias for `%s%s%s`.' % (
173 173 magic_escapes['line'], name,
174 174 magic_escapes['line'], target, params_str))
175 175
176 176 if args.cell:
177 177 mman.register_alias(name, target, 'cell', params)
178 178 print('Created `%s%s` as an alias for `%s%s%s`.' % (
179 179 magic_escapes['cell'], name,
180 180 magic_escapes['cell'], target, params_str))
181 181
182 182 @line_magic
183 183 def lsmagic(self, parameter_s=''):
184 184 """List currently available magic functions."""
185 185 return MagicsDisplay(self.shell.magics_manager, ignore=[])
186 186
187 187 def _magic_docs(self, brief=False, rest=False):
188 188 """Return docstrings from magic functions."""
189 189 mman = self.shell.magics_manager
190 190 docs = mman.lsmagic_docs(brief, missing='No documentation')
191 191
192 192 if rest:
193 193 format_string = '**%s%s**::\n\n%s\n\n'
194 194 else:
195 195 format_string = '%s%s:\n%s\n'
196 196
197 197 return ''.join(
198 198 [format_string % (magic_escapes['line'], fname,
199 199 indent(dedent(fndoc)))
200 200 for fname, fndoc in sorted(docs['line'].items())]
201 201 +
202 202 [format_string % (magic_escapes['cell'], fname,
203 203 indent(dedent(fndoc)))
204 204 for fname, fndoc in sorted(docs['cell'].items())]
205 205 )
206 206
207 207 @line_magic
208 208 def magic(self, parameter_s=''):
209 209 """Print information about the magic function system.
210 210
211 211 Supported formats: -latex, -brief, -rest
212 212 """
213 213
214 214 mode = ''
215 215 try:
216 216 mode = parameter_s.split()[0][1:]
217 217 except IndexError:
218 218 pass
219 219
220 220 brief = (mode == 'brief')
221 221 rest = (mode == 'rest')
222 222 magic_docs = self._magic_docs(brief, rest)
223 223
224 224 if mode == 'latex':
225 225 print(self.format_latex(magic_docs))
226 226 return
227 227 else:
228 228 magic_docs = format_screen(magic_docs)
229 229
230 230 out = ["""
231 231 IPython's 'magic' functions
232 232 ===========================
233 233
234 234 The magic function system provides a series of functions which allow you to
235 235 control the behavior of IPython itself, plus a lot of system-type
236 236 features. There are two kinds of magics, line-oriented and cell-oriented.
237 237
238 238 Line magics are prefixed with the % character and work much like OS
239 239 command-line calls: they get as an argument the rest of the line, where
240 240 arguments are passed without parentheses or quotes. For example, this will
241 241 time the given statement::
242 242
243 243 %timeit range(1000)
244 244
245 245 Cell magics are prefixed with a double %%, and they are functions that get as
246 246 an argument not only the rest of the line, but also the lines below it in a
247 247 separate argument. These magics are called with two arguments: the rest of the
248 248 call line and the body of the cell, consisting of the lines below the first.
249 249 For example::
250 250
251 251 %%timeit x = numpy.random.randn((100, 100))
252 252 numpy.linalg.svd(x)
253 253
254 254 will time the execution of the numpy svd routine, running the assignment of x
255 255 as part of the setup phase, which is not timed.
256 256
257 257 In a line-oriented client (the terminal or Qt console IPython), starting a new
258 258 input with %% will automatically enter cell mode, and IPython will continue
259 259 reading input until a blank line is given. In the notebook, simply type the
260 260 whole cell as one entity, but keep in mind that the %% escape can only be at
261 261 the very start of the cell.
262 262
263 263 NOTE: If you have 'automagic' enabled (via the command line option or with the
264 264 %automagic function), you don't need to type in the % explicitly for line
265 265 magics; cell magics always require an explicit '%%' escape. By default,
266 266 IPython ships with automagic on, so you should only rarely need the % escape.
267 267
268 268 Example: typing '%cd mydir' (without the quotes) changes your working directory
269 269 to 'mydir', if it exists.
270 270
271 271 For a list of the available magic functions, use %lsmagic. For a description
272 272 of any of them, type %magic_name?, e.g. '%cd?'.
273 273
274 274 Currently the magic system has the following functions:""",
275 275 magic_docs,
276 276 "Summary of magic functions (from %slsmagic):" % magic_escapes['line'],
277 277 str(self.lsmagic()),
278 278 ]
279 279 page.page('\n'.join(out))
280 280
281 281
282 282 @line_magic
283 283 def page(self, parameter_s=''):
284 284 """Pretty print the object and display it through a pager.
285 285
286 286 %page [options] OBJECT
287 287
288 288 If no object is given, use _ (last output).
289 289
290 290 Options:
291 291
292 292 -r: page str(object), don't pretty-print it."""
293 293
294 294 # After a function contributed by Olivier Aubert, slightly modified.
295 295
296 296 # Process options/args
297 297 opts, args = self.parse_options(parameter_s, 'r')
298 298 raw = 'r' in opts
299 299
300 300 oname = args and args or '_'
301 301 info = self.shell._ofind(oname)
302 302 if info.found:
303 303 if raw:
304 304 txt = str(info.obj)
305 305 else:
306 306 txt = pformat(info.obj)
307 307 page.page(txt)
308 308 else:
309 309 print('Object `%s` not found' % oname)
310 310
311 311 @line_magic
312 312 def pprint(self, parameter_s=''):
313 313 """Toggle pretty printing on/off."""
314 314 ptformatter = self.shell.display_formatter.formatters['text/plain']
315 315 ptformatter.pprint = bool(1 - ptformatter.pprint)
316 316 print('Pretty printing has been turned',
317 317 ['OFF','ON'][ptformatter.pprint])
318 318
319 319 @line_magic
320 320 def colors(self, parameter_s=''):
321 321 """Switch color scheme for prompts, info system and exception handlers.
322 322
323 323 Currently implemented schemes: NoColor, Linux, LightBG.
324 324
325 325 Color scheme names are not case-sensitive.
326 326
327 327 Examples
328 328 --------
329 329 To get a plain black and white terminal::
330 330
331 331 %colors nocolor
332 332 """
333 333 def color_switch_err(name):
334 334 warn('Error changing %s color schemes.\n%s' %
335 335 (name, sys.exc_info()[1]), stacklevel=2)
336 336
337 337
338 338 new_scheme = parameter_s.strip()
339 339 if not new_scheme:
340 340 raise UsageError(
341 341 "%colors: you must specify a color scheme. See '%colors?'")
342 342 # local shortcut
343 343 shell = self.shell
344 344
345 345 # Set shell colour scheme
346 346 try:
347 347 shell.colors = new_scheme
348 348 shell.refresh_style()
349 349 except:
350 350 color_switch_err('shell')
351 351
352 352 # Set exception colors
353 353 try:
354 354 shell.InteractiveTB.set_colors(scheme = new_scheme)
355 355 shell.SyntaxTB.set_colors(scheme = new_scheme)
356 356 except:
357 357 color_switch_err('exception')
358 358
359 359 # Set info (for 'object?') colors
360 360 if shell.color_info:
361 361 try:
362 362 shell.inspector.set_active_scheme(new_scheme)
363 363 except:
364 364 color_switch_err('object inspector')
365 365 else:
366 366 shell.inspector.set_active_scheme('NoColor')
367 367
368 368 @line_magic
369 369 def xmode(self, parameter_s=''):
370 370 """Switch modes for the exception handlers.
371 371
372 372 Valid modes: Plain, Context, Verbose, and Minimal.
373 373
374 374 If called without arguments, acts as a toggle.
375 375
376 376 When in verbose mode the value `--show` (and `--hide`)
377 377 will respectively show (or hide) frames with ``__tracebackhide__ =
378 378 True`` value set.
379 379 """
380 380
381 381 def xmode_switch_err(name):
382 382 warn('Error changing %s exception modes.\n%s' %
383 383 (name,sys.exc_info()[1]))
384 384
385 385 shell = self.shell
386 386 if parameter_s.strip() == "--show":
387 387 shell.InteractiveTB.skip_hidden = False
388 388 return
389 389 if parameter_s.strip() == "--hide":
390 390 shell.InteractiveTB.skip_hidden = True
391 391 return
392 392
393 393 new_mode = parameter_s.strip().capitalize()
394 394 try:
395 395 shell.InteractiveTB.set_mode(mode=new_mode)
396 396 print('Exception reporting mode:',shell.InteractiveTB.mode)
397 397 except:
398 398 xmode_switch_err('user')
399 399
400 400 @line_magic
401 401 def quickref(self, arg):
402 402 """ Show a quick reference sheet """
403 403 from IPython.core.usage import quick_reference
404 404 qr = quick_reference + self._magic_docs(brief=True)
405 405 page.page(qr)
406 406
407 407 @line_magic
408 408 def doctest_mode(self, parameter_s=''):
409 409 """Toggle doctest mode on and off.
410 410
411 411 This mode is intended to make IPython behave as much as possible like a
412 412 plain Python shell, from the perspective of how its prompts, exceptions
413 413 and output look. This makes it easy to copy and paste parts of a
414 414 session into doctests. It does so by:
415 415
416 416 - Changing the prompts to the classic ``>>>`` ones.
417 417 - Changing the exception reporting mode to 'Plain'.
418 418 - Disabling pretty-printing of output.
419 419
420 420 Note that IPython also supports the pasting of code snippets that have
421 421 leading '>>>' and '...' prompts in them. This means that you can paste
422 422 doctests from files or docstrings (even if they have leading
423 423 whitespace), and the code will execute correctly. You can then use
424 424 '%history -t' to see the translated history; this will give you the
425 425 input after removal of all the leading prompts and whitespace, which
426 426 can be pasted back into an editor.
427 427
428 428 With these features, you can switch into this mode easily whenever you
429 429 need to do testing and changes to doctests, without having to leave
430 430 your existing IPython session.
431 431 """
432 432
433 433 # Shorthands
434 434 shell = self.shell
435 435 meta = shell.meta
436 436 disp_formatter = self.shell.display_formatter
437 437 ptformatter = disp_formatter.formatters['text/plain']
438 438 # dstore is a data store kept in the instance metadata bag to track any
439 439 # changes we make, so we can undo them later.
440 440 dstore = meta.setdefault('doctest_mode',Struct())
441 441 save_dstore = dstore.setdefault
442 442
443 443 # save a few values we'll need to recover later
444 444 mode = save_dstore('mode',False)
445 445 save_dstore('rc_pprint',ptformatter.pprint)
446 446 save_dstore('xmode',shell.InteractiveTB.mode)
447 447 save_dstore('rc_separate_out',shell.separate_out)
448 448 save_dstore('rc_separate_out2',shell.separate_out2)
449 449 save_dstore('rc_separate_in',shell.separate_in)
450 450 save_dstore('rc_active_types',disp_formatter.active_types)
451 451
452 452 if not mode:
453 453 # turn on
454 454
455 455 # Prompt separators like plain python
456 456 shell.separate_in = ''
457 457 shell.separate_out = ''
458 458 shell.separate_out2 = ''
459 459
460 460
461 461 ptformatter.pprint = False
462 462 disp_formatter.active_types = ['text/plain']
463 463
464 464 shell.magic('xmode Plain')
465 465 else:
466 466 # turn off
467 467 shell.separate_in = dstore.rc_separate_in
468 468
469 469 shell.separate_out = dstore.rc_separate_out
470 470 shell.separate_out2 = dstore.rc_separate_out2
471 471
472 472 ptformatter.pprint = dstore.rc_pprint
473 473 disp_formatter.active_types = dstore.rc_active_types
474 474
475 shell.magic('xmode ' + dstore.xmode)
475 shell.run_line_magic("xmode", dstore.xmode)
476 476
477 477 # mode here is the state before we switch; switch_doctest_mode takes
478 478 # the mode we're switching to.
479 479 shell.switch_doctest_mode(not mode)
480 480
481 481 # Store new mode and inform
482 482 dstore.mode = bool(not mode)
483 483 mode_label = ['OFF','ON'][dstore.mode]
484 484 print('Doctest mode is:', mode_label)
485 485
486 486 @line_magic
487 487 def gui(self, parameter_s=''):
488 488 """Enable or disable IPython GUI event loop integration.
489 489
490 490 %gui [GUINAME]
491 491
492 492 This magic replaces IPython's threaded shells that were activated
493 493 using the (pylab/wthread/etc.) command line flags. GUI toolkits
494 494 can now be enabled at runtime and keyboard
495 495 interrupts should work without any problems. The following toolkits
496 496 are supported: wxPython, PyQt4, PyGTK, Tk and Cocoa (OSX)::
497 497
498 498 %gui wx # enable wxPython event loop integration
499 499 %gui qt # enable PyQt/PySide event loop integration
500 500 # with the latest version available.
501 501 %gui qt6 # enable PyQt6/PySide6 event loop integration
502 502 %gui qt5 # enable PyQt5/PySide2 event loop integration
503 503 %gui gtk # enable PyGTK event loop integration
504 504 %gui gtk3 # enable Gtk3 event loop integration
505 505 %gui gtk4 # enable Gtk4 event loop integration
506 506 %gui tk # enable Tk event loop integration
507 507 %gui osx # enable Cocoa event loop integration
508 508 # (requires %matplotlib 1.1)
509 509 %gui # disable all event loop integration
510 510
511 511 WARNING: after any of these has been called you can simply create
512 512 an application object, but DO NOT start the event loop yourself, as
513 513 we have already handled that.
514 514 """
515 515 opts, arg = self.parse_options(parameter_s, '')
516 516 if arg=='': arg = None
517 517 try:
518 518 return self.shell.enable_gui(arg)
519 519 except Exception as e:
520 520 # print simple error message, rather than traceback if we can't
521 521 # hook up the GUI
522 522 error(str(e))
523 523
524 524 @skip_doctest
525 525 @line_magic
526 526 def precision(self, s=''):
527 527 """Set floating point precision for pretty printing.
528 528
529 529 Can set either integer precision or a format string.
530 530
531 531 If numpy has been imported and precision is an int,
532 532 numpy display precision will also be set, via ``numpy.set_printoptions``.
533 533
534 534 If no argument is given, defaults will be restored.
535 535
536 536 Examples
537 537 --------
538 538 ::
539 539
540 540 In [1]: from math import pi
541 541
542 542 In [2]: %precision 3
543 543 Out[2]: '%.3f'
544 544
545 545 In [3]: pi
546 546 Out[3]: 3.142
547 547
548 548 In [4]: %precision %i
549 549 Out[4]: '%i'
550 550
551 551 In [5]: pi
552 552 Out[5]: 3
553 553
554 554 In [6]: %precision %e
555 555 Out[6]: '%e'
556 556
557 557 In [7]: pi**10
558 558 Out[7]: 9.364805e+04
559 559
560 560 In [8]: %precision
561 561 Out[8]: '%r'
562 562
563 563 In [9]: pi**10
564 564 Out[9]: 93648.047476082982
565 565 """
566 566 ptformatter = self.shell.display_formatter.formatters['text/plain']
567 567 ptformatter.float_precision = s
568 568 return ptformatter.float_format
569 569
570 570 @magic_arguments.magic_arguments()
571 571 @magic_arguments.argument(
572 572 'filename', type=str,
573 573 help='Notebook name or filename'
574 574 )
575 575 @line_magic
576 576 def notebook(self, s):
577 577 """Export and convert IPython notebooks.
578 578
579 579 This function can export the current IPython history to a notebook file.
580 580 For example, to export the history to "foo.ipynb" do "%notebook foo.ipynb".
581 581 """
582 582 args = magic_arguments.parse_argstring(self.notebook, s)
583 583 outfname = os.path.expanduser(args.filename)
584 584
585 585 from nbformat import write, v4
586 586
587 587 cells = []
588 588 hist = list(self.shell.history_manager.get_range())
589 589 if(len(hist)<=1):
590 590 raise ValueError('History is empty, cannot export')
591 591 for session, execution_count, source in hist[:-1]:
592 592 cells.append(v4.new_code_cell(
593 593 execution_count=execution_count,
594 594 source=source
595 595 ))
596 596 nb = v4.new_notebook(cells=cells)
597 597 with io.open(outfname, "w", encoding="utf-8") as f:
598 598 write(nb, f, version=4)
599 599
600 600 @magics_class
601 601 class AsyncMagics(BasicMagics):
602 602
603 603 @line_magic
604 604 def autoawait(self, parameter_s):
605 605 """
606 606 Allow to change the status of the autoawait option.
607 607
608 608 This allow you to set a specific asynchronous code runner.
609 609
610 610 If no value is passed, print the currently used asynchronous integration
611 611 and whether it is activated.
612 612
613 613 It can take a number of value evaluated in the following order:
614 614
615 615 - False/false/off deactivate autoawait integration
616 616 - True/true/on activate autoawait integration using configured default
617 617 loop
618 618 - asyncio/curio/trio activate autoawait integration and use integration
619 619 with said library.
620 620
621 621 - `sync` turn on the pseudo-sync integration (mostly used for
622 622 `IPython.embed()` which does not run IPython with a real eventloop and
623 623 deactivate running asynchronous code. Turning on Asynchronous code with
624 624 the pseudo sync loop is undefined behavior and may lead IPython to crash.
625 625
626 626 If the passed parameter does not match any of the above and is a python
627 627 identifier, get said object from user namespace and set it as the
628 628 runner, and activate autoawait.
629 629
630 630 If the object is a fully qualified object name, attempt to import it and
631 631 set it as the runner, and activate autoawait.
632 632
633 633 The exact behavior of autoawait is experimental and subject to change
634 634 across version of IPython and Python.
635 635 """
636 636
637 637 param = parameter_s.strip()
638 638 d = {True: "on", False: "off"}
639 639
640 640 if not param:
641 641 print("IPython autoawait is `{}`, and set to use `{}`".format(
642 642 d[self.shell.autoawait],
643 643 self.shell.loop_runner
644 644 ))
645 645 return None
646 646
647 647 if param.lower() in ('false', 'off'):
648 648 self.shell.autoawait = False
649 649 return None
650 650 if param.lower() in ('true', 'on'):
651 651 self.shell.autoawait = True
652 652 return None
653 653
654 654 if param in self.shell.loop_runner_map:
655 655 self.shell.loop_runner, self.shell.autoawait = self.shell.loop_runner_map[param]
656 656 return None
657 657
658 658 if param in self.shell.user_ns :
659 659 self.shell.loop_runner = self.shell.user_ns[param]
660 660 self.shell.autoawait = True
661 661 return None
662 662
663 663 runner = import_item(param)
664 664
665 665 self.shell.loop_runner = runner
666 666 self.shell.autoawait = True
@@ -1,27 +1,27
1 1 # -*- coding: utf-8 -*-
2 2 """Test IPython.core.logger"""
3 3
4 4 import os.path
5 5
6 6 import pytest
7 7 from tempfile import TemporaryDirectory
8 8
9 9
10 10 def test_logstart_inaccessible_file():
11 11 with pytest.raises(IOError):
12 12 _ip.logger.logstart(logfname="/") # Opening that filename will fail.
13 13
14 14 try:
15 15 _ip.run_cell("a=1") # Check it doesn't try to log this
16 16 finally:
17 17 _ip.logger.log_active = False # If this fails, don't let later tests fail
18 18
19 19 def test_logstart_unicode():
20 20 with TemporaryDirectory() as tdir:
21 21 logfname = os.path.join(tdir, "test_unicode.log")
22 22 _ip.run_cell("'abc€'")
23 23 try:
24 _ip.magic("logstart -to %s" % logfname)
24 _ip.run_line_magic("logstart", "-to %s" % logfname)
25 25 _ip.run_cell("'abc€'")
26 26 finally:
27 27 _ip.logger.logstop()
@@ -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 from .test_debugger import PdbTestInput
39 39
40 40 from tempfile import NamedTemporaryFile
41 41
42 42 @magic.magics_class
43 43 class DummyMagics(magic.Magics): pass
44 44
45 45 def test_extract_code_ranges():
46 46 instr = "1 3 5-6 7-9 10:15 17: :10 10- -13 :"
47 47 expected = [
48 48 (0, 1),
49 49 (2, 3),
50 50 (4, 6),
51 51 (6, 9),
52 52 (9, 14),
53 53 (16, None),
54 54 (None, 9),
55 55 (9, None),
56 56 (None, 13),
57 57 (None, None),
58 58 ]
59 59 actual = list(code.extract_code_ranges(instr))
60 60 assert actual == expected
61 61
62 62 def test_extract_symbols():
63 63 source = """import foo\na = 10\ndef b():\n return 42\n\n\nclass A: pass\n\n\n"""
64 64 symbols_args = ["a", "b", "A", "A,b", "A,a", "z"]
65 65 expected = [([], ['a']),
66 66 (["def b():\n return 42\n"], []),
67 67 (["class A: pass\n"], []),
68 68 (["class A: pass\n", "def b():\n return 42\n"], []),
69 69 (["class A: pass\n"], ['a']),
70 70 ([], ['z'])]
71 71 for symbols, exp in zip(symbols_args, expected):
72 72 assert code.extract_symbols(source, symbols) == exp
73 73
74 74
75 75 def test_extract_symbols_raises_exception_with_non_python_code():
76 76 source = ("=begin A Ruby program :)=end\n"
77 77 "def hello\n"
78 78 "puts 'Hello world'\n"
79 79 "end")
80 80 with pytest.raises(SyntaxError):
81 81 code.extract_symbols(source, "hello")
82 82
83 83
84 84 def test_magic_not_found():
85 85 # magic not found raises UsageError
86 86 with pytest.raises(UsageError):
87 87 _ip.run_line_magic("doesntexist", "")
88 88
89 89 # ensure result isn't success when a magic isn't found
90 90 result = _ip.run_cell('%doesntexist')
91 91 assert isinstance(result.error_in_exec, UsageError)
92 92
93 93
94 94 def test_cell_magic_not_found():
95 95 # magic not found raises UsageError
96 96 with pytest.raises(UsageError):
97 97 _ip.run_cell_magic('doesntexist', 'line', 'cell')
98 98
99 99 # ensure result isn't success when a magic isn't found
100 100 result = _ip.run_cell('%%doesntexist')
101 101 assert isinstance(result.error_in_exec, UsageError)
102 102
103 103
104 104 def test_magic_error_status():
105 105 def fail(shell):
106 106 1/0
107 107 _ip.register_magic_function(fail)
108 108 result = _ip.run_cell('%fail')
109 109 assert isinstance(result.error_in_exec, ZeroDivisionError)
110 110
111 111
112 112 def test_config():
113 113 """ test that config magic does not raise
114 114 can happen if Configurable init is moved too early into
115 115 Magics.__init__ as then a Config object will be registered as a
116 116 magic.
117 117 """
118 118 ## should not raise.
119 119 _ip.run_line_magic("config", "")
120 120
121 121
122 122 def test_config_available_configs():
123 123 """ test that config magic prints available configs in unique and
124 124 sorted order. """
125 125 with capture_output() as captured:
126 126 _ip.run_line_magic("config", "")
127 127
128 128 stdout = captured.stdout
129 129 config_classes = stdout.strip().split('\n')[1:]
130 130 assert config_classes == sorted(set(config_classes))
131 131
132 132 def test_config_print_class():
133 133 """ test that config with a classname prints the class's options. """
134 134 with capture_output() as captured:
135 135 _ip.run_line_magic("config", "TerminalInteractiveShell")
136 136
137 137 stdout = captured.stdout
138 138 assert re.match(
139 139 "TerminalInteractiveShell.* options", stdout.splitlines()[0]
140 140 ), f"{stdout}\n\n1st line of stdout not like 'TerminalInteractiveShell.* options'"
141 141
142 142
143 143 def test_rehashx():
144 144 # clear up everything
145 145 _ip.alias_manager.clear_aliases()
146 146 del _ip.db['syscmdlist']
147 147
148 148 _ip.run_line_magic("rehashx", "")
149 149 # Practically ALL ipython development systems will have more than 10 aliases
150 150
151 151 assert len(_ip.alias_manager.aliases) > 10
152 152 for name, cmd in _ip.alias_manager.aliases:
153 153 # we must strip dots from alias names
154 154 assert "." not in name
155 155
156 156 # rehashx must fill up syscmdlist
157 157 scoms = _ip.db['syscmdlist']
158 158 assert len(scoms) > 10
159 159
160 160
161 161 def test_magic_parse_options():
162 162 """Test that we don't mangle paths when parsing magic options."""
163 163 ip = get_ipython()
164 164 path = 'c:\\x'
165 165 m = DummyMagics(ip)
166 166 opts = m.parse_options('-f %s' % path,'f:')[0]
167 167 # argv splitting is os-dependent
168 168 if os.name == 'posix':
169 169 expected = 'c:x'
170 170 else:
171 171 expected = path
172 172 assert opts["f"] == expected
173 173
174 174
175 175 def test_magic_parse_long_options():
176 176 """Magic.parse_options can handle --foo=bar long options"""
177 177 ip = get_ipython()
178 178 m = DummyMagics(ip)
179 179 opts, _ = m.parse_options("--foo --bar=bubble", "a", "foo", "bar=")
180 180 assert "foo" in opts
181 181 assert "bar" in opts
182 182 assert opts["bar"] == "bubble"
183 183
184 184
185 185 def doctest_hist_f():
186 186 """Test %hist -f with temporary filename.
187 187
188 188 In [9]: import tempfile
189 189
190 190 In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-')
191 191
192 192 In [11]: %hist -nl -f $tfile 3
193 193
194 194 In [13]: import os; os.unlink(tfile)
195 195 """
196 196
197 197
198 198 def doctest_hist_op():
199 199 """Test %hist -op
200 200
201 201 In [1]: class b(float):
202 202 ...: pass
203 203 ...:
204 204
205 205 In [2]: class s(object):
206 206 ...: def __str__(self):
207 207 ...: return 's'
208 208 ...:
209 209
210 210 In [3]:
211 211
212 212 In [4]: class r(b):
213 213 ...: def __repr__(self):
214 214 ...: return 'r'
215 215 ...:
216 216
217 217 In [5]: class sr(s,r): pass
218 218 ...:
219 219
220 220 In [6]:
221 221
222 222 In [7]: bb=b()
223 223
224 224 In [8]: ss=s()
225 225
226 226 In [9]: rr=r()
227 227
228 228 In [10]: ssrr=sr()
229 229
230 230 In [11]: 4.5
231 231 Out[11]: 4.5
232 232
233 233 In [12]: str(ss)
234 234 Out[12]: 's'
235 235
236 236 In [13]:
237 237
238 238 In [14]: %hist -op
239 239 >>> class b:
240 240 ... pass
241 241 ...
242 242 >>> class s(b):
243 243 ... def __str__(self):
244 244 ... return 's'
245 245 ...
246 246 >>>
247 247 >>> class r(b):
248 248 ... def __repr__(self):
249 249 ... return 'r'
250 250 ...
251 251 >>> class sr(s,r): pass
252 252 >>>
253 253 >>> bb=b()
254 254 >>> ss=s()
255 255 >>> rr=r()
256 256 >>> ssrr=sr()
257 257 >>> 4.5
258 258 4.5
259 259 >>> str(ss)
260 260 's'
261 261 >>>
262 262 """
263 263
264 264 def test_hist_pof():
265 265 ip = get_ipython()
266 266 ip.run_cell("1+2", store_history=True)
267 267 #raise Exception(ip.history_manager.session_number)
268 268 #raise Exception(list(ip.history_manager._get_range_session()))
269 269 with TemporaryDirectory() as td:
270 270 tf = os.path.join(td, 'hist.py')
271 271 ip.run_line_magic('history', '-pof %s' % tf)
272 272 assert os.path.isfile(tf)
273 273
274 274
275 275 def test_macro():
276 276 ip = get_ipython()
277 277 ip.history_manager.reset() # Clear any existing history.
278 278 cmds = ["a=1", "def b():\n return a**2", "print(a,b())"]
279 279 for i, cmd in enumerate(cmds, start=1):
280 280 ip.history_manager.store_inputs(i, cmd)
281 281 ip.run_line_magic("macro", "test 1-3")
282 282 assert ip.user_ns["test"].value == "\n".join(cmds) + "\n"
283 283
284 284 # List macros
285 285 assert "test" in ip.run_line_magic("macro", "")
286 286
287 287
288 288 def test_macro_run():
289 289 """Test that we can run a multi-line macro successfully."""
290 290 ip = get_ipython()
291 291 ip.history_manager.reset()
292 292 cmds = ["a=10", "a+=1", "print(a)", "%macro test 2-3"]
293 293 for cmd in cmds:
294 294 ip.run_cell(cmd, store_history=True)
295 295 assert ip.user_ns["test"].value == "a+=1\nprint(a)\n"
296 296 with tt.AssertPrints("12"):
297 297 ip.run_cell("test")
298 298 with tt.AssertPrints("13"):
299 299 ip.run_cell("test")
300 300
301 301
302 302 def test_magic_magic():
303 303 """Test %magic"""
304 304 ip = get_ipython()
305 305 with capture_output() as captured:
306 306 ip.run_line_magic("magic", "")
307 307
308 308 stdout = captured.stdout
309 309 assert "%magic" in stdout
310 310 assert "IPython" in stdout
311 311 assert "Available" in stdout
312 312
313 313
314 314 @dec.skipif_not_numpy
315 315 def test_numpy_reset_array_undec():
316 316 "Test '%reset array' functionality"
317 317 _ip.ex("import numpy as np")
318 318 _ip.ex("a = np.empty(2)")
319 319 assert "a" in _ip.user_ns
320 320 _ip.run_line_magic("reset", "-f array")
321 321 assert "a" not in _ip.user_ns
322 322
323 323
324 324 def test_reset_out():
325 325 "Test '%reset out' magic"
326 326 _ip.run_cell("parrot = 'dead'", store_history=True)
327 327 # test '%reset -f out', make an Out prompt
328 328 _ip.run_cell("parrot", store_history=True)
329 329 assert "dead" in [_ip.user_ns[x] for x in ("_", "__", "___")]
330 330 _ip.run_line_magic("reset", "-f out")
331 331 assert "dead" not in [_ip.user_ns[x] for x in ("_", "__", "___")]
332 332 assert len(_ip.user_ns["Out"]) == 0
333 333
334 334
335 335 def test_reset_in():
336 336 "Test '%reset in' magic"
337 337 # test '%reset -f in'
338 338 _ip.run_cell("parrot", store_history=True)
339 339 assert "parrot" in [_ip.user_ns[x] for x in ("_i", "_ii", "_iii")]
340 340 _ip.run_line_magic("reset", "-f in")
341 341 assert "parrot" not in [_ip.user_ns[x] for x in ("_i", "_ii", "_iii")]
342 342 assert len(set(_ip.user_ns["In"])) == 1
343 343
344 344
345 345 def test_reset_dhist():
346 346 "Test '%reset dhist' magic"
347 347 _ip.run_cell("tmp = [d for d in _dh]") # copy before clearing
348 348 _ip.run_line_magic("cd", os.path.dirname(pytest.__file__))
349 349 _ip.run_line_magic("cd", "-")
350 350 assert len(_ip.user_ns["_dh"]) > 0
351 351 _ip.run_line_magic("reset", "-f dhist")
352 352 assert len(_ip.user_ns["_dh"]) == 0
353 353 _ip.run_cell("_dh = [d for d in tmp]") # restore
354 354
355 355
356 356 def test_reset_in_length():
357 357 "Test that '%reset in' preserves In[] length"
358 358 _ip.run_cell("print 'foo'")
359 359 _ip.run_cell("reset -f in")
360 360 assert len(_ip.user_ns["In"]) == _ip.displayhook.prompt_count + 1
361 361
362 362
363 363 class TestResetErrors(TestCase):
364 364
365 365 def test_reset_redefine(self):
366 366
367 367 @magics_class
368 368 class KernelMagics(Magics):
369 369 @line_magic
370 370 def less(self, shell): pass
371 371
372 372 _ip.register_magics(KernelMagics)
373 373
374 374 with self.assertLogs() as cm:
375 375 # hack, we want to just capture logs, but assertLogs fails if not
376 376 # logs get produce.
377 377 # so log one things we ignore.
378 378 import logging as log_mod
379 379 log = log_mod.getLogger()
380 380 log.info('Nothing')
381 381 # end hack.
382 382 _ip.run_cell("reset -f")
383 383
384 384 assert len(cm.output) == 1
385 385 for out in cm.output:
386 386 assert "Invalid alias" not in out
387 387
388 388 def test_tb_syntaxerror():
389 389 """test %tb after a SyntaxError"""
390 390 ip = get_ipython()
391 391 ip.run_cell("for")
392 392
393 393 # trap and validate stdout
394 394 save_stdout = sys.stdout
395 395 try:
396 396 sys.stdout = StringIO()
397 397 ip.run_cell("%tb")
398 398 out = sys.stdout.getvalue()
399 399 finally:
400 400 sys.stdout = save_stdout
401 401 # trim output, and only check the last line
402 402 last_line = out.rstrip().splitlines()[-1].strip()
403 403 assert last_line == "SyntaxError: invalid syntax"
404 404
405 405
406 406 def test_time():
407 407 ip = get_ipython()
408 408
409 409 with tt.AssertPrints("Wall time: "):
410 410 ip.run_cell("%time None")
411 411
412 412 ip.run_cell("def f(kmjy):\n"
413 413 " %time print (2*kmjy)")
414 414
415 415 with tt.AssertPrints("Wall time: "):
416 416 with tt.AssertPrints("hihi", suppress=False):
417 417 ip.run_cell("f('hi')")
418 418
419 419
420 420 # ';' at the end of %time prevents instruction value to be printed.
421 421 # This tests fix for #13837.
422 422 def test_time_no_output_with_semicolon():
423 423 ip = get_ipython()
424 424
425 425 # Test %time cases
426 426 with tt.AssertPrints(" 123456"):
427 427 with tt.AssertPrints("Wall time: ", suppress=False):
428 428 with tt.AssertPrints("CPU times: ", suppress=False):
429 429 ip.run_cell("%time 123000+456")
430 430
431 431 with tt.AssertNotPrints(" 123456"):
432 432 with tt.AssertPrints("Wall time: ", suppress=False):
433 433 with tt.AssertPrints("CPU times: ", suppress=False):
434 434 ip.run_cell("%time 123000+456;")
435 435
436 436 with tt.AssertPrints(" 123456"):
437 437 with tt.AssertPrints("Wall time: ", suppress=False):
438 438 with tt.AssertPrints("CPU times: ", suppress=False):
439 439 ip.run_cell("%time 123000+456 # Comment")
440 440
441 441 with tt.AssertNotPrints(" 123456"):
442 442 with tt.AssertPrints("Wall time: ", suppress=False):
443 443 with tt.AssertPrints("CPU times: ", suppress=False):
444 444 ip.run_cell("%time 123000+456; # Comment")
445 445
446 446 with tt.AssertPrints(" 123456"):
447 447 with tt.AssertPrints("Wall time: ", suppress=False):
448 448 with tt.AssertPrints("CPU times: ", suppress=False):
449 449 ip.run_cell("%time 123000+456 # ;Comment")
450 450
451 451 # Test %%time cases
452 452 with tt.AssertPrints("123456"):
453 453 with tt.AssertPrints("Wall time: ", suppress=False):
454 454 with tt.AssertPrints("CPU times: ", suppress=False):
455 455 ip.run_cell("%%time\n123000+456\n\n\n")
456 456
457 457 with tt.AssertNotPrints("123456"):
458 458 with tt.AssertPrints("Wall time: ", suppress=False):
459 459 with tt.AssertPrints("CPU times: ", suppress=False):
460 460 ip.run_cell("%%time\n123000+456;\n\n\n")
461 461
462 462 with tt.AssertPrints("123456"):
463 463 with tt.AssertPrints("Wall time: ", suppress=False):
464 464 with tt.AssertPrints("CPU times: ", suppress=False):
465 465 ip.run_cell("%%time\n123000+456 # Comment\n\n\n")
466 466
467 467 with tt.AssertNotPrints("123456"):
468 468 with tt.AssertPrints("Wall time: ", suppress=False):
469 469 with tt.AssertPrints("CPU times: ", suppress=False):
470 470 ip.run_cell("%%time\n123000+456; # Comment\n\n\n")
471 471
472 472 with tt.AssertPrints("123456"):
473 473 with tt.AssertPrints("Wall time: ", suppress=False):
474 474 with tt.AssertPrints("CPU times: ", suppress=False):
475 475 ip.run_cell("%%time\n123000+456 # ;Comment\n\n\n")
476 476
477 477
478 478 def test_time_last_not_expression():
479 479 ip.run_cell("%%time\n"
480 480 "var_1 = 1\n"
481 481 "var_2 = 2\n")
482 482 assert ip.user_ns['var_1'] == 1
483 483 del ip.user_ns['var_1']
484 484 assert ip.user_ns['var_2'] == 2
485 485 del ip.user_ns['var_2']
486 486
487 487
488 488 @dec.skip_win32
489 489 def test_time2():
490 490 ip = get_ipython()
491 491
492 492 with tt.AssertPrints("CPU times: user "):
493 493 ip.run_cell("%time None")
494 494
495 495 def test_time3():
496 496 """Erroneous magic function calls, issue gh-3334"""
497 497 ip = get_ipython()
498 498 ip.user_ns.pop('run', None)
499 499
500 500 with tt.AssertNotPrints("not found", channel='stderr'):
501 501 ip.run_cell("%%time\n"
502 502 "run = 0\n"
503 503 "run += 1")
504 504
505 505 def test_multiline_time():
506 506 """Make sure last statement from time return a value."""
507 507 ip = get_ipython()
508 508 ip.user_ns.pop('run', None)
509 509
510 510 ip.run_cell(
511 511 dedent(
512 512 """\
513 513 %%time
514 514 a = "ho"
515 515 b = "hey"
516 516 a+b
517 517 """
518 518 )
519 519 )
520 520 assert ip.user_ns_hidden["_"] == "hohey"
521 521
522 522
523 523 def test_time_local_ns():
524 524 """
525 525 Test that local_ns is actually global_ns when running a cell magic
526 526 """
527 527 ip = get_ipython()
528 528 ip.run_cell("%%time\n" "myvar = 1")
529 529 assert ip.user_ns["myvar"] == 1
530 530 del ip.user_ns["myvar"]
531 531
532 532
533 533 def test_time_microseconds_display():
534 534 """Ensure ASCII is used when necessary"""
535 535 with mock.patch("sys.stdout", io.TextIOWrapper(StringIO(), encoding="utf-8")):
536 536 assert execution._format_time(0.000001) == "1 \u03bcs"
537 537 with mock.patch("sys.stdout", io.TextIOWrapper(StringIO(), encoding="ascii")):
538 538 assert execution._format_time(0.000001) == "1 us"
539 539
540 540
541 541 # Test %%capture magic. Added to test issue #13926
542 542 def test_capture():
543 543 ip = get_ipython()
544 544
545 545 # Test %%capture nominal case
546 546 ip.run_cell("%%capture abc\n1+2")
547 547 with tt.AssertPrints("True", suppress=False):
548 548 ip.run_cell("'abc' in locals()")
549 549 with tt.AssertPrints("True", suppress=False):
550 550 ip.run_cell("'outputs' in dir(abc)")
551 551 with tt.AssertPrints("3", suppress=False):
552 552 ip.run_cell("abc.outputs[0]")
553 553
554 554 # Test %%capture with ';' at end of expression
555 555 ip.run_cell("%%capture abc\n7+8;")
556 556 with tt.AssertPrints("False", suppress=False):
557 557 ip.run_cell("'abc' in locals()")
558 558
559 559
560 560 def test_doctest_mode():
561 561 "Toggle doctest_mode twice, it should be a no-op and run without error"
562 562 _ip.run_line_magic("doctest_mode", "")
563 563 _ip.run_line_magic("doctest_mode", "")
564 564
565 565
566 566 def test_parse_options():
567 567 """Tests for basic options parsing in magics."""
568 568 # These are only the most minimal of tests, more should be added later. At
569 569 # the very least we check that basic text/unicode calls work OK.
570 570 m = DummyMagics(_ip)
571 571 assert m.parse_options("foo", "")[1] == "foo"
572 572 assert m.parse_options("foo", "")[1] == "foo"
573 573
574 574
575 575 def test_parse_options_preserve_non_option_string():
576 576 """Test to assert preservation of non-option part of magic-block, while parsing magic options."""
577 577 m = DummyMagics(_ip)
578 578 opts, stmt = m.parse_options(
579 579 " -n1 -r 13 _ = 314 + foo", "n:r:", preserve_non_opts=True
580 580 )
581 581 assert opts == {"n": "1", "r": "13"}
582 582 assert stmt == "_ = 314 + foo"
583 583
584 584
585 585 def test_run_magic_preserve_code_block():
586 586 """Test to assert preservation of non-option part of magic-block, while running magic."""
587 587 _ip.user_ns["spaces"] = []
588 588 _ip.run_line_magic(
589 589 "timeit", "-n1 -r1 spaces.append([s.count(' ') for s in ['document']])"
590 590 )
591 591 assert _ip.user_ns["spaces"] == [[0]]
592 592
593 593
594 594 def test_dirops():
595 595 """Test various directory handling operations."""
596 596 # curpath = lambda :os.path.splitdrive(os.getcwd())[1].replace('\\','/')
597 597 curpath = os.getcwd
598 598 startdir = os.getcwd()
599 599 ipdir = os.path.realpath(_ip.ipython_dir)
600 600 try:
601 601 _ip.run_line_magic("cd", '"%s"' % ipdir)
602 602 assert curpath() == ipdir
603 603 _ip.run_line_magic("cd", "-")
604 604 assert curpath() == startdir
605 605 _ip.run_line_magic("pushd", '"%s"' % ipdir)
606 606 assert curpath() == ipdir
607 607 _ip.run_line_magic("popd", "")
608 608 assert curpath() == startdir
609 609 finally:
610 610 os.chdir(startdir)
611 611
612 612
613 613 def test_cd_force_quiet():
614 614 """Test OSMagics.cd_force_quiet option"""
615 615 _ip.config.OSMagics.cd_force_quiet = True
616 616 osmagics = osm.OSMagics(shell=_ip)
617 617
618 618 startdir = os.getcwd()
619 619 ipdir = os.path.realpath(_ip.ipython_dir)
620 620
621 621 try:
622 622 with tt.AssertNotPrints(ipdir):
623 623 osmagics.cd('"%s"' % ipdir)
624 624 with tt.AssertNotPrints(startdir):
625 625 osmagics.cd('-')
626 626 finally:
627 627 os.chdir(startdir)
628 628
629 629
630 630 def test_xmode():
631 631 # Calling xmode three times should be a no-op
632 632 xmode = _ip.InteractiveTB.mode
633 633 for i in range(4):
634 634 _ip.run_line_magic("xmode", "")
635 635 assert _ip.InteractiveTB.mode == xmode
636 636
637 637 def test_reset_hard():
638 638 monitor = []
639 639 class A(object):
640 640 def __del__(self):
641 641 monitor.append(1)
642 642 def __repr__(self):
643 643 return "<A instance>"
644 644
645 645 _ip.user_ns["a"] = A()
646 646 _ip.run_cell("a")
647 647
648 648 assert monitor == []
649 649 _ip.run_line_magic("reset", "-f")
650 650 assert monitor == [1]
651 651
652 652 class TestXdel(tt.TempFileMixin):
653 653 def test_xdel(self):
654 654 """Test that references from %run are cleared by xdel."""
655 655 src = ("class A(object):\n"
656 656 " monitor = []\n"
657 657 " def __del__(self):\n"
658 658 " self.monitor.append(1)\n"
659 659 "a = A()\n")
660 660 self.mktmp(src)
661 661 # %run creates some hidden references...
662 662 _ip.run_line_magic("run", "%s" % self.fname)
663 663 # ... as does the displayhook.
664 664 _ip.run_cell("a")
665 665
666 666 monitor = _ip.user_ns["A"].monitor
667 667 assert monitor == []
668 668
669 669 _ip.run_line_magic("xdel", "a")
670 670
671 671 # Check that a's __del__ method has been called.
672 672 gc.collect(0)
673 673 assert monitor == [1]
674 674
675 675 def doctest_who():
676 676 """doctest for %who
677 677
678 678 In [1]: %reset -sf
679 679
680 680 In [2]: alpha = 123
681 681
682 682 In [3]: beta = 'beta'
683 683
684 684 In [4]: %who int
685 685 alpha
686 686
687 687 In [5]: %who str
688 688 beta
689 689
690 690 In [6]: %whos
691 691 Variable Type Data/Info
692 692 ----------------------------
693 693 alpha int 123
694 694 beta str beta
695 695
696 696 In [7]: %who_ls
697 697 Out[7]: ['alpha', 'beta']
698 698 """
699 699
700 700 def test_whos():
701 701 """Check that whos is protected against objects where repr() fails."""
702 702 class A(object):
703 703 def __repr__(self):
704 704 raise Exception()
705 705 _ip.user_ns['a'] = A()
706 706 _ip.run_line_magic("whos", "")
707 707
708 708 def doctest_precision():
709 709 """doctest for %precision
710 710
711 711 In [1]: f = get_ipython().display_formatter.formatters['text/plain']
712 712
713 713 In [2]: %precision 5
714 714 Out[2]: '%.5f'
715 715
716 716 In [3]: f.float_format
717 717 Out[3]: '%.5f'
718 718
719 719 In [4]: %precision %e
720 720 Out[4]: '%e'
721 721
722 722 In [5]: f(3.1415927)
723 723 Out[5]: '3.141593e+00'
724 724 """
725 725
726 726
727 727 def test_debug_magic():
728 728 """Test debugging a small code with %debug
729 729
730 730 In [1]: with PdbTestInput(['c']):
731 731 ...: %debug print("a b") #doctest: +ELLIPSIS
732 732 ...:
733 733 ...
734 734 ipdb> c
735 735 a b
736 736 In [2]:
737 737 """
738 738
739 739
740 740 def test_debug_magic_locals():
741 741 """Test debugging a small code with %debug with locals
742 742
743 743 In [1]: with PdbTestInput(['c']):
744 744 ...: def fun():
745 745 ...: res = 1
746 746 ...: %debug print(res)
747 747 ...: fun()
748 748 ...:
749 749 ...
750 750 ipdb> c
751 751 1
752 752 In [2]:
753 753 """
754 754
755 755 def test_psearch():
756 756 with tt.AssertPrints("dict.fromkeys"):
757 757 _ip.run_cell("dict.fr*?")
758 758 with tt.AssertPrints("Ο€.is_integer"):
759 759 _ip.run_cell("Ο€ = 3.14;\nΟ€.is_integ*?")
760 760
761 761 def test_timeit_shlex():
762 762 """test shlex issues with timeit (#1109)"""
763 763 _ip.ex("def f(*a,**kw): pass")
764 764 _ip.run_line_magic("timeit", '-n1 "this is a bug".count(" ")')
765 765 _ip.run_line_magic("timeit", '-r1 -n1 f(" ", 1)')
766 766 _ip.run_line_magic("timeit", '-r1 -n1 f(" ", 1, " ", 2, " ")')
767 767 _ip.run_line_magic("timeit", '-r1 -n1 ("a " + "b")')
768 768 _ip.run_line_magic("timeit", '-r1 -n1 f("a " + "b")')
769 769 _ip.run_line_magic("timeit", '-r1 -n1 f("a " + "b ")')
770 770
771 771
772 772 def test_timeit_special_syntax():
773 773 "Test %%timeit with IPython special syntax"
774 774 @register_line_magic
775 775 def lmagic(line):
776 776 ip = get_ipython()
777 777 ip.user_ns['lmagic_out'] = line
778 778
779 779 # line mode test
780 780 _ip.run_line_magic("timeit", "-n1 -r1 %lmagic my line")
781 781 assert _ip.user_ns["lmagic_out"] == "my line"
782 782 # cell mode test
783 783 _ip.run_cell_magic("timeit", "-n1 -r1", "%lmagic my line2")
784 784 assert _ip.user_ns["lmagic_out"] == "my line2"
785 785
786 786
787 787 def test_timeit_return():
788 788 """
789 789 test whether timeit -o return object
790 790 """
791 791
792 792 res = _ip.run_line_magic('timeit','-n10 -r10 -o 1')
793 793 assert(res is not None)
794 794
795 795 def test_timeit_quiet():
796 796 """
797 797 test quiet option of timeit magic
798 798 """
799 799 with tt.AssertNotPrints("loops"):
800 800 _ip.run_cell("%timeit -n1 -r1 -q 1")
801 801
802 802 def test_timeit_return_quiet():
803 803 with tt.AssertNotPrints("loops"):
804 804 res = _ip.run_line_magic('timeit', '-n1 -r1 -q -o 1')
805 805 assert (res is not None)
806 806
807 807 def test_timeit_invalid_return():
808 808 with pytest.raises(SyntaxError):
809 809 _ip.run_line_magic('timeit', 'return')
810 810
811 811 @dec.skipif(execution.profile is None)
812 812 def test_prun_special_syntax():
813 813 "Test %%prun with IPython special syntax"
814 814 @register_line_magic
815 815 def lmagic(line):
816 816 ip = get_ipython()
817 817 ip.user_ns['lmagic_out'] = line
818 818
819 819 # line mode test
820 820 _ip.run_line_magic("prun", "-q %lmagic my line")
821 821 assert _ip.user_ns["lmagic_out"] == "my line"
822 822 # cell mode test
823 823 _ip.run_cell_magic("prun", "-q", "%lmagic my line2")
824 824 assert _ip.user_ns["lmagic_out"] == "my line2"
825 825
826 826
827 827 @dec.skipif(execution.profile is None)
828 828 def test_prun_quotes():
829 829 "Test that prun does not clobber string escapes (GH #1302)"
830 _ip.magic(r"prun -q x = '\t'")
830 _ip.run_line_magic("prun", r"-q x = '\t'")
831 831 assert _ip.user_ns["x"] == "\t"
832 832
833 833
834 834 def test_extension():
835 835 # Debugging information for failures of this test
836 836 print('sys.path:')
837 837 for p in sys.path:
838 838 print(' ', p)
839 839 print('CWD', os.getcwd())
840 840
841 pytest.raises(ImportError, _ip.magic, "load_ext daft_extension")
841 pytest.raises(ImportError, _ip.run_line_magic, "load_ext", "daft_extension")
842 842 daft_path = os.path.join(os.path.dirname(__file__), "daft_extension")
843 843 sys.path.insert(0, daft_path)
844 844 try:
845 845 _ip.user_ns.pop('arq', None)
846 846 invalidate_caches() # Clear import caches
847 847 _ip.run_line_magic("load_ext", "daft_extension")
848 848 assert _ip.user_ns["arq"] == 185
849 849 _ip.run_line_magic("unload_ext", "daft_extension")
850 850 assert 'arq' not in _ip.user_ns
851 851 finally:
852 852 sys.path.remove(daft_path)
853 853
854 854
855 855 def test_notebook_export_json():
856 856 pytest.importorskip("nbformat")
857 857 _ip = get_ipython()
858 858 _ip.history_manager.reset() # Clear any existing history.
859 859 cmds = ["a=1", "def b():\n return a**2", "print('noΓ«l, Γ©tΓ©', b())"]
860 860 for i, cmd in enumerate(cmds, start=1):
861 861 _ip.history_manager.store_inputs(i, cmd)
862 862 with TemporaryDirectory() as td:
863 863 outfile = os.path.join(td, "nb.ipynb")
864 864 _ip.run_line_magic("notebook", "%s" % outfile)
865 865
866 866
867 867 class TestEnv(TestCase):
868 868
869 869 def test_env(self):
870 870 env = _ip.run_line_magic("env", "")
871 871 self.assertTrue(isinstance(env, dict))
872 872
873 873 def test_env_secret(self):
874 874 env = _ip.run_line_magic("env", "")
875 875 hidden = "<hidden>"
876 876 with mock.patch.dict(
877 877 os.environ,
878 878 {
879 879 "API_KEY": "abc123",
880 880 "SECRET_THING": "ssshhh",
881 881 "JUPYTER_TOKEN": "",
882 882 "VAR": "abc"
883 883 }
884 884 ):
885 885 env = _ip.run_line_magic("env", "")
886 886 assert env["API_KEY"] == hidden
887 887 assert env["SECRET_THING"] == hidden
888 888 assert env["JUPYTER_TOKEN"] == hidden
889 889 assert env["VAR"] == "abc"
890 890
891 891 def test_env_get_set_simple(self):
892 892 env = _ip.run_line_magic("env", "var val1")
893 893 self.assertEqual(env, None)
894 894 self.assertEqual(os.environ["var"], "val1")
895 895 self.assertEqual(_ip.run_line_magic("env", "var"), "val1")
896 896 env = _ip.run_line_magic("env", "var=val2")
897 897 self.assertEqual(env, None)
898 898 self.assertEqual(os.environ['var'], 'val2')
899 899
900 900 def test_env_get_set_complex(self):
901 901 env = _ip.run_line_magic("env", "var 'val1 '' 'val2")
902 902 self.assertEqual(env, None)
903 903 self.assertEqual(os.environ['var'], "'val1 '' 'val2")
904 904 self.assertEqual(_ip.run_line_magic("env", "var"), "'val1 '' 'val2")
905 905 env = _ip.run_line_magic("env", 'var=val2 val3="val4')
906 906 self.assertEqual(env, None)
907 907 self.assertEqual(os.environ['var'], 'val2 val3="val4')
908 908
909 909 def test_env_set_bad_input(self):
910 910 self.assertRaises(UsageError, lambda: _ip.run_line_magic("set_env", "var"))
911 911
912 912 def test_env_set_whitespace(self):
913 913 self.assertRaises(UsageError, lambda: _ip.run_line_magic("env", "var A=B"))
914 914
915 915
916 916 class CellMagicTestCase(TestCase):
917 917
918 918 def check_ident(self, magic):
919 919 # Manually called, we get the result
920 920 out = _ip.run_cell_magic(magic, "a", "b")
921 921 assert out == ("a", "b")
922 922 # Via run_cell, it goes into the user's namespace via displayhook
923 923 _ip.run_cell("%%" + magic + " c\nd\n")
924 924 assert _ip.user_ns["_"] == ("c", "d\n")
925 925
926 926 def test_cell_magic_func_deco(self):
927 927 "Cell magic using simple decorator"
928 928 @register_cell_magic
929 929 def cellm(line, cell):
930 930 return line, cell
931 931
932 932 self.check_ident('cellm')
933 933
934 934 def test_cell_magic_reg(self):
935 935 "Cell magic manually registered"
936 936 def cellm(line, cell):
937 937 return line, cell
938 938
939 939 _ip.register_magic_function(cellm, 'cell', 'cellm2')
940 940 self.check_ident('cellm2')
941 941
942 942 def test_cell_magic_class(self):
943 943 "Cell magics declared via a class"
944 944 @magics_class
945 945 class MyMagics(Magics):
946 946
947 947 @cell_magic
948 948 def cellm3(self, line, cell):
949 949 return line, cell
950 950
951 951 _ip.register_magics(MyMagics)
952 952 self.check_ident('cellm3')
953 953
954 954 def test_cell_magic_class2(self):
955 955 "Cell magics declared via a class, #2"
956 956 @magics_class
957 957 class MyMagics2(Magics):
958 958
959 959 @cell_magic('cellm4')
960 960 def cellm33(self, line, cell):
961 961 return line, cell
962 962
963 963 _ip.register_magics(MyMagics2)
964 964 self.check_ident('cellm4')
965 965 # Check that nothing is registered as 'cellm33'
966 966 c33 = _ip.find_cell_magic('cellm33')
967 967 assert c33 == None
968 968
969 969 def test_file():
970 970 """Basic %%writefile"""
971 971 ip = get_ipython()
972 972 with TemporaryDirectory() as td:
973 973 fname = os.path.join(td, "file1")
974 974 ip.run_cell_magic(
975 975 "writefile",
976 976 fname,
977 977 "\n".join(
978 978 [
979 979 "line1",
980 980 "line2",
981 981 ]
982 982 ),
983 983 )
984 984 s = Path(fname).read_text(encoding="utf-8")
985 985 assert "line1\n" in s
986 986 assert "line2" in s
987 987
988 988
989 989 @dec.skip_win32
990 990 def test_file_single_quote():
991 991 """Basic %%writefile with embedded single quotes"""
992 992 ip = get_ipython()
993 993 with TemporaryDirectory() as td:
994 994 fname = os.path.join(td, "'file1'")
995 995 ip.run_cell_magic(
996 996 "writefile",
997 997 fname,
998 998 "\n".join(
999 999 [
1000 1000 "line1",
1001 1001 "line2",
1002 1002 ]
1003 1003 ),
1004 1004 )
1005 1005 s = Path(fname).read_text(encoding="utf-8")
1006 1006 assert "line1\n" in s
1007 1007 assert "line2" in s
1008 1008
1009 1009
1010 1010 @dec.skip_win32
1011 1011 def test_file_double_quote():
1012 1012 """Basic %%writefile with embedded double quotes"""
1013 1013 ip = get_ipython()
1014 1014 with TemporaryDirectory() as td:
1015 1015 fname = os.path.join(td, '"file1"')
1016 1016 ip.run_cell_magic(
1017 1017 "writefile",
1018 1018 fname,
1019 1019 "\n".join(
1020 1020 [
1021 1021 "line1",
1022 1022 "line2",
1023 1023 ]
1024 1024 ),
1025 1025 )
1026 1026 s = Path(fname).read_text(encoding="utf-8")
1027 1027 assert "line1\n" in s
1028 1028 assert "line2" in s
1029 1029
1030 1030
1031 1031 def test_file_var_expand():
1032 1032 """%%writefile $filename"""
1033 1033 ip = get_ipython()
1034 1034 with TemporaryDirectory() as td:
1035 1035 fname = os.path.join(td, "file1")
1036 1036 ip.user_ns["filename"] = fname
1037 1037 ip.run_cell_magic(
1038 1038 "writefile",
1039 1039 "$filename",
1040 1040 "\n".join(
1041 1041 [
1042 1042 "line1",
1043 1043 "line2",
1044 1044 ]
1045 1045 ),
1046 1046 )
1047 1047 s = Path(fname).read_text(encoding="utf-8")
1048 1048 assert "line1\n" in s
1049 1049 assert "line2" in s
1050 1050
1051 1051
1052 1052 def test_file_unicode():
1053 1053 """%%writefile with unicode cell"""
1054 1054 ip = get_ipython()
1055 1055 with TemporaryDirectory() as td:
1056 1056 fname = os.path.join(td, 'file1')
1057 1057 ip.run_cell_magic("writefile", fname, u'\n'.join([
1058 1058 u'linΓ©1',
1059 1059 u'linΓ©2',
1060 1060 ]))
1061 1061 with io.open(fname, encoding='utf-8') as f:
1062 1062 s = f.read()
1063 1063 assert "linΓ©1\n" in s
1064 1064 assert "linΓ©2" in s
1065 1065
1066 1066
1067 1067 def test_file_amend():
1068 1068 """%%writefile -a amends files"""
1069 1069 ip = get_ipython()
1070 1070 with TemporaryDirectory() as td:
1071 1071 fname = os.path.join(td, "file2")
1072 1072 ip.run_cell_magic(
1073 1073 "writefile",
1074 1074 fname,
1075 1075 "\n".join(
1076 1076 [
1077 1077 "line1",
1078 1078 "line2",
1079 1079 ]
1080 1080 ),
1081 1081 )
1082 1082 ip.run_cell_magic(
1083 1083 "writefile",
1084 1084 "-a %s" % fname,
1085 1085 "\n".join(
1086 1086 [
1087 1087 "line3",
1088 1088 "line4",
1089 1089 ]
1090 1090 ),
1091 1091 )
1092 1092 s = Path(fname).read_text(encoding="utf-8")
1093 1093 assert "line1\n" in s
1094 1094 assert "line3\n" in s
1095 1095
1096 1096
1097 1097 def test_file_spaces():
1098 1098 """%%file with spaces in filename"""
1099 1099 ip = get_ipython()
1100 1100 with TemporaryWorkingDirectory() as td:
1101 1101 fname = "file name"
1102 1102 ip.run_cell_magic(
1103 1103 "file",
1104 1104 '"%s"' % fname,
1105 1105 "\n".join(
1106 1106 [
1107 1107 "line1",
1108 1108 "line2",
1109 1109 ]
1110 1110 ),
1111 1111 )
1112 1112 s = Path(fname).read_text(encoding="utf-8")
1113 1113 assert "line1\n" in s
1114 1114 assert "line2" in s
1115 1115
1116 1116
1117 1117 def test_script_config():
1118 1118 ip = get_ipython()
1119 1119 ip.config.ScriptMagics.script_magics = ['whoda']
1120 1120 sm = script.ScriptMagics(shell=ip)
1121 1121 assert "whoda" in sm.magics["cell"]
1122 1122
1123 1123
1124 1124 def test_script_out():
1125 1125 ip = get_ipython()
1126 1126 ip.run_cell_magic("script", f"--out output {sys.executable}", "print('hi')")
1127 1127 assert ip.user_ns["output"].strip() == "hi"
1128 1128
1129 1129
1130 1130 def test_script_err():
1131 1131 ip = get_ipython()
1132 1132 ip.run_cell_magic(
1133 1133 "script",
1134 1134 f"--err error {sys.executable}",
1135 1135 "import sys; print('hello', file=sys.stderr)",
1136 1136 )
1137 1137 assert ip.user_ns["error"].strip() == "hello"
1138 1138
1139 1139
1140 1140 def test_script_out_err():
1141 1141 ip = get_ipython()
1142 1142 ip.run_cell_magic(
1143 1143 "script",
1144 1144 f"--out output --err error {sys.executable}",
1145 1145 "\n".join(
1146 1146 [
1147 1147 "import sys",
1148 1148 "print('hi')",
1149 1149 "print('hello', file=sys.stderr)",
1150 1150 ]
1151 1151 ),
1152 1152 )
1153 1153 assert ip.user_ns["output"].strip() == "hi"
1154 1154 assert ip.user_ns["error"].strip() == "hello"
1155 1155
1156 1156
1157 1157 async def test_script_bg_out():
1158 1158 ip = get_ipython()
1159 1159 ip.run_cell_magic("script", f"--bg --out output {sys.executable}", "print('hi')")
1160 1160 assert (await ip.user_ns["output"].read()).strip() == b"hi"
1161 1161 assert ip.user_ns["output"].at_eof()
1162 1162
1163 1163
1164 1164 async def test_script_bg_err():
1165 1165 ip = get_ipython()
1166 1166 ip.run_cell_magic(
1167 1167 "script",
1168 1168 f"--bg --err error {sys.executable}",
1169 1169 "import sys; print('hello', file=sys.stderr)",
1170 1170 )
1171 1171 assert (await ip.user_ns["error"].read()).strip() == b"hello"
1172 1172 assert ip.user_ns["error"].at_eof()
1173 1173
1174 1174
1175 1175 async def test_script_bg_out_err():
1176 1176 ip = get_ipython()
1177 1177 ip.run_cell_magic(
1178 1178 "script",
1179 1179 f"--bg --out output --err error {sys.executable}",
1180 1180 "\n".join(
1181 1181 [
1182 1182 "import sys",
1183 1183 "print('hi')",
1184 1184 "print('hello', file=sys.stderr)",
1185 1185 ]
1186 1186 ),
1187 1187 )
1188 1188 assert (await ip.user_ns["output"].read()).strip() == b"hi"
1189 1189 assert (await ip.user_ns["error"].read()).strip() == b"hello"
1190 1190 assert ip.user_ns["output"].at_eof()
1191 1191 assert ip.user_ns["error"].at_eof()
1192 1192
1193 1193
1194 1194 async def test_script_bg_proc():
1195 1195 ip = get_ipython()
1196 1196 ip.run_cell_magic(
1197 1197 "script",
1198 1198 f"--bg --out output --proc p {sys.executable}",
1199 1199 "\n".join(
1200 1200 [
1201 1201 "import sys",
1202 1202 "print('hi')",
1203 1203 "print('hello', file=sys.stderr)",
1204 1204 ]
1205 1205 ),
1206 1206 )
1207 1207 p = ip.user_ns["p"]
1208 1208 await p.wait()
1209 1209 assert p.returncode == 0
1210 1210 assert (await p.stdout.read()).strip() == b"hi"
1211 1211 # not captured, so empty
1212 1212 assert (await p.stderr.read()) == b""
1213 1213 assert p.stdout.at_eof()
1214 1214 assert p.stderr.at_eof()
1215 1215
1216 1216
1217 1217 def test_script_defaults():
1218 1218 ip = get_ipython()
1219 1219 for cmd in ['sh', 'bash', 'perl', 'ruby']:
1220 1220 try:
1221 1221 find_cmd(cmd)
1222 1222 except Exception:
1223 1223 pass
1224 1224 else:
1225 1225 assert cmd in ip.magics_manager.magics["cell"]
1226 1226
1227 1227
1228 1228 @magics_class
1229 1229 class FooFoo(Magics):
1230 1230 """class with both %foo and %%foo magics"""
1231 1231 @line_magic('foo')
1232 1232 def line_foo(self, line):
1233 1233 "I am line foo"
1234 1234 pass
1235 1235
1236 1236 @cell_magic("foo")
1237 1237 def cell_foo(self, line, cell):
1238 1238 "I am cell foo, not line foo"
1239 1239 pass
1240 1240
1241 1241 def test_line_cell_info():
1242 1242 """%%foo and %foo magics are distinguishable to inspect"""
1243 1243 ip = get_ipython()
1244 1244 ip.magics_manager.register(FooFoo)
1245 1245 oinfo = ip.object_inspect("foo")
1246 1246 assert oinfo["found"] is True
1247 1247 assert oinfo["ismagic"] is True
1248 1248
1249 1249 oinfo = ip.object_inspect("%%foo")
1250 1250 assert oinfo["found"] is True
1251 1251 assert oinfo["ismagic"] is True
1252 1252 assert oinfo["docstring"] == FooFoo.cell_foo.__doc__
1253 1253
1254 1254 oinfo = ip.object_inspect("%foo")
1255 1255 assert oinfo["found"] is True
1256 1256 assert oinfo["ismagic"] is True
1257 1257 assert oinfo["docstring"] == FooFoo.line_foo.__doc__
1258 1258
1259 1259
1260 1260 def test_multiple_magics():
1261 1261 ip = get_ipython()
1262 1262 foo1 = FooFoo(ip)
1263 1263 foo2 = FooFoo(ip)
1264 1264 mm = ip.magics_manager
1265 1265 mm.register(foo1)
1266 1266 assert mm.magics["line"]["foo"].__self__ is foo1
1267 1267 mm.register(foo2)
1268 1268 assert mm.magics["line"]["foo"].__self__ is foo2
1269 1269
1270 1270
1271 1271 def test_alias_magic():
1272 1272 """Test %alias_magic."""
1273 1273 ip = get_ipython()
1274 1274 mm = ip.magics_manager
1275 1275
1276 1276 # Basic operation: both cell and line magics are created, if possible.
1277 1277 ip.run_line_magic("alias_magic", "timeit_alias timeit")
1278 1278 assert "timeit_alias" in mm.magics["line"]
1279 1279 assert "timeit_alias" in mm.magics["cell"]
1280 1280
1281 1281 # --cell is specified, line magic not created.
1282 1282 ip.run_line_magic("alias_magic", "--cell timeit_cell_alias timeit")
1283 1283 assert "timeit_cell_alias" not in mm.magics["line"]
1284 1284 assert "timeit_cell_alias" in mm.magics["cell"]
1285 1285
1286 1286 # Test that line alias is created successfully.
1287 1287 ip.run_line_magic("alias_magic", "--line env_alias env")
1288 1288 assert ip.run_line_magic("env", "") == ip.run_line_magic("env_alias", "")
1289 1289
1290 1290 # Test that line alias with parameters passed in is created successfully.
1291 1291 ip.run_line_magic(
1292 1292 "alias_magic", "--line history_alias history --params " + shlex.quote("3")
1293 1293 )
1294 1294 assert "history_alias" in mm.magics["line"]
1295 1295
1296 1296
1297 1297 def test_save():
1298 1298 """Test %save."""
1299 1299 ip = get_ipython()
1300 1300 ip.history_manager.reset() # Clear any existing history.
1301 1301 cmds = ["a=1", "def b():\n return a**2", "print(a, b())"]
1302 1302 for i, cmd in enumerate(cmds, start=1):
1303 1303 ip.history_manager.store_inputs(i, cmd)
1304 1304 with TemporaryDirectory() as tmpdir:
1305 1305 file = os.path.join(tmpdir, "testsave.py")
1306 1306 ip.run_line_magic("save", "%s 1-10" % file)
1307 1307 content = Path(file).read_text(encoding="utf-8")
1308 1308 assert content.count(cmds[0]) == 1
1309 1309 assert "coding: utf-8" in content
1310 1310 ip.run_line_magic("save", "-a %s 1-10" % file)
1311 1311 content = Path(file).read_text(encoding="utf-8")
1312 1312 assert content.count(cmds[0]) == 2
1313 1313 assert "coding: utf-8" in content
1314 1314
1315 1315
1316 1316 def test_save_with_no_args():
1317 1317 ip = get_ipython()
1318 1318 ip.history_manager.reset() # Clear any existing history.
1319 1319 cmds = ["a=1", "def b():\n return a**2", "print(a, b())", "%save"]
1320 1320 for i, cmd in enumerate(cmds, start=1):
1321 1321 ip.history_manager.store_inputs(i, cmd)
1322 1322
1323 1323 with TemporaryDirectory() as tmpdir:
1324 1324 path = os.path.join(tmpdir, "testsave.py")
1325 1325 ip.run_line_magic("save", path)
1326 1326 content = Path(path).read_text(encoding="utf-8")
1327 1327 expected_content = dedent(
1328 1328 """\
1329 1329 # coding: utf-8
1330 1330 a=1
1331 1331 def b():
1332 1332 return a**2
1333 1333 print(a, b())
1334 1334 """
1335 1335 )
1336 1336 assert content == expected_content
1337 1337
1338 1338
1339 1339 def test_store():
1340 1340 """Test %store."""
1341 1341 ip = get_ipython()
1342 1342 ip.run_line_magic('load_ext', 'storemagic')
1343 1343
1344 1344 # make sure the storage is empty
1345 1345 ip.run_line_magic("store", "-z")
1346 1346 ip.user_ns["var"] = 42
1347 1347 ip.run_line_magic("store", "var")
1348 1348 ip.user_ns["var"] = 39
1349 1349 ip.run_line_magic("store", "-r")
1350 1350 assert ip.user_ns["var"] == 42
1351 1351
1352 1352 ip.run_line_magic("store", "-d var")
1353 1353 ip.user_ns["var"] = 39
1354 1354 ip.run_line_magic("store", "-r")
1355 1355 assert ip.user_ns["var"] == 39
1356 1356
1357 1357
1358 1358 def _run_edit_test(arg_s, exp_filename=None,
1359 1359 exp_lineno=-1,
1360 1360 exp_contents=None,
1361 1361 exp_is_temp=None):
1362 1362 ip = get_ipython()
1363 1363 M = code.CodeMagics(ip)
1364 1364 last_call = ['','']
1365 1365 opts,args = M.parse_options(arg_s,'prxn:')
1366 1366 filename, lineno, is_temp = M._find_edit_target(ip, args, opts, last_call)
1367 1367
1368 1368 if exp_filename is not None:
1369 1369 assert exp_filename == filename
1370 1370 if exp_contents is not None:
1371 1371 with io.open(filename, 'r', encoding='utf-8') as f:
1372 1372 contents = f.read()
1373 1373 assert exp_contents == contents
1374 1374 if exp_lineno != -1:
1375 1375 assert exp_lineno == lineno
1376 1376 if exp_is_temp is not None:
1377 1377 assert exp_is_temp == is_temp
1378 1378
1379 1379
1380 1380 def test_edit_interactive():
1381 1381 """%edit on interactively defined objects"""
1382 1382 ip = get_ipython()
1383 1383 n = ip.execution_count
1384 1384 ip.run_cell("def foo(): return 1", store_history=True)
1385 1385
1386 1386 with pytest.raises(code.InteractivelyDefined) as e:
1387 1387 _run_edit_test("foo")
1388 1388 assert e.value.index == n
1389 1389
1390 1390
1391 1391 def test_edit_cell():
1392 1392 """%edit [cell id]"""
1393 1393 ip = get_ipython()
1394 1394
1395 1395 ip.run_cell("def foo(): return 1", store_history=True)
1396 1396
1397 1397 # test
1398 1398 _run_edit_test("1", exp_contents=ip.user_ns['In'][1], exp_is_temp=True)
1399 1399
1400 1400 def test_edit_fname():
1401 1401 """%edit file"""
1402 1402 # test
1403 1403 _run_edit_test("test file.py", exp_filename="test file.py")
1404 1404
1405 1405 def test_bookmark():
1406 1406 ip = get_ipython()
1407 1407 ip.run_line_magic('bookmark', 'bmname')
1408 1408 with tt.AssertPrints('bmname'):
1409 1409 ip.run_line_magic('bookmark', '-l')
1410 1410 ip.run_line_magic('bookmark', '-d bmname')
1411 1411
1412 1412 def test_ls_magic():
1413 1413 ip = get_ipython()
1414 1414 json_formatter = ip.display_formatter.formatters['application/json']
1415 1415 json_formatter.enabled = True
1416 1416 lsmagic = ip.run_line_magic("lsmagic", "")
1417 1417 with warnings.catch_warnings(record=True) as w:
1418 1418 j = json_formatter(lsmagic)
1419 1419 assert sorted(j) == ["cell", "line"]
1420 1420 assert w == [] # no warnings
1421 1421
1422 1422
1423 1423 def test_strip_initial_indent():
1424 1424 def sii(s):
1425 1425 lines = s.splitlines()
1426 1426 return '\n'.join(code.strip_initial_indent(lines))
1427 1427
1428 1428 assert sii(" a = 1\nb = 2") == "a = 1\nb = 2"
1429 1429 assert sii(" a\n b\nc") == "a\n b\nc"
1430 1430 assert sii("a\n b") == "a\n b"
1431 1431
1432 1432 def test_logging_magic_quiet_from_arg():
1433 1433 _ip.config.LoggingMagics.quiet = False
1434 1434 lm = logging.LoggingMagics(shell=_ip)
1435 1435 with TemporaryDirectory() as td:
1436 1436 try:
1437 1437 with tt.AssertNotPrints(re.compile("Activating.*")):
1438 1438 lm.logstart('-q {}'.format(
1439 1439 os.path.join(td, "quiet_from_arg.log")))
1440 1440 finally:
1441 1441 _ip.logger.logstop()
1442 1442
1443 1443 def test_logging_magic_quiet_from_config():
1444 1444 _ip.config.LoggingMagics.quiet = True
1445 1445 lm = logging.LoggingMagics(shell=_ip)
1446 1446 with TemporaryDirectory() as td:
1447 1447 try:
1448 1448 with tt.AssertNotPrints(re.compile("Activating.*")):
1449 1449 lm.logstart(os.path.join(td, "quiet_from_config.log"))
1450 1450 finally:
1451 1451 _ip.logger.logstop()
1452 1452
1453 1453
1454 1454 def test_logging_magic_not_quiet():
1455 1455 _ip.config.LoggingMagics.quiet = False
1456 1456 lm = logging.LoggingMagics(shell=_ip)
1457 1457 with TemporaryDirectory() as td:
1458 1458 try:
1459 1459 with tt.AssertPrints(re.compile("Activating.*")):
1460 1460 lm.logstart(os.path.join(td, "not_quiet.log"))
1461 1461 finally:
1462 1462 _ip.logger.logstop()
1463 1463
1464 1464
1465 1465 def test_time_no_var_expand():
1466 1466 _ip.user_ns["a"] = 5
1467 1467 _ip.user_ns["b"] = []
1468 1468 _ip.run_line_magic("time", 'b.append("{a}")')
1469 1469 assert _ip.user_ns["b"] == ["{a}"]
1470 1470
1471 1471
1472 1472 # this is slow, put at the end for local testing.
1473 1473 def test_timeit_arguments():
1474 1474 "Test valid timeit arguments, should not cause SyntaxError (GH #1269)"
1475 1475 _ip.run_line_magic("timeit", "-n1 -r1 a=('#')")
1476 1476
1477 1477
1478 1478 MINIMAL_LAZY_MAGIC = """
1479 1479 from IPython.core.magic import (
1480 1480 Magics,
1481 1481 magics_class,
1482 1482 line_magic,
1483 1483 cell_magic,
1484 1484 )
1485 1485
1486 1486
1487 1487 @magics_class
1488 1488 class LazyMagics(Magics):
1489 1489 @line_magic
1490 1490 def lazy_line(self, line):
1491 1491 print("Lazy Line")
1492 1492
1493 1493 @cell_magic
1494 1494 def lazy_cell(self, line, cell):
1495 1495 print("Lazy Cell")
1496 1496
1497 1497
1498 1498 def load_ipython_extension(ipython):
1499 1499 ipython.register_magics(LazyMagics)
1500 1500 """
1501 1501
1502 1502
1503 1503 def test_lazy_magics():
1504 1504 with pytest.raises(UsageError):
1505 1505 ip.run_line_magic("lazy_line", "")
1506 1506
1507 1507 startdir = os.getcwd()
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 1514 ip.magics_manager.register_lazy("lazy_line", Path(tf.name).name[:-3])
1515 1515 with tt.AssertPrints("Lazy Line"):
1516 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,626 +1,626
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 21 import platform
22 22 import random
23 23 import string
24 24 import sys
25 25 import textwrap
26 26 import unittest
27 27 from os.path import join as pjoin
28 28 from unittest.mock import patch
29 29
30 30 import pytest
31 31 from tempfile import TemporaryDirectory
32 32
33 33 from IPython.core import debugger
34 34 from IPython.testing import decorators as dec
35 35 from IPython.testing import tools as tt
36 36 from IPython.utils.io import capture_output
37 37
38 38
39 39 def doctest_refbug():
40 40 """Very nasty problem with references held by multiple runs of a script.
41 41 See: https://github.com/ipython/ipython/issues/141
42 42
43 43 In [1]: _ip.clear_main_mod_cache()
44 44 # random
45 45
46 46 In [2]: %run refbug
47 47
48 48 In [3]: call_f()
49 49 lowercased: hello
50 50
51 51 In [4]: %run refbug
52 52
53 53 In [5]: call_f()
54 54 lowercased: hello
55 55 lowercased: hello
56 56 """
57 57
58 58
59 59 def doctest_run_builtins():
60 60 r"""Check that %run doesn't damage __builtins__.
61 61
62 62 In [1]: import tempfile
63 63
64 64 In [2]: bid1 = id(__builtins__)
65 65
66 66 In [3]: fname = tempfile.mkstemp('.py')[1]
67 67
68 68 In [3]: f = open(fname, 'w', encoding='utf-8')
69 69
70 70 In [4]: dummy= f.write('pass\n')
71 71
72 72 In [5]: f.flush()
73 73
74 74 In [6]: t1 = type(__builtins__)
75 75
76 76 In [7]: %run $fname
77 77
78 78 In [7]: f.close()
79 79
80 80 In [8]: bid2 = id(__builtins__)
81 81
82 82 In [9]: t2 = type(__builtins__)
83 83
84 84 In [10]: t1 == t2
85 85 Out[10]: True
86 86
87 87 In [10]: bid1 == bid2
88 88 Out[10]: True
89 89
90 90 In [12]: try:
91 91 ....: os.unlink(fname)
92 92 ....: except:
93 93 ....: pass
94 94 ....:
95 95 """
96 96
97 97
98 98 def doctest_run_option_parser():
99 99 r"""Test option parser in %run.
100 100
101 101 In [1]: %run print_argv.py
102 102 []
103 103
104 104 In [2]: %run print_argv.py print*.py
105 105 ['print_argv.py']
106 106
107 107 In [3]: %run -G print_argv.py print*.py
108 108 ['print*.py']
109 109
110 110 """
111 111
112 112
113 113 @dec.skip_win32
114 114 def doctest_run_option_parser_for_posix():
115 115 r"""Test option parser in %run (Linux/OSX specific).
116 116
117 117 You need double quote to escape glob in POSIX systems:
118 118
119 119 In [1]: %run print_argv.py print\\*.py
120 120 ['print*.py']
121 121
122 122 You can't use quote to escape glob in POSIX systems:
123 123
124 124 In [2]: %run print_argv.py 'print*.py'
125 125 ['print_argv.py']
126 126
127 127 """
128 128
129 129
130 130 doctest_run_option_parser_for_posix.__skip_doctest__ = sys.platform == "win32"
131 131
132 132
133 133 @dec.skip_if_not_win32
134 134 def doctest_run_option_parser_for_windows():
135 135 r"""Test option parser in %run (Windows specific).
136 136
137 137 In Windows, you can't escape ``*` `by backslash:
138 138
139 139 In [1]: %run print_argv.py print\\*.py
140 140 ['print\\\\*.py']
141 141
142 142 You can use quote to escape glob:
143 143
144 144 In [2]: %run print_argv.py 'print*.py'
145 145 ["'print*.py'"]
146 146
147 147 """
148 148
149 149
150 150 doctest_run_option_parser_for_windows.__skip_doctest__ = sys.platform != "win32"
151 151
152 152
153 153 def doctest_reset_del():
154 154 """Test that resetting doesn't cause errors in __del__ methods.
155 155
156 156 In [2]: class A(object):
157 157 ...: def __del__(self):
158 158 ...: print(str("Hi"))
159 159 ...:
160 160
161 161 In [3]: a = A()
162 162
163 163 In [4]: get_ipython().reset(); import gc; x = gc.collect(0)
164 164 Hi
165 165
166 166 In [5]: 1+1
167 167 Out[5]: 2
168 168 """
169 169
170 170 # For some tests, it will be handy to organize them in a class with a common
171 171 # setup that makes a temp file
172 172
173 173 class TestMagicRunPass(tt.TempFileMixin):
174 174
175 175 def setUp(self):
176 176 content = "a = [1,2,3]\nb = 1"
177 177 self.mktmp(content)
178 178
179 179 def run_tmpfile(self):
180 180 _ip = get_ipython()
181 181 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
182 182 # See below and ticket https://bugs.launchpad.net/bugs/366353
183 183 _ip.run_line_magic("run", self.fname)
184 184
185 185 def run_tmpfile_p(self):
186 186 _ip = get_ipython()
187 187 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
188 188 # See below and ticket https://bugs.launchpad.net/bugs/366353
189 189 _ip.run_line_magic("run", "-p %s" % self.fname)
190 190
191 191 def test_builtins_id(self):
192 192 """Check that %run doesn't damage __builtins__ """
193 193 _ip = get_ipython()
194 194 # Test that the id of __builtins__ is not modified by %run
195 195 bid1 = id(_ip.user_ns['__builtins__'])
196 196 self.run_tmpfile()
197 197 bid2 = id(_ip.user_ns['__builtins__'])
198 198 assert bid1 == bid2
199 199
200 200 def test_builtins_type(self):
201 201 """Check that the type of __builtins__ doesn't change with %run.
202 202
203 203 However, the above could pass if __builtins__ was already modified to
204 204 be a dict (it should be a module) by a previous use of %run. So we
205 205 also check explicitly that it really is a module:
206 206 """
207 207 _ip = get_ipython()
208 208 self.run_tmpfile()
209 209 assert type(_ip.user_ns["__builtins__"]) == type(sys)
210 210
211 211 def test_run_profile(self):
212 212 """Test that the option -p, which invokes the profiler, do not
213 213 crash by invoking execfile"""
214 214 self.run_tmpfile_p()
215 215
216 216 def test_run_debug_twice(self):
217 217 # https://github.com/ipython/ipython/issues/10028
218 218 _ip = get_ipython()
219 219 with tt.fake_input(["c"]):
220 220 _ip.run_line_magic("run", "-d %s" % self.fname)
221 221 with tt.fake_input(["c"]):
222 222 _ip.run_line_magic("run", "-d %s" % self.fname)
223 223
224 224 def test_run_debug_twice_with_breakpoint(self):
225 225 """Make a valid python temp file."""
226 226 _ip = get_ipython()
227 227 with tt.fake_input(["b 2", "c", "c"]):
228 228 _ip.run_line_magic("run", "-d %s" % self.fname)
229 229
230 230 with tt.fake_input(["c"]):
231 231 with tt.AssertNotPrints("KeyError"):
232 232 _ip.run_line_magic("run", "-d %s" % self.fname)
233 233
234 234
235 235 class TestMagicRunSimple(tt.TempFileMixin):
236 236
237 237 def test_simpledef(self):
238 238 """Test that simple class definitions work."""
239 239 src = ("class foo: pass\n"
240 240 "def f(): return foo()")
241 241 self.mktmp(src)
242 242 _ip.run_line_magic("run", str(self.fname))
243 243 _ip.run_cell("t = isinstance(f(), foo)")
244 244 assert _ip.user_ns["t"] is True
245 245
246 246 @pytest.mark.xfail(
247 247 platform.python_implementation() == "PyPy",
248 248 reason="expecting __del__ call on exit is unreliable and doesn't happen on PyPy",
249 249 )
250 250 def test_obj_del(self):
251 251 """Test that object's __del__ methods are called on exit."""
252 252 src = ("class A(object):\n"
253 253 " def __del__(self):\n"
254 254 " print('object A deleted')\n"
255 255 "a = A()\n")
256 256 self.mktmp(src)
257 257 err = None
258 258 tt.ipexec_validate(self.fname, 'object A deleted', err)
259 259
260 260 def test_aggressive_namespace_cleanup(self):
261 261 """Test that namespace cleanup is not too aggressive GH-238
262 262
263 263 Returning from another run magic deletes the namespace"""
264 264 # see ticket https://github.com/ipython/ipython/issues/238
265 265
266 266 with tt.TempFileMixin() as empty:
267 267 empty.mktmp("")
268 268 # On Windows, the filename will have \users in it, so we need to use the
269 269 # repr so that the \u becomes \\u.
270 270 src = (
271 271 "ip = get_ipython()\n"
272 272 "for i in range(5):\n"
273 273 " try:\n"
274 " ip.magic(%r)\n"
274 " ip.run_line_magic(%r, %r)\n"
275 275 " except NameError as e:\n"
276 276 " print(i)\n"
277 " break\n" % ("run " + empty.fname)
277 " break\n" % ("run", empty.fname)
278 278 )
279 279 self.mktmp(src)
280 280 _ip.run_line_magic("run", str(self.fname))
281 281 _ip.run_cell("ip == get_ipython()")
282 282 assert _ip.user_ns["i"] == 4
283 283
284 284 def test_run_second(self):
285 285 """Test that running a second file doesn't clobber the first, gh-3547"""
286 286 self.mktmp("avar = 1\n" "def afunc():\n" " return avar\n")
287 287
288 288 with tt.TempFileMixin() as empty:
289 289 empty.mktmp("")
290 290
291 291 _ip.run_line_magic("run", self.fname)
292 292 _ip.run_line_magic("run", empty.fname)
293 293 assert _ip.user_ns["afunc"]() == 1
294 294
295 295 def test_tclass(self):
296 296 mydir = os.path.dirname(__file__)
297 297 tc = os.path.join(mydir, "tclass")
298 298 src = f"""\
299 299 import gc
300 300 %run "{tc}" C-first
301 301 gc.collect(0)
302 302 %run "{tc}" C-second
303 303 gc.collect(0)
304 304 %run "{tc}" C-third
305 305 gc.collect(0)
306 306 %reset -f
307 307 """
308 308 self.mktmp(src, ".ipy")
309 309 out = """\
310 310 ARGV 1-: ['C-first']
311 311 ARGV 1-: ['C-second']
312 312 tclass.py: deleting object: C-first
313 313 ARGV 1-: ['C-third']
314 314 tclass.py: deleting object: C-second
315 315 tclass.py: deleting object: C-third
316 316 """
317 317 err = None
318 318 tt.ipexec_validate(self.fname, out, err)
319 319
320 320 def test_run_i_after_reset(self):
321 321 """Check that %run -i still works after %reset (gh-693)"""
322 322 src = "yy = zz\n"
323 323 self.mktmp(src)
324 324 _ip.run_cell("zz = 23")
325 325 try:
326 326 _ip.run_line_magic("run", "-i %s" % self.fname)
327 327 assert _ip.user_ns["yy"] == 23
328 328 finally:
329 329 _ip.run_line_magic("reset", "-f")
330 330
331 331 _ip.run_cell("zz = 23")
332 332 try:
333 333 _ip.run_line_magic("run", "-i %s" % self.fname)
334 334 assert _ip.user_ns["yy"] == 23
335 335 finally:
336 336 _ip.run_line_magic("reset", "-f")
337 337
338 338 def test_unicode(self):
339 339 """Check that files in odd encodings are accepted."""
340 340 mydir = os.path.dirname(__file__)
341 341 na = os.path.join(mydir, "nonascii.py")
342 _ip.magic('run "%s"' % na)
342 _ip.run_line_magic("run", na)
343 343 assert _ip.user_ns["u"] == "ΠŽΡ‚β„–Π€"
344 344
345 345 def test_run_py_file_attribute(self):
346 346 """Test handling of `__file__` attribute in `%run <file>.py`."""
347 347 src = "t = __file__\n"
348 348 self.mktmp(src)
349 349 _missing = object()
350 350 file1 = _ip.user_ns.get("__file__", _missing)
351 351 _ip.run_line_magic("run", self.fname)
352 352 file2 = _ip.user_ns.get("__file__", _missing)
353 353
354 354 # Check that __file__ was equal to the filename in the script's
355 355 # namespace.
356 356 assert _ip.user_ns["t"] == self.fname
357 357
358 358 # Check that __file__ was not leaked back into user_ns.
359 359 assert file1 == file2
360 360
361 361 def test_run_ipy_file_attribute(self):
362 362 """Test handling of `__file__` attribute in `%run <file.ipy>`."""
363 363 src = "t = __file__\n"
364 364 self.mktmp(src, ext='.ipy')
365 365 _missing = object()
366 366 file1 = _ip.user_ns.get("__file__", _missing)
367 367 _ip.run_line_magic("run", self.fname)
368 368 file2 = _ip.user_ns.get("__file__", _missing)
369 369
370 370 # Check that __file__ was equal to the filename in the script's
371 371 # namespace.
372 372 assert _ip.user_ns["t"] == self.fname
373 373
374 374 # Check that __file__ was not leaked back into user_ns.
375 375 assert file1 == file2
376 376
377 377 def test_run_formatting(self):
378 378 """ Test that %run -t -N<N> does not raise a TypeError for N > 1."""
379 379 src = "pass"
380 380 self.mktmp(src)
381 381 _ip.run_line_magic("run", "-t -N 1 %s" % self.fname)
382 382 _ip.run_line_magic("run", "-t -N 10 %s" % self.fname)
383 383
384 384 def test_ignore_sys_exit(self):
385 385 """Test the -e option to ignore sys.exit()"""
386 386 src = "import sys; sys.exit(1)"
387 387 self.mktmp(src)
388 388 with tt.AssertPrints("SystemExit"):
389 389 _ip.run_line_magic("run", self.fname)
390 390
391 391 with tt.AssertNotPrints("SystemExit"):
392 392 _ip.run_line_magic("run", "-e %s" % self.fname)
393 393
394 394 def test_run_nb(self):
395 395 """Test %run notebook.ipynb"""
396 396 pytest.importorskip("nbformat")
397 397 from nbformat import v4, writes
398 398 nb = v4.new_notebook(
399 399 cells=[
400 400 v4.new_markdown_cell("The Ultimate Question of Everything"),
401 401 v4.new_code_cell("answer=42")
402 402 ]
403 403 )
404 404 src = writes(nb, version=4)
405 405 self.mktmp(src, ext='.ipynb')
406 406
407 407 _ip.run_line_magic("run", self.fname)
408 408
409 409 assert _ip.user_ns["answer"] == 42
410 410
411 411 def test_run_nb_error(self):
412 412 """Test %run notebook.ipynb error"""
413 413 pytest.importorskip("nbformat")
414 414 from nbformat import v4, writes
415 415
416 416 # %run when a file name isn't provided
417 417 pytest.raises(Exception, _ip.magic, "run")
418 418
419 419 # %run when a file doesn't exist
420 420 pytest.raises(Exception, _ip.magic, "run foobar.ipynb")
421 421
422 422 # %run on a notebook with an error
423 423 nb = v4.new_notebook(
424 424 cells=[
425 425 v4.new_code_cell("0/0")
426 426 ]
427 427 )
428 428 src = writes(nb, version=4)
429 429 self.mktmp(src, ext='.ipynb')
430 430 pytest.raises(Exception, _ip.magic, "run %s" % self.fname)
431 431
432 432 def test_file_options(self):
433 433 src = ('import sys\n'
434 434 'a = " ".join(sys.argv[1:])\n')
435 435 self.mktmp(src)
436 436 test_opts = "-x 3 --verbose"
437 437 _ip.run_line_magic("run", "{0} {1}".format(self.fname, test_opts))
438 438 assert _ip.user_ns["a"] == test_opts
439 439
440 440
441 441 class TestMagicRunWithPackage(unittest.TestCase):
442 442
443 443 def writefile(self, name, content):
444 444 path = os.path.join(self.tempdir.name, name)
445 445 d = os.path.dirname(path)
446 446 if not os.path.isdir(d):
447 447 os.makedirs(d)
448 448 with open(path, "w", encoding="utf-8") as f:
449 449 f.write(textwrap.dedent(content))
450 450
451 451 def setUp(self):
452 452 self.package = package = 'tmp{0}'.format(''.join([random.choice(string.ascii_letters) for i in range(10)]))
453 453 """Temporary (probably) valid python package name."""
454 454
455 455 self.value = int(random.random() * 10000)
456 456
457 457 self.tempdir = TemporaryDirectory()
458 458 self.__orig_cwd = os.getcwd()
459 459 sys.path.insert(0, self.tempdir.name)
460 460
461 461 self.writefile(os.path.join(package, '__init__.py'), '')
462 462 self.writefile(os.path.join(package, 'sub.py'), """
463 463 x = {0!r}
464 464 """.format(self.value))
465 465 self.writefile(os.path.join(package, 'relative.py'), """
466 466 from .sub import x
467 467 """)
468 468 self.writefile(os.path.join(package, 'absolute.py'), """
469 469 from {0}.sub import x
470 470 """.format(package))
471 471 self.writefile(os.path.join(package, 'args.py'), """
472 472 import sys
473 473 a = " ".join(sys.argv[1:])
474 474 """.format(package))
475 475
476 476 def tearDown(self):
477 477 os.chdir(self.__orig_cwd)
478 478 sys.path[:] = [p for p in sys.path if p != self.tempdir.name]
479 479 self.tempdir.cleanup()
480 480
481 481 def check_run_submodule(self, submodule, opts=""):
482 482 _ip.user_ns.pop("x", None)
483 483 _ip.run_line_magic(
484 484 "run", "{2} -m {0}.{1}".format(self.package, submodule, opts)
485 485 )
486 486 self.assertEqual(
487 487 _ip.user_ns["x"],
488 488 self.value,
489 489 "Variable `x` is not loaded from module `{0}`.".format(submodule),
490 490 )
491 491
492 492 def test_run_submodule_with_absolute_import(self):
493 493 self.check_run_submodule('absolute')
494 494
495 495 def test_run_submodule_with_relative_import(self):
496 496 """Run submodule that has a relative import statement (#2727)."""
497 497 self.check_run_submodule('relative')
498 498
499 499 def test_prun_submodule_with_absolute_import(self):
500 500 self.check_run_submodule('absolute', '-p')
501 501
502 502 def test_prun_submodule_with_relative_import(self):
503 503 self.check_run_submodule('relative', '-p')
504 504
505 505 def with_fake_debugger(func):
506 506 @functools.wraps(func)
507 507 def wrapper(*args, **kwds):
508 508 with patch.object(debugger.Pdb, 'run', staticmethod(eval)):
509 509 return func(*args, **kwds)
510 510 return wrapper
511 511
512 512 @with_fake_debugger
513 513 def test_debug_run_submodule_with_absolute_import(self):
514 514 self.check_run_submodule('absolute', '-d')
515 515
516 516 @with_fake_debugger
517 517 def test_debug_run_submodule_with_relative_import(self):
518 518 self.check_run_submodule('relative', '-d')
519 519
520 520 def test_module_options(self):
521 521 _ip.user_ns.pop("a", None)
522 522 test_opts = "-x abc -m test"
523 523 _ip.run_line_magic("run", "-m {0}.args {1}".format(self.package, test_opts))
524 524 assert _ip.user_ns["a"] == test_opts
525 525
526 526 def test_module_options_with_separator(self):
527 527 _ip.user_ns.pop("a", None)
528 528 test_opts = "-x abc -m test"
529 529 _ip.run_line_magic("run", "-m {0}.args -- {1}".format(self.package, test_opts))
530 530 assert _ip.user_ns["a"] == test_opts
531 531
532 532
533 533 def test_run__name__():
534 534 with TemporaryDirectory() as td:
535 535 path = pjoin(td, "foo.py")
536 536 with open(path, "w", encoding="utf-8") as f:
537 537 f.write("q = __name__")
538 538
539 539 _ip.user_ns.pop("q", None)
540 540 _ip.run_line_magic("run", "{}".format(path))
541 541 assert _ip.user_ns.pop("q") == "__main__"
542 542
543 543 _ip.run_line_magic("run", "-n {}".format(path))
544 544 assert _ip.user_ns.pop("q") == "foo"
545 545
546 546 try:
547 547 _ip.run_line_magic("run", "-i -n {}".format(path))
548 548 assert _ip.user_ns.pop("q") == "foo"
549 549 finally:
550 550 _ip.run_line_magic("reset", "-f")
551 551
552 552
553 553 def test_run_tb():
554 554 """Test traceback offset in %run"""
555 555 with TemporaryDirectory() as td:
556 556 path = pjoin(td, "foo.py")
557 557 with open(path, "w", encoding="utf-8") as f:
558 558 f.write(
559 559 "\n".join(
560 560 [
561 561 "def foo():",
562 562 " return bar()",
563 563 "def bar():",
564 564 " raise RuntimeError('hello!')",
565 565 "foo()",
566 566 ]
567 567 )
568 568 )
569 569 with capture_output() as io:
570 570 _ip.run_line_magic("run", "{}".format(path))
571 571 out = io.stdout
572 572 assert "execfile" not in out
573 573 assert "RuntimeError" in out
574 574 assert out.count("---->") == 3
575 575 del ip.user_ns['bar']
576 576 del ip.user_ns['foo']
577 577
578 578
579 579 def test_multiprocessing_run():
580 580 """Set we can run mutiprocesgin without messing up up main namespace
581 581
582 582 Note that import `nose.tools as nt` modify the values
583 583 sys.module['__mp_main__'] so we need to temporarily set it to None to test
584 584 the issue.
585 585 """
586 586 with TemporaryDirectory() as td:
587 587 mpm = sys.modules.get('__mp_main__')
588 588 sys.modules['__mp_main__'] = None
589 589 try:
590 590 path = pjoin(td, "test.py")
591 591 with open(path, "w", encoding="utf-8") as f:
592 592 f.write("import multiprocessing\nprint('hoy')")
593 593 with capture_output() as io:
594 594 _ip.run_line_magic('run', path)
595 595 _ip.run_cell("i_m_undefined")
596 596 out = io.stdout
597 597 assert "hoy" in out
598 598 assert "AttributeError" not in out
599 599 assert "NameError" in out
600 600 assert out.count("---->") == 1
601 601 except:
602 602 raise
603 603 finally:
604 604 sys.modules['__mp_main__'] = mpm
605 605
606 606
607 607 def test_script_tb():
608 608 """Test traceback offset in `ipython script.py`"""
609 609 with TemporaryDirectory() as td:
610 610 path = pjoin(td, "foo.py")
611 611 with open(path, "w", encoding="utf-8") as f:
612 612 f.write(
613 613 "\n".join(
614 614 [
615 615 "def foo():",
616 616 " return bar()",
617 617 "def bar():",
618 618 " raise RuntimeError('hello!')",
619 619 "foo()",
620 620 ]
621 621 )
622 622 )
623 623 out, err = tt.ipexec(path)
624 624 assert "execfile" not in out
625 625 assert "RuntimeError" in out
626 626 assert out.count("---->") == 3
@@ -1,456 +1,457
1 1 # encoding: utf-8
2 2 """Tests for IPython.core.ultratb
3 3 """
4 4 import io
5 5 import os.path
6 6 import platform
7 7 import re
8 8 import sys
9 9 import traceback
10 10 import unittest
11 11 from textwrap import dedent
12 12
13 13 from tempfile import TemporaryDirectory
14 14
15 15 from IPython.core.ultratb import ColorTB, VerboseTB
16 16 from IPython.testing import tools as tt
17 17 from IPython.testing.decorators import onlyif_unicode_paths, skip_without
18 18 from IPython.utils.syspathcontext import prepended_to_syspath
19 19
20 20 file_1 = """1
21 21 2
22 22 3
23 23 def f():
24 24 1/0
25 25 """
26 26
27 27 file_2 = """def f():
28 28 1/0
29 29 """
30 30
31 31
32 32 def recursionlimit(frames):
33 33 """
34 34 decorator to set the recursion limit temporarily
35 35 """
36 36
37 37 def inner(test_function):
38 38 def wrapper(*args, **kwargs):
39 39 rl = sys.getrecursionlimit()
40 40 sys.setrecursionlimit(frames)
41 41 try:
42 42 return test_function(*args, **kwargs)
43 43 finally:
44 44 sys.setrecursionlimit(rl)
45 45
46 46 return wrapper
47 47
48 48 return inner
49 49
50 50
51 51 class ChangedPyFileTest(unittest.TestCase):
52 52 def test_changing_py_file(self):
53 53 """Traceback produced if the line where the error occurred is missing?
54 54
55 55 https://github.com/ipython/ipython/issues/1456
56 56 """
57 57 with TemporaryDirectory() as td:
58 58 fname = os.path.join(td, "foo.py")
59 59 with open(fname, "w", encoding="utf-8") as f:
60 60 f.write(file_1)
61 61
62 62 with prepended_to_syspath(td):
63 63 ip.run_cell("import foo")
64 64
65 65 with tt.AssertPrints("ZeroDivisionError"):
66 66 ip.run_cell("foo.f()")
67 67
68 68 # Make the file shorter, so the line of the error is missing.
69 69 with open(fname, "w", encoding="utf-8") as f:
70 70 f.write(file_2)
71 71
72 72 # For some reason, this was failing on the *second* call after
73 73 # changing the file, so we call f() twice.
74 74 with tt.AssertNotPrints("Internal Python error", channel='stderr'):
75 75 with tt.AssertPrints("ZeroDivisionError"):
76 76 ip.run_cell("foo.f()")
77 77 with tt.AssertPrints("ZeroDivisionError"):
78 78 ip.run_cell("foo.f()")
79 79
80 80 iso_8859_5_file = u'''# coding: iso-8859-5
81 81
82 82 def fail():
83 83 """Π΄Π±Π˜Π–"""
84 84 1/0 # Π΄Π±Π˜Π–
85 85 '''
86 86
87 87 class NonAsciiTest(unittest.TestCase):
88 88 @onlyif_unicode_paths
89 89 def test_nonascii_path(self):
90 90 # Non-ascii directory name as well.
91 91 with TemporaryDirectory(suffix=u'Γ©') as td:
92 92 fname = os.path.join(td, u"fooΓ©.py")
93 93 with open(fname, "w", encoding="utf-8") as f:
94 94 f.write(file_1)
95 95
96 96 with prepended_to_syspath(td):
97 97 ip.run_cell("import foo")
98 98
99 99 with tt.AssertPrints("ZeroDivisionError"):
100 100 ip.run_cell("foo.f()")
101 101
102 102 def test_iso8859_5(self):
103 103 with TemporaryDirectory() as td:
104 104 fname = os.path.join(td, 'dfghjkl.py')
105 105
106 106 with io.open(fname, 'w', encoding='iso-8859-5') as f:
107 107 f.write(iso_8859_5_file)
108 108
109 109 with prepended_to_syspath(td):
110 110 ip.run_cell("from dfghjkl import fail")
111 111
112 112 with tt.AssertPrints("ZeroDivisionError"):
113 113 with tt.AssertPrints(u'Π΄Π±Π˜Π–', suppress=False):
114 114 ip.run_cell('fail()')
115 115
116 116 def test_nonascii_msg(self):
117 117 cell = u"raise Exception('Γ©')"
118 118 expected = u"Exception('Γ©')"
119 119 ip.run_cell("%xmode plain")
120 120 with tt.AssertPrints(expected):
121 121 ip.run_cell(cell)
122 122
123 123 ip.run_cell("%xmode verbose")
124 124 with tt.AssertPrints(expected):
125 125 ip.run_cell(cell)
126 126
127 127 ip.run_cell("%xmode context")
128 128 with tt.AssertPrints(expected):
129 129 ip.run_cell(cell)
130 130
131 131 ip.run_cell("%xmode minimal")
132 132 with tt.AssertPrints(u"Exception: Γ©"):
133 133 ip.run_cell(cell)
134 134
135 135 # Put this back into Context mode for later tests.
136 136 ip.run_cell("%xmode context")
137 137
138 138 class NestedGenExprTestCase(unittest.TestCase):
139 139 """
140 140 Regression test for the following issues:
141 141 https://github.com/ipython/ipython/issues/8293
142 142 https://github.com/ipython/ipython/issues/8205
143 143 """
144 144 def test_nested_genexpr(self):
145 145 code = dedent(
146 146 """\
147 147 class SpecificException(Exception):
148 148 pass
149 149
150 150 def foo(x):
151 151 raise SpecificException("Success!")
152 152
153 153 sum(sum(foo(x) for _ in [0]) for x in [0])
154 154 """
155 155 )
156 156 with tt.AssertPrints('SpecificException: Success!', suppress=False):
157 157 ip.run_cell(code)
158 158
159 159
160 160 indentationerror_file = """if True:
161 161 zoom()
162 162 """
163 163
164 164 class IndentationErrorTest(unittest.TestCase):
165 165 def test_indentationerror_shows_line(self):
166 166 # See issue gh-2398
167 167 with tt.AssertPrints("IndentationError"):
168 168 with tt.AssertPrints("zoom()", suppress=False):
169 169 ip.run_cell(indentationerror_file)
170 170
171 171 with TemporaryDirectory() as td:
172 172 fname = os.path.join(td, "foo.py")
173 173 with open(fname, "w", encoding="utf-8") as f:
174 174 f.write(indentationerror_file)
175 175
176 176 with tt.AssertPrints("IndentationError"):
177 177 with tt.AssertPrints("zoom()", suppress=False):
178 ip.magic('run %s' % fname)
178 ip.run_line_magic("run", fname)
179
179 180
180 181 @skip_without("pandas")
181 182 def test_dynamic_code():
182 183 code = """
183 184 import pandas
184 185 df = pandas.DataFrame([])
185 186
186 187 # Important: only fails inside of an "exec" call:
187 188 exec("df.foobarbaz()")
188 189 """
189 190
190 191 with tt.AssertPrints("Could not get source"):
191 192 ip.run_cell(code)
192 193
193 194
194 195 se_file_1 = """1
195 196 2
196 197 7/
197 198 """
198 199
199 200 se_file_2 = """7/
200 201 """
201 202
202 203 class SyntaxErrorTest(unittest.TestCase):
203 204
204 205 def test_syntaxerror_no_stacktrace_at_compile_time(self):
205 206 syntax_error_at_compile_time = """
206 207 def foo():
207 208 ..
208 209 """
209 210 with tt.AssertPrints("SyntaxError"):
210 211 ip.run_cell(syntax_error_at_compile_time)
211 212
212 213 with tt.AssertNotPrints("foo()"):
213 214 ip.run_cell(syntax_error_at_compile_time)
214 215
215 216 def test_syntaxerror_stacktrace_when_running_compiled_code(self):
216 217 syntax_error_at_runtime = """
217 218 def foo():
218 219 eval("..")
219 220
220 221 def bar():
221 222 foo()
222 223
223 224 bar()
224 225 """
225 226 with tt.AssertPrints("SyntaxError"):
226 227 ip.run_cell(syntax_error_at_runtime)
227 228 # Assert syntax error during runtime generate stacktrace
228 229 with tt.AssertPrints(["foo()", "bar()"]):
229 230 ip.run_cell(syntax_error_at_runtime)
230 231 del ip.user_ns['bar']
231 232 del ip.user_ns['foo']
232 233
233 234 def test_changing_py_file(self):
234 235 with TemporaryDirectory() as td:
235 236 fname = os.path.join(td, "foo.py")
236 237 with open(fname, "w", encoding="utf-8") as f:
237 238 f.write(se_file_1)
238 239
239 240 with tt.AssertPrints(["7/", "SyntaxError"]):
240 ip.magic("run " + fname)
241 ip.run_line_magic("run", fname)
241 242
242 243 # Modify the file
243 244 with open(fname, "w", encoding="utf-8") as f:
244 245 f.write(se_file_2)
245 246
246 247 # The SyntaxError should point to the correct line
247 248 with tt.AssertPrints(["7/", "SyntaxError"]):
248 ip.magic("run " + fname)
249 ip.run_line_magic("run", fname)
249 250
250 251 def test_non_syntaxerror(self):
251 252 # SyntaxTB may be called with an error other than a SyntaxError
252 253 # See e.g. gh-4361
253 254 try:
254 255 raise ValueError('QWERTY')
255 256 except ValueError:
256 257 with tt.AssertPrints('QWERTY'):
257 258 ip.showsyntaxerror()
258 259
259 260 import sys
260 261
261 262 if platform.python_implementation() != "PyPy":
262 263 """
263 264 New 3.9 Pgen Parser does not raise Memory error, except on failed malloc.
264 265 """
265 266 class MemoryErrorTest(unittest.TestCase):
266 267 def test_memoryerror(self):
267 268 memoryerror_code = "(" * 200 + ")" * 200
268 269 ip.run_cell(memoryerror_code)
269 270
270 271
271 272 class Python3ChainedExceptionsTest(unittest.TestCase):
272 273 DIRECT_CAUSE_ERROR_CODE = """
273 274 try:
274 275 x = 1 + 2
275 276 print(not_defined_here)
276 277 except Exception as e:
277 278 x += 55
278 279 x - 1
279 280 y = {}
280 281 raise KeyError('uh') from e
281 282 """
282 283
283 284 EXCEPTION_DURING_HANDLING_CODE = """
284 285 try:
285 286 x = 1 + 2
286 287 print(not_defined_here)
287 288 except Exception as e:
288 289 x += 55
289 290 x - 1
290 291 y = {}
291 292 raise KeyError('uh')
292 293 """
293 294
294 295 SUPPRESS_CHAINING_CODE = """
295 296 try:
296 297 1/0
297 298 except Exception:
298 299 raise ValueError("Yikes") from None
299 300 """
300 301
301 302 SYS_EXIT_WITH_CONTEXT_CODE = """
302 303 try:
303 304 1/0
304 305 except Exception as e:
305 306 raise SystemExit(1)
306 307 """
307 308
308 309 def test_direct_cause_error(self):
309 310 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
310 311 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
311 312
312 313 def test_exception_during_handling_error(self):
313 314 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
314 315 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
315 316
316 317 def test_sysexit_while_handling_error(self):
317 318 with tt.AssertPrints(["SystemExit", "to see the full traceback"]):
318 319 with tt.AssertNotPrints(["another exception"], suppress=False):
319 320 ip.run_cell(self.SYS_EXIT_WITH_CONTEXT_CODE)
320 321
321 322 def test_suppress_exception_chaining(self):
322 323 with tt.AssertNotPrints("ZeroDivisionError"), \
323 324 tt.AssertPrints("ValueError", suppress=False):
324 325 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
325 326
326 327 def test_plain_direct_cause_error(self):
327 328 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
328 329 ip.run_cell("%xmode Plain")
329 330 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
330 331 ip.run_cell("%xmode Verbose")
331 332
332 333 def test_plain_exception_during_handling_error(self):
333 334 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
334 335 ip.run_cell("%xmode Plain")
335 336 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
336 337 ip.run_cell("%xmode Verbose")
337 338
338 339 def test_plain_suppress_exception_chaining(self):
339 340 with tt.AssertNotPrints("ZeroDivisionError"), \
340 341 tt.AssertPrints("ValueError", suppress=False):
341 342 ip.run_cell("%xmode Plain")
342 343 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
343 344 ip.run_cell("%xmode Verbose")
344 345
345 346
346 347 class RecursionTest(unittest.TestCase):
347 348 DEFINITIONS = """
348 349 def non_recurs():
349 350 1/0
350 351
351 352 def r1():
352 353 r1()
353 354
354 355 def r3a():
355 356 r3b()
356 357
357 358 def r3b():
358 359 r3c()
359 360
360 361 def r3c():
361 362 r3a()
362 363
363 364 def r3o1():
364 365 r3a()
365 366
366 367 def r3o2():
367 368 r3o1()
368 369 """
369 370 def setUp(self):
370 371 ip.run_cell(self.DEFINITIONS)
371 372
372 373 def test_no_recursion(self):
373 374 with tt.AssertNotPrints("skipping similar frames"):
374 375 ip.run_cell("non_recurs()")
375 376
376 377 @recursionlimit(200)
377 378 def test_recursion_one_frame(self):
378 379 with tt.AssertPrints(re.compile(
379 380 r"\[\.\.\. skipping similar frames: r1 at line 5 \(\d{2,3} times\)\]")
380 381 ):
381 382 ip.run_cell("r1()")
382 383
383 384 @recursionlimit(160)
384 385 def test_recursion_three_frames(self):
385 386 with tt.AssertPrints("[... skipping similar frames: "), \
386 387 tt.AssertPrints(re.compile(r"r3a at line 8 \(\d{2} times\)"), suppress=False), \
387 388 tt.AssertPrints(re.compile(r"r3b at line 11 \(\d{2} times\)"), suppress=False), \
388 389 tt.AssertPrints(re.compile(r"r3c at line 14 \(\d{2} times\)"), suppress=False):
389 390 ip.run_cell("r3o2()")
390 391
391 392
392 393 class PEP678NotesReportingTest(unittest.TestCase):
393 394 ERROR_WITH_NOTE = """
394 395 try:
395 396 raise AssertionError("Message")
396 397 except Exception as e:
397 398 try:
398 399 e.add_note("This is a PEP-678 note.")
399 400 except AttributeError: # Python <= 3.10
400 401 e.__notes__ = ("This is a PEP-678 note.",)
401 402 raise
402 403 """
403 404
404 405 def test_verbose_reports_notes(self):
405 406 with tt.AssertPrints(["AssertionError", "Message", "This is a PEP-678 note."]):
406 407 ip.run_cell(self.ERROR_WITH_NOTE)
407 408
408 409 def test_plain_reports_notes(self):
409 410 with tt.AssertPrints(["AssertionError", "Message", "This is a PEP-678 note."]):
410 411 ip.run_cell("%xmode Plain")
411 412 ip.run_cell(self.ERROR_WITH_NOTE)
412 413 ip.run_cell("%xmode Verbose")
413 414
414 415
415 416 #----------------------------------------------------------------------------
416 417
417 418 # module testing (minimal)
418 419 def test_handlers():
419 420 def spam(c, d_e):
420 421 (d, e) = d_e
421 422 x = c + d
422 423 y = c * d
423 424 foo(x, y)
424 425
425 426 def foo(a, b, bar=1):
426 427 eggs(a, b + bar)
427 428
428 429 def eggs(f, g, z=globals()):
429 430 h = f + g
430 431 i = f - g
431 432 return h / i
432 433
433 434 buff = io.StringIO()
434 435
435 436 buff.write('')
436 437 buff.write('*** Before ***')
437 438 try:
438 439 buff.write(spam(1, (2, 3)))
439 440 except:
440 441 traceback.print_exc(file=buff)
441 442
442 443 handler = ColorTB(ostream=buff)
443 444 buff.write('*** ColorTB ***')
444 445 try:
445 446 buff.write(spam(1, (2, 3)))
446 447 except:
447 448 handler(*sys.exc_info())
448 449 buff.write('')
449 450
450 451 handler = VerboseTB(ostream=buff)
451 452 buff.write('*** VerboseTB ***')
452 453 try:
453 454 buff.write(spam(1, (2, 3)))
454 455 except:
455 456 handler(*sys.exc_info())
456 457 buff.write('')
General Comments 0
You need to be logged in to leave comments. Login now