##// END OF EJS Templates
Display Greek small letter mu (#14426)...
M Bussonnier -
r28758:810faec9 merge
parent child Browse files
Show More
@@ -1,1614 +1,1614 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Implementation of execution-related magic functions."""
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7
8 8 import ast
9 9 import bdb
10 10 import builtins as builtin_mod
11 11 import copy
12 12 import cProfile as profile
13 13 import gc
14 14 import itertools
15 15 import math
16 16 import os
17 17 import pstats
18 18 import re
19 19 import shlex
20 20 import sys
21 21 import time
22 22 import timeit
23 23 from typing import Dict, Any
24 24 from ast import (
25 25 Assign,
26 26 Call,
27 27 Expr,
28 28 Load,
29 29 Module,
30 30 Name,
31 31 NodeTransformer,
32 32 Store,
33 33 parse,
34 34 unparse,
35 35 )
36 36 from io import StringIO
37 37 from logging import error
38 38 from pathlib import Path
39 39 from pdb import Restart
40 40 from textwrap import dedent, indent
41 41 from warnings import warn
42 42
43 43 from IPython.core import magic_arguments, oinspect, page
44 44 from IPython.core.displayhook import DisplayHook
45 45 from IPython.core.error import UsageError
46 46 from IPython.core.macro import Macro
47 47 from IPython.core.magic import (
48 48 Magics,
49 49 cell_magic,
50 50 line_cell_magic,
51 51 line_magic,
52 52 magics_class,
53 53 needs_local_scope,
54 54 no_var_expand,
55 55 on_off,
56 56 output_can_be_silenced,
57 57 )
58 58 from IPython.testing.skipdoctest import skip_doctest
59 59 from IPython.utils.capture import capture_output
60 60 from IPython.utils.contexts import preserve_keys
61 61 from IPython.utils.ipstruct import Struct
62 62 from IPython.utils.module_paths import find_mod
63 63 from IPython.utils.path import get_py_filename, shellglob
64 64 from IPython.utils.timing import clock, clock2
65 65 from IPython.core.magics.ast_mod import ReplaceCodeTransformer
66 66
67 67 #-----------------------------------------------------------------------------
68 68 # Magic implementation classes
69 69 #-----------------------------------------------------------------------------
70 70
71 71
72 72 class TimeitResult(object):
73 73 """
74 74 Object returned by the timeit magic with info about the run.
75 75
76 76 Contains the following attributes :
77 77
78 78 loops: (int) number of loops done per measurement
79 79 repeat: (int) number of times the measurement has been repeated
80 80 best: (float) best execution time / number
81 81 all_runs: (list of float) execution time of each run (in s)
82 82 compile_time: (float) time of statement compilation (s)
83 83
84 84 """
85 85 def __init__(self, loops, repeat, best, worst, all_runs, compile_time, precision):
86 86 self.loops = loops
87 87 self.repeat = repeat
88 88 self.best = best
89 89 self.worst = worst
90 90 self.all_runs = all_runs
91 91 self.compile_time = compile_time
92 92 self._precision = precision
93 93 self.timings = [ dt / self.loops for dt in all_runs]
94 94
95 95 @property
96 96 def average(self):
97 97 return math.fsum(self.timings) / len(self.timings)
98 98
99 99 @property
100 100 def stdev(self):
101 101 mean = self.average
102 102 return (math.fsum([(x - mean) ** 2 for x in self.timings]) / len(self.timings)) ** 0.5
103 103
104 104 def __str__(self):
105 105 pm = '+-'
106 106 if hasattr(sys.stdout, 'encoding') and sys.stdout.encoding:
107 107 try:
108 108 u'\xb1'.encode(sys.stdout.encoding)
109 109 pm = u'\xb1'
110 110 except:
111 111 pass
112 112 return "{mean} {pm} {std} per loop (mean {pm} std. dev. of {runs} run{run_plural}, {loops:,} loop{loop_plural} each)".format(
113 113 pm=pm,
114 114 runs=self.repeat,
115 115 loops=self.loops,
116 116 loop_plural="" if self.loops == 1 else "s",
117 117 run_plural="" if self.repeat == 1 else "s",
118 118 mean=_format_time(self.average, self._precision),
119 119 std=_format_time(self.stdev, self._precision),
120 120 )
121 121
122 122 def _repr_pretty_(self, p , cycle):
123 123 unic = self.__str__()
124 124 p.text(u'<TimeitResult : '+unic+u'>')
125 125
126 126
127 127 class TimeitTemplateFiller(ast.NodeTransformer):
128 128 """Fill in the AST template for timing execution.
129 129
130 130 This is quite closely tied to the template definition, which is in
131 131 :meth:`ExecutionMagics.timeit`.
132 132 """
133 133 def __init__(self, ast_setup, ast_stmt):
134 134 self.ast_setup = ast_setup
135 135 self.ast_stmt = ast_stmt
136 136
137 137 def visit_FunctionDef(self, node):
138 138 "Fill in the setup statement"
139 139 self.generic_visit(node)
140 140 if node.name == "inner":
141 141 node.body[:1] = self.ast_setup.body
142 142
143 143 return node
144 144
145 145 def visit_For(self, node):
146 146 "Fill in the statement to be timed"
147 147 if getattr(getattr(node.body[0], 'value', None), 'id', None) == 'stmt':
148 148 node.body = self.ast_stmt.body
149 149 return node
150 150
151 151
152 152 class Timer(timeit.Timer):
153 153 """Timer class that explicitly uses self.inner
154 154
155 155 which is an undocumented implementation detail of CPython,
156 156 not shared by PyPy.
157 157 """
158 158 # Timer.timeit copied from CPython 3.4.2
159 159 def timeit(self, number=timeit.default_number):
160 160 """Time 'number' executions of the main statement.
161 161
162 162 To be precise, this executes the setup statement once, and
163 163 then returns the time it takes to execute the main statement
164 164 a number of times, as a float measured in seconds. The
165 165 argument is the number of times through the loop, defaulting
166 166 to one million. The main statement, the setup statement and
167 167 the timer function to be used are passed to the constructor.
168 168 """
169 169 it = itertools.repeat(None, number)
170 170 gcold = gc.isenabled()
171 171 gc.disable()
172 172 try:
173 173 timing = self.inner(it, self.timer)
174 174 finally:
175 175 if gcold:
176 176 gc.enable()
177 177 return timing
178 178
179 179
180 180 @magics_class
181 181 class ExecutionMagics(Magics):
182 182 """Magics related to code execution, debugging, profiling, etc."""
183 183
184 184 _transformers: Dict[str, Any] = {}
185 185
186 186 def __init__(self, shell):
187 187 super(ExecutionMagics, self).__init__(shell)
188 188 # Default execution function used to actually run user code.
189 189 self.default_runner = None
190 190
191 191 @skip_doctest
192 192 @no_var_expand
193 193 @line_cell_magic
194 194 def prun(self, parameter_s='', cell=None):
195 195
196 196 """Run a statement through the python code profiler.
197 197
198 198 Usage, in line mode:
199 199 %prun [options] statement
200 200
201 201 Usage, in cell mode:
202 202 %%prun [options] [statement]
203 203 code...
204 204 code...
205 205
206 206 In cell mode, the additional code lines are appended to the (possibly
207 207 empty) statement in the first line. Cell mode allows you to easily
208 208 profile multiline blocks without having to put them in a separate
209 209 function.
210 210
211 211 The given statement (which doesn't require quote marks) is run via the
212 212 python profiler in a manner similar to the profile.run() function.
213 213 Namespaces are internally managed to work correctly; profile.run
214 214 cannot be used in IPython because it makes certain assumptions about
215 215 namespaces which do not hold under IPython.
216 216
217 217 Options:
218 218
219 219 -l <limit>
220 220 you can place restrictions on what or how much of the
221 221 profile gets printed. The limit value can be:
222 222
223 223 * A string: only information for function names containing this string
224 224 is printed.
225 225
226 226 * An integer: only these many lines are printed.
227 227
228 228 * A float (between 0 and 1): this fraction of the report is printed
229 229 (for example, use a limit of 0.4 to see the topmost 40% only).
230 230
231 231 You can combine several limits with repeated use of the option. For
232 232 example, ``-l __init__ -l 5`` will print only the topmost 5 lines of
233 233 information about class constructors.
234 234
235 235 -r
236 236 return the pstats.Stats object generated by the profiling. This
237 237 object has all the information about the profile in it, and you can
238 238 later use it for further analysis or in other functions.
239 239
240 240 -s <key>
241 241 sort profile by given key. You can provide more than one key
242 242 by using the option several times: '-s key1 -s key2 -s key3...'. The
243 243 default sorting key is 'time'.
244 244
245 245 The following is copied verbatim from the profile documentation
246 246 referenced below:
247 247
248 248 When more than one key is provided, additional keys are used as
249 249 secondary criteria when the there is equality in all keys selected
250 250 before them.
251 251
252 252 Abbreviations can be used for any key names, as long as the
253 253 abbreviation is unambiguous. The following are the keys currently
254 254 defined:
255 255
256 256 ============ =====================
257 257 Valid Arg Meaning
258 258 ============ =====================
259 259 "calls" call count
260 260 "cumulative" cumulative time
261 261 "file" file name
262 262 "module" file name
263 263 "pcalls" primitive call count
264 264 "line" line number
265 265 "name" function name
266 266 "nfl" name/file/line
267 267 "stdname" standard name
268 268 "time" internal time
269 269 ============ =====================
270 270
271 271 Note that all sorts on statistics are in descending order (placing
272 272 most time consuming items first), where as name, file, and line number
273 273 searches are in ascending order (i.e., alphabetical). The subtle
274 274 distinction between "nfl" and "stdname" is that the standard name is a
275 275 sort of the name as printed, which means that the embedded line
276 276 numbers get compared in an odd way. For example, lines 3, 20, and 40
277 277 would (if the file names were the same) appear in the string order
278 278 "20" "3" and "40". In contrast, "nfl" does a numeric compare of the
279 279 line numbers. In fact, sort_stats("nfl") is the same as
280 280 sort_stats("name", "file", "line").
281 281
282 282 -T <filename>
283 283 save profile results as shown on screen to a text
284 284 file. The profile is still shown on screen.
285 285
286 286 -D <filename>
287 287 save (via dump_stats) profile statistics to given
288 288 filename. This data is in a format understood by the pstats module, and
289 289 is generated by a call to the dump_stats() method of profile
290 290 objects. The profile is still shown on screen.
291 291
292 292 -q
293 293 suppress output to the pager. Best used with -T and/or -D above.
294 294
295 295 If you want to run complete programs under the profiler's control, use
296 296 ``%run -p [prof_opts] filename.py [args to program]`` where prof_opts
297 297 contains profiler specific options as described here.
298 298
299 299 You can read the complete documentation for the profile module with::
300 300
301 301 In [1]: import profile; profile.help()
302 302
303 303 .. versionchanged:: 7.3
304 304 User variables are no longer expanded,
305 305 the magic line is always left unmodified.
306 306
307 307 """
308 308 opts, arg_str = self.parse_options(parameter_s, 'D:l:rs:T:q',
309 309 list_all=True, posix=False)
310 310 if cell is not None:
311 311 arg_str += '\n' + cell
312 312 arg_str = self.shell.transform_cell(arg_str)
313 313 return self._run_with_profiler(arg_str, opts, self.shell.user_ns)
314 314
315 315 def _run_with_profiler(self, code, opts, namespace):
316 316 """
317 317 Run `code` with profiler. Used by ``%prun`` and ``%run -p``.
318 318
319 319 Parameters
320 320 ----------
321 321 code : str
322 322 Code to be executed.
323 323 opts : Struct
324 324 Options parsed by `self.parse_options`.
325 325 namespace : dict
326 326 A dictionary for Python namespace (e.g., `self.shell.user_ns`).
327 327
328 328 """
329 329
330 330 # Fill default values for unspecified options:
331 331 opts.merge(Struct(D=[''], l=[], s=['time'], T=['']))
332 332
333 333 prof = profile.Profile()
334 334 try:
335 335 prof = prof.runctx(code, namespace, namespace)
336 336 sys_exit = ''
337 337 except SystemExit:
338 338 sys_exit = """*** SystemExit exception caught in code being profiled."""
339 339
340 340 stats = pstats.Stats(prof).strip_dirs().sort_stats(*opts.s)
341 341
342 342 lims = opts.l
343 343 if lims:
344 344 lims = [] # rebuild lims with ints/floats/strings
345 345 for lim in opts.l:
346 346 try:
347 347 lims.append(int(lim))
348 348 except ValueError:
349 349 try:
350 350 lims.append(float(lim))
351 351 except ValueError:
352 352 lims.append(lim)
353 353
354 354 # Trap output.
355 355 stdout_trap = StringIO()
356 356 stats_stream = stats.stream
357 357 try:
358 358 stats.stream = stdout_trap
359 359 stats.print_stats(*lims)
360 360 finally:
361 361 stats.stream = stats_stream
362 362
363 363 output = stdout_trap.getvalue()
364 364 output = output.rstrip()
365 365
366 366 if 'q' not in opts:
367 367 page.page(output)
368 368 print(sys_exit, end=' ')
369 369
370 370 dump_file = opts.D[0]
371 371 text_file = opts.T[0]
372 372 if dump_file:
373 373 prof.dump_stats(dump_file)
374 374 print(
375 375 f"\n*** Profile stats marshalled to file {repr(dump_file)}.{sys_exit}"
376 376 )
377 377 if text_file:
378 378 pfile = Path(text_file)
379 379 pfile.touch(exist_ok=True)
380 380 pfile.write_text(output, encoding="utf-8")
381 381
382 382 print(
383 383 f"\n*** Profile printout saved to text file {repr(text_file)}.{sys_exit}"
384 384 )
385 385
386 386 if 'r' in opts:
387 387 return stats
388 388
389 389 return None
390 390
391 391 @line_magic
392 392 def pdb(self, parameter_s=''):
393 393 """Control the automatic calling of the pdb interactive debugger.
394 394
395 395 Call as '%pdb on', '%pdb 1', '%pdb off' or '%pdb 0'. If called without
396 396 argument it works as a toggle.
397 397
398 398 When an exception is triggered, IPython can optionally call the
399 399 interactive pdb debugger after the traceback printout. %pdb toggles
400 400 this feature on and off.
401 401
402 402 The initial state of this feature is set in your configuration
403 403 file (the option is ``InteractiveShell.pdb``).
404 404
405 405 If you want to just activate the debugger AFTER an exception has fired,
406 406 without having to type '%pdb on' and rerunning your code, you can use
407 407 the %debug magic."""
408 408
409 409 par = parameter_s.strip().lower()
410 410
411 411 if par:
412 412 try:
413 413 new_pdb = {'off':0,'0':0,'on':1,'1':1}[par]
414 414 except KeyError:
415 415 print ('Incorrect argument. Use on/1, off/0, '
416 416 'or nothing for a toggle.')
417 417 return
418 418 else:
419 419 # toggle
420 420 new_pdb = not self.shell.call_pdb
421 421
422 422 # set on the shell
423 423 self.shell.call_pdb = new_pdb
424 424 print('Automatic pdb calling has been turned',on_off(new_pdb))
425 425
426 426 @magic_arguments.magic_arguments()
427 427 @magic_arguments.argument('--breakpoint', '-b', metavar='FILE:LINE',
428 428 help="""
429 429 Set break point at LINE in FILE.
430 430 """
431 431 )
432 432 @magic_arguments.argument('statement', nargs='*',
433 433 help="""
434 434 Code to run in debugger.
435 435 You can omit this in cell magic mode.
436 436 """
437 437 )
438 438 @no_var_expand
439 439 @line_cell_magic
440 440 @needs_local_scope
441 441 def debug(self, line="", cell=None, local_ns=None):
442 442 """Activate the interactive debugger.
443 443
444 444 This magic command support two ways of activating debugger.
445 445 One is to activate debugger before executing code. This way, you
446 446 can set a break point, to step through the code from the point.
447 447 You can use this mode by giving statements to execute and optionally
448 448 a breakpoint.
449 449
450 450 The other one is to activate debugger in post-mortem mode. You can
451 451 activate this mode simply running %debug without any argument.
452 452 If an exception has just occurred, this lets you inspect its stack
453 453 frames interactively. Note that this will always work only on the last
454 454 traceback that occurred, so you must call this quickly after an
455 455 exception that you wish to inspect has fired, because if another one
456 456 occurs, it clobbers the previous one.
457 457
458 458 If you want IPython to automatically do this on every exception, see
459 459 the %pdb magic for more details.
460 460
461 461 .. versionchanged:: 7.3
462 462 When running code, user variables are no longer expanded,
463 463 the magic line is always left unmodified.
464 464
465 465 """
466 466 args = magic_arguments.parse_argstring(self.debug, line)
467 467
468 468 if not (args.breakpoint or args.statement or cell):
469 469 self._debug_post_mortem()
470 470 elif not (args.breakpoint or cell):
471 471 # If there is no breakpoints, the line is just code to execute
472 472 self._debug_exec(line, None, local_ns)
473 473 else:
474 474 # Here we try to reconstruct the code from the output of
475 475 # parse_argstring. This might not work if the code has spaces
476 476 # For example this fails for `print("a b")`
477 477 code = "\n".join(args.statement)
478 478 if cell:
479 479 code += "\n" + cell
480 480 self._debug_exec(code, args.breakpoint, local_ns)
481 481
482 482 def _debug_post_mortem(self):
483 483 self.shell.debugger(force=True)
484 484
485 485 def _debug_exec(self, code, breakpoint, local_ns=None):
486 486 if breakpoint:
487 487 (filename, bp_line) = breakpoint.rsplit(':', 1)
488 488 bp_line = int(bp_line)
489 489 else:
490 490 (filename, bp_line) = (None, None)
491 491 self._run_with_debugger(
492 492 code, self.shell.user_ns, filename, bp_line, local_ns=local_ns
493 493 )
494 494
495 495 @line_magic
496 496 def tb(self, s):
497 497 """Print the last traceback.
498 498
499 499 Optionally, specify an exception reporting mode, tuning the
500 500 verbosity of the traceback. By default the currently-active exception
501 501 mode is used. See %xmode for changing exception reporting modes.
502 502
503 503 Valid modes: Plain, Context, Verbose, and Minimal.
504 504 """
505 505 interactive_tb = self.shell.InteractiveTB
506 506 if s:
507 507 # Switch exception reporting mode for this one call.
508 508 # Ensure it is switched back.
509 509 def xmode_switch_err(name):
510 510 warn('Error changing %s exception modes.\n%s' %
511 511 (name,sys.exc_info()[1]))
512 512
513 513 new_mode = s.strip().capitalize()
514 514 original_mode = interactive_tb.mode
515 515 try:
516 516 try:
517 517 interactive_tb.set_mode(mode=new_mode)
518 518 except Exception:
519 519 xmode_switch_err('user')
520 520 else:
521 521 self.shell.showtraceback()
522 522 finally:
523 523 interactive_tb.set_mode(mode=original_mode)
524 524 else:
525 525 self.shell.showtraceback()
526 526
527 527 @skip_doctest
528 528 @line_magic
529 529 def run(self, parameter_s='', runner=None,
530 530 file_finder=get_py_filename):
531 531 """Run the named file inside IPython as a program.
532 532
533 533 Usage::
534 534
535 535 %run [-n -i -e -G]
536 536 [( -t [-N<N>] | -d [-b<N>] | -p [profile options] )]
537 537 ( -m mod | filename ) [args]
538 538
539 539 The filename argument should be either a pure Python script (with
540 540 extension ``.py``), or a file with custom IPython syntax (such as
541 541 magics). If the latter, the file can be either a script with ``.ipy``
542 542 extension, or a Jupyter notebook with ``.ipynb`` extension. When running
543 543 a Jupyter notebook, the output from print statements and other
544 544 displayed objects will appear in the terminal (even matplotlib figures
545 545 will open, if a terminal-compliant backend is being used). Note that,
546 546 at the system command line, the ``jupyter run`` command offers similar
547 547 functionality for executing notebooks (albeit currently with some
548 548 differences in supported options).
549 549
550 550 Parameters after the filename are passed as command-line arguments to
551 551 the program (put in sys.argv). Then, control returns to IPython's
552 552 prompt.
553 553
554 554 This is similar to running at a system prompt ``python file args``,
555 555 but with the advantage of giving you IPython's tracebacks, and of
556 556 loading all variables into your interactive namespace for further use
557 557 (unless -p is used, see below).
558 558
559 559 The file is executed in a namespace initially consisting only of
560 560 ``__name__=='__main__'`` and sys.argv constructed as indicated. It thus
561 561 sees its environment as if it were being run as a stand-alone program
562 562 (except for sharing global objects such as previously imported
563 563 modules). But after execution, the IPython interactive namespace gets
564 564 updated with all variables defined in the program (except for __name__
565 565 and sys.argv). This allows for very convenient loading of code for
566 566 interactive work, while giving each program a 'clean sheet' to run in.
567 567
568 568 Arguments are expanded using shell-like glob match. Patterns
569 569 '*', '?', '[seq]' and '[!seq]' can be used. Additionally,
570 570 tilde '~' will be expanded into user's home directory. Unlike
571 571 real shells, quotation does not suppress expansions. Use
572 572 *two* back slashes (e.g. ``\\\\*``) to suppress expansions.
573 573 To completely disable these expansions, you can use -G flag.
574 574
575 575 On Windows systems, the use of single quotes `'` when specifying
576 576 a file is not supported. Use double quotes `"`.
577 577
578 578 Options:
579 579
580 580 -n
581 581 __name__ is NOT set to '__main__', but to the running file's name
582 582 without extension (as python does under import). This allows running
583 583 scripts and reloading the definitions in them without calling code
584 584 protected by an ``if __name__ == "__main__"`` clause.
585 585
586 586 -i
587 587 run the file in IPython's namespace instead of an empty one. This
588 588 is useful if you are experimenting with code written in a text editor
589 589 which depends on variables defined interactively.
590 590
591 591 -e
592 592 ignore sys.exit() calls or SystemExit exceptions in the script
593 593 being run. This is particularly useful if IPython is being used to
594 594 run unittests, which always exit with a sys.exit() call. In such
595 595 cases you are interested in the output of the test results, not in
596 596 seeing a traceback of the unittest module.
597 597
598 598 -t
599 599 print timing information at the end of the run. IPython will give
600 600 you an estimated CPU time consumption for your script, which under
601 601 Unix uses the resource module to avoid the wraparound problems of
602 602 time.clock(). Under Unix, an estimate of time spent on system tasks
603 603 is also given (for Windows platforms this is reported as 0.0).
604 604
605 605 If -t is given, an additional ``-N<N>`` option can be given, where <N>
606 606 must be an integer indicating how many times you want the script to
607 607 run. The final timing report will include total and per run results.
608 608
609 609 For example (testing the script uniq_stable.py)::
610 610
611 611 In [1]: run -t uniq_stable
612 612
613 613 IPython CPU timings (estimated):
614 614 User : 0.19597 s.
615 615 System: 0.0 s.
616 616
617 617 In [2]: run -t -N5 uniq_stable
618 618
619 619 IPython CPU timings (estimated):
620 620 Total runs performed: 5
621 621 Times : Total Per run
622 622 User : 0.910862 s, 0.1821724 s.
623 623 System: 0.0 s, 0.0 s.
624 624
625 625 -d
626 626 run your program under the control of pdb, the Python debugger.
627 627 This allows you to execute your program step by step, watch variables,
628 628 etc. Internally, what IPython does is similar to calling::
629 629
630 630 pdb.run('execfile("YOURFILENAME")')
631 631
632 632 with a breakpoint set on line 1 of your file. You can change the line
633 633 number for this automatic breakpoint to be <N> by using the -bN option
634 634 (where N must be an integer). For example::
635 635
636 636 %run -d -b40 myscript
637 637
638 638 will set the first breakpoint at line 40 in myscript.py. Note that
639 639 the first breakpoint must be set on a line which actually does
640 640 something (not a comment or docstring) for it to stop execution.
641 641
642 642 Or you can specify a breakpoint in a different file::
643 643
644 644 %run -d -b myotherfile.py:20 myscript
645 645
646 646 When the pdb debugger starts, you will see a (Pdb) prompt. You must
647 647 first enter 'c' (without quotes) to start execution up to the first
648 648 breakpoint.
649 649
650 650 Entering 'help' gives information about the use of the debugger. You
651 651 can easily see pdb's full documentation with "import pdb;pdb.help()"
652 652 at a prompt.
653 653
654 654 -p
655 655 run program under the control of the Python profiler module (which
656 656 prints a detailed report of execution times, function calls, etc).
657 657
658 658 You can pass other options after -p which affect the behavior of the
659 659 profiler itself. See the docs for %prun for details.
660 660
661 661 In this mode, the program's variables do NOT propagate back to the
662 662 IPython interactive namespace (because they remain in the namespace
663 663 where the profiler executes them).
664 664
665 665 Internally this triggers a call to %prun, see its documentation for
666 666 details on the options available specifically for profiling.
667 667
668 668 There is one special usage for which the text above doesn't apply:
669 669 if the filename ends with .ipy[nb], the file is run as ipython script,
670 670 just as if the commands were written on IPython prompt.
671 671
672 672 -m
673 673 specify module name to load instead of script path. Similar to
674 674 the -m option for the python interpreter. Use this option last if you
675 675 want to combine with other %run options. Unlike the python interpreter
676 676 only source modules are allowed no .pyc or .pyo files.
677 677 For example::
678 678
679 679 %run -m example
680 680
681 681 will run the example module.
682 682
683 683 -G
684 684 disable shell-like glob expansion of arguments.
685 685
686 686 """
687 687
688 688 # Logic to handle issue #3664
689 689 # Add '--' after '-m <module_name>' to ignore additional args passed to a module.
690 690 if '-m' in parameter_s and '--' not in parameter_s:
691 691 argv = shlex.split(parameter_s, posix=(os.name == 'posix'))
692 692 for idx, arg in enumerate(argv):
693 693 if arg and arg.startswith('-') and arg != '-':
694 694 if arg == '-m':
695 695 argv.insert(idx + 2, '--')
696 696 break
697 697 else:
698 698 # Positional arg, break
699 699 break
700 700 parameter_s = ' '.join(shlex.quote(arg) for arg in argv)
701 701
702 702 # get arguments and set sys.argv for program to be run.
703 703 opts, arg_lst = self.parse_options(parameter_s,
704 704 'nidtN:b:pD:l:rs:T:em:G',
705 705 mode='list', list_all=1)
706 706 if "m" in opts:
707 707 modulename = opts["m"][0]
708 708 modpath = find_mod(modulename)
709 709 if modpath is None:
710 710 msg = '%r is not a valid modulename on sys.path'%modulename
711 711 raise Exception(msg)
712 712 arg_lst = [modpath] + arg_lst
713 713 try:
714 714 fpath = None # initialize to make sure fpath is in scope later
715 715 fpath = arg_lst[0]
716 716 filename = file_finder(fpath)
717 717 except IndexError as e:
718 718 msg = 'you must provide at least a filename.'
719 719 raise Exception(msg) from e
720 720 except IOError as e:
721 721 try:
722 722 msg = str(e)
723 723 except UnicodeError:
724 724 msg = e.message
725 725 if os.name == 'nt' and re.match(r"^'.*'$",fpath):
726 726 warn('For Windows, use double quotes to wrap a filename: %run "mypath\\myfile.py"')
727 727 raise Exception(msg) from e
728 728 except TypeError:
729 729 if fpath in sys.meta_path:
730 730 filename = ""
731 731 else:
732 732 raise
733 733
734 734 if filename.lower().endswith(('.ipy', '.ipynb')):
735 735 with preserve_keys(self.shell.user_ns, '__file__'):
736 736 self.shell.user_ns['__file__'] = filename
737 737 self.shell.safe_execfile_ipy(filename, raise_exceptions=True)
738 738 return
739 739
740 740 # Control the response to exit() calls made by the script being run
741 741 exit_ignore = 'e' in opts
742 742
743 743 # Make sure that the running script gets a proper sys.argv as if it
744 744 # were run from a system shell.
745 745 save_argv = sys.argv # save it for later restoring
746 746
747 747 if 'G' in opts:
748 748 args = arg_lst[1:]
749 749 else:
750 750 # tilde and glob expansion
751 751 args = shellglob(map(os.path.expanduser, arg_lst[1:]))
752 752
753 753 sys.argv = [filename] + args # put in the proper filename
754 754
755 755 if 'n' in opts:
756 756 name = Path(filename).stem
757 757 else:
758 758 name = '__main__'
759 759
760 760 if 'i' in opts:
761 761 # Run in user's interactive namespace
762 762 prog_ns = self.shell.user_ns
763 763 __name__save = self.shell.user_ns['__name__']
764 764 prog_ns['__name__'] = name
765 765 main_mod = self.shell.user_module
766 766
767 767 # Since '%run foo' emulates 'python foo.py' at the cmd line, we must
768 768 # set the __file__ global in the script's namespace
769 769 # TK: Is this necessary in interactive mode?
770 770 prog_ns['__file__'] = filename
771 771 else:
772 772 # Run in a fresh, empty namespace
773 773
774 774 # The shell MUST hold a reference to prog_ns so after %run
775 775 # exits, the python deletion mechanism doesn't zero it out
776 776 # (leaving dangling references). See interactiveshell for details
777 777 main_mod = self.shell.new_main_mod(filename, name)
778 778 prog_ns = main_mod.__dict__
779 779
780 780 # pickle fix. See interactiveshell for an explanation. But we need to
781 781 # make sure that, if we overwrite __main__, we replace it at the end
782 782 main_mod_name = prog_ns['__name__']
783 783
784 784 if main_mod_name == '__main__':
785 785 restore_main = sys.modules['__main__']
786 786 else:
787 787 restore_main = False
788 788
789 789 # This needs to be undone at the end to prevent holding references to
790 790 # every single object ever created.
791 791 sys.modules[main_mod_name] = main_mod
792 792
793 793 if 'p' in opts or 'd' in opts:
794 794 if 'm' in opts:
795 795 code = 'run_module(modulename, prog_ns)'
796 796 code_ns = {
797 797 'run_module': self.shell.safe_run_module,
798 798 'prog_ns': prog_ns,
799 799 'modulename': modulename,
800 800 }
801 801 else:
802 802 if 'd' in opts:
803 803 # allow exceptions to raise in debug mode
804 804 code = 'execfile(filename, prog_ns, raise_exceptions=True)'
805 805 else:
806 806 code = 'execfile(filename, prog_ns)'
807 807 code_ns = {
808 808 'execfile': self.shell.safe_execfile,
809 809 'prog_ns': prog_ns,
810 810 'filename': get_py_filename(filename),
811 811 }
812 812
813 813 try:
814 814 stats = None
815 815 if 'p' in opts:
816 816 stats = self._run_with_profiler(code, opts, code_ns)
817 817 else:
818 818 if 'd' in opts:
819 819 bp_file, bp_line = parse_breakpoint(
820 820 opts.get('b', ['1'])[0], filename)
821 821 self._run_with_debugger(
822 822 code, code_ns, filename, bp_line, bp_file)
823 823 else:
824 824 if 'm' in opts:
825 825 def run():
826 826 self.shell.safe_run_module(modulename, prog_ns)
827 827 else:
828 828 if runner is None:
829 829 runner = self.default_runner
830 830 if runner is None:
831 831 runner = self.shell.safe_execfile
832 832
833 833 def run():
834 834 runner(filename, prog_ns, prog_ns,
835 835 exit_ignore=exit_ignore)
836 836
837 837 if 't' in opts:
838 838 # timed execution
839 839 try:
840 840 nruns = int(opts['N'][0])
841 841 if nruns < 1:
842 842 error('Number of runs must be >=1')
843 843 return
844 844 except (KeyError):
845 845 nruns = 1
846 846 self._run_with_timing(run, nruns)
847 847 else:
848 848 # regular execution
849 849 run()
850 850
851 851 if 'i' in opts:
852 852 self.shell.user_ns['__name__'] = __name__save
853 853 else:
854 854 # update IPython interactive namespace
855 855
856 856 # Some forms of read errors on the file may mean the
857 857 # __name__ key was never set; using pop we don't have to
858 858 # worry about a possible KeyError.
859 859 prog_ns.pop('__name__', None)
860 860
861 861 with preserve_keys(self.shell.user_ns, '__file__'):
862 862 self.shell.user_ns.update(prog_ns)
863 863 finally:
864 864 # It's a bit of a mystery why, but __builtins__ can change from
865 865 # being a module to becoming a dict missing some key data after
866 866 # %run. As best I can see, this is NOT something IPython is doing
867 867 # at all, and similar problems have been reported before:
868 868 # http://coding.derkeiler.com/Archive/Python/comp.lang.python/2004-10/0188.html
869 869 # Since this seems to be done by the interpreter itself, the best
870 870 # we can do is to at least restore __builtins__ for the user on
871 871 # exit.
872 872 self.shell.user_ns['__builtins__'] = builtin_mod
873 873
874 874 # Ensure key global structures are restored
875 875 sys.argv = save_argv
876 876 if restore_main:
877 877 sys.modules['__main__'] = restore_main
878 878 if '__mp_main__' in sys.modules:
879 879 sys.modules['__mp_main__'] = restore_main
880 880 else:
881 881 # Remove from sys.modules the reference to main_mod we'd
882 882 # added. Otherwise it will trap references to objects
883 883 # contained therein.
884 884 del sys.modules[main_mod_name]
885 885
886 886 return stats
887 887
888 888 def _run_with_debugger(
889 889 self, code, code_ns, filename=None, bp_line=None, bp_file=None, local_ns=None
890 890 ):
891 891 """
892 892 Run `code` in debugger with a break point.
893 893
894 894 Parameters
895 895 ----------
896 896 code : str
897 897 Code to execute.
898 898 code_ns : dict
899 899 A namespace in which `code` is executed.
900 900 filename : str
901 901 `code` is ran as if it is in `filename`.
902 902 bp_line : int, optional
903 903 Line number of the break point.
904 904 bp_file : str, optional
905 905 Path to the file in which break point is specified.
906 906 `filename` is used if not given.
907 907 local_ns : dict, optional
908 908 A local namespace in which `code` is executed.
909 909
910 910 Raises
911 911 ------
912 912 UsageError
913 913 If the break point given by `bp_line` is not valid.
914 914
915 915 """
916 916 deb = self.shell.InteractiveTB.pdb
917 917 if not deb:
918 918 self.shell.InteractiveTB.pdb = self.shell.InteractiveTB.debugger_cls()
919 919 deb = self.shell.InteractiveTB.pdb
920 920
921 921 # deb.checkline() fails if deb.curframe exists but is None; it can
922 922 # handle it not existing. https://github.com/ipython/ipython/issues/10028
923 923 if hasattr(deb, 'curframe'):
924 924 del deb.curframe
925 925
926 926 # reset Breakpoint state, which is moronically kept
927 927 # in a class
928 928 bdb.Breakpoint.next = 1
929 929 bdb.Breakpoint.bplist = {}
930 930 bdb.Breakpoint.bpbynumber = [None]
931 931 deb.clear_all_breaks()
932 932 if bp_line is not None:
933 933 # Set an initial breakpoint to stop execution
934 934 maxtries = 10
935 935 bp_file = bp_file or filename
936 936 checkline = deb.checkline(bp_file, bp_line)
937 937 if not checkline:
938 938 for bp in range(bp_line + 1, bp_line + maxtries + 1):
939 939 if deb.checkline(bp_file, bp):
940 940 break
941 941 else:
942 942 msg = ("\nI failed to find a valid line to set "
943 943 "a breakpoint\n"
944 944 "after trying up to line: %s.\n"
945 945 "Please set a valid breakpoint manually "
946 946 "with the -b option." % bp)
947 947 raise UsageError(msg)
948 948 # if we find a good linenumber, set the breakpoint
949 949 deb.do_break('%s:%s' % (bp_file, bp_line))
950 950
951 951 if filename:
952 952 # Mimic Pdb._runscript(...)
953 953 deb._wait_for_mainpyfile = True
954 954 deb.mainpyfile = deb.canonic(filename)
955 955
956 956 # Start file run
957 957 print("NOTE: Enter 'c' at the %s prompt to continue execution." % deb.prompt)
958 958 try:
959 959 if filename:
960 960 # save filename so it can be used by methods on the deb object
961 961 deb._exec_filename = filename
962 962 while True:
963 963 try:
964 964 trace = sys.gettrace()
965 965 deb.run(code, code_ns, local_ns)
966 966 except Restart:
967 967 print("Restarting")
968 968 if filename:
969 969 deb._wait_for_mainpyfile = True
970 970 deb.mainpyfile = deb.canonic(filename)
971 971 continue
972 972 else:
973 973 break
974 974 finally:
975 975 sys.settrace(trace)
976 976
977 977
978 978 except:
979 979 etype, value, tb = sys.exc_info()
980 980 # Skip three frames in the traceback: the %run one,
981 981 # one inside bdb.py, and the command-line typed by the
982 982 # user (run by exec in pdb itself).
983 983 self.shell.InteractiveTB(etype, value, tb, tb_offset=3)
984 984
985 985 @staticmethod
986 986 def _run_with_timing(run, nruns):
987 987 """
988 988 Run function `run` and print timing information.
989 989
990 990 Parameters
991 991 ----------
992 992 run : callable
993 993 Any callable object which takes no argument.
994 994 nruns : int
995 995 Number of times to execute `run`.
996 996
997 997 """
998 998 twall0 = time.perf_counter()
999 999 if nruns == 1:
1000 1000 t0 = clock2()
1001 1001 run()
1002 1002 t1 = clock2()
1003 1003 t_usr = t1[0] - t0[0]
1004 1004 t_sys = t1[1] - t0[1]
1005 1005 print("\nIPython CPU timings (estimated):")
1006 1006 print(" User : %10.2f s." % t_usr)
1007 1007 print(" System : %10.2f s." % t_sys)
1008 1008 else:
1009 1009 runs = range(nruns)
1010 1010 t0 = clock2()
1011 1011 for nr in runs:
1012 1012 run()
1013 1013 t1 = clock2()
1014 1014 t_usr = t1[0] - t0[0]
1015 1015 t_sys = t1[1] - t0[1]
1016 1016 print("\nIPython CPU timings (estimated):")
1017 1017 print("Total runs performed:", nruns)
1018 1018 print(" Times : %10s %10s" % ('Total', 'Per run'))
1019 1019 print(" User : %10.2f s, %10.2f s." % (t_usr, t_usr / nruns))
1020 1020 print(" System : %10.2f s, %10.2f s." % (t_sys, t_sys / nruns))
1021 1021 twall1 = time.perf_counter()
1022 1022 print("Wall time: %10.2f s." % (twall1 - twall0))
1023 1023
1024 1024 @skip_doctest
1025 1025 @no_var_expand
1026 1026 @line_cell_magic
1027 1027 @needs_local_scope
1028 1028 def timeit(self, line='', cell=None, local_ns=None):
1029 1029 """Time execution of a Python statement or expression
1030 1030
1031 1031 Usage, in line mode:
1032 1032 %timeit [-n<N> -r<R> [-t|-c] -q -p<P> -o] statement
1033 1033 or in cell mode:
1034 1034 %%timeit [-n<N> -r<R> [-t|-c] -q -p<P> -o] setup_code
1035 1035 code
1036 1036 code...
1037 1037
1038 1038 Time execution of a Python statement or expression using the timeit
1039 1039 module. This function can be used both as a line and cell magic:
1040 1040
1041 1041 - In line mode you can time a single-line statement (though multiple
1042 1042 ones can be chained with using semicolons).
1043 1043
1044 1044 - In cell mode, the statement in the first line is used as setup code
1045 1045 (executed but not timed) and the body of the cell is timed. The cell
1046 1046 body has access to any variables created in the setup code.
1047 1047
1048 1048 Options:
1049 1049 -n<N>: execute the given statement <N> times in a loop. If <N> is not
1050 1050 provided, <N> is determined so as to get sufficient accuracy.
1051 1051
1052 1052 -r<R>: number of repeats <R>, each consisting of <N> loops, and take the
1053 1053 average result.
1054 1054 Default: 7
1055 1055
1056 1056 -t: use time.time to measure the time, which is the default on Unix.
1057 1057 This function measures wall time.
1058 1058
1059 1059 -c: use time.clock to measure the time, which is the default on
1060 1060 Windows and measures wall time. On Unix, resource.getrusage is used
1061 1061 instead and returns the CPU user time.
1062 1062
1063 1063 -p<P>: use a precision of <P> digits to display the timing result.
1064 1064 Default: 3
1065 1065
1066 1066 -q: Quiet, do not print result.
1067 1067
1068 1068 -o: return a TimeitResult that can be stored in a variable to inspect
1069 1069 the result in more details.
1070 1070
1071 1071 .. versionchanged:: 7.3
1072 1072 User variables are no longer expanded,
1073 1073 the magic line is always left unmodified.
1074 1074
1075 1075 Examples
1076 1076 --------
1077 1077 ::
1078 1078
1079 1079 In [1]: %timeit pass
1080 1080 8.26 ns ± 0.12 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each)
1081 1081
1082 1082 In [2]: u = None
1083 1083
1084 1084 In [3]: %timeit u is None
1085 1085 29.9 ns ± 0.643 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
1086 1086
1087 1087 In [4]: %timeit -r 4 u == None
1088 1088
1089 1089 In [5]: import time
1090 1090
1091 1091 In [6]: %timeit -n1 time.sleep(2)
1092 1092
1093 1093 The times reported by %timeit will be slightly higher than those
1094 1094 reported by the timeit.py script when variables are accessed. This is
1095 1095 due to the fact that %timeit executes the statement in the namespace
1096 1096 of the shell, compared with timeit.py, which uses a single setup
1097 1097 statement to import function or create variables. Generally, the bias
1098 1098 does not matter as long as results from timeit.py are not mixed with
1099 1099 those from %timeit."""
1100 1100
1101 1101 opts, stmt = self.parse_options(
1102 1102 line, "n:r:tcp:qo", posix=False, strict=False, preserve_non_opts=True
1103 1103 )
1104 1104 if stmt == "" and cell is None:
1105 1105 return
1106 1106
1107 1107 timefunc = timeit.default_timer
1108 1108 number = int(getattr(opts, "n", 0))
1109 1109 default_repeat = 7 if timeit.default_repeat < 7 else timeit.default_repeat
1110 1110 repeat = int(getattr(opts, "r", default_repeat))
1111 1111 precision = int(getattr(opts, "p", 3))
1112 1112 quiet = 'q' in opts
1113 1113 return_result = 'o' in opts
1114 1114 if hasattr(opts, "t"):
1115 1115 timefunc = time.time
1116 1116 if hasattr(opts, "c"):
1117 1117 timefunc = clock
1118 1118
1119 1119 timer = Timer(timer=timefunc)
1120 1120 # this code has tight coupling to the inner workings of timeit.Timer,
1121 1121 # but is there a better way to achieve that the code stmt has access
1122 1122 # to the shell namespace?
1123 1123 transform = self.shell.transform_cell
1124 1124
1125 1125 if cell is None:
1126 1126 # called as line magic
1127 1127 ast_setup = self.shell.compile.ast_parse("pass")
1128 1128 ast_stmt = self.shell.compile.ast_parse(transform(stmt))
1129 1129 else:
1130 1130 ast_setup = self.shell.compile.ast_parse(transform(stmt))
1131 1131 ast_stmt = self.shell.compile.ast_parse(transform(cell))
1132 1132
1133 1133 ast_setup = self.shell.transform_ast(ast_setup)
1134 1134 ast_stmt = self.shell.transform_ast(ast_stmt)
1135 1135
1136 1136 # Check that these compile to valid Python code *outside* the timer func
1137 1137 # Invalid code may become valid when put inside the function & loop,
1138 1138 # which messes up error messages.
1139 1139 # https://github.com/ipython/ipython/issues/10636
1140 1140 self.shell.compile(ast_setup, "<magic-timeit-setup>", "exec")
1141 1141 self.shell.compile(ast_stmt, "<magic-timeit-stmt>", "exec")
1142 1142
1143 1143 # This codestring is taken from timeit.template - we fill it in as an
1144 1144 # AST, so that we can apply our AST transformations to the user code
1145 1145 # without affecting the timing code.
1146 1146 timeit_ast_template = ast.parse('def inner(_it, _timer):\n'
1147 1147 ' setup\n'
1148 1148 ' _t0 = _timer()\n'
1149 1149 ' for _i in _it:\n'
1150 1150 ' stmt\n'
1151 1151 ' _t1 = _timer()\n'
1152 1152 ' return _t1 - _t0\n')
1153 1153
1154 1154 timeit_ast = TimeitTemplateFiller(ast_setup, ast_stmt).visit(timeit_ast_template)
1155 1155 timeit_ast = ast.fix_missing_locations(timeit_ast)
1156 1156
1157 1157 # Track compilation time so it can be reported if too long
1158 1158 # Minimum time above which compilation time will be reported
1159 1159 tc_min = 0.1
1160 1160
1161 1161 t0 = clock()
1162 1162 code = self.shell.compile(timeit_ast, "<magic-timeit>", "exec")
1163 1163 tc = clock()-t0
1164 1164
1165 1165 ns = {}
1166 1166 glob = self.shell.user_ns
1167 1167 # handles global vars with same name as local vars. We store them in conflict_globs.
1168 1168 conflict_globs = {}
1169 1169 if local_ns and cell is None:
1170 1170 for var_name, var_val in glob.items():
1171 1171 if var_name in local_ns:
1172 1172 conflict_globs[var_name] = var_val
1173 1173 glob.update(local_ns)
1174 1174
1175 1175 exec(code, glob, ns)
1176 1176 timer.inner = ns["inner"]
1177 1177
1178 1178 # This is used to check if there is a huge difference between the
1179 1179 # best and worst timings.
1180 1180 # Issue: https://github.com/ipython/ipython/issues/6471
1181 1181 if number == 0:
1182 1182 # determine number so that 0.2 <= total time < 2.0
1183 1183 for index in range(0, 10):
1184 1184 number = 10 ** index
1185 1185 time_number = timer.timeit(number)
1186 1186 if time_number >= 0.2:
1187 1187 break
1188 1188
1189 1189 all_runs = timer.repeat(repeat, number)
1190 1190 best = min(all_runs) / number
1191 1191 worst = max(all_runs) / number
1192 1192 timeit_result = TimeitResult(number, repeat, best, worst, all_runs, tc, precision)
1193 1193
1194 1194 # Restore global vars from conflict_globs
1195 1195 if conflict_globs:
1196 1196 glob.update(conflict_globs)
1197 1197
1198 1198 if not quiet :
1199 1199 # Check best timing is greater than zero to avoid a
1200 1200 # ZeroDivisionError.
1201 1201 # In cases where the slowest timing is lesser than a microsecond
1202 1202 # we assume that it does not really matter if the fastest
1203 1203 # timing is 4 times faster than the slowest timing or not.
1204 1204 if worst > 4 * best and best > 0 and worst > 1e-6:
1205 1205 print("The slowest run took %0.2f times longer than the "
1206 1206 "fastest. This could mean that an intermediate result "
1207 1207 "is being cached." % (worst / best))
1208 1208
1209 1209 print( timeit_result )
1210 1210
1211 1211 if tc > tc_min:
1212 1212 print("Compiler time: %.2f s" % tc)
1213 1213 if return_result:
1214 1214 return timeit_result
1215 1215
1216 1216 @skip_doctest
1217 1217 @no_var_expand
1218 1218 @needs_local_scope
1219 1219 @line_cell_magic
1220 1220 @output_can_be_silenced
1221 1221 def time(self,line='', cell=None, local_ns=None):
1222 1222 """Time execution of a Python statement or expression.
1223 1223
1224 1224 The CPU and wall clock times are printed, and the value of the
1225 1225 expression (if any) is returned. Note that under Win32, system time
1226 1226 is always reported as 0, since it can not be measured.
1227 1227
1228 1228 This function can be used both as a line and cell magic:
1229 1229
1230 1230 - In line mode you can time a single-line statement (though multiple
1231 1231 ones can be chained with using semicolons).
1232 1232
1233 1233 - In cell mode, you can time the cell body (a directly
1234 1234 following statement raises an error).
1235 1235
1236 1236 This function provides very basic timing functionality. Use the timeit
1237 1237 magic for more control over the measurement.
1238 1238
1239 1239 .. versionchanged:: 7.3
1240 1240 User variables are no longer expanded,
1241 1241 the magic line is always left unmodified.
1242 1242
1243 1243 Examples
1244 1244 --------
1245 1245 ::
1246 1246
1247 1247 In [1]: %time 2**128
1248 1248 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
1249 1249 Wall time: 0.00
1250 1250 Out[1]: 340282366920938463463374607431768211456L
1251 1251
1252 1252 In [2]: n = 1000000
1253 1253
1254 1254 In [3]: %time sum(range(n))
1255 1255 CPU times: user 1.20 s, sys: 0.05 s, total: 1.25 s
1256 1256 Wall time: 1.37
1257 1257 Out[3]: 499999500000L
1258 1258
1259 1259 In [4]: %time print 'hello world'
1260 1260 hello world
1261 1261 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
1262 1262 Wall time: 0.00
1263 1263
1264 1264 .. note::
1265 1265 The time needed by Python to compile the given expression will be
1266 1266 reported if it is more than 0.1s.
1267 1267
1268 1268 In the example below, the actual exponentiation is done by Python
1269 1269 at compilation time, so while the expression can take a noticeable
1270 1270 amount of time to compute, that time is purely due to the
1271 1271 compilation::
1272 1272
1273 1273 In [5]: %time 3**9999;
1274 1274 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
1275 1275 Wall time: 0.00 s
1276 1276
1277 1277 In [6]: %time 3**999999;
1278 1278 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
1279 1279 Wall time: 0.00 s
1280 1280 Compiler : 0.78 s
1281 1281 """
1282 1282 # fail immediately if the given expression can't be compiled
1283 1283
1284 1284 if line and cell:
1285 1285 raise UsageError("Can't use statement directly after '%%time'!")
1286 1286
1287 1287 if cell:
1288 1288 expr = self.shell.transform_cell(cell)
1289 1289 else:
1290 1290 expr = self.shell.transform_cell(line)
1291 1291
1292 1292 # Minimum time above which parse time will be reported
1293 1293 tp_min = 0.1
1294 1294
1295 1295 t0 = clock()
1296 1296 expr_ast = self.shell.compile.ast_parse(expr)
1297 1297 tp = clock()-t0
1298 1298
1299 1299 # Apply AST transformations
1300 1300 expr_ast = self.shell.transform_ast(expr_ast)
1301 1301
1302 1302 # Minimum time above which compilation time will be reported
1303 1303 tc_min = 0.1
1304 1304
1305 1305 expr_val=None
1306 1306 if len(expr_ast.body)==1 and isinstance(expr_ast.body[0], ast.Expr):
1307 1307 mode = 'eval'
1308 1308 source = '<timed eval>'
1309 1309 expr_ast = ast.Expression(expr_ast.body[0].value)
1310 1310 else:
1311 1311 mode = 'exec'
1312 1312 source = '<timed exec>'
1313 1313 # multi-line %%time case
1314 1314 if len(expr_ast.body) > 1 and isinstance(expr_ast.body[-1], ast.Expr):
1315 1315 expr_val= expr_ast.body[-1]
1316 1316 expr_ast = expr_ast.body[:-1]
1317 1317 expr_ast = Module(expr_ast, [])
1318 1318 expr_val = ast.Expression(expr_val.value)
1319 1319
1320 1320 t0 = clock()
1321 1321 code = self.shell.compile(expr_ast, source, mode)
1322 1322 tc = clock()-t0
1323 1323
1324 1324 # skew measurement as little as possible
1325 1325 glob = self.shell.user_ns
1326 1326 wtime = time.time
1327 1327 # time execution
1328 1328 wall_st = wtime()
1329 1329 if mode=='eval':
1330 1330 st = clock2()
1331 1331 try:
1332 1332 out = eval(code, glob, local_ns)
1333 1333 except:
1334 1334 self.shell.showtraceback()
1335 1335 return
1336 1336 end = clock2()
1337 1337 else:
1338 1338 st = clock2()
1339 1339 try:
1340 1340 exec(code, glob, local_ns)
1341 1341 out=None
1342 1342 # multi-line %%time case
1343 1343 if expr_val is not None:
1344 1344 code_2 = self.shell.compile(expr_val, source, 'eval')
1345 1345 out = eval(code_2, glob, local_ns)
1346 1346 except:
1347 1347 self.shell.showtraceback()
1348 1348 return
1349 1349 end = clock2()
1350 1350
1351 1351 wall_end = wtime()
1352 1352 # Compute actual times and report
1353 1353 wall_time = wall_end - wall_st
1354 1354 cpu_user = end[0] - st[0]
1355 1355 cpu_sys = end[1] - st[1]
1356 1356 cpu_tot = cpu_user + cpu_sys
1357 1357 # On windows cpu_sys is always zero, so only total is displayed
1358 1358 if sys.platform != "win32":
1359 1359 print(
1360 1360 f"CPU times: user {_format_time(cpu_user)}, sys: {_format_time(cpu_sys)}, total: {_format_time(cpu_tot)}"
1361 1361 )
1362 1362 else:
1363 1363 print(f"CPU times: total: {_format_time(cpu_tot)}")
1364 1364 print(f"Wall time: {_format_time(wall_time)}")
1365 1365 if tc > tc_min:
1366 1366 print(f"Compiler : {_format_time(tc)}")
1367 1367 if tp > tp_min:
1368 1368 print(f"Parser : {_format_time(tp)}")
1369 1369 return out
1370 1370
1371 1371 @skip_doctest
1372 1372 @line_magic
1373 1373 def macro(self, parameter_s=''):
1374 1374 """Define a macro for future re-execution. It accepts ranges of history,
1375 1375 filenames or string objects.
1376 1376
1377 1377 Usage:\\
1378 1378 %macro [options] name n1-n2 n3-n4 ... n5 .. n6 ...
1379 1379
1380 1380 Options:
1381 1381
1382 1382 -r: use 'raw' input. By default, the 'processed' history is used,
1383 1383 so that magics are loaded in their transformed version to valid
1384 1384 Python. If this option is given, the raw input as typed at the
1385 1385 command line is used instead.
1386 1386
1387 1387 -q: quiet macro definition. By default, a tag line is printed
1388 1388 to indicate the macro has been created, and then the contents of
1389 1389 the macro are printed. If this option is given, then no printout
1390 1390 is produced once the macro is created.
1391 1391
1392 1392 This will define a global variable called `name` which is a string
1393 1393 made of joining the slices and lines you specify (n1,n2,... numbers
1394 1394 above) from your input history into a single string. This variable
1395 1395 acts like an automatic function which re-executes those lines as if
1396 1396 you had typed them. You just type 'name' at the prompt and the code
1397 1397 executes.
1398 1398
1399 1399 The syntax for indicating input ranges is described in %history.
1400 1400
1401 1401 Note: as a 'hidden' feature, you can also use traditional python slice
1402 1402 notation, where N:M means numbers N through M-1.
1403 1403
1404 1404 For example, if your history contains (print using %hist -n )::
1405 1405
1406 1406 44: x=1
1407 1407 45: y=3
1408 1408 46: z=x+y
1409 1409 47: print x
1410 1410 48: a=5
1411 1411 49: print 'x',x,'y',y
1412 1412
1413 1413 you can create a macro with lines 44 through 47 (included) and line 49
1414 1414 called my_macro with::
1415 1415
1416 1416 In [55]: %macro my_macro 44-47 49
1417 1417
1418 1418 Now, typing `my_macro` (without quotes) will re-execute all this code
1419 1419 in one pass.
1420 1420
1421 1421 You don't need to give the line-numbers in order, and any given line
1422 1422 number can appear multiple times. You can assemble macros with any
1423 1423 lines from your input history in any order.
1424 1424
1425 1425 The macro is a simple object which holds its value in an attribute,
1426 1426 but IPython's display system checks for macros and executes them as
1427 1427 code instead of printing them when you type their name.
1428 1428
1429 1429 You can view a macro's contents by explicitly printing it with::
1430 1430
1431 1431 print macro_name
1432 1432
1433 1433 """
1434 1434 opts,args = self.parse_options(parameter_s,'rq',mode='list')
1435 1435 if not args: # List existing macros
1436 1436 return sorted(k for k,v in self.shell.user_ns.items() if isinstance(v, Macro))
1437 1437 if len(args) == 1:
1438 1438 raise UsageError(
1439 1439 "%macro insufficient args; usage '%macro name n1-n2 n3-4...")
1440 1440 name, codefrom = args[0], " ".join(args[1:])
1441 1441
1442 1442 #print 'rng',ranges # dbg
1443 1443 try:
1444 1444 lines = self.shell.find_user_code(codefrom, 'r' in opts)
1445 1445 except (ValueError, TypeError) as e:
1446 1446 print(e.args[0])
1447 1447 return
1448 1448 macro = Macro(lines)
1449 1449 self.shell.define_macro(name, macro)
1450 1450 if not ( 'q' in opts) :
1451 1451 print('Macro `%s` created. To execute, type its name (without quotes).' % name)
1452 1452 print('=== Macro contents: ===')
1453 1453 print(macro, end=' ')
1454 1454
1455 1455 @magic_arguments.magic_arguments()
1456 1456 @magic_arguments.argument('output', type=str, default='', nargs='?',
1457 1457 help="""The name of the variable in which to store output.
1458 1458 This is a utils.io.CapturedIO object with stdout/err attributes
1459 1459 for the text of the captured output.
1460 1460
1461 1461 CapturedOutput also has a show() method for displaying the output,
1462 1462 and __call__ as well, so you can use that to quickly display the
1463 1463 output.
1464 1464
1465 1465 If unspecified, captured output is discarded.
1466 1466 """
1467 1467 )
1468 1468 @magic_arguments.argument('--no-stderr', action="store_true",
1469 1469 help="""Don't capture stderr."""
1470 1470 )
1471 1471 @magic_arguments.argument('--no-stdout', action="store_true",
1472 1472 help="""Don't capture stdout."""
1473 1473 )
1474 1474 @magic_arguments.argument('--no-display', action="store_true",
1475 1475 help="""Don't capture IPython's rich display."""
1476 1476 )
1477 1477 @cell_magic
1478 1478 def capture(self, line, cell):
1479 1479 """run the cell, capturing stdout, stderr, and IPython's rich display() calls."""
1480 1480 args = magic_arguments.parse_argstring(self.capture, line)
1481 1481 out = not args.no_stdout
1482 1482 err = not args.no_stderr
1483 1483 disp = not args.no_display
1484 1484 with capture_output(out, err, disp) as io:
1485 1485 self.shell.run_cell(cell)
1486 1486 if DisplayHook.semicolon_at_end_of_expression(cell):
1487 1487 if args.output in self.shell.user_ns:
1488 1488 del self.shell.user_ns[args.output]
1489 1489 elif args.output:
1490 1490 self.shell.user_ns[args.output] = io
1491 1491
1492 1492 @skip_doctest
1493 1493 @magic_arguments.magic_arguments()
1494 1494 @magic_arguments.argument("name", type=str, default="default", nargs="?")
1495 1495 @magic_arguments.argument(
1496 1496 "--remove", action="store_true", help="remove the current transformer"
1497 1497 )
1498 1498 @magic_arguments.argument(
1499 1499 "--list", action="store_true", help="list existing transformers name"
1500 1500 )
1501 1501 @magic_arguments.argument(
1502 1502 "--list-all",
1503 1503 action="store_true",
1504 1504 help="list existing transformers name and code template",
1505 1505 )
1506 1506 @line_cell_magic
1507 1507 def code_wrap(self, line, cell=None):
1508 1508 """
1509 1509 Simple magic to quickly define a code transformer for all IPython's future input.
1510 1510
1511 1511 ``__code__`` and ``__ret__`` are special variable that represent the code to run
1512 1512 and the value of the last expression of ``__code__`` respectively.
1513 1513
1514 1514 Examples
1515 1515 --------
1516 1516
1517 1517 .. ipython::
1518 1518
1519 1519 In [1]: %%code_wrap before_after
1520 1520 ...: print('before')
1521 1521 ...: __code__
1522 1522 ...: print('after')
1523 1523 ...: __ret__
1524 1524
1525 1525
1526 1526 In [2]: 1
1527 1527 before
1528 1528 after
1529 1529 Out[2]: 1
1530 1530
1531 1531 In [3]: %code_wrap --list
1532 1532 before_after
1533 1533
1534 1534 In [4]: %code_wrap --list-all
1535 1535 before_after :
1536 1536 print('before')
1537 1537 __code__
1538 1538 print('after')
1539 1539 __ret__
1540 1540
1541 1541 In [5]: %code_wrap --remove before_after
1542 1542
1543 1543 """
1544 1544 args = magic_arguments.parse_argstring(self.code_wrap, line)
1545 1545
1546 1546 if args.list:
1547 1547 for name in self._transformers.keys():
1548 1548 print(name)
1549 1549 return
1550 1550 if args.list_all:
1551 1551 for name, _t in self._transformers.items():
1552 1552 print(name, ":")
1553 1553 print(indent(ast.unparse(_t.template), " "))
1554 1554 print()
1555 1555 return
1556 1556
1557 1557 to_remove = self._transformers.pop(args.name, None)
1558 1558 if to_remove in self.shell.ast_transformers:
1559 1559 self.shell.ast_transformers.remove(to_remove)
1560 1560 if cell is None or args.remove:
1561 1561 return
1562 1562
1563 1563 _trs = ReplaceCodeTransformer(ast.parse(cell))
1564 1564
1565 1565 self._transformers[args.name] = _trs
1566 1566 self.shell.ast_transformers.append(_trs)
1567 1567
1568 1568
1569 1569 def parse_breakpoint(text, current_file):
1570 1570 '''Returns (file, line) for file:line and (current_file, line) for line'''
1571 1571 colon = text.find(':')
1572 1572 if colon == -1:
1573 1573 return current_file, int(text)
1574 1574 else:
1575 1575 return text[:colon], int(text[colon+1:])
1576 1576
1577 1577 def _format_time(timespan, precision=3):
1578 1578 """Formats the timespan in a human readable form"""
1579 1579
1580 1580 if timespan >= 60.0:
1581 1581 # we have more than a minute, format that in a human readable form
1582 1582 # Idea from http://snipplr.com/view/5713/
1583 1583 parts = [("d", 60*60*24),("h", 60*60),("min", 60), ("s", 1)]
1584 1584 time = []
1585 1585 leftover = timespan
1586 1586 for suffix, length in parts:
1587 1587 value = int(leftover / length)
1588 1588 if value > 0:
1589 1589 leftover = leftover % length
1590 1590 time.append(u'%s%s' % (str(value), suffix))
1591 1591 if leftover < 1:
1592 1592 break
1593 1593 return " ".join(time)
1594 1594
1595
1596 # Unfortunately the unicode 'micro' symbol can cause problems in
1597 # certain terminals.
1595
1596 # Unfortunately characters outside of range(128) can cause problems in
1597 # certain terminals.
1598 1598 # See bug: https://bugs.launchpad.net/ipython/+bug/348466
1599 1599 # Try to prevent crashes by being more secure than it needs to
1600 1600 # E.g. eclipse is able to print a µ, but has no sys.stdout.encoding set.
1601 units = [u"s", u"ms",u'us',"ns"] # the save value
1602 if hasattr(sys.stdout, 'encoding') and sys.stdout.encoding:
1601 units = ["s", "ms", "us", "ns"] # the safe value
1602 if hasattr(sys.stdout, "encoding") and sys.stdout.encoding:
1603 1603 try:
1604 u'\xb5'.encode(sys.stdout.encoding)
1605 units = [u"s", u"ms",u'\xb5s',"ns"]
1604 "μ".encode(sys.stdout.encoding)
1605 units = ["s", "ms", "μs", "ns"]
1606 1606 except:
1607 1607 pass
1608 1608 scaling = [1, 1e3, 1e6, 1e9]
1609 1609
1610 1610 if timespan > 0.0:
1611 1611 order = min(-int(math.floor(math.log10(timespan)) // 3), 3)
1612 1612 else:
1613 1613 order = 3
1614 1614 return "%.*g %s" % (precision, timespan * scaling[order], units[order])
@@ -1,1548 +1,1556 b''
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 def test_time_microseconds_display():
534 """Ensure ASCII is used when necessary"""
535 with mock.patch("sys.stdout", io.TextIOWrapper(StringIO(), encoding="utf-8")):
536 assert execution._format_time(0.000001) == "1 \u03bcs"
537 with mock.patch("sys.stdout", io.TextIOWrapper(StringIO(), encoding="ascii")):
538 assert execution._format_time(0.000001) == "1 us"
539
540
533 541 # Test %%capture magic. Added to test issue #13926
534 542 def test_capture():
535 543 ip = get_ipython()
536 544
537 545 # Test %%capture nominal case
538 546 ip.run_cell("%%capture abc\n1+2")
539 547 with tt.AssertPrints("True", suppress=False):
540 548 ip.run_cell("'abc' in locals()")
541 549 with tt.AssertPrints("True", suppress=False):
542 550 ip.run_cell("'outputs' in dir(abc)")
543 551 with tt.AssertPrints("3", suppress=False):
544 552 ip.run_cell("abc.outputs[0]")
545 553
546 554 # Test %%capture with ';' at end of expression
547 555 ip.run_cell("%%capture abc\n7+8;")
548 556 with tt.AssertPrints("False", suppress=False):
549 557 ip.run_cell("'abc' in locals()")
550 558
551 559
552 560 def test_doctest_mode():
553 561 "Toggle doctest_mode twice, it should be a no-op and run without error"
554 562 _ip.run_line_magic("doctest_mode", "")
555 563 _ip.run_line_magic("doctest_mode", "")
556 564
557 565
558 566 def test_parse_options():
559 567 """Tests for basic options parsing in magics."""
560 568 # These are only the most minimal of tests, more should be added later. At
561 569 # the very least we check that basic text/unicode calls work OK.
562 570 m = DummyMagics(_ip)
563 571 assert m.parse_options("foo", "")[1] == "foo"
564 572 assert m.parse_options("foo", "")[1] == "foo"
565 573
566 574
567 575 def test_parse_options_preserve_non_option_string():
568 576 """Test to assert preservation of non-option part of magic-block, while parsing magic options."""
569 577 m = DummyMagics(_ip)
570 578 opts, stmt = m.parse_options(
571 579 " -n1 -r 13 _ = 314 + foo", "n:r:", preserve_non_opts=True
572 580 )
573 581 assert opts == {"n": "1", "r": "13"}
574 582 assert stmt == "_ = 314 + foo"
575 583
576 584
577 585 def test_run_magic_preserve_code_block():
578 586 """Test to assert preservation of non-option part of magic-block, while running magic."""
579 587 _ip.user_ns["spaces"] = []
580 588 _ip.run_line_magic(
581 589 "timeit", "-n1 -r1 spaces.append([s.count(' ') for s in ['document']])"
582 590 )
583 591 assert _ip.user_ns["spaces"] == [[0]]
584 592
585 593
586 594 def test_dirops():
587 595 """Test various directory handling operations."""
588 596 # curpath = lambda :os.path.splitdrive(os.getcwd())[1].replace('\\','/')
589 597 curpath = os.getcwd
590 598 startdir = os.getcwd()
591 599 ipdir = os.path.realpath(_ip.ipython_dir)
592 600 try:
593 601 _ip.run_line_magic("cd", '"%s"' % ipdir)
594 602 assert curpath() == ipdir
595 603 _ip.run_line_magic("cd", "-")
596 604 assert curpath() == startdir
597 605 _ip.run_line_magic("pushd", '"%s"' % ipdir)
598 606 assert curpath() == ipdir
599 607 _ip.run_line_magic("popd", "")
600 608 assert curpath() == startdir
601 609 finally:
602 610 os.chdir(startdir)
603 611
604 612
605 613 def test_cd_force_quiet():
606 614 """Test OSMagics.cd_force_quiet option"""
607 615 _ip.config.OSMagics.cd_force_quiet = True
608 616 osmagics = osm.OSMagics(shell=_ip)
609 617
610 618 startdir = os.getcwd()
611 619 ipdir = os.path.realpath(_ip.ipython_dir)
612 620
613 621 try:
614 622 with tt.AssertNotPrints(ipdir):
615 623 osmagics.cd('"%s"' % ipdir)
616 624 with tt.AssertNotPrints(startdir):
617 625 osmagics.cd('-')
618 626 finally:
619 627 os.chdir(startdir)
620 628
621 629
622 630 def test_xmode():
623 631 # Calling xmode three times should be a no-op
624 632 xmode = _ip.InteractiveTB.mode
625 633 for i in range(4):
626 634 _ip.run_line_magic("xmode", "")
627 635 assert _ip.InteractiveTB.mode == xmode
628 636
629 637 def test_reset_hard():
630 638 monitor = []
631 639 class A(object):
632 640 def __del__(self):
633 641 monitor.append(1)
634 642 def __repr__(self):
635 643 return "<A instance>"
636 644
637 645 _ip.user_ns["a"] = A()
638 646 _ip.run_cell("a")
639 647
640 648 assert monitor == []
641 649 _ip.run_line_magic("reset", "-f")
642 650 assert monitor == [1]
643 651
644 652 class TestXdel(tt.TempFileMixin):
645 653 def test_xdel(self):
646 654 """Test that references from %run are cleared by xdel."""
647 655 src = ("class A(object):\n"
648 656 " monitor = []\n"
649 657 " def __del__(self):\n"
650 658 " self.monitor.append(1)\n"
651 659 "a = A()\n")
652 660 self.mktmp(src)
653 661 # %run creates some hidden references...
654 662 _ip.run_line_magic("run", "%s" % self.fname)
655 663 # ... as does the displayhook.
656 664 _ip.run_cell("a")
657 665
658 666 monitor = _ip.user_ns["A"].monitor
659 667 assert monitor == []
660 668
661 669 _ip.run_line_magic("xdel", "a")
662 670
663 671 # Check that a's __del__ method has been called.
664 672 gc.collect(0)
665 673 assert monitor == [1]
666 674
667 675 def doctest_who():
668 676 """doctest for %who
669 677
670 678 In [1]: %reset -sf
671 679
672 680 In [2]: alpha = 123
673 681
674 682 In [3]: beta = 'beta'
675 683
676 684 In [4]: %who int
677 685 alpha
678 686
679 687 In [5]: %who str
680 688 beta
681 689
682 690 In [6]: %whos
683 691 Variable Type Data/Info
684 692 ----------------------------
685 693 alpha int 123
686 694 beta str beta
687 695
688 696 In [7]: %who_ls
689 697 Out[7]: ['alpha', 'beta']
690 698 """
691 699
692 700 def test_whos():
693 701 """Check that whos is protected against objects where repr() fails."""
694 702 class A(object):
695 703 def __repr__(self):
696 704 raise Exception()
697 705 _ip.user_ns['a'] = A()
698 706 _ip.run_line_magic("whos", "")
699 707
700 708 def doctest_precision():
701 709 """doctest for %precision
702 710
703 711 In [1]: f = get_ipython().display_formatter.formatters['text/plain']
704 712
705 713 In [2]: %precision 5
706 714 Out[2]: '%.5f'
707 715
708 716 In [3]: f.float_format
709 717 Out[3]: '%.5f'
710 718
711 719 In [4]: %precision %e
712 720 Out[4]: '%e'
713 721
714 722 In [5]: f(3.1415927)
715 723 Out[5]: '3.141593e+00'
716 724 """
717 725
718 726
719 727 def test_debug_magic():
720 728 """Test debugging a small code with %debug
721 729
722 730 In [1]: with PdbTestInput(['c']):
723 731 ...: %debug print("a b") #doctest: +ELLIPSIS
724 732 ...:
725 733 ...
726 734 ipdb> c
727 735 a b
728 736 In [2]:
729 737 """
730 738
731 739
732 740 def test_debug_magic_locals():
733 741 """Test debugging a small code with %debug with locals
734 742
735 743 In [1]: with PdbTestInput(['c']):
736 744 ...: def fun():
737 745 ...: res = 1
738 746 ...: %debug print(res)
739 747 ...: fun()
740 748 ...:
741 749 ...
742 750 ipdb> c
743 751 1
744 752 In [2]:
745 753 """
746 754
747 755 def test_psearch():
748 756 with tt.AssertPrints("dict.fromkeys"):
749 757 _ip.run_cell("dict.fr*?")
750 758 with tt.AssertPrints("π.is_integer"):
751 759 _ip.run_cell("π = 3.14;\nπ.is_integ*?")
752 760
753 761 def test_timeit_shlex():
754 762 """test shlex issues with timeit (#1109)"""
755 763 _ip.ex("def f(*a,**kw): pass")
756 764 _ip.run_line_magic("timeit", '-n1 "this is a bug".count(" ")')
757 765 _ip.run_line_magic("timeit", '-r1 -n1 f(" ", 1)')
758 766 _ip.run_line_magic("timeit", '-r1 -n1 f(" ", 1, " ", 2, " ")')
759 767 _ip.run_line_magic("timeit", '-r1 -n1 ("a " + "b")')
760 768 _ip.run_line_magic("timeit", '-r1 -n1 f("a " + "b")')
761 769 _ip.run_line_magic("timeit", '-r1 -n1 f("a " + "b ")')
762 770
763 771
764 772 def test_timeit_special_syntax():
765 773 "Test %%timeit with IPython special syntax"
766 774 @register_line_magic
767 775 def lmagic(line):
768 776 ip = get_ipython()
769 777 ip.user_ns['lmagic_out'] = line
770 778
771 779 # line mode test
772 780 _ip.run_line_magic("timeit", "-n1 -r1 %lmagic my line")
773 781 assert _ip.user_ns["lmagic_out"] == "my line"
774 782 # cell mode test
775 783 _ip.run_cell_magic("timeit", "-n1 -r1", "%lmagic my line2")
776 784 assert _ip.user_ns["lmagic_out"] == "my line2"
777 785
778 786
779 787 def test_timeit_return():
780 788 """
781 789 test whether timeit -o return object
782 790 """
783 791
784 792 res = _ip.run_line_magic('timeit','-n10 -r10 -o 1')
785 793 assert(res is not None)
786 794
787 795 def test_timeit_quiet():
788 796 """
789 797 test quiet option of timeit magic
790 798 """
791 799 with tt.AssertNotPrints("loops"):
792 800 _ip.run_cell("%timeit -n1 -r1 -q 1")
793 801
794 802 def test_timeit_return_quiet():
795 803 with tt.AssertNotPrints("loops"):
796 804 res = _ip.run_line_magic('timeit', '-n1 -r1 -q -o 1')
797 805 assert (res is not None)
798 806
799 807 def test_timeit_invalid_return():
800 808 with pytest.raises(SyntaxError):
801 809 _ip.run_line_magic('timeit', 'return')
802 810
803 811 @dec.skipif(execution.profile is None)
804 812 def test_prun_special_syntax():
805 813 "Test %%prun with IPython special syntax"
806 814 @register_line_magic
807 815 def lmagic(line):
808 816 ip = get_ipython()
809 817 ip.user_ns['lmagic_out'] = line
810 818
811 819 # line mode test
812 820 _ip.run_line_magic("prun", "-q %lmagic my line")
813 821 assert _ip.user_ns["lmagic_out"] == "my line"
814 822 # cell mode test
815 823 _ip.run_cell_magic("prun", "-q", "%lmagic my line2")
816 824 assert _ip.user_ns["lmagic_out"] == "my line2"
817 825
818 826
819 827 @dec.skipif(execution.profile is None)
820 828 def test_prun_quotes():
821 829 "Test that prun does not clobber string escapes (GH #1302)"
822 830 _ip.magic(r"prun -q x = '\t'")
823 831 assert _ip.user_ns["x"] == "\t"
824 832
825 833
826 834 def test_extension():
827 835 # Debugging information for failures of this test
828 836 print('sys.path:')
829 837 for p in sys.path:
830 838 print(' ', p)
831 839 print('CWD', os.getcwd())
832 840
833 841 pytest.raises(ImportError, _ip.magic, "load_ext daft_extension")
834 842 daft_path = os.path.join(os.path.dirname(__file__), "daft_extension")
835 843 sys.path.insert(0, daft_path)
836 844 try:
837 845 _ip.user_ns.pop('arq', None)
838 846 invalidate_caches() # Clear import caches
839 847 _ip.run_line_magic("load_ext", "daft_extension")
840 848 assert _ip.user_ns["arq"] == 185
841 849 _ip.run_line_magic("unload_ext", "daft_extension")
842 850 assert 'arq' not in _ip.user_ns
843 851 finally:
844 852 sys.path.remove(daft_path)
845 853
846 854
847 855 def test_notebook_export_json():
848 856 pytest.importorskip("nbformat")
849 857 _ip = get_ipython()
850 858 _ip.history_manager.reset() # Clear any existing history.
851 859 cmds = ["a=1", "def b():\n return a**2", "print('noël, été', b())"]
852 860 for i, cmd in enumerate(cmds, start=1):
853 861 _ip.history_manager.store_inputs(i, cmd)
854 862 with TemporaryDirectory() as td:
855 863 outfile = os.path.join(td, "nb.ipynb")
856 864 _ip.run_line_magic("notebook", "%s" % outfile)
857 865
858 866
859 867 class TestEnv(TestCase):
860 868
861 869 def test_env(self):
862 870 env = _ip.run_line_magic("env", "")
863 871 self.assertTrue(isinstance(env, dict))
864 872
865 873 def test_env_secret(self):
866 874 env = _ip.run_line_magic("env", "")
867 875 hidden = "<hidden>"
868 876 with mock.patch.dict(
869 877 os.environ,
870 878 {
871 879 "API_KEY": "abc123",
872 880 "SECRET_THING": "ssshhh",
873 881 "JUPYTER_TOKEN": "",
874 882 "VAR": "abc"
875 883 }
876 884 ):
877 885 env = _ip.run_line_magic("env", "")
878 886 assert env["API_KEY"] == hidden
879 887 assert env["SECRET_THING"] == hidden
880 888 assert env["JUPYTER_TOKEN"] == hidden
881 889 assert env["VAR"] == "abc"
882 890
883 891 def test_env_get_set_simple(self):
884 892 env = _ip.run_line_magic("env", "var val1")
885 893 self.assertEqual(env, None)
886 894 self.assertEqual(os.environ["var"], "val1")
887 895 self.assertEqual(_ip.run_line_magic("env", "var"), "val1")
888 896 env = _ip.run_line_magic("env", "var=val2")
889 897 self.assertEqual(env, None)
890 898 self.assertEqual(os.environ['var'], 'val2')
891 899
892 900 def test_env_get_set_complex(self):
893 901 env = _ip.run_line_magic("env", "var 'val1 '' 'val2")
894 902 self.assertEqual(env, None)
895 903 self.assertEqual(os.environ['var'], "'val1 '' 'val2")
896 904 self.assertEqual(_ip.run_line_magic("env", "var"), "'val1 '' 'val2")
897 905 env = _ip.run_line_magic("env", 'var=val2 val3="val4')
898 906 self.assertEqual(env, None)
899 907 self.assertEqual(os.environ['var'], 'val2 val3="val4')
900 908
901 909 def test_env_set_bad_input(self):
902 910 self.assertRaises(UsageError, lambda: _ip.run_line_magic("set_env", "var"))
903 911
904 912 def test_env_set_whitespace(self):
905 913 self.assertRaises(UsageError, lambda: _ip.run_line_magic("env", "var A=B"))
906 914
907 915
908 916 class CellMagicTestCase(TestCase):
909 917
910 918 def check_ident(self, magic):
911 919 # Manually called, we get the result
912 920 out = _ip.run_cell_magic(magic, "a", "b")
913 921 assert out == ("a", "b")
914 922 # Via run_cell, it goes into the user's namespace via displayhook
915 923 _ip.run_cell("%%" + magic + " c\nd\n")
916 924 assert _ip.user_ns["_"] == ("c", "d\n")
917 925
918 926 def test_cell_magic_func_deco(self):
919 927 "Cell magic using simple decorator"
920 928 @register_cell_magic
921 929 def cellm(line, cell):
922 930 return line, cell
923 931
924 932 self.check_ident('cellm')
925 933
926 934 def test_cell_magic_reg(self):
927 935 "Cell magic manually registered"
928 936 def cellm(line, cell):
929 937 return line, cell
930 938
931 939 _ip.register_magic_function(cellm, 'cell', 'cellm2')
932 940 self.check_ident('cellm2')
933 941
934 942 def test_cell_magic_class(self):
935 943 "Cell magics declared via a class"
936 944 @magics_class
937 945 class MyMagics(Magics):
938 946
939 947 @cell_magic
940 948 def cellm3(self, line, cell):
941 949 return line, cell
942 950
943 951 _ip.register_magics(MyMagics)
944 952 self.check_ident('cellm3')
945 953
946 954 def test_cell_magic_class2(self):
947 955 "Cell magics declared via a class, #2"
948 956 @magics_class
949 957 class MyMagics2(Magics):
950 958
951 959 @cell_magic('cellm4')
952 960 def cellm33(self, line, cell):
953 961 return line, cell
954 962
955 963 _ip.register_magics(MyMagics2)
956 964 self.check_ident('cellm4')
957 965 # Check that nothing is registered as 'cellm33'
958 966 c33 = _ip.find_cell_magic('cellm33')
959 967 assert c33 == None
960 968
961 969 def test_file():
962 970 """Basic %%writefile"""
963 971 ip = get_ipython()
964 972 with TemporaryDirectory() as td:
965 973 fname = os.path.join(td, "file1")
966 974 ip.run_cell_magic(
967 975 "writefile",
968 976 fname,
969 977 "\n".join(
970 978 [
971 979 "line1",
972 980 "line2",
973 981 ]
974 982 ),
975 983 )
976 984 s = Path(fname).read_text(encoding="utf-8")
977 985 assert "line1\n" in s
978 986 assert "line2" in s
979 987
980 988
981 989 @dec.skip_win32
982 990 def test_file_single_quote():
983 991 """Basic %%writefile with embedded single quotes"""
984 992 ip = get_ipython()
985 993 with TemporaryDirectory() as td:
986 994 fname = os.path.join(td, "'file1'")
987 995 ip.run_cell_magic(
988 996 "writefile",
989 997 fname,
990 998 "\n".join(
991 999 [
992 1000 "line1",
993 1001 "line2",
994 1002 ]
995 1003 ),
996 1004 )
997 1005 s = Path(fname).read_text(encoding="utf-8")
998 1006 assert "line1\n" in s
999 1007 assert "line2" in s
1000 1008
1001 1009
1002 1010 @dec.skip_win32
1003 1011 def test_file_double_quote():
1004 1012 """Basic %%writefile with embedded double quotes"""
1005 1013 ip = get_ipython()
1006 1014 with TemporaryDirectory() as td:
1007 1015 fname = os.path.join(td, '"file1"')
1008 1016 ip.run_cell_magic(
1009 1017 "writefile",
1010 1018 fname,
1011 1019 "\n".join(
1012 1020 [
1013 1021 "line1",
1014 1022 "line2",
1015 1023 ]
1016 1024 ),
1017 1025 )
1018 1026 s = Path(fname).read_text(encoding="utf-8")
1019 1027 assert "line1\n" in s
1020 1028 assert "line2" in s
1021 1029
1022 1030
1023 1031 def test_file_var_expand():
1024 1032 """%%writefile $filename"""
1025 1033 ip = get_ipython()
1026 1034 with TemporaryDirectory() as td:
1027 1035 fname = os.path.join(td, "file1")
1028 1036 ip.user_ns["filename"] = fname
1029 1037 ip.run_cell_magic(
1030 1038 "writefile",
1031 1039 "$filename",
1032 1040 "\n".join(
1033 1041 [
1034 1042 "line1",
1035 1043 "line2",
1036 1044 ]
1037 1045 ),
1038 1046 )
1039 1047 s = Path(fname).read_text(encoding="utf-8")
1040 1048 assert "line1\n" in s
1041 1049 assert "line2" in s
1042 1050
1043 1051
1044 1052 def test_file_unicode():
1045 1053 """%%writefile with unicode cell"""
1046 1054 ip = get_ipython()
1047 1055 with TemporaryDirectory() as td:
1048 1056 fname = os.path.join(td, 'file1')
1049 1057 ip.run_cell_magic("writefile", fname, u'\n'.join([
1050 1058 u'liné1',
1051 1059 u'liné2',
1052 1060 ]))
1053 1061 with io.open(fname, encoding='utf-8') as f:
1054 1062 s = f.read()
1055 1063 assert "liné1\n" in s
1056 1064 assert "liné2" in s
1057 1065
1058 1066
1059 1067 def test_file_amend():
1060 1068 """%%writefile -a amends files"""
1061 1069 ip = get_ipython()
1062 1070 with TemporaryDirectory() as td:
1063 1071 fname = os.path.join(td, "file2")
1064 1072 ip.run_cell_magic(
1065 1073 "writefile",
1066 1074 fname,
1067 1075 "\n".join(
1068 1076 [
1069 1077 "line1",
1070 1078 "line2",
1071 1079 ]
1072 1080 ),
1073 1081 )
1074 1082 ip.run_cell_magic(
1075 1083 "writefile",
1076 1084 "-a %s" % fname,
1077 1085 "\n".join(
1078 1086 [
1079 1087 "line3",
1080 1088 "line4",
1081 1089 ]
1082 1090 ),
1083 1091 )
1084 1092 s = Path(fname).read_text(encoding="utf-8")
1085 1093 assert "line1\n" in s
1086 1094 assert "line3\n" in s
1087 1095
1088 1096
1089 1097 def test_file_spaces():
1090 1098 """%%file with spaces in filename"""
1091 1099 ip = get_ipython()
1092 1100 with TemporaryWorkingDirectory() as td:
1093 1101 fname = "file name"
1094 1102 ip.run_cell_magic(
1095 1103 "file",
1096 1104 '"%s"' % fname,
1097 1105 "\n".join(
1098 1106 [
1099 1107 "line1",
1100 1108 "line2",
1101 1109 ]
1102 1110 ),
1103 1111 )
1104 1112 s = Path(fname).read_text(encoding="utf-8")
1105 1113 assert "line1\n" in s
1106 1114 assert "line2" in s
1107 1115
1108 1116
1109 1117 def test_script_config():
1110 1118 ip = get_ipython()
1111 1119 ip.config.ScriptMagics.script_magics = ['whoda']
1112 1120 sm = script.ScriptMagics(shell=ip)
1113 1121 assert "whoda" in sm.magics["cell"]
1114 1122
1115 1123
1116 1124 def test_script_out():
1117 1125 ip = get_ipython()
1118 1126 ip.run_cell_magic("script", f"--out output {sys.executable}", "print('hi')")
1119 1127 assert ip.user_ns["output"].strip() == "hi"
1120 1128
1121 1129
1122 1130 def test_script_err():
1123 1131 ip = get_ipython()
1124 1132 ip.run_cell_magic(
1125 1133 "script",
1126 1134 f"--err error {sys.executable}",
1127 1135 "import sys; print('hello', file=sys.stderr)",
1128 1136 )
1129 1137 assert ip.user_ns["error"].strip() == "hello"
1130 1138
1131 1139
1132 1140 def test_script_out_err():
1133 1141 ip = get_ipython()
1134 1142 ip.run_cell_magic(
1135 1143 "script",
1136 1144 f"--out output --err error {sys.executable}",
1137 1145 "\n".join(
1138 1146 [
1139 1147 "import sys",
1140 1148 "print('hi')",
1141 1149 "print('hello', file=sys.stderr)",
1142 1150 ]
1143 1151 ),
1144 1152 )
1145 1153 assert ip.user_ns["output"].strip() == "hi"
1146 1154 assert ip.user_ns["error"].strip() == "hello"
1147 1155
1148 1156
1149 1157 async def test_script_bg_out():
1150 1158 ip = get_ipython()
1151 1159 ip.run_cell_magic("script", f"--bg --out output {sys.executable}", "print('hi')")
1152 1160 assert (await ip.user_ns["output"].read()).strip() == b"hi"
1153 1161 assert ip.user_ns["output"].at_eof()
1154 1162
1155 1163
1156 1164 async def test_script_bg_err():
1157 1165 ip = get_ipython()
1158 1166 ip.run_cell_magic(
1159 1167 "script",
1160 1168 f"--bg --err error {sys.executable}",
1161 1169 "import sys; print('hello', file=sys.stderr)",
1162 1170 )
1163 1171 assert (await ip.user_ns["error"].read()).strip() == b"hello"
1164 1172 assert ip.user_ns["error"].at_eof()
1165 1173
1166 1174
1167 1175 async def test_script_bg_out_err():
1168 1176 ip = get_ipython()
1169 1177 ip.run_cell_magic(
1170 1178 "script",
1171 1179 f"--bg --out output --err error {sys.executable}",
1172 1180 "\n".join(
1173 1181 [
1174 1182 "import sys",
1175 1183 "print('hi')",
1176 1184 "print('hello', file=sys.stderr)",
1177 1185 ]
1178 1186 ),
1179 1187 )
1180 1188 assert (await ip.user_ns["output"].read()).strip() == b"hi"
1181 1189 assert (await ip.user_ns["error"].read()).strip() == b"hello"
1182 1190 assert ip.user_ns["output"].at_eof()
1183 1191 assert ip.user_ns["error"].at_eof()
1184 1192
1185 1193
1186 1194 async def test_script_bg_proc():
1187 1195 ip = get_ipython()
1188 1196 ip.run_cell_magic(
1189 1197 "script",
1190 1198 f"--bg --out output --proc p {sys.executable}",
1191 1199 "\n".join(
1192 1200 [
1193 1201 "import sys",
1194 1202 "print('hi')",
1195 1203 "print('hello', file=sys.stderr)",
1196 1204 ]
1197 1205 ),
1198 1206 )
1199 1207 p = ip.user_ns["p"]
1200 1208 await p.wait()
1201 1209 assert p.returncode == 0
1202 1210 assert (await p.stdout.read()).strip() == b"hi"
1203 1211 # not captured, so empty
1204 1212 assert (await p.stderr.read()) == b""
1205 1213 assert p.stdout.at_eof()
1206 1214 assert p.stderr.at_eof()
1207 1215
1208 1216
1209 1217 def test_script_defaults():
1210 1218 ip = get_ipython()
1211 1219 for cmd in ['sh', 'bash', 'perl', 'ruby']:
1212 1220 try:
1213 1221 find_cmd(cmd)
1214 1222 except Exception:
1215 1223 pass
1216 1224 else:
1217 1225 assert cmd in ip.magics_manager.magics["cell"]
1218 1226
1219 1227
1220 1228 @magics_class
1221 1229 class FooFoo(Magics):
1222 1230 """class with both %foo and %%foo magics"""
1223 1231 @line_magic('foo')
1224 1232 def line_foo(self, line):
1225 1233 "I am line foo"
1226 1234 pass
1227 1235
1228 1236 @cell_magic("foo")
1229 1237 def cell_foo(self, line, cell):
1230 1238 "I am cell foo, not line foo"
1231 1239 pass
1232 1240
1233 1241 def test_line_cell_info():
1234 1242 """%%foo and %foo magics are distinguishable to inspect"""
1235 1243 ip = get_ipython()
1236 1244 ip.magics_manager.register(FooFoo)
1237 1245 oinfo = ip.object_inspect("foo")
1238 1246 assert oinfo["found"] is True
1239 1247 assert oinfo["ismagic"] is True
1240 1248
1241 1249 oinfo = ip.object_inspect("%%foo")
1242 1250 assert oinfo["found"] is True
1243 1251 assert oinfo["ismagic"] is True
1244 1252 assert oinfo["docstring"] == FooFoo.cell_foo.__doc__
1245 1253
1246 1254 oinfo = ip.object_inspect("%foo")
1247 1255 assert oinfo["found"] is True
1248 1256 assert oinfo["ismagic"] is True
1249 1257 assert oinfo["docstring"] == FooFoo.line_foo.__doc__
1250 1258
1251 1259
1252 1260 def test_multiple_magics():
1253 1261 ip = get_ipython()
1254 1262 foo1 = FooFoo(ip)
1255 1263 foo2 = FooFoo(ip)
1256 1264 mm = ip.magics_manager
1257 1265 mm.register(foo1)
1258 1266 assert mm.magics["line"]["foo"].__self__ is foo1
1259 1267 mm.register(foo2)
1260 1268 assert mm.magics["line"]["foo"].__self__ is foo2
1261 1269
1262 1270
1263 1271 def test_alias_magic():
1264 1272 """Test %alias_magic."""
1265 1273 ip = get_ipython()
1266 1274 mm = ip.magics_manager
1267 1275
1268 1276 # Basic operation: both cell and line magics are created, if possible.
1269 1277 ip.run_line_magic("alias_magic", "timeit_alias timeit")
1270 1278 assert "timeit_alias" in mm.magics["line"]
1271 1279 assert "timeit_alias" in mm.magics["cell"]
1272 1280
1273 1281 # --cell is specified, line magic not created.
1274 1282 ip.run_line_magic("alias_magic", "--cell timeit_cell_alias timeit")
1275 1283 assert "timeit_cell_alias" not in mm.magics["line"]
1276 1284 assert "timeit_cell_alias" in mm.magics["cell"]
1277 1285
1278 1286 # Test that line alias is created successfully.
1279 1287 ip.run_line_magic("alias_magic", "--line env_alias env")
1280 1288 assert ip.run_line_magic("env", "") == ip.run_line_magic("env_alias", "")
1281 1289
1282 1290 # Test that line alias with parameters passed in is created successfully.
1283 1291 ip.run_line_magic(
1284 1292 "alias_magic", "--line history_alias history --params " + shlex.quote("3")
1285 1293 )
1286 1294 assert "history_alias" in mm.magics["line"]
1287 1295
1288 1296
1289 1297 def test_save():
1290 1298 """Test %save."""
1291 1299 ip = get_ipython()
1292 1300 ip.history_manager.reset() # Clear any existing history.
1293 1301 cmds = ["a=1", "def b():\n return a**2", "print(a, b())"]
1294 1302 for i, cmd in enumerate(cmds, start=1):
1295 1303 ip.history_manager.store_inputs(i, cmd)
1296 1304 with TemporaryDirectory() as tmpdir:
1297 1305 file = os.path.join(tmpdir, "testsave.py")
1298 1306 ip.run_line_magic("save", "%s 1-10" % file)
1299 1307 content = Path(file).read_text(encoding="utf-8")
1300 1308 assert content.count(cmds[0]) == 1
1301 1309 assert "coding: utf-8" in content
1302 1310 ip.run_line_magic("save", "-a %s 1-10" % file)
1303 1311 content = Path(file).read_text(encoding="utf-8")
1304 1312 assert content.count(cmds[0]) == 2
1305 1313 assert "coding: utf-8" in content
1306 1314
1307 1315
1308 1316 def test_save_with_no_args():
1309 1317 ip = get_ipython()
1310 1318 ip.history_manager.reset() # Clear any existing history.
1311 1319 cmds = ["a=1", "def b():\n return a**2", "print(a, b())", "%save"]
1312 1320 for i, cmd in enumerate(cmds, start=1):
1313 1321 ip.history_manager.store_inputs(i, cmd)
1314 1322
1315 1323 with TemporaryDirectory() as tmpdir:
1316 1324 path = os.path.join(tmpdir, "testsave.py")
1317 1325 ip.run_line_magic("save", path)
1318 1326 content = Path(path).read_text(encoding="utf-8")
1319 1327 expected_content = dedent(
1320 1328 """\
1321 1329 # coding: utf-8
1322 1330 a=1
1323 1331 def b():
1324 1332 return a**2
1325 1333 print(a, b())
1326 1334 """
1327 1335 )
1328 1336 assert content == expected_content
1329 1337
1330 1338
1331 1339 def test_store():
1332 1340 """Test %store."""
1333 1341 ip = get_ipython()
1334 1342 ip.run_line_magic('load_ext', 'storemagic')
1335 1343
1336 1344 # make sure the storage is empty
1337 1345 ip.run_line_magic("store", "-z")
1338 1346 ip.user_ns["var"] = 42
1339 1347 ip.run_line_magic("store", "var")
1340 1348 ip.user_ns["var"] = 39
1341 1349 ip.run_line_magic("store", "-r")
1342 1350 assert ip.user_ns["var"] == 42
1343 1351
1344 1352 ip.run_line_magic("store", "-d var")
1345 1353 ip.user_ns["var"] = 39
1346 1354 ip.run_line_magic("store", "-r")
1347 1355 assert ip.user_ns["var"] == 39
1348 1356
1349 1357
1350 1358 def _run_edit_test(arg_s, exp_filename=None,
1351 1359 exp_lineno=-1,
1352 1360 exp_contents=None,
1353 1361 exp_is_temp=None):
1354 1362 ip = get_ipython()
1355 1363 M = code.CodeMagics(ip)
1356 1364 last_call = ['','']
1357 1365 opts,args = M.parse_options(arg_s,'prxn:')
1358 1366 filename, lineno, is_temp = M._find_edit_target(ip, args, opts, last_call)
1359 1367
1360 1368 if exp_filename is not None:
1361 1369 assert exp_filename == filename
1362 1370 if exp_contents is not None:
1363 1371 with io.open(filename, 'r', encoding='utf-8') as f:
1364 1372 contents = f.read()
1365 1373 assert exp_contents == contents
1366 1374 if exp_lineno != -1:
1367 1375 assert exp_lineno == lineno
1368 1376 if exp_is_temp is not None:
1369 1377 assert exp_is_temp == is_temp
1370 1378
1371 1379
1372 1380 def test_edit_interactive():
1373 1381 """%edit on interactively defined objects"""
1374 1382 ip = get_ipython()
1375 1383 n = ip.execution_count
1376 1384 ip.run_cell("def foo(): return 1", store_history=True)
1377 1385
1378 1386 with pytest.raises(code.InteractivelyDefined) as e:
1379 1387 _run_edit_test("foo")
1380 1388 assert e.value.index == n
1381 1389
1382 1390
1383 1391 def test_edit_cell():
1384 1392 """%edit [cell id]"""
1385 1393 ip = get_ipython()
1386 1394
1387 1395 ip.run_cell("def foo(): return 1", store_history=True)
1388 1396
1389 1397 # test
1390 1398 _run_edit_test("1", exp_contents=ip.user_ns['In'][1], exp_is_temp=True)
1391 1399
1392 1400 def test_edit_fname():
1393 1401 """%edit file"""
1394 1402 # test
1395 1403 _run_edit_test("test file.py", exp_filename="test file.py")
1396 1404
1397 1405 def test_bookmark():
1398 1406 ip = get_ipython()
1399 1407 ip.run_line_magic('bookmark', 'bmname')
1400 1408 with tt.AssertPrints('bmname'):
1401 1409 ip.run_line_magic('bookmark', '-l')
1402 1410 ip.run_line_magic('bookmark', '-d bmname')
1403 1411
1404 1412 def test_ls_magic():
1405 1413 ip = get_ipython()
1406 1414 json_formatter = ip.display_formatter.formatters['application/json']
1407 1415 json_formatter.enabled = True
1408 1416 lsmagic = ip.run_line_magic("lsmagic", "")
1409 1417 with warnings.catch_warnings(record=True) as w:
1410 1418 j = json_formatter(lsmagic)
1411 1419 assert sorted(j) == ["cell", "line"]
1412 1420 assert w == [] # no warnings
1413 1421
1414 1422
1415 1423 def test_strip_initial_indent():
1416 1424 def sii(s):
1417 1425 lines = s.splitlines()
1418 1426 return '\n'.join(code.strip_initial_indent(lines))
1419 1427
1420 1428 assert sii(" a = 1\nb = 2") == "a = 1\nb = 2"
1421 1429 assert sii(" a\n b\nc") == "a\n b\nc"
1422 1430 assert sii("a\n b") == "a\n b"
1423 1431
1424 1432 def test_logging_magic_quiet_from_arg():
1425 1433 _ip.config.LoggingMagics.quiet = False
1426 1434 lm = logging.LoggingMagics(shell=_ip)
1427 1435 with TemporaryDirectory() as td:
1428 1436 try:
1429 1437 with tt.AssertNotPrints(re.compile("Activating.*")):
1430 1438 lm.logstart('-q {}'.format(
1431 1439 os.path.join(td, "quiet_from_arg.log")))
1432 1440 finally:
1433 1441 _ip.logger.logstop()
1434 1442
1435 1443 def test_logging_magic_quiet_from_config():
1436 1444 _ip.config.LoggingMagics.quiet = True
1437 1445 lm = logging.LoggingMagics(shell=_ip)
1438 1446 with TemporaryDirectory() as td:
1439 1447 try:
1440 1448 with tt.AssertNotPrints(re.compile("Activating.*")):
1441 1449 lm.logstart(os.path.join(td, "quiet_from_config.log"))
1442 1450 finally:
1443 1451 _ip.logger.logstop()
1444 1452
1445 1453
1446 1454 def test_logging_magic_not_quiet():
1447 1455 _ip.config.LoggingMagics.quiet = False
1448 1456 lm = logging.LoggingMagics(shell=_ip)
1449 1457 with TemporaryDirectory() as td:
1450 1458 try:
1451 1459 with tt.AssertPrints(re.compile("Activating.*")):
1452 1460 lm.logstart(os.path.join(td, "not_quiet.log"))
1453 1461 finally:
1454 1462 _ip.logger.logstop()
1455 1463
1456 1464
1457 1465 def test_time_no_var_expand():
1458 1466 _ip.user_ns["a"] = 5
1459 1467 _ip.user_ns["b"] = []
1460 1468 _ip.run_line_magic("time", 'b.append("{a}")')
1461 1469 assert _ip.user_ns["b"] == ["{a}"]
1462 1470
1463 1471
1464 1472 # this is slow, put at the end for local testing.
1465 1473 def test_timeit_arguments():
1466 1474 "Test valid timeit arguments, should not cause SyntaxError (GH #1269)"
1467 1475 _ip.run_line_magic("timeit", "-n1 -r1 a=('#')")
1468 1476
1469 1477
1470 1478 MINIMAL_LAZY_MAGIC = """
1471 1479 from IPython.core.magic import (
1472 1480 Magics,
1473 1481 magics_class,
1474 1482 line_magic,
1475 1483 cell_magic,
1476 1484 )
1477 1485
1478 1486
1479 1487 @magics_class
1480 1488 class LazyMagics(Magics):
1481 1489 @line_magic
1482 1490 def lazy_line(self, line):
1483 1491 print("Lazy Line")
1484 1492
1485 1493 @cell_magic
1486 1494 def lazy_cell(self, line, cell):
1487 1495 print("Lazy Cell")
1488 1496
1489 1497
1490 1498 def load_ipython_extension(ipython):
1491 1499 ipython.register_magics(LazyMagics)
1492 1500 """
1493 1501
1494 1502
1495 1503 def test_lazy_magics():
1496 1504 with pytest.raises(UsageError):
1497 1505 ip.run_line_magic("lazy_line", "")
1498 1506
1499 1507 startdir = os.getcwd()
1500 1508
1501 1509 with TemporaryDirectory() as tmpdir:
1502 1510 with prepended_to_syspath(tmpdir):
1503 1511 ptempdir = Path(tmpdir)
1504 1512 tf = ptempdir / "lazy_magic_module.py"
1505 1513 tf.write_text(MINIMAL_LAZY_MAGIC)
1506 1514 ip.magics_manager.register_lazy("lazy_line", Path(tf.name).name[:-3])
1507 1515 with tt.AssertPrints("Lazy Line"):
1508 1516 ip.run_line_magic("lazy_line", "")
1509 1517
1510 1518
1511 1519 TEST_MODULE = """
1512 1520 print('Loaded my_tmp')
1513 1521 if __name__ == "__main__":
1514 1522 print('I just ran a script')
1515 1523 """
1516 1524
1517 1525 def test_run_module_from_import_hook():
1518 1526 "Test that a module can be loaded via an import hook"
1519 1527 with TemporaryDirectory() as tmpdir:
1520 1528 fullpath = os.path.join(tmpdir, "my_tmp.py")
1521 1529 Path(fullpath).write_text(TEST_MODULE, encoding="utf-8")
1522 1530
1523 1531 import importlib.abc
1524 1532 import importlib.util
1525 1533
1526 1534 class MyTempImporter(importlib.abc.MetaPathFinder, importlib.abc.SourceLoader):
1527 1535 def find_spec(self, fullname, path, target=None):
1528 1536 if fullname == "my_tmp":
1529 1537 return importlib.util.spec_from_loader(fullname, self)
1530 1538
1531 1539 def get_filename(self, fullname):
1532 1540 assert fullname == "my_tmp"
1533 1541 return fullpath
1534 1542
1535 1543 def get_data(self, path):
1536 1544 assert Path(path).samefile(fullpath)
1537 1545 return Path(fullpath).read_text(encoding="utf-8")
1538 1546
1539 1547 sys.meta_path.insert(0, MyTempImporter())
1540 1548
1541 1549 with capture_output() as captured:
1542 1550 _ip.run_line_magic("run", "-m my_tmp")
1543 1551 _ip.run_cell("import my_tmp")
1544 1552
1545 1553 output = "Loaded my_tmp\nI just ran a script\nLoaded my_tmp\n"
1546 1554 assert output == captured.stdout
1547 1555
1548 1556 sys.meta_path.pop(0)
General Comments 0
You need to be logged in to leave comments. Login now