Spaces:
Runtime error
Runtime error
| # -*- coding: utf-8 -*- | |
| """Implementation of execution-related magic functions.""" | |
| # Copyright (c) IPython Development Team. | |
| # Distributed under the terms of the Modified BSD License. | |
| import ast | |
| import bdb | |
| import builtins as builtin_mod | |
| import copy | |
| import cProfile as profile | |
| import gc | |
| import itertools | |
| import math | |
| import os | |
| import pstats | |
| import re | |
| import shlex | |
| import sys | |
| import time | |
| import timeit | |
| import signal | |
| from typing import Dict, Any | |
| from ast import ( | |
| Assign, | |
| Call, | |
| Expr, | |
| Load, | |
| Module, | |
| Name, | |
| NodeTransformer, | |
| Store, | |
| parse, | |
| unparse, | |
| ) | |
| from io import StringIO | |
| from logging import error | |
| from pathlib import Path | |
| from pdb import Restart | |
| from textwrap import dedent, indent | |
| from warnings import warn | |
| from IPython.core import magic_arguments, oinspect, page | |
| from IPython.core.displayhook import DisplayHook | |
| from IPython.core.error import UsageError | |
| from IPython.core.macro import Macro | |
| from IPython.core.magic import ( | |
| Magics, | |
| cell_magic, | |
| line_cell_magic, | |
| line_magic, | |
| magics_class, | |
| needs_local_scope, | |
| no_var_expand, | |
| output_can_be_silenced, | |
| ) | |
| from IPython.testing.skipdoctest import skip_doctest | |
| from IPython.utils.capture import capture_output | |
| from IPython.utils.contexts import preserve_keys | |
| from IPython.utils.ipstruct import Struct | |
| from IPython.utils.module_paths import find_mod | |
| from IPython.utils.path import get_py_filename, shellglob | |
| from IPython.utils.timing import clock, clock2 | |
| from IPython.core.magics.ast_mod import ReplaceCodeTransformer | |
| #----------------------------------------------------------------------------- | |
| # Magic implementation classes | |
| #----------------------------------------------------------------------------- | |
| class TimeitResult: | |
| """ | |
| Object returned by the timeit magic with info about the run. | |
| Contains the following attributes: | |
| loops: int | |
| number of loops done per measurement | |
| repeat: int | |
| number of times the measurement was repeated | |
| best: float | |
| best execution time / number | |
| all_runs : list[float] | |
| execution time of each run (in s) | |
| compile_time: float | |
| time of statement compilation (s) | |
| """ | |
| def __init__(self, loops, repeat, best, worst, all_runs, compile_time, precision): | |
| self.loops = loops | |
| self.repeat = repeat | |
| self.best = best | |
| self.worst = worst | |
| self.all_runs = all_runs | |
| self.compile_time = compile_time | |
| self._precision = precision | |
| self.timings = [dt / self.loops for dt in all_runs] | |
| def average(self): | |
| return math.fsum(self.timings) / len(self.timings) | |
| def stdev(self): | |
| mean = self.average | |
| return (math.fsum([(x - mean) ** 2 for x in self.timings]) / len(self.timings)) ** 0.5 | |
| def __str__(self): | |
| pm = '+-' | |
| if hasattr(sys.stdout, 'encoding') and sys.stdout.encoding: | |
| try: | |
| "\xb1".encode(sys.stdout.encoding) | |
| pm = "\xb1" | |
| except: | |
| pass | |
| return "{mean} {pm} {std} per loop (mean {pm} std. dev. of {runs} run{run_plural}, {loops:,} loop{loop_plural} each)".format( | |
| pm=pm, | |
| runs=self.repeat, | |
| loops=self.loops, | |
| loop_plural="" if self.loops == 1 else "s", | |
| run_plural="" if self.repeat == 1 else "s", | |
| mean=_format_time(self.average, self._precision), | |
| std=_format_time(self.stdev, self._precision), | |
| ) | |
| def _repr_pretty_(self, p , cycle): | |
| unic = self.__str__() | |
| p.text("<TimeitResult : " + unic + ">") | |
| class TimeitTemplateFiller(ast.NodeTransformer): | |
| """Fill in the AST template for timing execution. | |
| This is quite closely tied to the template definition, which is in | |
| :meth:`ExecutionMagics.timeit`. | |
| """ | |
| def __init__(self, ast_setup, ast_stmt): | |
| self.ast_setup = ast_setup | |
| self.ast_stmt = ast_stmt | |
| def visit_FunctionDef(self, node): | |
| "Fill in the setup statement" | |
| self.generic_visit(node) | |
| if node.name == "inner": | |
| node.body[:1] = self.ast_setup.body | |
| return node | |
| def visit_For(self, node): | |
| "Fill in the statement to be timed" | |
| if getattr(getattr(node.body[0], 'value', None), 'id', None) == 'stmt': | |
| node.body = self.ast_stmt.body | |
| return node | |
| class Timer(timeit.Timer): | |
| """Timer class that explicitly uses self.inner | |
| which is an undocumented implementation detail of CPython, | |
| not shared by PyPy. | |
| """ | |
| # Timer.timeit copied from CPython 3.4.2 | |
| def timeit(self, number=timeit.default_number): | |
| """Time 'number' executions of the main statement. | |
| To be precise, this executes the setup statement once, and | |
| then returns the time it takes to execute the main statement | |
| a number of times, as a float measured in seconds. The | |
| argument is the number of times through the loop, defaulting | |
| to one million. The main statement, the setup statement and | |
| the timer function to be used are passed to the constructor. | |
| """ | |
| it = itertools.repeat(None, number) | |
| gcold = gc.isenabled() | |
| gc.disable() | |
| try: | |
| timing = self.inner(it, self.timer) | |
| finally: | |
| if gcold: | |
| gc.enable() | |
| return timing | |
| class ExecutionMagics(Magics): | |
| """Magics related to code execution, debugging, profiling, etc.""" | |
| _transformers: Dict[str, Any] = {} | |
| def __init__(self, shell): | |
| super(ExecutionMagics, self).__init__(shell) | |
| # Default execution function used to actually run user code. | |
| self.default_runner = None | |
| def prun(self, parameter_s='', cell=None): | |
| """Run a statement through the python code profiler. | |
| **Usage, in line mode**:: | |
| %prun [options] statement | |
| **Usage, in cell mode**:: | |
| %%prun [options] [statement] | |
| code... | |
| code... | |
| In cell mode, the additional code lines are appended to the (possibly | |
| empty) statement in the first line. Cell mode allows you to easily | |
| profile multiline blocks without having to put them in a separate | |
| function. | |
| The given statement (which doesn't require quote marks) is run via the | |
| python profiler in a manner similar to the profile.run() function. | |
| Namespaces are internally managed to work correctly; profile.run | |
| cannot be used in IPython because it makes certain assumptions about | |
| namespaces which do not hold under IPython. | |
| Options: | |
| -l <limit> | |
| you can place restrictions on what or how much of the | |
| profile gets printed. The limit value can be: | |
| * A string: only information for function names containing this string | |
| is printed. | |
| * An integer: only these many lines are printed. | |
| * A float (between 0 and 1): this fraction of the report is printed | |
| (for example, use a limit of 0.4 to see the topmost 40% only). | |
| You can combine several limits with repeated use of the option. For | |
| example, ``-l __init__ -l 5`` will print only the topmost 5 lines of | |
| information about class constructors. | |
| -r | |
| return the pstats.Stats object generated by the profiling. This | |
| object has all the information about the profile in it, and you can | |
| later use it for further analysis or in other functions. | |
| -s <key> | |
| sort profile by given key. You can provide more than one key | |
| by using the option several times: '-s key1 -s key2 -s key3...'. The | |
| default sorting key is 'time'. | |
| The following is copied verbatim from the profile documentation | |
| referenced below: | |
| When more than one key is provided, additional keys are used as | |
| secondary criteria when the there is equality in all keys selected | |
| before them. | |
| Abbreviations can be used for any key names, as long as the | |
| abbreviation is unambiguous. The following are the keys currently | |
| defined: | |
| ============ ===================== | |
| Valid Arg Meaning | |
| ============ ===================== | |
| "calls" call count | |
| "cumulative" cumulative time | |
| "file" file name | |
| "module" file name | |
| "pcalls" primitive call count | |
| "line" line number | |
| "name" function name | |
| "nfl" name/file/line | |
| "stdname" standard name | |
| "time" internal time | |
| ============ ===================== | |
| Note that all sorts on statistics are in descending order (placing | |
| most time consuming items first), where as name, file, and line number | |
| searches are in ascending order (i.e., alphabetical). The subtle | |
| distinction between "nfl" and "stdname" is that the standard name is a | |
| sort of the name as printed, which means that the embedded line | |
| numbers get compared in an odd way. For example, lines 3, 20, and 40 | |
| would (if the file names were the same) appear in the string order | |
| "20" "3" and "40". In contrast, "nfl" does a numeric compare of the | |
| line numbers. In fact, sort_stats("nfl") is the same as | |
| sort_stats("name", "file", "line"). | |
| -T <filename> | |
| save profile results as shown on screen to a text | |
| file. The profile is still shown on screen. | |
| -D <filename> | |
| save (via dump_stats) profile statistics to given | |
| filename. This data is in a format understood by the pstats module, and | |
| is generated by a call to the dump_stats() method of profile | |
| objects. The profile is still shown on screen. | |
| -q | |
| suppress output to the pager. Best used with -T and/or -D above. | |
| If you want to run complete programs under the profiler's control, use | |
| ``%run -p [prof_opts] filename.py [args to program]`` where prof_opts | |
| contains profiler specific options as described here. | |
| You can read the complete documentation for the profile module with:: | |
| In [1]: import profile; profile.help() | |
| .. versionchanged:: 7.3 | |
| User variables are no longer expanded, | |
| the magic line is always left unmodified. | |
| """ | |
| # TODO: port to magic_arguments as currently this is duplicated in IPCompleter._extract_code | |
| opts, arg_str = self.parse_options(parameter_s, 'D:l:rs:T:q', | |
| list_all=True, posix=False) | |
| if cell is not None: | |
| arg_str += '\n' + cell | |
| arg_str = self.shell.transform_cell(arg_str) | |
| return self._run_with_profiler(arg_str, opts, self.shell.user_ns) | |
| def _run_with_profiler(self, code, opts, namespace): | |
| """ | |
| Run `code` with profiler. Used by ``%prun`` and ``%run -p``. | |
| Parameters | |
| ---------- | |
| code : str | |
| Code to be executed. | |
| opts : Struct | |
| Options parsed by `self.parse_options`. | |
| namespace : dict | |
| A dictionary for Python namespace (e.g., `self.shell.user_ns`). | |
| """ | |
| # Fill default values for unspecified options: | |
| opts.merge(Struct(D=[''], l=[], s=['time'], T=[''])) | |
| prof = profile.Profile() | |
| try: | |
| prof = prof.runctx(code, namespace, namespace) | |
| sys_exit = '' | |
| except SystemExit: | |
| sys_exit = """*** SystemExit exception caught in code being profiled.""" | |
| stats = pstats.Stats(prof).strip_dirs().sort_stats(*opts.s) | |
| lims = opts.l | |
| if lims: | |
| lims = [] # rebuild lims with ints/floats/strings | |
| for lim in opts.l: | |
| try: | |
| lims.append(int(lim)) | |
| except ValueError: | |
| try: | |
| lims.append(float(lim)) | |
| except ValueError: | |
| lims.append(lim) | |
| # Trap output. | |
| stdout_trap = StringIO() | |
| stats_stream = stats.stream | |
| try: | |
| stats.stream = stdout_trap | |
| stats.print_stats(*lims) | |
| finally: | |
| stats.stream = stats_stream | |
| output = stdout_trap.getvalue() | |
| output = output.rstrip() | |
| if 'q' not in opts: | |
| page.page(output) | |
| print(sys_exit, end=' ') | |
| dump_file = opts.D[0] | |
| text_file = opts.T[0] | |
| if dump_file: | |
| prof.dump_stats(dump_file) | |
| print( | |
| f"\n*** Profile stats marshalled to file {repr(dump_file)}.{sys_exit}" | |
| ) | |
| if text_file: | |
| pfile = Path(text_file) | |
| pfile.touch(exist_ok=True) | |
| pfile.write_text(output, encoding="utf-8") | |
| print( | |
| f"\n*** Profile printout saved to text file {repr(text_file)}.{sys_exit}" | |
| ) | |
| if 'r' in opts: | |
| return stats | |
| return None | |
| def pdb(self, parameter_s=''): | |
| """Control the automatic calling of the pdb interactive debugger. | |
| Call as '%pdb on', '%pdb 1', '%pdb off' or '%pdb 0'. If called without | |
| argument it works as a toggle. | |
| When an exception is triggered, IPython can optionally call the | |
| interactive pdb debugger after the traceback printout. %pdb toggles | |
| this feature on and off. | |
| The initial state of this feature is set in your configuration | |
| file (the option is ``InteractiveShell.pdb``). | |
| If you want to just activate the debugger AFTER an exception has fired, | |
| without having to type '%pdb on' and rerunning your code, you can use | |
| the %debug magic.""" | |
| par = parameter_s.strip().lower() | |
| new_pdb: bool | |
| if par: | |
| try: | |
| new_pdb = {"off": False, "0": False, "on": True, "1": True}[par] | |
| except KeyError: | |
| print ('Incorrect argument. Use on/1, off/0, ' | |
| 'or nothing for a toggle.') | |
| return | |
| else: | |
| # toggle | |
| new_pdb = not self.shell.call_pdb | |
| # set on the shell | |
| self.shell.call_pdb = new_pdb | |
| print("Automatic pdb calling has been turned", "ON" if new_pdb else "OFF") | |
| def debug(self, line="", cell=None, local_ns=None): | |
| """Activate the interactive debugger. | |
| This magic command support two ways of activating debugger. | |
| One is to activate debugger before executing code. This way, you | |
| can set a break point, to step through the code from the point. | |
| You can use this mode by giving statements to execute and optionally | |
| a breakpoint. | |
| The other one is to activate debugger in post-mortem mode. You can | |
| activate this mode simply running %debug without any argument. | |
| If an exception has just occurred, this lets you inspect its stack | |
| frames interactively. Note that this will always work only on the last | |
| traceback that occurred, so you must call this quickly after an | |
| exception that you wish to inspect has fired, because if another one | |
| occurs, it clobbers the previous one. | |
| If you want IPython to automatically do this on every exception, see | |
| the %pdb magic for more details. | |
| .. versionchanged:: 7.3 | |
| When running code, user variables are no longer expanded, | |
| the magic line is always left unmodified. | |
| """ | |
| args, extra = magic_arguments.parse_argstring(self.debug, line, partial=True) | |
| if not (args.breakpoint or extra or cell): | |
| self._debug_post_mortem() | |
| elif not (args.breakpoint or cell): | |
| # If there is no breakpoints, the line is just code to execute | |
| self._debug_exec(line, None, local_ns) | |
| else: | |
| # Here we try to reconstruct the code from the output of | |
| # parse_argstring. This might not work if the code has spaces | |
| # For example this fails for `print("a b")` | |
| code = " ".join(extra) | |
| if cell: | |
| code += "\n" + cell | |
| self._debug_exec(code, args.breakpoint, local_ns) | |
| def _debug_post_mortem(self): | |
| self.shell.debugger(force=True) | |
| def _debug_exec(self, code, breakpoint, local_ns=None): | |
| if breakpoint: | |
| (filename, bp_line) = breakpoint.rsplit(':', 1) | |
| bp_line = int(bp_line) | |
| else: | |
| (filename, bp_line) = (None, None) | |
| self._run_with_debugger( | |
| code, self.shell.user_ns, filename, bp_line, local_ns=local_ns | |
| ) | |
| def tb(self, s): | |
| """Print the last traceback. | |
| Optionally, specify an exception reporting mode, tuning the | |
| verbosity of the traceback. By default the currently-active exception | |
| mode is used. See %xmode for changing exception reporting modes. | |
| Valid modes: Plain, Context, Verbose, and Minimal. | |
| """ | |
| interactive_tb = self.shell.InteractiveTB | |
| if s: | |
| # Switch exception reporting mode for this one call. | |
| # Ensure it is switched back. | |
| def xmode_switch_err(name): | |
| warn('Error changing %s exception modes.\n%s' % | |
| (name,sys.exc_info()[1])) | |
| new_mode = s.strip().capitalize() | |
| original_mode = interactive_tb.mode | |
| try: | |
| try: | |
| interactive_tb.set_mode(mode=new_mode) | |
| except Exception: | |
| xmode_switch_err('user') | |
| else: | |
| self.shell.showtraceback() | |
| finally: | |
| interactive_tb.set_mode(mode=original_mode) | |
| else: | |
| self.shell.showtraceback() | |
| def run(self, parameter_s='', runner=None, | |
| file_finder=get_py_filename): | |
| """Run the named file inside IPython as a program. | |
| Usage:: | |
| %run [-n -i -e -G] | |
| [( -t [-N<N>] | -d [-b<N>] | -p [profile options] )] | |
| ( -m mod | filename ) [args] | |
| The filename argument should be either a pure Python script (with | |
| extension ``.py``), or a file with custom IPython syntax (such as | |
| magics). If the latter, the file can be either a script with ``.ipy`` | |
| extension, or a Jupyter notebook with ``.ipynb`` extension. When running | |
| a Jupyter notebook, the output from print statements and other | |
| displayed objects will appear in the terminal (even matplotlib figures | |
| will open, if a terminal-compliant backend is being used). Note that, | |
| at the system command line, the ``jupyter run`` command offers similar | |
| functionality for executing notebooks (albeit currently with some | |
| differences in supported options). | |
| Parameters after the filename are passed as command-line arguments to | |
| the program (put in sys.argv). Then, control returns to IPython's | |
| prompt. | |
| This is similar to running at a system prompt ``python file args``, | |
| but with the advantage of giving you IPython's tracebacks, and of | |
| loading all variables into your interactive namespace for further use | |
| (unless -p is used, see below). | |
| The file is executed in a namespace initially consisting only of | |
| ``__name__=='__main__'`` and sys.argv constructed as indicated. It thus | |
| sees its environment as if it were being run as a stand-alone program | |
| (except for sharing global objects such as previously imported | |
| modules). But after execution, the IPython interactive namespace gets | |
| updated with all variables defined in the program (except for ``__name__`` | |
| and ``sys.argv``). This allows for very convenient loading of code for | |
| interactive work, while giving each program a 'clean sheet' to run in. | |
| Arguments are expanded using shell-like glob match. Patterns | |
| '*', '?', '[seq]' and '[!seq]' can be used. Additionally, | |
| tilde '~' will be expanded into user's home directory. Unlike | |
| real shells, quotation does not suppress expansions. Use | |
| *two* back slashes (e.g. ``\\\\*``) to suppress expansions. | |
| To completely disable these expansions, you can use -G flag. | |
| On Windows systems, the use of single quotes `'` when specifying | |
| a file is not supported. Use double quotes `"`. | |
| Options: | |
| -n | |
| __name__ is NOT set to '__main__', but to the running file's name | |
| without extension (as python does under import). This allows running | |
| scripts and reloading the definitions in them without calling code | |
| protected by an ``if __name__ == "__main__"`` clause. | |
| -i | |
| run the file in IPython's namespace instead of an empty one. This | |
| is useful if you are experimenting with code written in a text editor | |
| which depends on variables defined interactively. | |
| -e | |
| ignore sys.exit() calls or SystemExit exceptions in the script | |
| being run. This is particularly useful if IPython is being used to | |
| run unittests, which always exit with a sys.exit() call. In such | |
| cases you are interested in the output of the test results, not in | |
| seeing a traceback of the unittest module. | |
| -t | |
| print timing information at the end of the run. IPython will give | |
| you an estimated CPU time consumption for your script, which under | |
| Unix uses the resource module to avoid the wraparound problems of | |
| time.clock(). Under Unix, an estimate of time spent on system tasks | |
| is also given (for Windows platforms this is reported as 0.0). | |
| If -t is given, an additional ``-N<N>`` option can be given, where <N> | |
| must be an integer indicating how many times you want the script to | |
| run. The final timing report will include total and per run results. | |
| For example (testing the script myscript.py):: | |
| In [1]: run -t myscript | |
| IPython CPU timings (estimated): | |
| User : 0.19597 s. | |
| System: 0.0 s. | |
| In [2]: run -t -N5 myscript | |
| IPython CPU timings (estimated): | |
| Total runs performed: 5 | |
| Times : Total Per run | |
| User : 0.910862 s, 0.1821724 s. | |
| System: 0.0 s, 0.0 s. | |
| -d | |
| run your program under the control of pdb, the Python debugger. | |
| This allows you to execute your program step by step, watch variables, | |
| etc. Internally, what IPython does is similar to calling:: | |
| pdb.run('execfile("YOURFILENAME")') | |
| with a breakpoint set on line 1 of your file. You can change the line | |
| number for this automatic breakpoint to be <N> by using the -bN option | |
| (where N must be an integer). For example:: | |
| %run -d -b40 myscript | |
| will set the first breakpoint at line 40 in myscript.py. Note that | |
| the first breakpoint must be set on a line which actually does | |
| something (not a comment or docstring) for it to stop execution. | |
| Or you can specify a breakpoint in a different file:: | |
| %run -d -b myotherfile.py:20 myscript | |
| When the pdb debugger starts, you will see a (Pdb) prompt. You must | |
| first enter 'c' (without quotes) to start execution up to the first | |
| breakpoint. | |
| Entering 'help' gives information about the use of the debugger. You | |
| can easily see pdb's full documentation with "import pdb;pdb.help()" | |
| at a prompt. | |
| -p | |
| run program under the control of the Python profiler module (which | |
| prints a detailed report of execution times, function calls, etc). | |
| You can pass other options after -p which affect the behavior of the | |
| profiler itself. See the docs for %prun for details. | |
| In this mode, the program's variables do NOT propagate back to the | |
| IPython interactive namespace (because they remain in the namespace | |
| where the profiler executes them). | |
| Internally this triggers a call to %prun, see its documentation for | |
| details on the options available specifically for profiling. | |
| There is one special usage for which the text above doesn't apply: | |
| if the filename ends with .ipy[nb], the file is run as ipython script, | |
| just as if the commands were written on IPython prompt. | |
| -m | |
| specify module name to load instead of script path. Similar to | |
| the -m option for the python interpreter. Use this option last if you | |
| want to combine with other %run options. Unlike the python interpreter | |
| only source modules are allowed no .pyc or .pyo files. | |
| For example:: | |
| %run -m example | |
| will run the example module. | |
| -G | |
| disable shell-like glob expansion of arguments. | |
| """ | |
| # Logic to handle issue #3664 | |
| # Add '--' after '-m <module_name>' to ignore additional args passed to a module. | |
| if '-m' in parameter_s and '--' not in parameter_s: | |
| argv = shlex.split(parameter_s, posix=(os.name == 'posix')) | |
| for idx, arg in enumerate(argv): | |
| if arg and arg.startswith('-') and arg != '-': | |
| if arg == '-m': | |
| argv.insert(idx + 2, '--') | |
| break | |
| else: | |
| # Positional arg, break | |
| break | |
| parameter_s = ' '.join(shlex.quote(arg) for arg in argv) | |
| # get arguments and set sys.argv for program to be run. | |
| opts, arg_lst = self.parse_options(parameter_s, | |
| 'nidtN:b:pD:l:rs:T:em:G', | |
| mode='list', list_all=1) | |
| if "m" in opts: | |
| modulename = opts["m"][0] | |
| modpath = find_mod(modulename) | |
| if modpath is None: | |
| msg = '%r is not a valid modulename on sys.path'%modulename | |
| raise Exception(msg) | |
| arg_lst = [modpath] + arg_lst | |
| try: | |
| fpath = None # initialize to make sure fpath is in scope later | |
| fpath = arg_lst[0] | |
| filename = file_finder(fpath) | |
| except IndexError as e: | |
| msg = 'you must provide at least a filename.' | |
| raise Exception(msg) from e | |
| except IOError as e: | |
| try: | |
| msg = str(e) | |
| except UnicodeError: | |
| msg = e.message | |
| if os.name == 'nt' and re.match(r"^'.*'$",fpath): | |
| warn('For Windows, use double quotes to wrap a filename: %run "mypath\\myfile.py"') | |
| raise Exception(msg) from e | |
| except TypeError: | |
| if fpath in sys.meta_path: | |
| filename = "" | |
| else: | |
| raise | |
| if filename.lower().endswith(('.ipy', '.ipynb')): | |
| with preserve_keys(self.shell.user_ns, '__file__'): | |
| self.shell.user_ns['__file__'] = filename | |
| self.shell.safe_execfile_ipy(filename, raise_exceptions=True) | |
| return | |
| # Control the response to exit() calls made by the script being run | |
| exit_ignore = 'e' in opts | |
| # Make sure that the running script gets a proper sys.argv as if it | |
| # were run from a system shell. | |
| save_argv = sys.argv # save it for later restoring | |
| if 'G' in opts: | |
| args = arg_lst[1:] | |
| else: | |
| # tilde and glob expansion | |
| args = shellglob(map(os.path.expanduser, arg_lst[1:])) | |
| sys.argv = [filename] + args # put in the proper filename | |
| if 'n' in opts: | |
| name = Path(filename).stem | |
| else: | |
| name = '__main__' | |
| if 'i' in opts: | |
| # Run in user's interactive namespace | |
| prog_ns = self.shell.user_ns | |
| __name__save = self.shell.user_ns['__name__'] | |
| prog_ns['__name__'] = name | |
| main_mod = self.shell.user_module | |
| # Since '%run foo' emulates 'python foo.py' at the cmd line, we must | |
| # set the __file__ global in the script's namespace | |
| # TK: Is this necessary in interactive mode? | |
| prog_ns['__file__'] = filename | |
| else: | |
| # Run in a fresh, empty namespace | |
| # The shell MUST hold a reference to prog_ns so after %run | |
| # exits, the python deletion mechanism doesn't zero it out | |
| # (leaving dangling references). See interactiveshell for details | |
| main_mod = self.shell.new_main_mod(filename, name) | |
| prog_ns = main_mod.__dict__ | |
| # pickle fix. See interactiveshell for an explanation. But we need to | |
| # make sure that, if we overwrite __main__, we replace it at the end | |
| main_mod_name = prog_ns['__name__'] | |
| if main_mod_name == '__main__': | |
| restore_main = sys.modules['__main__'] | |
| else: | |
| restore_main = False | |
| # This needs to be undone at the end to prevent holding references to | |
| # every single object ever created. | |
| sys.modules[main_mod_name] = main_mod | |
| if 'p' in opts or 'd' in opts: | |
| if 'm' in opts: | |
| code = 'run_module(modulename, prog_ns)' | |
| code_ns = { | |
| 'run_module': self.shell.safe_run_module, | |
| 'prog_ns': prog_ns, | |
| 'modulename': modulename, | |
| } | |
| else: | |
| if 'd' in opts: | |
| # allow exceptions to raise in debug mode | |
| code = 'execfile(filename, prog_ns, raise_exceptions=True)' | |
| else: | |
| code = 'execfile(filename, prog_ns)' | |
| code_ns = { | |
| 'execfile': self.shell.safe_execfile, | |
| 'prog_ns': prog_ns, | |
| 'filename': get_py_filename(filename), | |
| } | |
| try: | |
| stats = None | |
| if 'p' in opts: | |
| stats = self._run_with_profiler(code, opts, code_ns) | |
| else: | |
| if 'd' in opts: | |
| bp_file, bp_line = parse_breakpoint( | |
| opts.get('b', ['1'])[0], filename) | |
| self._run_with_debugger( | |
| code, code_ns, filename, bp_line, bp_file) | |
| else: | |
| if 'm' in opts: | |
| def run(): | |
| self.shell.safe_run_module(modulename, prog_ns) | |
| else: | |
| if runner is None: | |
| runner = self.default_runner | |
| if runner is None: | |
| runner = self.shell.safe_execfile | |
| def run(): | |
| runner(filename, prog_ns, prog_ns, | |
| exit_ignore=exit_ignore) | |
| if 't' in opts: | |
| # timed execution | |
| try: | |
| nruns = int(opts['N'][0]) | |
| if nruns < 1: | |
| error('Number of runs must be >=1') | |
| return | |
| except (KeyError): | |
| nruns = 1 | |
| self._run_with_timing(run, nruns) | |
| else: | |
| # regular execution | |
| run() | |
| if 'i' in opts: | |
| self.shell.user_ns['__name__'] = __name__save | |
| else: | |
| # update IPython interactive namespace | |
| # Some forms of read errors on the file may mean the | |
| # __name__ key was never set; using pop we don't have to | |
| # worry about a possible KeyError. | |
| prog_ns.pop('__name__', None) | |
| with preserve_keys(self.shell.user_ns, '__file__'): | |
| self.shell.user_ns.update(prog_ns) | |
| finally: | |
| # It's a bit of a mystery why, but __builtins__ can change from | |
| # being a module to becoming a dict missing some key data after | |
| # %run. As best I can see, this is NOT something IPython is doing | |
| # at all, and similar problems have been reported before: | |
| # http://coding.derkeiler.com/Archive/Python/comp.lang.python/2004-10/0188.html | |
| # Since this seems to be done by the interpreter itself, the best | |
| # we can do is to at least restore __builtins__ for the user on | |
| # exit. | |
| self.shell.user_ns['__builtins__'] = builtin_mod | |
| # Ensure key global structures are restored | |
| sys.argv = save_argv | |
| if restore_main: | |
| sys.modules['__main__'] = restore_main | |
| if '__mp_main__' in sys.modules: | |
| sys.modules['__mp_main__'] = restore_main | |
| else: | |
| # Remove from sys.modules the reference to main_mod we'd | |
| # added. Otherwise it will trap references to objects | |
| # contained therein. | |
| del sys.modules[main_mod_name] | |
| return stats | |
| def _run_with_debugger( | |
| self, code, code_ns, filename=None, bp_line=None, bp_file=None, local_ns=None | |
| ): | |
| """ | |
| Run `code` in debugger with a break point. | |
| Parameters | |
| ---------- | |
| code : str | |
| Code to execute. | |
| code_ns : dict | |
| A namespace in which `code` is executed. | |
| filename : str | |
| `code` is ran as if it is in `filename`. | |
| bp_line : int, optional | |
| Line number of the break point. | |
| bp_file : str, optional | |
| Path to the file in which break point is specified. | |
| `filename` is used if not given. | |
| local_ns : dict, optional | |
| A local namespace in which `code` is executed. | |
| Raises | |
| ------ | |
| UsageError | |
| If the break point given by `bp_line` is not valid. | |
| """ | |
| deb = self.shell.InteractiveTB.pdb | |
| if not deb: | |
| self.shell.InteractiveTB.pdb = self.shell.InteractiveTB.debugger_cls() | |
| deb = self.shell.InteractiveTB.pdb | |
| # reset Breakpoint state, which is moronically kept | |
| # in a class | |
| bdb.Breakpoint.next = 1 | |
| bdb.Breakpoint.bplist = {} | |
| bdb.Breakpoint.bpbynumber = [None] | |
| deb.clear_all_breaks() | |
| if bp_line is not None: | |
| # Set an initial breakpoint to stop execution | |
| maxtries = 10 | |
| bp_file = bp_file or filename | |
| checkline = deb.checkline(bp_file, bp_line) | |
| if not checkline: | |
| for bp in range(bp_line + 1, bp_line + maxtries + 1): | |
| if deb.checkline(bp_file, bp): | |
| break | |
| else: | |
| msg = ("\nI failed to find a valid line to set " | |
| "a breakpoint\n" | |
| "after trying up to line: %s.\n" | |
| "Please set a valid breakpoint manually " | |
| "with the -b option." % bp) | |
| raise UsageError(msg) | |
| # if we find a good linenumber, set the breakpoint | |
| deb.do_break('%s:%s' % (bp_file, bp_line)) | |
| if filename: | |
| # Mimic Pdb._runscript(...) | |
| deb._wait_for_mainpyfile = True | |
| deb.mainpyfile = deb.canonic(filename) | |
| # Start file run | |
| print("NOTE: Enter 'c' at the %s prompt to continue execution." % deb.prompt) | |
| try: | |
| if filename: | |
| # save filename so it can be used by methods on the deb object | |
| deb._exec_filename = filename | |
| while True: | |
| try: | |
| trace = sys.gettrace() | |
| deb.run(code, code_ns, local_ns) | |
| except Restart: | |
| print("Restarting") | |
| if filename: | |
| deb._wait_for_mainpyfile = True | |
| deb.mainpyfile = deb.canonic(filename) | |
| continue | |
| else: | |
| break | |
| finally: | |
| sys.settrace(trace) | |
| # Perform proper cleanup of the session in case if | |
| # it exited with "continue" and not "quit" command | |
| if hasattr(deb, "rcLines"): | |
| # Run this code defensively in case if custom debugger | |
| # class does not implement rcLines, which although public | |
| # is an implementation detail of `pdb.Pdb` and not part of | |
| # the more generic basic debugger framework (`bdb.Bdb`). | |
| deb.set_quit() | |
| deb.rcLines.extend(["q"]) | |
| try: | |
| deb.run("", code_ns, local_ns) | |
| except StopIteration: | |
| # Stop iteration is raised on quit command | |
| pass | |
| except Exception: | |
| etype, value, tb = sys.exc_info() | |
| # Skip three frames in the traceback: the %run one, | |
| # one inside bdb.py, and the command-line typed by the | |
| # user (run by exec in pdb itself). | |
| self.shell.InteractiveTB(etype, value, tb, tb_offset=3) | |
| def _run_with_timing(run, nruns): | |
| """ | |
| Run function `run` and print timing information. | |
| Parameters | |
| ---------- | |
| run : callable | |
| Any callable object which takes no argument. | |
| nruns : int | |
| Number of times to execute `run`. | |
| """ | |
| twall0 = time.perf_counter() | |
| if nruns == 1: | |
| t0 = clock2() | |
| run() | |
| t1 = clock2() | |
| t_usr = t1[0] - t0[0] | |
| t_sys = t1[1] - t0[1] | |
| print("\nIPython CPU timings (estimated):") | |
| print(" User : %10.2f s." % t_usr) | |
| print(" System : %10.2f s." % t_sys) | |
| else: | |
| runs = range(nruns) | |
| t0 = clock2() | |
| for nr in runs: | |
| run() | |
| t1 = clock2() | |
| t_usr = t1[0] - t0[0] | |
| t_sys = t1[1] - t0[1] | |
| print("\nIPython CPU timings (estimated):") | |
| print("Total runs performed:", nruns) | |
| print(" Times : %10s %10s" % ('Total', 'Per run')) | |
| print(" User : %10.2f s, %10.2f s." % (t_usr, t_usr / nruns)) | |
| print(" System : %10.2f s, %10.2f s." % (t_sys, t_sys / nruns)) | |
| twall1 = time.perf_counter() | |
| print("Wall time: %10.2f s." % (twall1 - twall0)) | |
| def timeit(self, line='', cell=None, local_ns=None): | |
| """Time execution of a Python statement or expression | |
| **Usage, in line mode**:: | |
| %timeit [-n<N> -r<R> [-t|-c] -q -p<P> [-o|-v <V>]] statement | |
| **or in cell mode**:: | |
| %%timeit [-n<N> -r<R> [-t|-c] -q -p<P> [-o|-v <V>]] setup_code | |
| code | |
| code... | |
| Time execution of a Python statement or expression using the timeit | |
| module. This function can be used both as a line and cell magic: | |
| - In line mode you can time a single-line statement (though multiple | |
| ones can be chained with using semicolons). | |
| - In cell mode, the statement in the first line is used as setup code | |
| (executed but not timed) and the body of the cell is timed. The cell | |
| body has access to any variables created in the setup code. | |
| Options: | |
| -n<N> | |
| Execute the given statement N times in a loop. If N is not | |
| provided, N is determined so as to get sufficient accuracy. | |
| -r<R> | |
| Number of repeats R, each consisting of N loops, and take the | |
| average result. | |
| Default: 7 | |
| -t | |
| Use ``time.time`` to measure the time, which is the default on Unix. | |
| This function measures wall time. | |
| -c | |
| Use ``time.clock`` to measure the time, which is the default on | |
| Windows and measures wall time. On Unix, ``resource.getrusage`` is used | |
| instead and returns the CPU user time. | |
| -p<P> | |
| Use a precision of P digits to display the timing result. | |
| Default: 3 | |
| -q | |
| Quiet, do not print result. | |
| -o | |
| Return a ``TimeitResult`` that can be stored in a variable to inspect | |
| the result in more details. | |
| -v <V> | |
| Like ``-o``, but save the ``TimeitResult`` directly to variable <V>. | |
| .. versionchanged:: 7.3 | |
| User variables are no longer expanded, | |
| the magic line is always left unmodified. | |
| Examples | |
| -------- | |
| :: | |
| In [1]: %timeit pass | |
| 8.26 ns ± 0.12 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each) | |
| In [2]: u = None | |
| In [3]: %timeit u is None | |
| 29.9 ns ± 0.643 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each) | |
| In [4]: %timeit -r 4 u == None | |
| In [5]: import time | |
| In [6]: %timeit -n1 time.sleep(2) | |
| The times reported by ``%timeit`` will be slightly higher than those | |
| reported by the timeit.py script when variables are accessed. This is | |
| due to the fact that ``%timeit`` executes the statement in the namespace | |
| of the shell, compared with timeit.py, which uses a single setup | |
| statement to import function or create variables. Generally, the bias | |
| does not matter as long as results from timeit.py are not mixed with | |
| those from ``%timeit``.""" | |
| # TODO: port to magic_arguments as currently this is duplicated in IPCompleter._extract_code | |
| opts, stmt = self.parse_options( | |
| line, "n:r:tcp:qov:", posix=False, strict=False, preserve_non_opts=True | |
| ) | |
| if stmt == "" and cell is None: | |
| return | |
| timefunc = timeit.default_timer | |
| number = int(getattr(opts, "n", 0)) | |
| default_repeat = 7 if timeit.default_repeat < 7 else timeit.default_repeat | |
| repeat = int(getattr(opts, "r", default_repeat)) | |
| precision = int(getattr(opts, "p", 3)) | |
| quiet = "q" in opts | |
| return_result = "o" in opts | |
| save_result = "v" in opts | |
| if hasattr(opts, "t"): | |
| timefunc = time.time | |
| if hasattr(opts, "c"): | |
| timefunc = clock | |
| timer = Timer(timer=timefunc) | |
| # this code has tight coupling to the inner workings of timeit.Timer, | |
| # but is there a better way to achieve that the code stmt has access | |
| # to the shell namespace? | |
| transform = self.shell.transform_cell | |
| if cell is None: | |
| # called as line magic | |
| ast_setup = self.shell.compile.ast_parse("pass") | |
| ast_stmt = self.shell.compile.ast_parse(transform(stmt)) | |
| else: | |
| ast_setup = self.shell.compile.ast_parse(transform(stmt)) | |
| ast_stmt = self.shell.compile.ast_parse(transform(cell)) | |
| ast_setup = self.shell.transform_ast(ast_setup) | |
| ast_stmt = self.shell.transform_ast(ast_stmt) | |
| # Check that these compile to valid Python code *outside* the timer func | |
| # Invalid code may become valid when put inside the function & loop, | |
| # which messes up error messages. | |
| # https://github.com/ipython/ipython/issues/10636 | |
| self.shell.compile(ast_setup, "<magic-timeit-setup>", "exec") | |
| self.shell.compile(ast_stmt, "<magic-timeit-stmt>", "exec") | |
| # This codestring is taken from timeit.template - we fill it in as an | |
| # AST, so that we can apply our AST transformations to the user code | |
| # without affecting the timing code. | |
| timeit_ast_template = ast.parse('def inner(_it, _timer):\n' | |
| ' setup\n' | |
| ' _t0 = _timer()\n' | |
| ' for _i in _it:\n' | |
| ' stmt\n' | |
| ' _t1 = _timer()\n' | |
| ' return _t1 - _t0\n') | |
| timeit_ast = TimeitTemplateFiller(ast_setup, ast_stmt).visit(timeit_ast_template) | |
| timeit_ast = ast.fix_missing_locations(timeit_ast) | |
| # Track compilation time so it can be reported if too long | |
| # Minimum time above which compilation time will be reported | |
| tc_min = 0.1 | |
| t0 = clock() | |
| code = self.shell.compile(timeit_ast, "<magic-timeit>", "exec") | |
| tc = clock()-t0 | |
| ns = {} | |
| glob = self.shell.user_ns | |
| # handles global vars with same name as local vars. We store them in conflict_globs. | |
| conflict_globs = {} | |
| if local_ns and cell is None: | |
| for var_name, var_val in glob.items(): | |
| if var_name in local_ns: | |
| conflict_globs[var_name] = var_val | |
| glob.update(local_ns) | |
| exec(code, glob, ns) | |
| timer.inner = ns["inner"] | |
| # This is used to check if there is a huge difference between the | |
| # best and worst timings. | |
| # Issue: https://github.com/ipython/ipython/issues/6471 | |
| if number == 0: | |
| # determine number so that 0.2 <= total time < 2.0 | |
| for index in range(0, 10): | |
| number = 10 ** index | |
| time_number = timer.timeit(number) | |
| if time_number >= 0.2: | |
| break | |
| all_runs = timer.repeat(repeat, number) | |
| best = min(all_runs) / number | |
| worst = max(all_runs) / number | |
| timeit_result = TimeitResult(number, repeat, best, worst, all_runs, tc, precision) | |
| # Restore global vars from conflict_globs | |
| if conflict_globs: | |
| glob.update(conflict_globs) | |
| if not quiet: | |
| # Check best timing is greater than zero to avoid a | |
| # ZeroDivisionError. | |
| # In cases where the slowest timing is lesser than a microsecond | |
| # we assume that it does not really matter if the fastest | |
| # timing is 4 times faster than the slowest timing or not. | |
| if worst > 4 * best and best > 0 and worst > 1e-6: | |
| print("The slowest run took %0.2f times longer than the " | |
| "fastest. This could mean that an intermediate result " | |
| "is being cached." % (worst / best)) | |
| print( timeit_result ) | |
| if tc > tc_min: | |
| print("Compiler time: %.2f s" % tc) | |
| if save_result: | |
| self.shell.user_ns[opts.v] = timeit_result | |
| if return_result: | |
| return timeit_result | |
| def time(self, line="", cell=None, local_ns=None): | |
| """Time execution of a Python statement or expression. | |
| The CPU and wall clock times are printed, and the value of the | |
| expression (if any) is returned. Note that under Win32, system time | |
| is always reported as 0, since it can not be measured. | |
| This function can be used both as a line and cell magic: | |
| - In line mode you can time a single-line statement (though multiple | |
| ones can be chained with using semicolons). | |
| - In cell mode, you can time the cell body (a directly | |
| following statement raises an error). | |
| This function provides very basic timing functionality. Use the timeit | |
| magic for more control over the measurement. | |
| .. versionchanged:: 7.3 | |
| User variables are no longer expanded, | |
| the magic line is always left unmodified. | |
| .. versionchanged:: 8.3 | |
| The time magic now correctly propagates system-exiting exceptions | |
| (such as ``KeyboardInterrupt`` invoked when interrupting execution) | |
| rather than just printing out the exception traceback. | |
| The non-system-exception will still be caught as before. | |
| Examples | |
| -------- | |
| :: | |
| In [1]: %time 2**128 | |
| CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s | |
| Wall time: 0.00 | |
| Out[1]: 340282366920938463463374607431768211456L | |
| In [2]: n = 1000000 | |
| In [3]: %time sum(range(n)) | |
| CPU times: user 1.20 s, sys: 0.05 s, total: 1.25 s | |
| Wall time: 1.37 | |
| Out[3]: 499999500000L | |
| In [4]: %time print('hello world') | |
| hello world | |
| CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s | |
| Wall time: 0.00 | |
| .. note:: | |
| The time needed by Python to compile the given expression will be | |
| reported if it is more than 0.1s. | |
| In the example below, the actual exponentiation is done by Python | |
| at compilation time, so while the expression can take a noticeable | |
| amount of time to compute, that time is purely due to the | |
| compilation:: | |
| In [5]: %time 3**9999; | |
| CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s | |
| Wall time: 0.00 s | |
| In [6]: %time 3**999999; | |
| CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s | |
| Wall time: 0.00 s | |
| Compiler : 0.78 s | |
| """ | |
| args, extra = magic_arguments.parse_argstring(self.time, line, partial=True) | |
| line = " ".join(extra) | |
| if line and cell: | |
| raise UsageError("Can't use statement directly after '%%time'!") | |
| if cell: | |
| expr = self.shell.transform_cell(cell) | |
| else: | |
| expr = self.shell.transform_cell(line) | |
| # Minimum time above which parse time will be reported | |
| tp_min = 0.1 | |
| t0 = clock() | |
| expr_ast = self.shell.compile.ast_parse(expr) | |
| tp = clock() - t0 | |
| # Apply AST transformations | |
| expr_ast = self.shell.transform_ast(expr_ast) | |
| # Minimum time above which compilation time will be reported | |
| tc_min = 0.1 | |
| expr_val = None | |
| if len(expr_ast.body) == 1 and isinstance(expr_ast.body[0], ast.Expr): | |
| mode = 'eval' | |
| source = '<timed eval>' | |
| expr_ast = ast.Expression(expr_ast.body[0].value) | |
| else: | |
| mode = 'exec' | |
| source = '<timed exec>' | |
| # multi-line %%time case | |
| if len(expr_ast.body) > 1 and isinstance(expr_ast.body[-1], ast.Expr): | |
| expr_val = expr_ast.body[-1] | |
| expr_ast = expr_ast.body[:-1] | |
| expr_ast = Module(expr_ast, []) | |
| expr_val = ast.Expression(expr_val.value) | |
| t0 = clock() | |
| code = self.shell.compile(expr_ast, source, mode) | |
| tc = clock() - t0 | |
| # skew measurement as little as possible | |
| glob = self.shell.user_ns | |
| wtime = time.time | |
| # time execution | |
| wall_st = wtime() | |
| # Track whether to propagate exceptions or exit | |
| exit_on_interrupt = False | |
| interrupt_occured = False | |
| captured_exception = None | |
| if mode == "eval": | |
| st = clock2() | |
| try: | |
| out = eval(code, glob, local_ns) | |
| except KeyboardInterrupt as e: | |
| captured_exception = e | |
| interrupt_occured = True | |
| exit_on_interrupt = True | |
| except Exception as e: | |
| captured_exception = e | |
| interrupt_occured = True | |
| if not args.no_raise_error: | |
| exit_on_interrupt = True | |
| end = clock2() | |
| else: | |
| st = clock2() | |
| try: | |
| exec(code, glob, local_ns) | |
| out = None | |
| # multi-line %%time case | |
| if expr_val is not None: | |
| code_2 = self.shell.compile(expr_val, source, 'eval') | |
| out = eval(code_2, glob, local_ns) | |
| except KeyboardInterrupt as e: | |
| captured_exception = e | |
| interrupt_occured = True | |
| exit_on_interrupt = True | |
| except Exception as e: | |
| captured_exception = e | |
| interrupt_occured = True | |
| if not args.no_raise_error: | |
| exit_on_interrupt = True | |
| end = clock2() | |
| wall_end = wtime() | |
| # Compute actual times and report | |
| wall_time = wall_end - wall_st | |
| cpu_user = end[0] - st[0] | |
| cpu_sys = end[1] - st[1] | |
| cpu_tot = cpu_user + cpu_sys | |
| # On windows cpu_sys is always zero, so only total is displayed | |
| if sys.platform != "win32": | |
| print( | |
| f"CPU times: user {_format_time(cpu_user)}, sys: {_format_time(cpu_sys)}, total: {_format_time(cpu_tot)}" | |
| ) | |
| else: | |
| print(f"CPU times: total: {_format_time(cpu_tot)}") | |
| print(f"Wall time: {_format_time(wall_time)}") | |
| if tc > tc_min: | |
| print(f"Compiler : {_format_time(tc)}") | |
| if tp > tp_min: | |
| print(f"Parser : {_format_time(tp)}") | |
| if interrupt_occured: | |
| if exit_on_interrupt and captured_exception: | |
| raise captured_exception | |
| return | |
| return out | |
| def macro(self, parameter_s=''): | |
| """Define a macro for future re-execution. It accepts ranges of history, | |
| filenames or string objects. | |
| Usage:: | |
| %macro [options] name n1-n2 n3-n4 ... n5 .. n6 ... | |
| Options: | |
| -r | |
| Use 'raw' input. By default, the 'processed' history is used, | |
| so that magics are loaded in their transformed version to valid | |
| Python. If this option is given, the raw input as typed at the | |
| command line is used instead. | |
| -q | |
| Quiet macro definition. By default, a tag line is printed | |
| to indicate the macro has been created, and then the contents of | |
| the macro are printed. If this option is given, then no printout | |
| is produced once the macro is created. | |
| This will define a global variable called `name` which is a string | |
| made of joining the slices and lines you specify (n1,n2,... numbers | |
| above) from your input history into a single string. This variable | |
| acts like an automatic function which re-executes those lines as if | |
| you had typed them. You just type 'name' at the prompt and the code | |
| executes. | |
| The syntax for indicating input ranges is described in %history. | |
| Note: as a 'hidden' feature, you can also use traditional python slice | |
| notation, where N:M means numbers N through M-1. | |
| For example, if your history contains (print using %hist -n ):: | |
| 44: x=1 | |
| 45: y=3 | |
| 46: z=x+y | |
| 47: print(x) | |
| 48: a=5 | |
| 49: print('x',x,'y',y) | |
| you can create a macro with lines 44 through 47 (included) and line 49 | |
| called my_macro with:: | |
| In [55]: %macro my_macro 44-47 49 | |
| Now, typing `my_macro` (without quotes) will re-execute all this code | |
| in one pass. | |
| You don't need to give the line-numbers in order, and any given line | |
| number can appear multiple times. You can assemble macros with any | |
| lines from your input history in any order. | |
| The macro is a simple object which holds its value in an attribute, | |
| but IPython's display system checks for macros and executes them as | |
| code instead of printing them when you type their name. | |
| You can view a macro's contents by explicitly printing it with:: | |
| print(macro_name) | |
| """ | |
| opts,args = self.parse_options(parameter_s,'rq',mode='list') | |
| if not args: # List existing macros | |
| return sorted(k for k,v in self.shell.user_ns.items() if isinstance(v, Macro)) | |
| if len(args) == 1: | |
| raise UsageError( | |
| "%macro insufficient args; usage '%macro name n1-n2 n3-4...") | |
| name, codefrom = args[0], " ".join(args[1:]) | |
| # print('rng',ranges) # dbg | |
| try: | |
| lines = self.shell.find_user_code(codefrom, 'r' in opts) | |
| except (ValueError, TypeError) as e: | |
| print(e.args[0]) | |
| return | |
| macro = Macro(lines) | |
| self.shell.define_macro(name, macro) | |
| if "q" not in opts: | |
| print( | |
| "Macro `%s` created. To execute, type its name (without quotes)." % name | |
| ) | |
| print("=== Macro contents: ===") | |
| print(macro, end=" ") | |
| def capture(self, line, cell): | |
| """run the cell, capturing stdout, stderr, and IPython's rich display() calls.""" | |
| args = magic_arguments.parse_argstring(self.capture, line) | |
| out = not args.no_stdout | |
| err = not args.no_stderr | |
| disp = not args.no_display | |
| with capture_output(out, err, disp) as io: | |
| self.shell.run_cell(cell) | |
| if DisplayHook.semicolon_at_end_of_expression(cell): | |
| if args.output in self.shell.user_ns: | |
| del self.shell.user_ns[args.output] | |
| elif args.output: | |
| self.shell.user_ns[args.output] = io | |
| def code_wrap(self, line, cell=None): | |
| """ | |
| Simple magic to quickly define a code transformer for all IPython's future input. | |
| ``__code__`` and ``__ret__`` are special variable that represent the code to run | |
| and the value of the last expression of ``__code__`` respectively. | |
| Examples | |
| -------- | |
| .. ipython:: | |
| In [1]: %%code_wrap before_after | |
| ...: print('before') | |
| ...: __code__ | |
| ...: print('after') | |
| ...: __ret__ | |
| In [2]: 1 | |
| before | |
| after | |
| Out[2]: 1 | |
| In [3]: %code_wrap --list | |
| before_after | |
| In [4]: %code_wrap --list-all | |
| before_after : | |
| print('before') | |
| __code__ | |
| print('after') | |
| __ret__ | |
| In [5]: %code_wrap --remove before_after | |
| """ | |
| args = magic_arguments.parse_argstring(self.code_wrap, line) | |
| if args.list: | |
| for name in self._transformers.keys(): | |
| print(name) | |
| return | |
| if args.list_all: | |
| for name, _t in self._transformers.items(): | |
| print(name, ":") | |
| print(indent(ast.unparse(_t.template), " ")) | |
| print() | |
| return | |
| to_remove = self._transformers.pop(args.name, None) | |
| if to_remove in self.shell.ast_transformers: | |
| self.shell.ast_transformers.remove(to_remove) | |
| if cell is None or args.remove: | |
| return | |
| _trs = ReplaceCodeTransformer(ast.parse(cell)) | |
| self._transformers[args.name] = _trs | |
| self.shell.ast_transformers.append(_trs) | |
| def parse_breakpoint(text, current_file): | |
| '''Returns (file, line) for file:line and (current_file, line) for line''' | |
| colon = text.find(':') | |
| if colon == -1: | |
| return current_file, int(text) | |
| else: | |
| return text[:colon], int(text[colon+1:]) | |
| def _format_time(timespan, precision=3): | |
| """Formats the timespan in a human readable form""" | |
| if timespan >= 60.0: | |
| # we have more than a minute, format that in a human readable form | |
| # Idea from http://snipplr.com/view/5713/ | |
| parts = [("d", 60 * 60 * 24), ("h", 60 * 60), ("min", 60), ("s", 1)] | |
| time = [] | |
| leftover = timespan | |
| for suffix, length in parts: | |
| value = int(leftover / length) | |
| if value > 0: | |
| leftover = leftover % length | |
| time.append("%s%s" % (str(value), suffix)) | |
| if leftover < 1: | |
| break | |
| return " ".join(time) | |
| # Unfortunately characters outside of range(128) can cause problems in | |
| # certain terminals. | |
| # See bug: https://bugs.launchpad.net/ipython/+bug/348466 | |
| # Try to prevent crashes by being more secure than it needs to | |
| # E.g. eclipse is able to print a µ, but has no sys.stdout.encoding set. | |
| units = ["s", "ms", "us", "ns"] # the safe value | |
| if hasattr(sys.stdout, "encoding") and sys.stdout.encoding: | |
| try: | |
| "μ".encode(sys.stdout.encoding) | |
| units = ["s", "ms", "μs", "ns"] | |
| except: | |
| pass | |
| scaling = [1, 1e3, 1e6, 1e9] | |
| if timespan > 0.0: | |
| order = min(-int(math.floor(math.log10(timespan)) // 3), 3) | |
| else: | |
| order = 3 | |
| return "%.*g %s" % (precision, timespan * scaling[order], units[order]) | |