diff options
| author | nkozlovskiy <[email protected]> | 2023-09-29 12:24:06 +0300 | 
|---|---|---|
| committer | nkozlovskiy <[email protected]> | 2023-09-29 12:41:34 +0300 | 
| commit | e0e3e1717e3d33762ce61950504f9637a6e669ed (patch) | |
| tree | bca3ff6939b10ed60c3d5c12439963a1146b9711 /contrib/python/ipython/py3/IPython/core/magics | |
| parent | 38f2c5852db84c7b4d83adfcb009eb61541d1ccd (diff) | |
add ydb deps
Diffstat (limited to 'contrib/python/ipython/py3/IPython/core/magics')
15 files changed, 6173 insertions, 0 deletions
diff --git a/contrib/python/ipython/py3/IPython/core/magics/__init__.py b/contrib/python/ipython/py3/IPython/core/magics/__init__.py new file mode 100644 index 00000000000..a6c5f474c15 --- /dev/null +++ b/contrib/python/ipython/py3/IPython/core/magics/__init__.py @@ -0,0 +1,42 @@ +"""Implementation of all the magic functions built into IPython. +""" +#----------------------------------------------------------------------------- +#  Copyright (c) 2012 The IPython Development Team. +# +#  Distributed under the terms of the Modified BSD License. +# +#  The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +from ..magic import Magics, magics_class +from .auto import AutoMagics +from .basic import BasicMagics, AsyncMagics +from .code import CodeMagics, MacroToEdit +from .config import ConfigMagics +from .display import DisplayMagics +from .execution import ExecutionMagics +from .extension import ExtensionMagics +from .history import HistoryMagics +from .logging import LoggingMagics +from .namespace import NamespaceMagics +from .osm import OSMagics +from .packaging import PackagingMagics +from .pylab import PylabMagics +from .script import ScriptMagics + +#----------------------------------------------------------------------------- +# Magic implementation classes +#----------------------------------------------------------------------------- + +@magics_class +class UserMagics(Magics): +    """Placeholder for user-defined magics to be added at runtime. + +    All magics are eventually merged into a single namespace at runtime, but we +    use this class to isolate the magics defined dynamically by the user into +    their own class. +    """ diff --git a/contrib/python/ipython/py3/IPython/core/magics/auto.py b/contrib/python/ipython/py3/IPython/core/magics/auto.py new file mode 100644 index 00000000000..56aa4f72eb3 --- /dev/null +++ b/contrib/python/ipython/py3/IPython/core/magics/auto.py @@ -0,0 +1,144 @@ +"""Implementation of magic functions that control various automatic behaviors. +""" +#----------------------------------------------------------------------------- +#  Copyright (c) 2012 The IPython Development Team. +# +#  Distributed under the terms of the Modified BSD License. +# +#  The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +# Our own packages +from IPython.core.magic import Bunch, Magics, magics_class, line_magic +from IPython.testing.skipdoctest import skip_doctest +from logging import error + +#----------------------------------------------------------------------------- +# Magic implementation classes +#----------------------------------------------------------------------------- + +@magics_class +class AutoMagics(Magics): +    """Magics that control various autoX behaviors.""" + +    def __init__(self, shell): +        super(AutoMagics, self).__init__(shell) +        # namespace for holding state we may need +        self._magic_state = Bunch() + +    @line_magic +    def automagic(self, parameter_s=''): +        """Make magic functions callable without having to type the initial %. + +        Without arguments toggles on/off (when off, you must call it as +        %automagic, of course).  With arguments it sets the value, and you can +        use any of (case insensitive): + +         - on, 1, True: to activate + +         - off, 0, False: to deactivate. + +        Note that magic functions have lowest priority, so if there's a +        variable whose name collides with that of a magic fn, automagic won't +        work for that function (you get the variable instead). However, if you +        delete the variable (del var), the previously shadowed magic function +        becomes visible to automagic again.""" + +        arg = parameter_s.lower() +        mman = self.shell.magics_manager +        if arg in ('on', '1', 'true'): +            val = True +        elif arg in ('off', '0', 'false'): +            val = False +        else: +            val = not mman.auto_magic +        mman.auto_magic = val +        print('\n' + self.shell.magics_manager.auto_status()) + +    @skip_doctest +    @line_magic +    def autocall(self, parameter_s=''): +        """Make functions callable without having to type parentheses. + +        Usage: + +           %autocall [mode] + +        The mode can be one of: 0->Off, 1->Smart, 2->Full.  If not given, the +        value is toggled on and off (remembering the previous state). + +        In more detail, these values mean: + +        0 -> fully disabled + +        1 -> active, but do not apply if there are no arguments on the line. + +        In this mode, you get:: + +          In [1]: callable +          Out[1]: <built-in function callable> + +          In [2]: callable 'hello' +          ------> callable('hello') +          Out[2]: False + +        2 -> Active always.  Even if no arguments are present, the callable +        object is called:: + +          In [2]: float +          ------> float() +          Out[2]: 0.0 + +        Note that even with autocall off, you can still use '/' at the start of +        a line to treat the first argument on the command line as a function +        and add parentheses to it:: + +          In [8]: /str 43 +          ------> str(43) +          Out[8]: '43' + +        # all-random (note for auto-testing) +        """ + +        valid_modes = { +            0: "Off", +            1: "Smart", +            2: "Full", +        } + +        def errorMessage() -> str: +            error = "Valid modes: " +            for k, v in valid_modes.items(): +                error += str(k) + "->" + v + ", " +            error = error[:-2]  # remove tailing `, ` after last element +            return error + +        if parameter_s: +            if not parameter_s in map(str, valid_modes.keys()): +                error(errorMessage()) +                return +            arg = int(parameter_s) +        else: +            arg = 'toggle' + +        if not arg in (*list(valid_modes.keys()), "toggle"): +            error(errorMessage()) +            return + +        if arg in (valid_modes.keys()): +            self.shell.autocall = arg +        else: # toggle +            if self.shell.autocall: +                self._magic_state.autocall_save = self.shell.autocall +                self.shell.autocall = 0 +            else: +                try: +                    self.shell.autocall = self._magic_state.autocall_save +                except AttributeError: +                    self.shell.autocall = self._magic_state.autocall_save = 1 + +        print("Automatic calling is:", list(valid_modes.values())[self.shell.autocall]) diff --git a/contrib/python/ipython/py3/IPython/core/magics/basic.py b/contrib/python/ipython/py3/IPython/core/magics/basic.py new file mode 100644 index 00000000000..814dec72e29 --- /dev/null +++ b/contrib/python/ipython/py3/IPython/core/magics/basic.py @@ -0,0 +1,663 @@ +"""Implementation of basic magic functions.""" + + +from logging import error +import io +import os +from pprint import pformat +import sys +from warnings import warn + +from traitlets.utils.importstring import import_item +from IPython.core import magic_arguments, page +from IPython.core.error import UsageError +from IPython.core.magic import Magics, magics_class, line_magic, magic_escapes +from IPython.utils.text import format_screen, dedent, indent +from IPython.testing.skipdoctest import skip_doctest +from IPython.utils.ipstruct import Struct + + +class MagicsDisplay(object): +    def __init__(self, magics_manager, ignore=None): +        self.ignore = ignore if ignore else [] +        self.magics_manager = magics_manager +     +    def _lsmagic(self): +        """The main implementation of the %lsmagic""" +        mesc = magic_escapes['line'] +        cesc = magic_escapes['cell'] +        mman = self.magics_manager +        magics = mman.lsmagic() +        out = ['Available line magics:', +               mesc + ('  '+mesc).join(sorted([m for m,v in magics['line'].items() if (v not in self.ignore)])), +               '', +               'Available cell magics:', +               cesc + ('  '+cesc).join(sorted([m for m,v in magics['cell'].items() if (v not in self.ignore)])), +               '', +               mman.auto_status()] +        return '\n'.join(out) + +    def _repr_pretty_(self, p, cycle): +        p.text(self._lsmagic()) +     +    def __str__(self): +        return self._lsmagic() +     +    def _jsonable(self): +        """turn magics dict into jsonable dict of the same structure + +        replaces object instances with their class names as strings +        """ +        magic_dict = {} +        mman = self.magics_manager +        magics = mman.lsmagic() +        for key, subdict in magics.items(): +            d = {} +            magic_dict[key] = d +            for name, obj in subdict.items(): +                try: +                    classname = obj.__self__.__class__.__name__ +                except AttributeError: +                    classname = 'Other' +                 +                d[name] = classname +        return magic_dict +         +    def _repr_json_(self): +        return self._jsonable() + + +@magics_class +class BasicMagics(Magics): +    """Magics that provide central IPython functionality. + +    These are various magics that don't fit into specific categories but that +    are all part of the base 'IPython experience'.""" + +    @skip_doctest +    @magic_arguments.magic_arguments() +    @magic_arguments.argument( +        '-l', '--line', action='store_true', +        help="""Create a line magic alias.""" +    ) +    @magic_arguments.argument( +        '-c', '--cell', action='store_true', +        help="""Create a cell magic alias.""" +    ) +    @magic_arguments.argument( +        'name', +        help="""Name of the magic to be created.""" +    ) +    @magic_arguments.argument( +        'target', +        help="""Name of the existing line or cell magic.""" +    ) +    @magic_arguments.argument( +        '-p', '--params', default=None, +        help="""Parameters passed to the magic function.""" +    ) +    @line_magic +    def alias_magic(self, line=''): +        """Create an alias for an existing line or cell magic. + +        Examples +        -------- +        :: + +          In [1]: %alias_magic t timeit +          Created `%t` as an alias for `%timeit`. +          Created `%%t` as an alias for `%%timeit`. + +          In [2]: %t -n1 pass +          1 loops, best of 3: 954 ns per loop + +          In [3]: %%t -n1 +             ...: pass +             ...: +          1 loops, best of 3: 954 ns per loop + +          In [4]: %alias_magic --cell whereami pwd +          UsageError: Cell magic function `%%pwd` not found. +          In [5]: %alias_magic --line whereami pwd +          Created `%whereami` as an alias for `%pwd`. + +          In [6]: %whereami +          Out[6]: u'/home/testuser' + +          In [7]: %alias_magic h history "-p -l 30" --line +          Created `%h` as an alias for `%history -l 30`. +        """ + +        args = magic_arguments.parse_argstring(self.alias_magic, line) +        shell = self.shell +        mman = self.shell.magics_manager +        escs = ''.join(magic_escapes.values()) + +        target = args.target.lstrip(escs) +        name = args.name.lstrip(escs) + +        params = args.params +        if (params and +                ((params.startswith('"') and params.endswith('"')) +                or (params.startswith("'") and params.endswith("'")))): +            params = params[1:-1] + +        # Find the requested magics. +        m_line = shell.find_magic(target, 'line') +        m_cell = shell.find_magic(target, 'cell') +        if args.line and m_line is None: +            raise UsageError('Line magic function `%s%s` not found.' % +                             (magic_escapes['line'], target)) +        if args.cell and m_cell is None: +            raise UsageError('Cell magic function `%s%s` not found.' % +                             (magic_escapes['cell'], target)) + +        # If --line and --cell are not specified, default to the ones +        # that are available. +        if not args.line and not args.cell: +            if not m_line and not m_cell: +                raise UsageError( +                    'No line or cell magic with name `%s` found.' % target +                ) +            args.line = bool(m_line) +            args.cell = bool(m_cell) + +        params_str = "" if params is None else " " + params + +        if args.line: +            mman.register_alias(name, target, 'line', params) +            print('Created `%s%s` as an alias for `%s%s%s`.' % ( +                magic_escapes['line'], name, +                magic_escapes['line'], target, params_str)) + +        if args.cell: +            mman.register_alias(name, target, 'cell', params) +            print('Created `%s%s` as an alias for `%s%s%s`.' % ( +                magic_escapes['cell'], name, +                magic_escapes['cell'], target, params_str)) + +    @line_magic +    def lsmagic(self, parameter_s=''): +        """List currently available magic functions.""" +        return MagicsDisplay(self.shell.magics_manager, ignore=[]) + +    def _magic_docs(self, brief=False, rest=False): +        """Return docstrings from magic functions.""" +        mman = self.shell.magics_manager +        docs = mman.lsmagic_docs(brief, missing='No documentation') + +        if rest: +            format_string = '**%s%s**::\n\n%s\n\n' +        else: +            format_string = '%s%s:\n%s\n' + +        return ''.join( +            [format_string % (magic_escapes['line'], fname, +                              indent(dedent(fndoc))) +             for fname, fndoc in sorted(docs['line'].items())] +            + +            [format_string % (magic_escapes['cell'], fname, +                              indent(dedent(fndoc))) +             for fname, fndoc in sorted(docs['cell'].items())] +        ) + +    @line_magic +    def magic(self, parameter_s=''): +        """Print information about the magic function system. + +        Supported formats: -latex, -brief, -rest +        """ + +        mode = '' +        try: +            mode = parameter_s.split()[0][1:] +        except IndexError: +            pass + +        brief = (mode == 'brief') +        rest = (mode == 'rest') +        magic_docs = self._magic_docs(brief, rest) + +        if mode == 'latex': +            print(self.format_latex(magic_docs)) +            return +        else: +            magic_docs = format_screen(magic_docs) + +        out = [""" +IPython's 'magic' functions +=========================== + +The magic function system provides a series of functions which allow you to +control the behavior of IPython itself, plus a lot of system-type +features. There are two kinds of magics, line-oriented and cell-oriented. + +Line magics are prefixed with the % character and work much like OS +command-line calls: they get as an argument the rest of the line, where +arguments are passed without parentheses or quotes.  For example, this will +time the given statement:: + +        %timeit range(1000) + +Cell magics are prefixed with a double %%, and they are functions that get as +an argument not only the rest of the line, but also the lines below it in a +separate argument.  These magics are called with two arguments: the rest of the +call line and the body of the cell, consisting of the lines below the first. +For example:: + +        %%timeit x = numpy.random.randn((100, 100)) +        numpy.linalg.svd(x) + +will time the execution of the numpy svd routine, running the assignment of x +as part of the setup phase, which is not timed. + +In a line-oriented client (the terminal or Qt console IPython), starting a new +input with %% will automatically enter cell mode, and IPython will continue +reading input until a blank line is given.  In the notebook, simply type the +whole cell as one entity, but keep in mind that the %% escape can only be at +the very start of the cell. + +NOTE: If you have 'automagic' enabled (via the command line option or with the +%automagic function), you don't need to type in the % explicitly for line +magics; cell magics always require an explicit '%%' escape.  By default, +IPython ships with automagic on, so you should only rarely need the % escape. + +Example: typing '%cd mydir' (without the quotes) changes your working directory +to 'mydir', if it exists. + +For a list of the available magic functions, use %lsmagic. For a description +of any of them, type %magic_name?, e.g. '%cd?'. + +Currently the magic system has the following functions:""", +       magic_docs, +       "Summary of magic functions (from %slsmagic):" % magic_escapes['line'], +       str(self.lsmagic()), +       ] +        page.page('\n'.join(out)) + + +    @line_magic +    def page(self, parameter_s=''): +        """Pretty print the object and display it through a pager. + +        %page [options] OBJECT + +        If no object is given, use _ (last output). + +        Options: + +          -r: page str(object), don't pretty-print it.""" + +        # After a function contributed by Olivier Aubert, slightly modified. + +        # Process options/args +        opts, args = self.parse_options(parameter_s, 'r') +        raw = 'r' in opts + +        oname = args and args or '_' +        info = self.shell._ofind(oname) +        if info.found: +            if raw: +                txt = str(info.obj) +            else: +                txt = pformat(info.obj) +            page.page(txt) +        else: +            print('Object `%s` not found' % oname) + +    @line_magic +    def pprint(self, parameter_s=''): +        """Toggle pretty printing on/off.""" +        ptformatter = self.shell.display_formatter.formatters['text/plain'] +        ptformatter.pprint = bool(1 - ptformatter.pprint) +        print('Pretty printing has been turned', +              ['OFF','ON'][ptformatter.pprint]) + +    @line_magic +    def colors(self, parameter_s=''): +        """Switch color scheme for prompts, info system and exception handlers. + +        Currently implemented schemes: NoColor, Linux, LightBG. + +        Color scheme names are not case-sensitive. + +        Examples +        -------- +        To get a plain black and white terminal:: + +          %colors nocolor +        """ +        def color_switch_err(name): +            warn('Error changing %s color schemes.\n%s' % +                 (name, sys.exc_info()[1]), stacklevel=2) + + +        new_scheme = parameter_s.strip() +        if not new_scheme: +            raise UsageError( +                "%colors: you must specify a color scheme. See '%colors?'") +        # local shortcut +        shell = self.shell + +        # Set shell colour scheme +        try: +            shell.colors = new_scheme +            shell.refresh_style() +        except: +            color_switch_err('shell') + +        # Set exception colors +        try: +            shell.InteractiveTB.set_colors(scheme = new_scheme) +            shell.SyntaxTB.set_colors(scheme = new_scheme) +        except: +            color_switch_err('exception') + +        # Set info (for 'object?') colors +        if shell.color_info: +            try: +                shell.inspector.set_active_scheme(new_scheme) +            except: +                color_switch_err('object inspector') +        else: +            shell.inspector.set_active_scheme('NoColor') + +    @line_magic +    def xmode(self, parameter_s=''): +        """Switch modes for the exception handlers. + +        Valid modes: Plain, Context, Verbose, and Minimal. + +        If called without arguments, acts as a toggle. + +        When in verbose mode the value `--show` (and `--hide`) +        will respectively show (or hide) frames with ``__tracebackhide__ = +        True`` value set. +        """ + +        def xmode_switch_err(name): +            warn('Error changing %s exception modes.\n%s' % +                 (name,sys.exc_info()[1])) + +        shell = self.shell +        if parameter_s.strip() == "--show": +            shell.InteractiveTB.skip_hidden = False +            return +        if parameter_s.strip() == "--hide": +            shell.InteractiveTB.skip_hidden = True +            return + +        new_mode = parameter_s.strip().capitalize() +        try: +            shell.InteractiveTB.set_mode(mode=new_mode) +            print('Exception reporting mode:',shell.InteractiveTB.mode) +        except: +            xmode_switch_err('user') + +    @line_magic +    def quickref(self, arg): +        """ Show a quick reference sheet """ +        from IPython.core.usage import quick_reference +        qr = quick_reference + self._magic_docs(brief=True) +        page.page(qr) + +    @line_magic +    def doctest_mode(self, parameter_s=''): +        """Toggle doctest mode on and off. + +        This mode is intended to make IPython behave as much as possible like a +        plain Python shell, from the perspective of how its prompts, exceptions +        and output look.  This makes it easy to copy and paste parts of a +        session into doctests.  It does so by: + +        - Changing the prompts to the classic ``>>>`` ones. +        - Changing the exception reporting mode to 'Plain'. +        - Disabling pretty-printing of output. + +        Note that IPython also supports the pasting of code snippets that have +        leading '>>>' and '...' prompts in them.  This means that you can paste +        doctests from files or docstrings (even if they have leading +        whitespace), and the code will execute correctly.  You can then use +        '%history -t' to see the translated history; this will give you the +        input after removal of all the leading prompts and whitespace, which +        can be pasted back into an editor. + +        With these features, you can switch into this mode easily whenever you +        need to do testing and changes to doctests, without having to leave +        your existing IPython session. +        """ + +        # Shorthands +        shell = self.shell +        meta = shell.meta +        disp_formatter = self.shell.display_formatter +        ptformatter = disp_formatter.formatters['text/plain'] +        # dstore is a data store kept in the instance metadata bag to track any +        # changes we make, so we can undo them later. +        dstore = meta.setdefault('doctest_mode',Struct()) +        save_dstore = dstore.setdefault + +        # save a few values we'll need to recover later +        mode = save_dstore('mode',False) +        save_dstore('rc_pprint',ptformatter.pprint) +        save_dstore('xmode',shell.InteractiveTB.mode) +        save_dstore('rc_separate_out',shell.separate_out) +        save_dstore('rc_separate_out2',shell.separate_out2) +        save_dstore('rc_separate_in',shell.separate_in) +        save_dstore('rc_active_types',disp_formatter.active_types) + +        if not mode: +            # turn on + +            # Prompt separators like plain python +            shell.separate_in = '' +            shell.separate_out = '' +            shell.separate_out2 = '' + + +            ptformatter.pprint = False +            disp_formatter.active_types = ['text/plain'] + +            shell.magic('xmode Plain') +        else: +            # turn off +            shell.separate_in = dstore.rc_separate_in + +            shell.separate_out = dstore.rc_separate_out +            shell.separate_out2 = dstore.rc_separate_out2 + +            ptformatter.pprint = dstore.rc_pprint +            disp_formatter.active_types = dstore.rc_active_types + +            shell.magic('xmode ' + dstore.xmode) + +        # mode here is the state before we switch; switch_doctest_mode takes +        # the mode we're switching to. +        shell.switch_doctest_mode(not mode) + +        # Store new mode and inform +        dstore.mode = bool(not mode) +        mode_label = ['OFF','ON'][dstore.mode] +        print('Doctest mode is:', mode_label) + +    @line_magic +    def gui(self, parameter_s=''): +        """Enable or disable IPython GUI event loop integration. + +        %gui [GUINAME] + +        This magic replaces IPython's threaded shells that were activated +        using the (pylab/wthread/etc.) command line flags.  GUI toolkits +        can now be enabled at runtime and keyboard +        interrupts should work without any problems.  The following toolkits +        are supported:  wxPython, PyQt4, PyGTK, Tk and Cocoa (OSX):: + +            %gui wx      # enable wxPython event loop integration +            %gui qt      # enable PyQt/PySide event loop integration +                         # with the latest version available. +            %gui qt6     # enable PyQt6/PySide6 event loop integration +            %gui qt5     # enable PyQt5/PySide2 event loop integration +            %gui gtk     # enable PyGTK event loop integration +            %gui gtk3    # enable Gtk3 event loop integration +            %gui gtk4    # enable Gtk4 event loop integration +            %gui tk      # enable Tk event loop integration +            %gui osx     # enable Cocoa event loop integration +                         # (requires %matplotlib 1.1) +            %gui         # disable all event loop integration + +        WARNING:  after any of these has been called you can simply create +        an application object, but DO NOT start the event loop yourself, as +        we have already handled that. +        """ +        opts, arg = self.parse_options(parameter_s, '') +        if arg=='': arg = None +        try: +            return self.shell.enable_gui(arg) +        except Exception as e: +            # print simple error message, rather than traceback if we can't +            # hook up the GUI +            error(str(e)) + +    @skip_doctest +    @line_magic +    def precision(self, s=''): +        """Set floating point precision for pretty printing. + +        Can set either integer precision or a format string. + +        If numpy has been imported and precision is an int, +        numpy display precision will also be set, via ``numpy.set_printoptions``. + +        If no argument is given, defaults will be restored. + +        Examples +        -------- +        :: + +            In [1]: from math import pi + +            In [2]: %precision 3 +            Out[2]: u'%.3f' + +            In [3]: pi +            Out[3]: 3.142 + +            In [4]: %precision %i +            Out[4]: u'%i' + +            In [5]: pi +            Out[5]: 3 + +            In [6]: %precision %e +            Out[6]: u'%e' + +            In [7]: pi**10 +            Out[7]: 9.364805e+04 + +            In [8]: %precision +            Out[8]: u'%r' + +            In [9]: pi**10 +            Out[9]: 93648.047476082982 +        """ +        ptformatter = self.shell.display_formatter.formatters['text/plain'] +        ptformatter.float_precision = s +        return ptformatter.float_format + +    @magic_arguments.magic_arguments() +    @magic_arguments.argument( +        'filename', type=str, +        help='Notebook name or filename' +    ) +    @line_magic +    def notebook(self, s): +        """Export and convert IPython notebooks. + +        This function can export the current IPython history to a notebook file. +        For example, to export the history to "foo.ipynb" do "%notebook foo.ipynb". +        """ +        args = magic_arguments.parse_argstring(self.notebook, s) +        outfname = os.path.expanduser(args.filename) + +        from nbformat import write, v4 + +        cells = [] +        hist = list(self.shell.history_manager.get_range()) +        if(len(hist)<=1): +            raise ValueError('History is empty, cannot export') +        for session, execution_count, source in hist[:-1]: +            cells.append(v4.new_code_cell( +                execution_count=execution_count, +                source=source +            )) +        nb = v4.new_notebook(cells=cells) +        with io.open(outfname, "w", encoding="utf-8") as f: +            write(nb, f, version=4) + +@magics_class +class AsyncMagics(BasicMagics): + +    @line_magic +    def autoawait(self, parameter_s): +        """ +        Allow to change the status of the autoawait option. + +        This allow you to set a specific asynchronous code runner. + +        If no value is passed, print the currently used asynchronous integration +        and whether it is activated. + +        It can take a number of value evaluated in the following order: + +        - False/false/off deactivate autoawait integration +        - True/true/on activate autoawait integration using configured default +          loop +        - asyncio/curio/trio activate autoawait integration and use integration +          with said library. + +        - `sync` turn on the pseudo-sync integration (mostly used for +          `IPython.embed()` which does not run IPython with a real eventloop and +          deactivate running asynchronous code. Turning on Asynchronous code with +          the pseudo sync loop is undefined behavior and may lead IPython to crash. + +        If the passed parameter does not match any of the above and is a python +        identifier, get said object from user namespace and set it as the +        runner, and activate autoawait. + +        If the object is a fully qualified object name, attempt to import it and +        set it as the runner, and activate autoawait. + +        The exact behavior of autoawait is experimental and subject to change +        across version of IPython and Python. +        """ + +        param = parameter_s.strip() +        d = {True: "on", False: "off"} + +        if not param: +            print("IPython autoawait is `{}`, and set to use `{}`".format( +                d[self.shell.autoawait], +                self.shell.loop_runner +            )) +            return None + +        if param.lower() in ('false', 'off'): +            self.shell.autoawait = False +            return None +        if param.lower() in ('true', 'on'): +            self.shell.autoawait = True +            return None + +        if param in self.shell.loop_runner_map: +            self.shell.loop_runner, self.shell.autoawait = self.shell.loop_runner_map[param] +            return None + +        if param in self.shell.user_ns : +            self.shell.loop_runner = self.shell.user_ns[param] +            self.shell.autoawait = True +            return None + +        runner = import_item(param) + +        self.shell.loop_runner = runner +        self.shell.autoawait = True diff --git a/contrib/python/ipython/py3/IPython/core/magics/code.py b/contrib/python/ipython/py3/IPython/core/magics/code.py new file mode 100644 index 00000000000..65ba52b8bbf --- /dev/null +++ b/contrib/python/ipython/py3/IPython/core/magics/code.py @@ -0,0 +1,755 @@ +"""Implementation of code management magic functions. +""" +#----------------------------------------------------------------------------- +#  Copyright (c) 2012 The IPython Development Team. +# +#  Distributed under the terms of the Modified BSD License. +# +#  The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +# Stdlib +import inspect +import io +import os +import re +import sys +import ast +from itertools import chain +from urllib.request import Request, urlopen +from urllib.parse import urlencode +from pathlib import Path + +# Our own packages +from IPython.core.error import TryNext, StdinNotImplementedError, UsageError +from IPython.core.macro import Macro +from IPython.core.magic import Magics, magics_class, line_magic +from IPython.core.oinspect import find_file, find_source_lines +from IPython.core.release import version +from IPython.testing.skipdoctest import skip_doctest +from IPython.utils.contexts import preserve_keys +from IPython.utils.path import get_py_filename +from warnings import warn +from logging import error +from IPython.utils.text import get_text_list + +#----------------------------------------------------------------------------- +# Magic implementation classes +#----------------------------------------------------------------------------- + +# Used for exception handling in magic_edit +class MacroToEdit(ValueError): pass + +ipython_input_pat = re.compile(r"<ipython\-input\-(\d+)-[a-z\d]+>$") + +# To match, e.g. 8-10 1:5 :10 3- +range_re = re.compile(r""" +(?P<start>\d+)? +((?P<sep>[\-:]) + (?P<end>\d+)?)? +$""", re.VERBOSE) + + +def extract_code_ranges(ranges_str): +    """Turn a string of range for %%load into 2-tuples of (start, stop) +    ready to use as a slice of the content split by lines. + +    Examples +    -------- +    list(extract_input_ranges("5-10 2")) +    [(4, 10), (1, 2)] +    """ +    for range_str in ranges_str.split(): +        rmatch = range_re.match(range_str) +        if not rmatch: +            continue +        sep = rmatch.group("sep") +        start = rmatch.group("start") +        end = rmatch.group("end") + +        if sep == '-': +            start = int(start) - 1 if start else None +            end = int(end) if end else None +        elif sep == ':': +            start = int(start) - 1 if start else None +            end = int(end) - 1 if end else None +        else: +            end = int(start) +            start = int(start) - 1 +        yield (start, end) + + +def extract_symbols(code, symbols): +    """ +    Return a tuple  (blocks, not_found) +    where ``blocks`` is a list of code fragments +    for each symbol parsed from code, and ``not_found`` are +    symbols not found in the code. + +    For example:: + +        In [1]: code = '''a = 10 +           ...: def b(): return 42 +           ...: class A: pass''' + +        In [2]: extract_symbols(code, 'A,b,z') +        Out[2]: (['class A: pass\\n', 'def b(): return 42\\n'], ['z']) +    """ +    symbols = symbols.split(',') + +    # this will raise SyntaxError if code isn't valid Python +    py_code = ast.parse(code) + +    marks = [(getattr(s, 'name', None), s.lineno) for s in py_code.body] +    code = code.split('\n') + +    symbols_lines = {} +     +    # we already know the start_lineno of each symbol (marks).  +    # To find each end_lineno, we traverse in reverse order until each  +    # non-blank line +    end = len(code)   +    for name, start in reversed(marks): +        while not code[end - 1].strip(): +            end -= 1 +        if name: +            symbols_lines[name] = (start - 1, end) +        end = start - 1 + +    # Now symbols_lines is a map +    # {'symbol_name': (start_lineno, end_lineno), ...} +     +    # fill a list with chunks of codes for each requested symbol +    blocks = [] +    not_found = [] +    for symbol in symbols: +        if symbol in symbols_lines: +            start, end = symbols_lines[symbol] +            blocks.append('\n'.join(code[start:end]) + '\n') +        else: +            not_found.append(symbol) + +    return blocks, not_found + +def strip_initial_indent(lines): +    """For %load, strip indent from lines until finding an unindented line. + +    https://github.com/ipython/ipython/issues/9775 +    """ +    indent_re = re.compile(r'\s+') + +    it = iter(lines) +    first_line = next(it) +    indent_match = indent_re.match(first_line) + +    if indent_match: +        # First line was indented +        indent = indent_match.group() +        yield first_line[len(indent):] + +        for line in it: +            if line.startswith(indent): +                yield line[len(indent):] +            else: +                # Less indented than the first line - stop dedenting +                yield line +                break +    else: +        yield first_line + +    # Pass the remaining lines through without dedenting +    for line in it: +        yield line + + +class InteractivelyDefined(Exception): +    """Exception for interactively defined variable in magic_edit""" +    def __init__(self, index): +        self.index = index + + +@magics_class +class CodeMagics(Magics): +    """Magics related to code management (loading, saving, editing, ...).""" + +    def __init__(self, *args, **kwargs): +        self._knowntemps = set() +        super(CodeMagics, self).__init__(*args, **kwargs) + +    @line_magic +    def save(self, parameter_s=''): +        """Save a set of lines or a macro to a given filename. + +        Usage:\\ +          %save [options] filename [history] + +        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 as the +          command line is used instead. +           +          -f: force overwrite.  If file exists, %save will prompt for overwrite +          unless -f is given. + +          -a: append to the file instead of overwriting it. + +        The history argument uses the same syntax as %history for input ranges, +        then saves the lines to the filename you specify. + +        If no ranges are specified, saves history of the current session up to +        this point. + +        It adds a '.py' extension to the file if you don't do so yourself, and +        it asks for confirmation before overwriting existing files. + +        If `-r` option is used, the default extension is `.ipy`. +        """ + +        opts,args = self.parse_options(parameter_s,'fra',mode='list') +        if not args: +            raise UsageError('Missing filename.') +        raw = 'r' in opts +        force = 'f' in opts +        append = 'a' in opts +        mode = 'a' if append else 'w' +        ext = '.ipy' if raw else '.py' +        fname, codefrom = args[0], " ".join(args[1:]) +        if not fname.endswith(('.py','.ipy')): +            fname += ext +        fname = os.path.expanduser(fname) +        file_exists = os.path.isfile(fname) +        if file_exists and not force and not append: +            try: +                overwrite = self.shell.ask_yes_no('File `%s` exists. Overwrite (y/[N])? ' % fname, default='n') +            except StdinNotImplementedError: +                print("File `%s` exists. Use `%%save -f %s` to force overwrite" % (fname, parameter_s)) +                return +            if not overwrite : +                print('Operation cancelled.') +                return +        try: +            cmds = self.shell.find_user_code(codefrom,raw) +        except (TypeError, ValueError) as e: +            print(e.args[0]) +            return +        with io.open(fname, mode, encoding="utf-8") as f: +            if not file_exists or not append: +                f.write("# coding: utf-8\n") +            f.write(cmds) +            # make sure we end on a newline +            if not cmds.endswith('\n'): +                f.write('\n') +        print('The following commands were written to file `%s`:' % fname) +        print(cmds) + +    @line_magic +    def pastebin(self, parameter_s=''): +        """Upload code to dpaste.com, returning the URL. + +        Usage:\\ +          %pastebin [-d "Custom description"][-e 24] 1-7 + +        The argument can be an input history range, a filename, or the name of a +        string or macro. + +        If no arguments are given, uploads the history of this session up to +        this point. + +        Options: + +          -d: Pass a custom description. The default will say +              "Pasted from IPython". +          -e: Pass number of days for the link to be expired. +              The default will be 7 days. +        """ +        opts, args = self.parse_options(parameter_s, "d:e:") + +        try: +            code = self.shell.find_user_code(args) +        except (ValueError, TypeError) as e: +            print(e.args[0]) +            return + +        expiry_days = 7 +        try: +            expiry_days = int(opts.get("e", 7)) +        except ValueError as e: +            print(e.args[0].capitalize()) +            return +        if expiry_days < 1 or expiry_days > 365: +            print("Expiry days should be in range of 1 to 365") +            return + +        post_data = urlencode( +            { +                "title": opts.get("d", "Pasted from IPython"), +                "syntax": "python", +                "content": code, +                "expiry_days": expiry_days, +            } +        ).encode("utf-8") + +        request = Request( +            "https://dpaste.com/api/v2/", +            headers={"User-Agent": "IPython v{}".format(version)}, +        ) +        response = urlopen(request, post_data) +        return response.headers.get('Location') + +    @line_magic +    def loadpy(self, arg_s): +        """Alias of `%load` + +        `%loadpy` has gained some flexibility and dropped the requirement of a `.py` +        extension. So it has been renamed simply into %load. You can look at +        `%load`'s docstring for more info. +        """ +        self.load(arg_s) + +    @line_magic +    def load(self, arg_s): +        """Load code into the current frontend. + +        Usage:\\ +          %load [options] source + +          where source can be a filename, URL, input history range, macro, or +          element in the user namespace + +        If no arguments are given, loads the history of this session up to this +        point. + +        Options: + +          -r <lines>: Specify lines or ranges of lines to load from the source. +          Ranges could be specified as x-y (x..y) or in python-style x:y  +          (x..(y-1)). Both limits x and y can be left blank (meaning the  +          beginning and end of the file, respectively). + +          -s <symbols>: Specify function or classes to load from python source.  + +          -y : Don't ask confirmation for loading source above 200 000 characters. + +          -n : Include the user's namespace when searching for source code. + +        This magic command can either take a local filename, a URL, an history +        range (see %history) or a macro as argument, it will prompt for +        confirmation before loading source with more than 200 000 characters, unless +        -y flag is passed or if the frontend does not support raw_input:: + +        %load +        %load myscript.py +        %load 7-27 +        %load myMacro +        %load http://www.example.com/myscript.py +        %load -r 5-10 myscript.py +        %load -r 10-20,30,40: foo.py +        %load -s MyClass,wonder_function myscript.py +        %load -n MyClass +        %load -n my_module.wonder_function +        """ +        opts,args = self.parse_options(arg_s,'yns:r:') +        search_ns = 'n' in opts +        contents = self.shell.find_user_code(args, search_ns=search_ns) + +        if 's' in opts: +            try: +                blocks, not_found = extract_symbols(contents, opts['s']) +            except SyntaxError: +                # non python code +                error("Unable to parse the input as valid Python code") +                return + +            if len(not_found) == 1: +                warn('The symbol `%s` was not found' % not_found[0]) +            elif len(not_found) > 1: +                warn('The symbols %s were not found' % get_text_list(not_found, +                                                                     wrap_item_with='`') +                ) + +            contents = '\n'.join(blocks) + +        if 'r' in opts: +            ranges = opts['r'].replace(',', ' ') +            lines = contents.split('\n') +            slices = extract_code_ranges(ranges) +            contents = [lines[slice(*slc)] for slc in slices] +            contents = '\n'.join(strip_initial_indent(chain.from_iterable(contents))) + +        l = len(contents) + +        # 200 000 is ~ 2500 full 80 character lines +        # so in average, more than 5000 lines +        if l > 200000 and 'y' not in opts: +            try: +                ans = self.shell.ask_yes_no(("The text you're trying to load seems pretty big"\ +                " (%d characters). Continue (y/[N]) ?" % l), default='n' ) +            except StdinNotImplementedError: +                #assume yes if raw input not implemented +                ans = True + +            if ans is False : +                print('Operation cancelled.') +                return + +        contents = "# %load {}\n".format(arg_s) + contents + +        self.shell.set_next_input(contents, replace=True) + +    @staticmethod +    def _find_edit_target(shell, args, opts, last_call): +        """Utility method used by magic_edit to find what to edit.""" + +        def make_filename(arg): +            "Make a filename from the given args" +            try: +                filename = get_py_filename(arg) +            except IOError: +                # If it ends with .py but doesn't already exist, assume we want +                # a new file. +                if arg.endswith('.py'): +                    filename = arg +                else: +                    filename = None +            return filename + +        # Set a few locals from the options for convenience: +        opts_prev = 'p' in opts +        opts_raw = 'r' in opts + +        # custom exceptions +        class DataIsObject(Exception): pass + +        # Default line number value +        lineno = opts.get('n',None) + +        if opts_prev: +            args = '_%s' % last_call[0] +            if args not in shell.user_ns: +                args = last_call[1] + +        # by default this is done with temp files, except when the given +        # arg is a filename +        use_temp = True + +        data = '' + +        # First, see if the arguments should be a filename. +        filename = make_filename(args) +        if filename: +            use_temp = False +        elif args: +            # Mode where user specifies ranges of lines, like in %macro. +            data = shell.extract_input_lines(args, opts_raw) +            if not data: +                try: +                    # Load the parameter given as a variable. If not a string, +                    # process it as an object instead (below) + +                    #print '*** args',args,'type',type(args)  # dbg +                    data = eval(args, shell.user_ns) +                    if not isinstance(data, str): +                        raise DataIsObject + +                except (NameError,SyntaxError): +                    # given argument is not a variable, try as a filename +                    filename = make_filename(args) +                    if filename is None: +                        warn("Argument given (%s) can't be found as a variable " +                             "or as a filename." % args) +                        return (None, None, None) +                    use_temp = False + +                except DataIsObject as e: +                    # macros have a special edit function +                    if isinstance(data, Macro): +                        raise MacroToEdit(data) from e + +                    # For objects, try to edit the file where they are defined +                    filename = find_file(data) +                    if filename: +                        if 'fakemodule' in filename.lower() and \ +                            inspect.isclass(data): +                            # class created by %edit? Try to find source +                            # by looking for method definitions instead, the +                            # __module__ in those classes is FakeModule. +                            attrs = [getattr(data, aname) for aname in dir(data)] +                            for attr in attrs: +                                if not inspect.ismethod(attr): +                                    continue +                                filename = find_file(attr) +                                if filename and \ +                                  'fakemodule' not in filename.lower(): +                                    # change the attribute to be the edit +                                    # target instead +                                    data = attr +                                    break +                         +                        m = ipython_input_pat.match(os.path.basename(filename)) +                        if m: +                            raise InteractivelyDefined(int(m.groups()[0])) from e + +                        datafile = 1 +                    if filename is None: +                        filename = make_filename(args) +                        datafile = 1 +                        if filename is not None: +                            # only warn about this if we get a real name +                            warn('Could not find file where `%s` is defined.\n' +                             'Opening a file named `%s`' % (args, filename)) +                    # Now, make sure we can actually read the source (if it was +                    # in a temp file it's gone by now). +                    if datafile: +                        if lineno is None: +                            lineno = find_source_lines(data) +                        if lineno is None: +                            filename = make_filename(args) +                            if filename is None: +                                warn('The file where `%s` was defined ' +                                     'cannot be read or found.' % data) +                                return (None, None, None) +                    use_temp = False + +        if use_temp: +            filename = shell.mktempfile(data) +            print('IPython will make a temporary file named:',filename) + +        # use last_call to remember the state of the previous call, but don't +        # let it be clobbered by successive '-p' calls. +        try: +            last_call[0] = shell.displayhook.prompt_count +            if not opts_prev: +                last_call[1] = args +        except: +            pass + + +        return filename, lineno, use_temp + +    def _edit_macro(self,mname,macro): +        """open an editor with the macro data in a file""" +        filename = self.shell.mktempfile(macro.value) +        self.shell.hooks.editor(filename) + +        # and make a new macro object, to replace the old one +        mvalue = Path(filename).read_text(encoding="utf-8") +        self.shell.user_ns[mname] = Macro(mvalue) + +    @skip_doctest +    @line_magic +    def edit(self, parameter_s='',last_call=['','']): +        """Bring up an editor and execute the resulting code. + +        Usage: +          %edit [options] [args] + +        %edit runs IPython's editor hook. The default version of this hook is +        set to call the editor specified by your $EDITOR environment variable. +        If this isn't found, it will default to vi under Linux/Unix and to +        notepad under Windows. See the end of this docstring for how to change +        the editor hook. + +        You can also set the value of this editor via the +        ``TerminalInteractiveShell.editor`` option in your configuration file. +        This is useful if you wish to use a different editor from your typical +        default with IPython (and for Windows users who typically don't set +        environment variables). + +        This command allows you to conveniently edit multi-line code right in +        your IPython session. + +        If called without arguments, %edit opens up an empty editor with a +        temporary file and will execute the contents of this file when you +        close it (don't forget to save it!). + + +        Options: + +        -n <number>: open the editor at a specified line number.  By default, +        the IPython editor hook uses the unix syntax 'editor +N filename', but +        you can configure this by providing your own modified hook if your +        favorite editor supports line-number specifications with a different +        syntax. + +        -p: this will call the editor with the same data as the previous time +        it was used, regardless of how long ago (in your current session) it +        was. + +        -r: use 'raw' input.  This option only applies to input taken from the +        user's history.  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 as the command line is +        used instead.  When you exit the editor, it will be executed by +        IPython's own processor. + +        -x: do not execute the edited code immediately upon exit. This is +        mainly useful if you are editing programs which need to be called with +        command line arguments, which you can then do using %run. + + +        Arguments: + +        If arguments are given, the following possibilities exist: + +        - If the argument is a filename, IPython will load that into the +          editor. It will execute its contents with execfile() when you exit, +          loading any code in the file into your interactive namespace. + +        - The arguments are ranges of input history,  e.g. "7 ~1/4-6". +          The syntax is the same as in the %history magic. + +        - If the argument is a string variable, its contents are loaded +          into the editor. You can thus edit any string which contains +          python code (including the result of previous edits). + +        - If the argument is the name of an object (other than a string), +          IPython will try to locate the file where it was defined and open the +          editor at the point where it is defined. You can use `%edit function` +          to load an editor exactly at the point where 'function' is defined, +          edit it and have the file be executed automatically. + +        - If the object is a macro (see %macro for details), this opens up your +          specified editor with a temporary file containing the macro's data. +          Upon exit, the macro is reloaded with the contents of the file. + +        Note: opening at an exact line is only supported under Unix, and some +        editors (like kedit and gedit up to Gnome 2.8) do not understand the +        '+NUMBER' parameter necessary for this feature. Good editors like +        (X)Emacs, vi, jed, pico and joe all do. + +        After executing your code, %edit will return as output the code you +        typed in the editor (except when it was an existing file). This way +        you can reload the code in further invocations of %edit as a variable, +        via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of +        the output. + +        Note that %edit is also available through the alias %ed. + +        This is an example of creating a simple function inside the editor and +        then modifying it. First, start up the editor:: + +          In [1]: edit +          Editing... done. Executing edited code... +          Out[1]: 'def foo():\\n    print "foo() was defined in an editing +          session"\\n' + +        We can then call the function foo():: + +          In [2]: foo() +          foo() was defined in an editing session + +        Now we edit foo.  IPython automatically loads the editor with the +        (temporary) file where foo() was previously defined:: + +          In [3]: edit foo +          Editing... done. Executing edited code... + +        And if we call foo() again we get the modified version:: + +          In [4]: foo() +          foo() has now been changed! + +        Here is an example of how to edit a code snippet successive +        times. First we call the editor:: + +          In [5]: edit +          Editing... done. Executing edited code... +          hello +          Out[5]: "print 'hello'\\n" + +        Now we call it again with the previous output (stored in _):: + +          In [6]: edit _ +          Editing... done. Executing edited code... +          hello world +          Out[6]: "print 'hello world'\\n" + +        Now we call it with the output #8 (stored in _8, also as Out[8]):: + +          In [7]: edit _8 +          Editing... done. Executing edited code... +          hello again +          Out[7]: "print 'hello again'\\n" + + +        Changing the default editor hook: + +        If you wish to write your own editor hook, you can put it in a +        configuration file which you load at startup time.  The default hook +        is defined in the IPython.core.hooks module, and you can use that as a +        starting example for further modifications.  That file also has +        general instructions on how to set a new hook for use once you've +        defined it.""" +        opts,args = self.parse_options(parameter_s,'prxn:') + +        try: +            filename, lineno, is_temp = self._find_edit_target(self.shell,  +                                                       args, opts, last_call) +        except MacroToEdit as e: +            self._edit_macro(args, e.args[0]) +            return +        except InteractivelyDefined as e: +            print("Editing In[%i]" % e.index) +            args = str(e.index) +            filename, lineno, is_temp = self._find_edit_target(self.shell,  +                                                       args, opts, last_call) +        if filename is None: +            # nothing was found, warnings have already been issued, +            # just give up. +            return + +        if is_temp: +            self._knowntemps.add(filename) +        elif (filename in self._knowntemps): +            is_temp = True + + +        # do actual editing here +        print('Editing...', end=' ') +        sys.stdout.flush() +        filepath = Path(filename) +        try: +            # Quote filenames that may have spaces in them when opening +            # the editor +            quoted = filename = str(filepath.absolute()) +            if " " in quoted: +                quoted = "'%s'" % quoted +            self.shell.hooks.editor(quoted, lineno) +        except TryNext: +            warn('Could not open editor') +            return + +        # XXX TODO: should this be generalized for all string vars? +        # For now, this is special-cased to blocks created by cpaste +        if args.strip() == "pasted_block": +            self.shell.user_ns["pasted_block"] = filepath.read_text(encoding="utf-8") + +        if 'x' in opts:  # -x prevents actual execution +            print() +        else: +            print('done. Executing edited code...') +            with preserve_keys(self.shell.user_ns, '__file__'): +                if not is_temp: +                    self.shell.user_ns["__file__"] = filename +                if "r" in opts:  # Untranslated IPython code +                    source = filepath.read_text(encoding="utf-8") +                    self.shell.run_cell(source, store_history=False) +                else: +                    self.shell.safe_execfile(filename, self.shell.user_ns, +                                             self.shell.user_ns) + +        if is_temp: +            try: +                return filepath.read_text(encoding="utf-8") +            except IOError as msg: +                if Path(msg.filename) == filepath: +                    warn('File not found. Did you forget to save?') +                    return +                else: +                    self.shell.showtraceback() diff --git a/contrib/python/ipython/py3/IPython/core/magics/config.py b/contrib/python/ipython/py3/IPython/core/magics/config.py new file mode 100644 index 00000000000..9e1cb38c254 --- /dev/null +++ b/contrib/python/ipython/py3/IPython/core/magics/config.py @@ -0,0 +1,140 @@ +"""Implementation of configuration-related magic functions. +""" +#----------------------------------------------------------------------------- +#  Copyright (c) 2012 The IPython Development Team. +# +#  Distributed under the terms of the Modified BSD License. +# +#  The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +# Stdlib +import re + +# Our own packages +from IPython.core.error import UsageError +from IPython.core.magic import Magics, magics_class, line_magic +from logging import error + +#----------------------------------------------------------------------------- +# Magic implementation classes +#----------------------------------------------------------------------------- + +reg = re.compile(r'^\w+\.\w+$') +@magics_class +class ConfigMagics(Magics): + +    def __init__(self, shell): +        super(ConfigMagics, self).__init__(shell) +        self.configurables = [] + +    @line_magic +    def config(self, s): +        """configure IPython + +            %config Class[.trait=value] + +        This magic exposes most of the IPython config system. Any +        Configurable class should be able to be configured with the simple +        line:: + +            %config Class.trait=value + +        Where `value` will be resolved in the user's namespace, if it is an +        expression or variable name. + +        Examples +        -------- + +        To see what classes are available for config, pass no arguments:: + +            In [1]: %config +            Available objects for config: +                AliasManager +                DisplayFormatter +                HistoryManager +                IPCompleter +                LoggingMagics +                MagicsManager +                OSMagics +                PrefilterManager +                ScriptMagics +                TerminalInteractiveShell + +        To view what is configurable on a given class, just pass the class +        name:: + +            In [2]: %config LoggingMagics +            LoggingMagics(Magics) options +            --------------------------- +            LoggingMagics.quiet=<Bool> +                Suppress output of log state when logging is enabled +                Current: False + +        but the real use is in setting values:: + +            In [3]: %config LoggingMagics.quiet = True + +        and these values are read from the user_ns if they are variables:: + +            In [4]: feeling_quiet=False + +            In [5]: %config LoggingMagics.quiet = feeling_quiet + +        """ +        from traitlets.config.loader import Config +        # some IPython objects are Configurable, but do not yet have +        # any configurable traits.  Exclude them from the effects of +        # this magic, as their presence is just noise: +        configurables = sorted(set([ c for c in self.shell.configurables +                                     if c.__class__.class_traits(config=True) +                                     ]), key=lambda x: x.__class__.__name__) +        classnames = [ c.__class__.__name__ for c in configurables ] + +        line = s.strip() +        if not line: +            # print available configurable names +            print("Available objects for config:") +            for name in classnames: +                print("   ", name) +            return +        elif line in classnames: +            # `%config TerminalInteractiveShell` will print trait info for +            # TerminalInteractiveShell +            c = configurables[classnames.index(line)] +            cls = c.__class__ +            help = cls.class_get_help(c) +            # strip leading '--' from cl-args: +            help = re.sub(re.compile(r'^--', re.MULTILINE), '', help) +            print(help) +            return +        elif reg.match(line): +            cls, attr = line.split('.') +            return getattr(configurables[classnames.index(cls)],attr) +        elif '=' not in line: +            msg = "Invalid config statement: %r, "\ +                  "should be `Class.trait = value`." +             +            ll = line.lower() +            for classname in classnames: +                if ll == classname.lower(): +                    msg = msg + '\nDid you mean %s (note the case)?' % classname +                    break + +            raise UsageError( msg % line) + +        # otherwise, assume we are setting configurables. +        # leave quotes on args when splitting, because we want +        # unquoted args to eval in user_ns +        cfg = Config() +        exec("cfg."+line, self.shell.user_ns, locals()) + +        for configurable in configurables: +            try: +                configurable.update_config(cfg) +            except Exception as e: +                error(e) diff --git a/contrib/python/ipython/py3/IPython/core/magics/display.py b/contrib/python/ipython/py3/IPython/core/magics/display.py new file mode 100644 index 00000000000..6c0eff6884f --- /dev/null +++ b/contrib/python/ipython/py3/IPython/core/magics/display.py @@ -0,0 +1,93 @@ +"""Simple magics for display formats""" +#----------------------------------------------------------------------------- +#  Copyright (c) 2012 The IPython Development Team. +# +#  Distributed under the terms of the Modified BSD License. +# +#  The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +# Our own packages +from IPython.display import display, Javascript, Latex, SVG, HTML, Markdown +from IPython.core.magic import  ( +    Magics, magics_class, cell_magic +) +from IPython.core import magic_arguments + +#----------------------------------------------------------------------------- +# Magic implementation classes +#----------------------------------------------------------------------------- + + +@magics_class +class DisplayMagics(Magics): +    """Magics for displaying various output types with literals + +    Defines javascript/latex/svg/html cell magics for writing +    blocks in those languages, to be rendered in the frontend. +    """ + +    @cell_magic +    def js(self, line, cell): +        """Run the cell block of Javascript code + +        Alias of `%%javascript` + +        Starting with IPython 8.0 %%javascript is pending deprecation to be replaced +        by a more flexible system + +        Please See https://github.com/ipython/ipython/issues/13376 +        """ +        self.javascript(line, cell) + +    @cell_magic +    def javascript(self, line, cell): +        """Run the cell block of Javascript code + +        Starting with IPython 8.0 %%javascript is pending deprecation to be replaced +        by a more flexible system + +        Please See https://github.com/ipython/ipython/issues/13376 +        """ +        display(Javascript(cell)) + + +    @cell_magic +    def latex(self, line, cell): +        """Render the cell as a block of LaTeX + +        The subset of LaTeX which is supported depends on the implementation in +        the client.  In the Jupyter Notebook, this magic only renders the subset +        of LaTeX defined by MathJax +        [here](https://docs.mathjax.org/en/v2.5-latest/tex.html).""" +        display(Latex(cell)) + +    @cell_magic +    def svg(self, line, cell): +        """Render the cell as an SVG literal""" +        display(SVG(cell)) + +    @magic_arguments.magic_arguments() +    @magic_arguments.argument( +        '--isolated', action='store_true', default=False, +        help="""Annotate the cell as 'isolated'. +Isolated cells are rendered inside their own <iframe> tag""" +    ) +    @cell_magic +    def html(self, line, cell): +        """Render the cell as a block of HTML""" +        args = magic_arguments.parse_argstring(self.html, line) +        html = HTML(cell) +        if args.isolated: +            display(html, metadata={'text/html':{'isolated':True}}) +        else: +            display(html) + +    @cell_magic +    def markdown(self, line, cell): +        """Render the cell as Markdown text block""" +        display(Markdown(cell)) diff --git a/contrib/python/ipython/py3/IPython/core/magics/execution.py b/contrib/python/ipython/py3/IPython/core/magics/execution.py new file mode 100644 index 00000000000..228cbd9da77 --- /dev/null +++ b/contrib/python/ipython/py3/IPython/core/magics/execution.py @@ -0,0 +1,1522 @@ +# -*- 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 cProfile as profile +import gc +import itertools +import math +import os +import pstats +import re +import shlex +import sys +import time +import timeit +from ast import Module +from io import StringIO +from logging import error +from pathlib import Path +from pdb import Restart +from warnings import warn + +from IPython.core import magic_arguments, oinspect, page +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, +    on_off, +) +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.displayhook import DisplayHook + +#----------------------------------------------------------------------------- +# Magic implementation classes +#----------------------------------------------------------------------------- + + +class TimeitResult(object): +    """ +    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 has been repeated +    best: (float) best execution time / number +    all_runs: (list of 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] + +    @property +    def average(self): +        return math.fsum(self.timings) / len(self.timings) + +    @property +    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: +                u'\xb1'.encode(sys.stdout.encoding) +                pm = u'\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(u'<TimeitResult : '+unic+u'>') + + +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 + + +@magics_class +class ExecutionMagics(Magics): +    """Magics related to code execution, debugging, profiling, etc. + +    """ + +    def __init__(self, shell): +        super(ExecutionMagics, self).__init__(shell) +        # Default execution function used to actually run user code. +        self.default_runner = None + +    @skip_doctest +    @no_var_expand +    @line_cell_magic +    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. + +        """ +        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 + +    @line_magic +    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() + +        if par: +            try: +                new_pdb = {'off':0,'0':0,'on':1,'1':1}[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_off(new_pdb)) + +    @magic_arguments.magic_arguments() +    @magic_arguments.argument('--breakpoint', '-b', metavar='FILE:LINE', +        help=""" +        Set break point at LINE in FILE. +        """ +    ) +    @magic_arguments.argument('statement', nargs='*', +        help=""" +        Code to run in debugger. +        You can omit this in cell magic mode. +        """ +    ) +    @no_var_expand +    @line_cell_magic +    @needs_local_scope +    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 = magic_arguments.parse_argstring(self.debug, line) + +        if not (args.breakpoint or args.statement 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 = "\n".join(args.statement) +            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 +        ) + +    @line_magic +    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() + +    @skip_doctest +    @line_magic +    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 uniq_stable.py):: + +            In [1]: run -t uniq_stable + +            IPython CPU timings (estimated): +              User  :    0.19597 s. +              System:        0.0 s. + +            In [2]: run -t -N5 uniq_stable + +            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 + +        # deb.checkline() fails if deb.curframe exists but is None; it can +        # handle it not existing. https://github.com/ipython/ipython/issues/10028 +        if hasattr(deb, 'curframe'): +            del deb.curframe + +        # 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) +             + +        except: +            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) + +    @staticmethod +    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)) + +    @skip_doctest +    @no_var_expand +    @line_cell_magic +    @needs_local_scope +    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] statement +        or in cell mode: +          %%timeit [-n<N> -r<R> [-t|-c] -q -p<P> -o] 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 +        best 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. + +        .. 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.""" + +        opts, stmt = self.parse_options( +            line, "n:r:tcp:qo", 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 +        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 return_result: +            return timeit_result + +    @skip_doctest +    @no_var_expand +    @needs_local_scope +    @line_cell_magic +    @output_can_be_silenced +    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. + +        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 +        """ +        # fail immediately if the given expression can't be compiled +         +        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() +        if mode=='eval': +            st = clock2() +            try: +                out = eval(code, glob, local_ns) +            except: +                self.shell.showtraceback() +                return +            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: +                self.shell.showtraceback() +                return +            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)}") +        return out + +    @skip_doctest +    @line_magic +    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 not ( 'q' in opts) :  +            print('Macro `%s` created. To execute, type its name (without quotes).' % name) +            print('=== Macro contents: ===') +            print(macro, end=' ') + +    @magic_arguments.magic_arguments() +    @magic_arguments.argument('output', type=str, default='', nargs='?', +        help="""The name of the variable in which to store output. +        This is a utils.io.CapturedIO object with stdout/err attributes +        for the text of the captured output. + +        CapturedOutput also has a show() method for displaying the output, +        and __call__ as well, so you can use that to quickly display the +        output. + +        If unspecified, captured output is discarded. +        """ +    ) +    @magic_arguments.argument('--no-stderr', action="store_true", +        help="""Don't capture stderr.""" +    ) +    @magic_arguments.argument('--no-stdout', action="store_true", +        help="""Don't capture stdout.""" +    ) +    @magic_arguments.argument('--no-display', action="store_true", +        help="""Don't capture IPython's rich display.""" +    ) +    @cell_magic +    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 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(u'%s%s' % (str(value), suffix)) +            if leftover < 1: +                break +        return " ".join(time) + +     +    # Unfortunately the unicode 'micro' symbol 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 = [u"s", u"ms",u'us',"ns"] # the save value    +    if hasattr(sys.stdout, 'encoding') and sys.stdout.encoding: +        try: +            u'\xb5'.encode(sys.stdout.encoding) +            units = [u"s", u"ms",u'\xb5s',"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 u"%.*g %s" % (precision, timespan * scaling[order], units[order]) diff --git a/contrib/python/ipython/py3/IPython/core/magics/extension.py b/contrib/python/ipython/py3/IPython/core/magics/extension.py new file mode 100644 index 00000000000..2bc76b2d552 --- /dev/null +++ b/contrib/python/ipython/py3/IPython/core/magics/extension.py @@ -0,0 +1,63 @@ +"""Implementation of magic functions for the extension machinery. +""" +#----------------------------------------------------------------------------- +#  Copyright (c) 2012 The IPython Development Team. +# +#  Distributed under the terms of the Modified BSD License. +# +#  The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + + +# Our own packages +from IPython.core.error import UsageError +from IPython.core.magic import Magics, magics_class, line_magic + +#----------------------------------------------------------------------------- +# Magic implementation classes +#----------------------------------------------------------------------------- + +@magics_class +class ExtensionMagics(Magics): +    """Magics to manage the IPython extensions system.""" + +    @line_magic +    def load_ext(self, module_str): +        """Load an IPython extension by its module name.""" +        if not module_str: +            raise UsageError('Missing module name.') +        res = self.shell.extension_manager.load_extension(module_str) +         +        if res == 'already loaded': +            print("The %s extension is already loaded. To reload it, use:" % module_str) +            print("  %reload_ext", module_str) +        elif res == 'no load function': +            print("The %s module is not an IPython extension." % module_str) + +    @line_magic +    def unload_ext(self, module_str): +        """Unload an IPython extension by its module name. + +        Not all extensions can be unloaded, only those which define an +        ``unload_ipython_extension`` function. +        """ +        if not module_str: +            raise UsageError('Missing module name.') +         +        res = self.shell.extension_manager.unload_extension(module_str) +         +        if res == 'no unload function': +            print("The %s extension doesn't define how to unload it." % module_str) +        elif res == "not loaded": +            print("The %s extension is not loaded." % module_str) + +    @line_magic +    def reload_ext(self, module_str): +        """Reload an IPython extension by its module name.""" +        if not module_str: +            raise UsageError('Missing module name.') +        self.shell.extension_manager.reload_extension(module_str) diff --git a/contrib/python/ipython/py3/IPython/core/magics/history.py b/contrib/python/ipython/py3/IPython/core/magics/history.py new file mode 100644 index 00000000000..faa4335faa8 --- /dev/null +++ b/contrib/python/ipython/py3/IPython/core/magics/history.py @@ -0,0 +1,338 @@ +"""Implementation of magic functions related to History. +""" +#----------------------------------------------------------------------------- +#  Copyright (c) 2012, IPython Development Team. +# +#  Distributed under the terms of the Modified BSD License. +# +#  The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +# Stdlib +import os +import sys +from io import open as io_open +import fnmatch + +# Our own packages +from IPython.core.error import StdinNotImplementedError +from IPython.core.magic import Magics, magics_class, line_magic +from IPython.core.magic_arguments import (argument, magic_arguments, +                                          parse_argstring) +from IPython.testing.skipdoctest import skip_doctest +from IPython.utils import io + +#----------------------------------------------------------------------------- +# Magics class implementation +#----------------------------------------------------------------------------- + + +_unspecified = object() + + +@magics_class +class HistoryMagics(Magics): + +    @magic_arguments() +    @argument( +        '-n', dest='print_nums', action='store_true', default=False, +        help=""" +        print line numbers for each input. +        This feature is only available if numbered prompts are in use. +        """) +    @argument( +        '-o', dest='get_output', action='store_true', default=False, +        help="also print outputs for each input.") +    @argument( +        '-p', dest='pyprompts', action='store_true', default=False, +        help=""" +        print classic '>>>' python prompts before each input. +        This is useful for making documentation, and in conjunction +        with -o, for producing doctest-ready output. +        """) +    @argument( +        '-t', dest='raw', action='store_false', default=True, +        help=""" +        print the 'translated' history, as IPython understands it. +        IPython filters your input and converts it all into valid Python +        source before executing it (things like magics or aliases are turned +        into function calls, for example). With this option, you'll see the +        native history instead of the user-entered version: '%%cd /' will be +        seen as 'get_ipython().run_line_magic("cd", "/")' instead of '%%cd /'. +        """) +    @argument( +        '-f', dest='filename', +        help=""" +        FILENAME: instead of printing the output to the screen, redirect +        it to the given file.  The file is always overwritten, though *when +        it can*, IPython asks for confirmation first. In particular, running +        the command 'history -f FILENAME' from the IPython Notebook +        interface will replace FILENAME even if it already exists *without* +        confirmation. +        """) +    @argument( +        '-g', dest='pattern', nargs='*', default=None, +        help=""" +        treat the arg as a glob pattern to search for in (full) history. +        This includes the saved history (almost all commands ever written). +        The pattern may contain '?' to match one unknown character and '*' +        to match any number of unknown characters. Use '%%hist -g' to show +        full saved history (may be very long). +        """) +    @argument( +        '-l', dest='limit', type=int, nargs='?', default=_unspecified, +        help=""" +        get the last n lines from all sessions. Specify n as a single +        arg, or the default is the last 10 lines. +        """) +    @argument( +        '-u', dest='unique', action='store_true', +        help=""" +        when searching history using `-g`, show only unique history. +        """) +    @argument('range', nargs='*') +    @skip_doctest +    @line_magic +    def history(self, parameter_s = ''): +        """Print input history (_i<n> variables), with most recent last. + +        By default, input history is printed without line numbers so it can be +        directly pasted into an editor. Use -n to show them. + +        By default, all input history from the current session is displayed. +        Ranges of history can be indicated using the syntax: + +        ``4`` +            Line 4, current session +        ``4-6`` +            Lines 4-6, current session +        ``243/1-5`` +            Lines 1-5, session 243 +        ``~2/7`` +            Line 7, session 2 before current +        ``~8/1-~6/5`` +            From the first line of 8 sessions ago, to the fifth line of 6 +            sessions ago. + +        Multiple ranges can be entered, separated by spaces + +        The same syntax is used by %macro, %save, %edit, %rerun + +        Examples +        -------- +        :: + +          In [6]: %history -n 4-6 +          4:a = 12 +          5:print a**2 +          6:%history -n 4-6 + +        """ + +        args = parse_argstring(self.history, parameter_s) + +        # For brevity +        history_manager = self.shell.history_manager + +        def _format_lineno(session, line): +            """Helper function to format line numbers properly.""" +            if session in (0, history_manager.session_number): +                return str(line) +            return "%s/%s" % (session, line) + +        # Check if output to specific file was requested. +        outfname = args.filename +        if not outfname: +            outfile = sys.stdout  # default +            # We don't want to close stdout at the end! +            close_at_end = False +        else: +            outfname = os.path.expanduser(outfname) +            if os.path.exists(outfname): +                try: +                    ans = io.ask_yes_no("File %r exists. Overwrite?" % outfname) +                except StdinNotImplementedError: +                    ans = True +                if not ans: +                    print('Aborting.') +                    return +                print("Overwriting file.") +            outfile = io_open(outfname, 'w', encoding='utf-8') +            close_at_end = True + +        print_nums = args.print_nums +        get_output = args.get_output +        pyprompts = args.pyprompts +        raw = args.raw + +        pattern = None +        limit = None if args.limit is _unspecified else args.limit + +        range_pattern = False +        if args.pattern is not None and not args.range: +            if args.pattern: +                pattern = "*" + " ".join(args.pattern) + "*" +            else: +                pattern = "*" +            hist = history_manager.search(pattern, raw=raw, output=get_output, +                                          n=limit, unique=args.unique) +            print_nums = True +        elif args.limit is not _unspecified: +            n = 10 if limit is None else limit +            hist = history_manager.get_tail(n, raw=raw, output=get_output) +        else: +            if args.pattern: +                range_pattern = "*" + " ".join(args.pattern) + "*" +                print_nums = True +            hist = history_manager.get_range_by_str( +                " ".join(args.range), raw, get_output +            ) + +        # We could be displaying the entire history, so let's not try to pull +        # it into a list in memory. Anything that needs more space will just +        # misalign. +        width = 4 + +        for session, lineno, inline in hist: +            # Print user history with tabs expanded to 4 spaces.  The GUI +            # clients use hard tabs for easier usability in auto-indented code, +            # but we want to produce PEP-8 compliant history for safe pasting +            # into an editor. +            if get_output: +                inline, output = inline +            if range_pattern: +                if not fnmatch.fnmatch(inline, range_pattern): +                    continue +            inline = inline.expandtabs(4).rstrip() + +            multiline = "\n" in inline +            line_sep = '\n' if multiline else ' ' +            if print_nums: +                print(u'%s:%s' % (_format_lineno(session, lineno).rjust(width), +                        line_sep),  file=outfile, end=u'') +            if pyprompts: +                print(u">>> ", end=u"", file=outfile) +                if multiline: +                    inline = "\n... ".join(inline.splitlines()) + "\n..." +            print(inline, file=outfile) +            if get_output and output: +                print(output, file=outfile) + +        if close_at_end: +            outfile.close() + +    @line_magic +    def recall(self, arg): +        r"""Repeat a command, or get command to input line for editing. + +        %recall and %rep are equivalent. + +        - %recall (no arguments): + +        Place a string version of last computation result (stored in the +        special '_' variable) to the next input prompt. Allows you to create +        elaborate command lines without using copy-paste:: + +             In[1]: l = ["hei", "vaan"] +             In[2]: "".join(l) +            Out[2]: heivaan +             In[3]: %recall +             In[4]: heivaan_ <== cursor blinking + +        %recall 45 + +        Place history line 45 on the next input prompt. Use %hist to find +        out the number. + +        %recall 1-4 + +        Combine the specified lines into one cell, and place it on the next +        input prompt. See %history for the slice syntax. + +        %recall foo+bar + +        If foo+bar can be evaluated in the user namespace, the result is +        placed at the next input prompt. Otherwise, the history is searched +        for lines which contain that substring, and the most recent one is +        placed at the next input prompt. +        """ +        if not arg:                 # Last output +            self.shell.set_next_input(str(self.shell.user_ns["_"])) +            return +                                    # Get history range +        histlines = self.shell.history_manager.get_range_by_str(arg) +        cmd = "\n".join(x[2] for x in histlines) +        if cmd: +            self.shell.set_next_input(cmd.rstrip()) +            return + +        try:                        # Variable in user namespace +            cmd = str(eval(arg, self.shell.user_ns)) +        except Exception:           # Search for term in history +            histlines = self.shell.history_manager.search("*"+arg+"*") +            for h in reversed([x[2] for x in histlines]): +                if 'recall' in h or 'rep' in h: +                    continue +                self.shell.set_next_input(h.rstrip()) +                return +        else: +            self.shell.set_next_input(cmd.rstrip()) +            return +        print("Couldn't evaluate or find in history:", arg) + +    @line_magic +    def rerun(self, parameter_s=''): +        """Re-run previous input + +        By default, you can specify ranges of input history to be repeated +        (as with %history). With no arguments, it will repeat the last line. + +        Options: + +          -l <n> : Repeat the last n lines of input, not including the +          current command. + +          -g foo : Repeat the most recent line which contains foo +        """ +        opts, args = self.parse_options(parameter_s, 'l:g:', mode='string') +        if "l" in opts:         # Last n lines +            try: +                n = int(opts["l"]) +            except ValueError: +                print("Number of lines must be an integer") +                return + +            if n == 0: +                print("Requested 0 last lines - nothing to run") +                return +            elif n < 0: +                print("Number of lines to rerun cannot be negative") +                return + +            hist = self.shell.history_manager.get_tail(n) +        elif "g" in opts:       # Search +            p = "*"+opts['g']+"*" +            hist = list(self.shell.history_manager.search(p)) +            for l in reversed(hist): +                if "rerun" not in l[2]: +                    hist = [l]     # The last match which isn't a %rerun +                    break +            else: +                hist = []          # No matches except %rerun +        elif args:              # Specify history ranges +            hist = self.shell.history_manager.get_range_by_str(args) +        else:                   # Last line +            hist = self.shell.history_manager.get_tail(1) +        hist = [x[2] for x in hist] +        if not hist: +            print("No lines in history match specification") +            return +        histlines = "\n".join(hist) +        print("=== Executing: ===") +        print(histlines) +        print("=== Output: ===") +        self.shell.run_cell("\n".join(hist), store_history=False) diff --git a/contrib/python/ipython/py3/IPython/core/magics/logging.py b/contrib/python/ipython/py3/IPython/core/magics/logging.py new file mode 100644 index 00000000000..b6b8d8a5af6 --- /dev/null +++ b/contrib/python/ipython/py3/IPython/core/magics/logging.py @@ -0,0 +1,195 @@ +"""Implementation of magic functions for IPython's own logging. +""" +#----------------------------------------------------------------------------- +#  Copyright (c) 2012 The IPython Development Team. +# +#  Distributed under the terms of the Modified BSD License. +# +#  The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +# Stdlib +import os +import sys + +# Our own packages +from IPython.core.magic import Magics, magics_class, line_magic +from warnings import warn +from traitlets import Bool + +#----------------------------------------------------------------------------- +# Magic implementation classes +#----------------------------------------------------------------------------- + +@magics_class +class LoggingMagics(Magics): +    """Magics related to all logging machinery.""" + +    quiet = Bool(False, help= +        """ +        Suppress output of log state when logging is enabled +        """ +    ).tag(config=True) + +    @line_magic +    def logstart(self, parameter_s=''): +        """Start logging anywhere in a session. + +        %logstart [-o|-r|-t|-q] [log_name [log_mode]] + +        If no name is given, it defaults to a file named 'ipython_log.py' in your +        current directory, in 'rotate' mode (see below). + +        '%logstart name' saves to file 'name' in 'backup' mode.  It saves your +        history up to that point and then continues logging. + +        %logstart takes a second optional parameter: logging mode. This can be one +        of (note that the modes are given unquoted): + +        append +            Keep logging at the end of any existing file. + +        backup +            Rename any existing file to name~ and start name. + +        global +            Append to  a single logfile in your home directory. + +        over +            Overwrite any existing log. + +        rotate +            Create rotating logs: name.1~, name.2~, etc. + +        Options: + +          -o +            log also IPython's output. In this mode, all commands which +            generate an Out[NN] prompt are recorded to the logfile, right after +            their corresponding input line. The output lines are always +            prepended with a '#[Out]# ' marker, so that the log remains valid +            Python code. + +          Since this marker is always the same, filtering only the output from +          a log is very easy, using for example a simple awk call:: + +            awk -F'#\\[Out\\]# ' '{if($2) {print $2}}' ipython_log.py + +          -r +            log 'raw' input.  Normally, IPython's logs contain the processed +            input, so that user lines are logged in their final form, converted +            into valid Python.  For example, %Exit is logged as +            _ip.magic("Exit").  If the -r flag is given, all input is logged +            exactly as typed, with no transformations applied. + +          -t +            put timestamps before each input line logged (these are put in +            comments). + +          -q  +            suppress output of logstate message when logging is invoked +        """ + +        opts,par = self.parse_options(parameter_s,'ortq') +        log_output = 'o' in opts +        log_raw_input = 'r' in opts +        timestamp = 't' in opts +        quiet = 'q' in opts + +        logger = self.shell.logger + +        # if no args are given, the defaults set in the logger constructor by +        # ipython remain valid +        if par: +            try: +                logfname,logmode = par.split() +            except: +                logfname = par +                logmode = 'backup' +        else: +            logfname = logger.logfname +            logmode = logger.logmode +        # put logfname into rc struct as if it had been called on the command +        # line, so it ends up saved in the log header Save it in case we need +        # to restore it... +        old_logfile = self.shell.logfile +        if logfname: +            logfname = os.path.expanduser(logfname) +        self.shell.logfile = logfname + +        loghead = u'# IPython log file\n\n' +        try: +            logger.logstart(logfname, loghead, logmode, log_output, timestamp, +                            log_raw_input) +        except: +            self.shell.logfile = old_logfile +            warn("Couldn't start log: %s" % sys.exc_info()[1]) +        else: +            # log input history up to this point, optionally interleaving +            # output if requested + +            if timestamp: +                # disable timestamping for the previous history, since we've +                # lost those already (no time machine here). +                logger.timestamp = False + +            if log_raw_input: +                input_hist = self.shell.history_manager.input_hist_raw +            else: +                input_hist = self.shell.history_manager.input_hist_parsed + +            if log_output: +                log_write = logger.log_write +                output_hist = self.shell.history_manager.output_hist +                for n in range(1,len(input_hist)-1): +                    log_write(input_hist[n].rstrip() + u'\n') +                    if n in output_hist: +                        log_write(repr(output_hist[n]),'output') +            else: +                logger.log_write(u'\n'.join(input_hist[1:])) +                logger.log_write(u'\n') +            if timestamp: +                # re-enable timestamping +                logger.timestamp = True + +            if not (self.quiet or quiet): +                print ('Activating auto-logging. ' +                       'Current session state plus future input saved.') +                logger.logstate() + +    @line_magic +    def logstop(self, parameter_s=''): +        """Fully stop logging and close log file. + +        In order to start logging again, a new %logstart call needs to be made, +        possibly (though not necessarily) with a new filename, mode and other +        options.""" +        self.shell.logger.logstop() + +    @line_magic +    def logoff(self, parameter_s=''): +        """Temporarily stop logging. + +        You must have previously started logging.""" +        self.shell.logger.switch_log(0) + +    @line_magic +    def logon(self, parameter_s=''): +        """Restart logging. + +        This function is for restarting logging which you've temporarily +        stopped with %logoff. For starting logging for the first time, you +        must use the %logstart function, which allows you to specify an +        optional log filename.""" + +        self.shell.logger.switch_log(1) + +    @line_magic +    def logstate(self, parameter_s=''): +        """Print the status of the logging system.""" + +        self.shell.logger.logstate() diff --git a/contrib/python/ipython/py3/IPython/core/magics/namespace.py b/contrib/python/ipython/py3/IPython/core/magics/namespace.py new file mode 100644 index 00000000000..5da8f7161a0 --- /dev/null +++ b/contrib/python/ipython/py3/IPython/core/magics/namespace.py @@ -0,0 +1,711 @@ +"""Implementation of namespace-related magic functions. +""" +#----------------------------------------------------------------------------- +#  Copyright (c) 2012 The IPython Development Team. +# +#  Distributed under the terms of the Modified BSD License. +# +#  The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +# Stdlib +import gc +import re +import sys + +# Our own packages +from IPython.core import page +from IPython.core.error import StdinNotImplementedError, UsageError +from IPython.core.magic import Magics, magics_class, line_magic +from IPython.testing.skipdoctest import skip_doctest +from IPython.utils.encoding import DEFAULT_ENCODING +from IPython.utils.openpy import read_py_file +from IPython.utils.path import get_py_filename + +#----------------------------------------------------------------------------- +# Magic implementation classes +#----------------------------------------------------------------------------- + +@magics_class +class NamespaceMagics(Magics): +    """Magics to manage various aspects of the user's namespace. + +    These include listing variables, introspecting into them, etc. +    """ + +    @line_magic +    def pinfo(self, parameter_s='', namespaces=None): +        """Provide detailed information about an object. + +        '%pinfo object' is just a synonym for object? or ?object.""" + +        #print 'pinfo par: <%s>' % parameter_s  # dbg +        # detail_level: 0 -> obj? , 1 -> obj?? +        detail_level = 0 +        # We need to detect if we got called as 'pinfo pinfo foo', which can +        # happen if the user types 'pinfo foo?' at the cmd line. +        pinfo,qmark1,oname,qmark2 = \ +               re.match(r'(pinfo )?(\?*)(.*?)(\??$)',parameter_s).groups() +        if pinfo or qmark1 or qmark2: +            detail_level = 1 +        if "*" in oname: +            self.psearch(oname) +        else: +            self.shell._inspect('pinfo', oname, detail_level=detail_level, +                                namespaces=namespaces) + +    @line_magic +    def pinfo2(self, parameter_s='', namespaces=None): +        """Provide extra detailed information about an object. + +        '%pinfo2 object' is just a synonym for object?? or ??object.""" +        self.shell._inspect('pinfo', parameter_s, detail_level=1, +                            namespaces=namespaces) + +    @skip_doctest +    @line_magic +    def pdef(self, parameter_s='', namespaces=None): +        """Print the call signature for any callable object. + +        If the object is a class, print the constructor information. + +        Examples +        -------- +        :: + +          In [3]: %pdef urllib.urlopen +          urllib.urlopen(url, data=None, proxies=None) +        """ +        self.shell._inspect('pdef',parameter_s, namespaces) + +    @line_magic +    def pdoc(self, parameter_s='', namespaces=None): +        """Print the docstring for an object. + +        If the given object is a class, it will print both the class and the +        constructor docstrings.""" +        self.shell._inspect('pdoc',parameter_s, namespaces) + +    @line_magic +    def psource(self, parameter_s='', namespaces=None): +        """Print (or run through pager) the source code for an object.""" +        if not parameter_s: +            raise UsageError('Missing object name.') +        self.shell._inspect('psource',parameter_s, namespaces) + +    @line_magic +    def pfile(self, parameter_s='', namespaces=None): +        """Print (or run through pager) the file where an object is defined. + +        The file opens at the line where the object definition begins. IPython +        will honor the environment variable PAGER if set, and otherwise will +        do its best to print the file in a convenient form. + +        If the given argument is not an object currently defined, IPython will +        try to interpret it as a filename (automatically adding a .py extension +        if needed). You can thus use %pfile as a syntax highlighting code +        viewer.""" + +        # first interpret argument as an object name +        out = self.shell._inspect('pfile',parameter_s, namespaces) +        # if not, try the input as a filename +        if out == 'not found': +            try: +                filename = get_py_filename(parameter_s) +            except IOError as msg: +                print(msg) +                return +            page.page(self.shell.pycolorize(read_py_file(filename, skip_encoding_cookie=False))) + +    @line_magic +    def psearch(self, parameter_s=''): +        """Search for object in namespaces by wildcard. + +        %psearch [options] PATTERN [OBJECT TYPE] + +        Note: ? can be used as a synonym for %psearch, at the beginning or at +        the end: both a*? and ?a* are equivalent to '%psearch a*'.  Still, the +        rest of the command line must be unchanged (options come first), so +        for example the following forms are equivalent + +        %psearch -i a* function +        -i a* function? +        ?-i a* function + +        Arguments: + +          PATTERN + +          where PATTERN is a string containing * as a wildcard similar to its +          use in a shell.  The pattern is matched in all namespaces on the +          search path. By default objects starting with a single _ are not +          matched, many IPython generated objects have a single +          underscore. The default is case insensitive matching. Matching is +          also done on the attributes of objects and not only on the objects +          in a module. + +          [OBJECT TYPE] + +          Is the name of a python type from the types module. The name is +          given in lowercase without the ending type, ex. StringType is +          written string. By adding a type here only objects matching the +          given type are matched. Using all here makes the pattern match all +          types (this is the default). + +        Options: + +          -a: makes the pattern match even objects whose names start with a +          single underscore.  These names are normally omitted from the +          search. + +          -i/-c: make the pattern case insensitive/sensitive.  If neither of +          these options are given, the default is read from your configuration +          file, with the option ``InteractiveShell.wildcards_case_sensitive``. +          If this option is not specified in your configuration file, IPython's +          internal default is to do a case sensitive search. + +          -e/-s NAMESPACE: exclude/search a given namespace.  The pattern you +          specify can be searched in any of the following namespaces: +          'builtin', 'user', 'user_global','internal', 'alias', where +          'builtin' and 'user' are the search defaults.  Note that you should +          not use quotes when specifying namespaces. + +          -l: List all available object types for object matching. This function +          can be used without arguments. + +          'Builtin' contains the python module builtin, 'user' contains all +          user data, 'alias' only contain the shell aliases and no python +          objects, 'internal' contains objects used by IPython.  The +          'user_global' namespace is only used by embedded IPython instances, +          and it contains module-level globals.  You can add namespaces to the +          search with -s or exclude them with -e (these options can be given +          more than once). + +        Examples +        -------- +        :: + +          %psearch a*            -> objects beginning with an a +          %psearch -e builtin a* -> objects NOT in the builtin space starting in a +          %psearch a* function   -> all functions beginning with an a +          %psearch re.e*         -> objects beginning with an e in module re +          %psearch r*.e*         -> objects that start with e in modules starting in r +          %psearch r*.* string   -> all strings in modules beginning with r + +        Case sensitive search:: + +          %psearch -c a*         list all object beginning with lower case a + +        Show objects beginning with a single _:: + +          %psearch -a _*         list objects beginning with a single underscore + +        List available objects:: + +          %psearch -l            list all available object types +        """ +        # default namespaces to be searched +        def_search = ['user_local', 'user_global', 'builtin'] + +        # Process options/args +        opts,args = self.parse_options(parameter_s,'cias:e:l',list_all=True) +        opt = opts.get +        shell = self.shell +        psearch = shell.inspector.psearch +         +        # select list object types +        list_types = False +        if 'l' in opts: +            list_types = True + +        # select case options +        if 'i' in opts: +            ignore_case = True +        elif 'c' in opts: +            ignore_case = False +        else: +            ignore_case = not shell.wildcards_case_sensitive + +        # Build list of namespaces to search from user options +        def_search.extend(opt('s',[])) +        ns_exclude = ns_exclude=opt('e',[]) +        ns_search = [nm for nm in def_search if nm not in ns_exclude] + +        # Call the actual search +        try: +            psearch(args,shell.ns_table,ns_search, +                    show_all=opt('a'),ignore_case=ignore_case, list_types=list_types) +        except: +            shell.showtraceback() + +    @skip_doctest +    @line_magic +    def who_ls(self, parameter_s=''): +        """Return a sorted list of all interactive variables. + +        If arguments are given, only variables of types matching these +        arguments are returned. + +        Examples +        -------- +        Define two variables and list them with who_ls:: + +          In [1]: alpha = 123 + +          In [2]: beta = 'test' + +          In [3]: %who_ls +          Out[3]: ['alpha', 'beta'] + +          In [4]: %who_ls int +          Out[4]: ['alpha'] + +          In [5]: %who_ls str +          Out[5]: ['beta'] +        """ + +        user_ns = self.shell.user_ns +        user_ns_hidden = self.shell.user_ns_hidden +        nonmatching = object()  # This can never be in user_ns +        out = [ i for i in user_ns +                if not i.startswith('_') \ +                and (user_ns[i] is not user_ns_hidden.get(i, nonmatching)) ] + +        typelist = parameter_s.split() +        if typelist: +            typeset = set(typelist) +            out = [i for i in out if type(user_ns[i]).__name__ in typeset] + +        out.sort() +        return out + +    @skip_doctest +    @line_magic +    def who(self, parameter_s=''): +        """Print all interactive variables, with some minimal formatting. + +        If any arguments are given, only variables whose type matches one of +        these are printed.  For example:: + +          %who function str + +        will only list functions and strings, excluding all other types of +        variables.  To find the proper type names, simply use type(var) at a +        command line to see how python prints type names.  For example: + +        :: + +          In [1]: type('hello')\\ +          Out[1]: <type 'str'> + +        indicates that the type name for strings is 'str'. + +        ``%who`` always excludes executed names loaded through your configuration +        file and things which are internal to IPython. + +        This is deliberate, as typically you may load many modules and the +        purpose of %who is to show you only what you've manually defined. + +        Examples +        -------- + +        Define two variables and list them with who:: + +          In [1]: alpha = 123 + +          In [2]: beta = 'test' + +          In [3]: %who +          alpha   beta + +          In [4]: %who int +          alpha + +          In [5]: %who str +          beta +        """ + +        varlist = self.who_ls(parameter_s) +        if not varlist: +            if parameter_s: +                print('No variables match your requested type.') +            else: +                print('Interactive namespace is empty.') +            return + +        # if we have variables, move on... +        count = 0 +        for i in varlist: +            print(i+'\t', end=' ') +            count += 1 +            if count > 8: +                count = 0 +                print() +        print() + +    @skip_doctest +    @line_magic +    def whos(self, parameter_s=''): +        """Like %who, but gives some extra information about each variable. + +        The same type filtering of %who can be applied here. + +        For all variables, the type is printed. Additionally it prints: + +          - For {},[],(): their length. + +          - For numpy arrays, a summary with shape, number of +            elements, typecode and size in memory. + +          - Everything else: a string representation, snipping their middle if +            too long. + +        Examples +        -------- +        Define two variables and list them with whos:: + +          In [1]: alpha = 123 + +          In [2]: beta = 'test' + +          In [3]: %whos +          Variable   Type        Data/Info +          -------------------------------- +          alpha      int         123 +          beta       str         test +        """ + +        varnames = self.who_ls(parameter_s) +        if not varnames: +            if parameter_s: +                print('No variables match your requested type.') +            else: +                print('Interactive namespace is empty.') +            return + +        # if we have variables, move on... + +        # for these types, show len() instead of data: +        seq_types = ['dict', 'list', 'tuple'] + +        # for numpy arrays, display summary info +        ndarray_type = None +        if 'numpy' in sys.modules: +            try: +                from numpy import ndarray +            except ImportError: +                pass +            else: +                ndarray_type = ndarray.__name__ + +        # Find all variable names and types so we can figure out column sizes + +        # some types are well known and can be shorter +        abbrevs = {'IPython.core.macro.Macro' : 'Macro'} +        def type_name(v): +            tn = type(v).__name__ +            return abbrevs.get(tn,tn) + +        varlist = [self.shell.user_ns[n] for n in varnames] + +        typelist = [] +        for vv in varlist: +            tt = type_name(vv) + +            if tt=='instance': +                typelist.append( abbrevs.get(str(vv.__class__), +                                             str(vv.__class__))) +            else: +                typelist.append(tt) + +        # column labels and # of spaces as separator +        varlabel = 'Variable' +        typelabel = 'Type' +        datalabel = 'Data/Info' +        colsep = 3 +        # variable format strings +        vformat    = "{0:<{varwidth}}{1:<{typewidth}}" +        aformat    = "%s: %s elems, type `%s`, %s bytes" +        # find the size of the columns to format the output nicely +        varwidth = max(max(map(len,varnames)), len(varlabel)) + colsep +        typewidth = max(max(map(len,typelist)), len(typelabel)) + colsep +        # table header +        print(varlabel.ljust(varwidth) + typelabel.ljust(typewidth) + \ +              ' '+datalabel+'\n' + '-'*(varwidth+typewidth+len(datalabel)+1)) +        # and the table itself +        kb = 1024 +        Mb = 1048576  # kb**2 +        for vname,var,vtype in zip(varnames,varlist,typelist): +            print(vformat.format(vname, vtype, varwidth=varwidth, typewidth=typewidth), end=' ') +            if vtype in seq_types: +                print("n="+str(len(var))) +            elif vtype == ndarray_type: +                vshape = str(var.shape).replace(',','').replace(' ','x')[1:-1] +                if vtype==ndarray_type: +                    # numpy +                    vsize  = var.size +                    vbytes = vsize*var.itemsize +                    vdtype = var.dtype + +                if vbytes < 100000: +                    print(aformat % (vshape, vsize, vdtype, vbytes)) +                else: +                    print(aformat % (vshape, vsize, vdtype, vbytes), end=' ') +                    if vbytes < Mb: +                        print('(%s kb)' % (vbytes/kb,)) +                    else: +                        print('(%s Mb)' % (vbytes/Mb,)) +            else: +                try: +                    vstr = str(var) +                except UnicodeEncodeError: +                    vstr = var.encode(DEFAULT_ENCODING, +                                      'backslashreplace') +                except: +                    vstr = "<object with id %d (str() failed)>" % id(var) +                vstr = vstr.replace('\n', '\\n') +                if len(vstr) < 50: +                    print(vstr) +                else: +                    print(vstr[:25] + "<...>" + vstr[-25:]) + +    @line_magic +    def reset(self, parameter_s=''): +        """Resets the namespace by removing all names defined by the user, if +        called without arguments, or by removing some types of objects, such +        as everything currently in IPython's In[] and Out[] containers (see +        the parameters for details). + +        Parameters +        ---------- +        -f +            force reset without asking for confirmation. +        -s +            'Soft' reset: Only clears your namespace, leaving history intact. +            References to objects may be kept. By default (without this option), +            we do a 'hard' reset, giving you a new session and removing all +            references to objects from the current session. +        --aggressive +            Try to aggressively remove modules from sys.modules ; this +            may allow you to reimport Python modules that have been updated and +            pick up changes, but can have unintended consequences. + +        in +            reset input history +        out +            reset output history +        dhist +            reset directory history +        array +            reset only variables that are NumPy arrays + +        See Also +        -------- +        reset_selective : invoked as ``%reset_selective`` + +        Examples +        -------- +        :: + +          In [6]: a = 1 + +          In [7]: a +          Out[7]: 1 + +          In [8]: 'a' in get_ipython().user_ns +          Out[8]: True + +          In [9]: %reset -f + +          In [1]: 'a' in get_ipython().user_ns +          Out[1]: False + +          In [2]: %reset -f in +          Flushing input history + +          In [3]: %reset -f dhist in +          Flushing directory history +          Flushing input history + +        Notes +        ----- +        Calling this magic from clients that do not implement standard input, +        such as the ipython notebook interface, will reset the namespace +        without confirmation. +        """ +        opts, args = self.parse_options(parameter_s, "sf", "aggressive", mode="list") +        if "f" in opts: +            ans = True +        else: +            try: +                ans = self.shell.ask_yes_no( +                "Once deleted, variables cannot be recovered. Proceed (y/[n])?", +                default='n') +            except StdinNotImplementedError: +                ans = True +        if not ans: +            print('Nothing done.') +            return + +        if 's' in opts:                     # Soft reset +            user_ns = self.shell.user_ns +            for i in self.who_ls(): +                del(user_ns[i]) +        elif len(args) == 0:                # Hard reset +            self.shell.reset(new_session=False, aggressive=("aggressive" in opts)) + +        # reset in/out/dhist/array: previously extensinions/clearcmd.py +        ip = self.shell +        user_ns = self.shell.user_ns  # local lookup, heavily used + +        for target in args: +            target = target.lower() # make matches case insensitive +            if target == 'out': +                print("Flushing output cache (%d entries)" % len(user_ns['_oh'])) +                self.shell.displayhook.flush() + +            elif target == 'in': +                print("Flushing input history") +                pc = self.shell.displayhook.prompt_count + 1 +                for n in range(1, pc): +                    key = '_i'+repr(n) +                    user_ns.pop(key,None) +                user_ns.update(dict(_i=u'',_ii=u'',_iii=u'')) +                hm = ip.history_manager +                # don't delete these, as %save and %macro depending on the +                # length of these lists to be preserved +                hm.input_hist_parsed[:] = [''] * pc +                hm.input_hist_raw[:] = [''] * pc +                # hm has internal machinery for _i,_ii,_iii, clear it out +                hm._i = hm._ii = hm._iii = hm._i00 =  u'' + +            elif target == 'array': +                # Support cleaning up numpy arrays +                try: +                    from numpy import ndarray +                    # This must be done with items and not iteritems because +                    # we're going to modify the dict in-place. +                    for x,val in list(user_ns.items()): +                        if isinstance(val,ndarray): +                            del user_ns[x] +                except ImportError: +                    print("reset array only works if Numpy is available.") + +            elif target == 'dhist': +                print("Flushing directory history") +                del user_ns['_dh'][:] + +            else: +                print("Don't know how to reset ", end=' ') +                print(target + ", please run `%reset?` for details") + +        gc.collect() + +    @line_magic +    def reset_selective(self, parameter_s=''): +        """Resets the namespace by removing names defined by the user. + +        Input/Output history are left around in case you need them. + +        %reset_selective [-f] regex + +        No action is taken if regex is not included + +        Options +          -f : force reset without asking for confirmation. + +        See Also +        -------- +        reset : invoked as ``%reset`` + +        Examples +        -------- +        We first fully reset the namespace so your output looks identical to +        this example for pedagogical reasons; in practice you do not need a +        full reset:: + +          In [1]: %reset -f + +        Now, with a clean namespace we can make a few variables and use +        ``%reset_selective`` to only delete names that match our regexp:: + +          In [2]: a=1; b=2; c=3; b1m=4; b2m=5; b3m=6; b4m=7; b2s=8 + +          In [3]: who_ls +          Out[3]: ['a', 'b', 'b1m', 'b2m', 'b2s', 'b3m', 'b4m', 'c'] + +          In [4]: %reset_selective -f b[2-3]m + +          In [5]: who_ls +          Out[5]: ['a', 'b', 'b1m', 'b2s', 'b4m', 'c'] + +          In [6]: %reset_selective -f d + +          In [7]: who_ls +          Out[7]: ['a', 'b', 'b1m', 'b2s', 'b4m', 'c'] + +          In [8]: %reset_selective -f c + +          In [9]: who_ls +          Out[9]: ['a', 'b', 'b1m', 'b2s', 'b4m'] + +          In [10]: %reset_selective -f b + +          In [11]: who_ls +          Out[11]: ['a'] + +        Notes +        ----- +        Calling this magic from clients that do not implement standard input, +        such as the ipython notebook interface, will reset the namespace +        without confirmation. +        """ + +        opts, regex = self.parse_options(parameter_s,'f') + +        if 'f' in opts: +            ans = True +        else: +            try: +                ans = self.shell.ask_yes_no( +                "Once deleted, variables cannot be recovered. Proceed (y/[n])? ", +                default='n') +            except StdinNotImplementedError: +                ans = True +        if not ans: +            print('Nothing done.') +            return +        user_ns = self.shell.user_ns +        if not regex: +            print('No regex pattern specified. Nothing done.') +            return +        else: +            try: +                m = re.compile(regex) +            except TypeError as e: +                raise TypeError('regex must be a string or compiled pattern') from e +            for i in self.who_ls(): +                if m.search(i): +                    del(user_ns[i]) + +    @line_magic +    def xdel(self, parameter_s=''): +        """Delete a variable, trying to clear it from anywhere that +        IPython's machinery has references to it. By default, this uses +        the identity of the named object in the user namespace to remove +        references held under other names. The object is also removed +        from the output history. + +        Options +          -n : Delete the specified name from all namespaces, without +          checking their identity. +        """ +        opts, varname = self.parse_options(parameter_s,'n') +        try: +            self.shell.del_var(varname, ('n' in opts)) +        except (NameError, ValueError) as e: +            print(type(e).__name__ +": "+ str(e)) diff --git a/contrib/python/ipython/py3/IPython/core/magics/osm.py b/contrib/python/ipython/py3/IPython/core/magics/osm.py new file mode 100644 index 00000000000..f64f1bce6ae --- /dev/null +++ b/contrib/python/ipython/py3/IPython/core/magics/osm.py @@ -0,0 +1,855 @@ +"""Implementation of magic functions for interaction with the OS. + +Note: this module is named 'osm' instead of 'os' to avoid a collision with the +builtin. +""" +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. + +import io +import os +import pathlib +import re +import sys +from pprint import pformat + +from IPython.core import magic_arguments +from IPython.core import oinspect +from IPython.core import page +from IPython.core.alias import AliasError, Alias +from IPython.core.error import UsageError +from IPython.core.magic import  ( +    Magics, compress_dhist, magics_class, line_magic, cell_magic, line_cell_magic +) +from IPython.testing.skipdoctest import skip_doctest +from IPython.utils.openpy import source_to_unicode +from IPython.utils.process import abbrev_cwd +from IPython.utils.terminal import set_term_title +from traitlets import Bool +from warnings import warn + + +@magics_class +class OSMagics(Magics): +    """Magics to interact with the underlying OS (shell-type functionality). +    """ + +    cd_force_quiet = Bool(False, +        help="Force %cd magic to be quiet even if -q is not passed." +    ).tag(config=True) + +    def __init__(self, shell=None, **kwargs): + +        # Now define isexec in a cross platform manner. +        self.is_posix = False +        self.execre = None +        if os.name == 'posix': +            self.is_posix = True +        else: +            try: +                winext = os.environ['pathext'].replace(';','|').replace('.','') +            except KeyError: +                winext = 'exe|com|bat|py' +            try: +                self.execre = re.compile(r'(.*)\.(%s)$' % winext,re.IGNORECASE) +            except re.error: +                warn("Seems like your pathext environmental " +                     "variable is malformed. Please check it to " +                     "enable a proper handle of file extensions " +                     "managed for your system") +                winext = 'exe|com|bat|py' +                self.execre = re.compile(r'(.*)\.(%s)$' % winext,re.IGNORECASE) + +        # call up the chain +        super().__init__(shell=shell, **kwargs) + + +    def _isexec_POSIX(self, file): +        """ +        Test for executable on a POSIX system +        """ +        if os.access(file.path, os.X_OK): +            # will fail on maxOS if access is not X_OK +            return file.is_file() +        return False + + +     +    def _isexec_WIN(self, file): +        """ +        Test for executable file on non POSIX system +        """ +        return file.is_file() and self.execre.match(file.name) is not None + +    def isexec(self, file): +        """ +        Test for executable file on non POSIX system +        """ +        if self.is_posix: +            return self._isexec_POSIX(file) +        else: +            return self._isexec_WIN(file) + + +    @skip_doctest +    @line_magic +    def alias(self, parameter_s=''): +        """Define an alias for a system command. + +        '%alias alias_name cmd' defines 'alias_name' as an alias for 'cmd' + +        Then, typing 'alias_name params' will execute the system command 'cmd +        params' (from your underlying operating system). + +        Aliases have lower precedence than magic functions and Python normal +        variables, so if 'foo' is both a Python variable and an alias, the +        alias can not be executed until 'del foo' removes the Python variable. + +        You can use the %l specifier in an alias definition to represent the +        whole line when the alias is called.  For example:: + +          In [2]: alias bracket echo "Input in brackets: <%l>" +          In [3]: bracket hello world +          Input in brackets: <hello world> + +        You can also define aliases with parameters using %s specifiers (one +        per parameter):: + +          In [1]: alias parts echo first %s second %s +          In [2]: %parts A B +          first A second B +          In [3]: %parts A +          Incorrect number of arguments: 2 expected. +          parts is an alias to: 'echo first %s second %s' + +        Note that %l and %s are mutually exclusive.  You can only use one or +        the other in your aliases. + +        Aliases expand Python variables just like system calls using ! or !! +        do: all expressions prefixed with '$' get expanded.  For details of +        the semantic rules, see PEP-215: +        https://peps.python.org/pep-0215/.  This is the library used by +        IPython for variable expansion.  If you want to access a true shell +        variable, an extra $ is necessary to prevent its expansion by +        IPython:: + +          In [6]: alias show echo +          In [7]: PATH='A Python string' +          In [8]: show $PATH +          A Python string +          In [9]: show $$PATH +          /usr/local/lf9560/bin:/usr/local/intel/compiler70/ia32/bin:... + +        You can use the alias facility to access all of $PATH.  See the %rehashx +        function, which automatically creates aliases for the contents of your +        $PATH. + +        If called with no parameters, %alias prints the current alias table +        for your system.  For posix systems, the default aliases are 'cat', +        'cp', 'mv', 'rm', 'rmdir', and 'mkdir', and other platform-specific +        aliases are added.  For windows-based systems, the default aliases are +        'copy', 'ddir', 'echo', 'ls', 'ldir', 'mkdir', 'ren', and 'rmdir'. + +        You can see the definition of alias by adding a question mark in the +        end:: + +          In [1]: cat? +          Repr: <alias cat for 'cat'>""" + +        par = parameter_s.strip() +        if not par: +            aliases = sorted(self.shell.alias_manager.aliases) +            # stored = self.shell.db.get('stored_aliases', {} ) +            # for k, v in stored: +            #     atab.append(k, v[0]) + +            print("Total number of aliases:", len(aliases)) +            sys.stdout.flush() +            return aliases + +        # Now try to define a new one +        try: +            alias,cmd = par.split(None, 1) +        except TypeError: +            print(oinspect.getdoc(self.alias)) +            return +         +        try: +            self.shell.alias_manager.define_alias(alias, cmd) +        except AliasError as e: +            print(e) +    # end magic_alias + +    @line_magic +    def unalias(self, parameter_s=''): +        """Remove an alias""" + +        aname = parameter_s.strip() +        try: +            self.shell.alias_manager.undefine_alias(aname) +        except ValueError as e: +            print(e) +            return +         +        stored = self.shell.db.get('stored_aliases', {} ) +        if aname in stored: +            print("Removing %stored alias",aname) +            del stored[aname] +            self.shell.db['stored_aliases'] = stored + +    @line_magic +    def rehashx(self, parameter_s=''): +        """Update the alias table with all executable files in $PATH. + +        rehashx explicitly checks that every entry in $PATH is a file +        with execute access (os.X_OK). + +        Under Windows, it checks executability as a match against a +        '|'-separated string of extensions, stored in the IPython config +        variable win_exec_ext.  This defaults to 'exe|com|bat'. + +        This function also resets the root module cache of module completer, +        used on slow filesystems. +        """ +        from IPython.core.alias import InvalidAliasError + +        # for the benefit of module completer in ipy_completers.py +        del self.shell.db['rootmodules_cache'] + +        path = [os.path.abspath(os.path.expanduser(p)) for p in +            os.environ.get('PATH','').split(os.pathsep)] + +        syscmdlist = [] +        savedir = os.getcwd() + +        # Now walk the paths looking for executables to alias. +        try: +            # write the whole loop for posix/Windows so we don't have an if in +            # the innermost part +            if self.is_posix: +                for pdir in path: +                    try: +                        os.chdir(pdir) +                    except OSError: +                        continue + +                    # for python 3.6+ rewrite to: with os.scandir(pdir) as dirlist: +                    dirlist = os.scandir(path=pdir) +                    for ff in dirlist: +                        if self.isexec(ff): +                            fname = ff.name +                            try: +                                # Removes dots from the name since ipython +                                # will assume names with dots to be python. +                                if not self.shell.alias_manager.is_alias(fname): +                                    self.shell.alias_manager.define_alias( +                                        fname.replace('.',''), fname) +                            except InvalidAliasError: +                                pass +                            else: +                                syscmdlist.append(fname) +            else: +                no_alias = Alias.blacklist +                for pdir in path: +                    try: +                        os.chdir(pdir) +                    except OSError: +                        continue + +                    # for python 3.6+ rewrite to: with os.scandir(pdir) as dirlist: +                    dirlist = os.scandir(pdir) +                    for ff in dirlist: +                        fname = ff.name +                        base, ext = os.path.splitext(fname) +                        if self.isexec(ff) and base.lower() not in no_alias: +                            if ext.lower() == '.exe': +                                fname = base +                                try: +                                    # Removes dots from the name since ipython +                                    # will assume names with dots to be python. +                                    self.shell.alias_manager.define_alias( +                                        base.lower().replace('.',''), fname) +                                except InvalidAliasError: +                                    pass +                                syscmdlist.append(fname) + +            self.shell.db['syscmdlist'] = syscmdlist +        finally: +            os.chdir(savedir) + +    @skip_doctest +    @line_magic +    def pwd(self, parameter_s=''): +        """Return the current working directory path. + +        Examples +        -------- +        :: + +          In [9]: pwd +          Out[9]: '/home/tsuser/sprint/ipython' +        """ +        try: +            return os.getcwd() +        except FileNotFoundError as e: +            raise UsageError("CWD no longer exists - please use %cd to change directory.") from e + +    @skip_doctest +    @line_magic +    def cd(self, parameter_s=''): +        """Change the current working directory. + +        This command automatically maintains an internal list of directories +        you visit during your IPython session, in the variable ``_dh``. The +        command :magic:`%dhist` shows this history nicely formatted. You can +        also do ``cd -<tab>`` to see directory history conveniently. +        Usage: + +          - ``cd 'dir'``: changes to directory 'dir'. +          - ``cd -``: changes to the last visited directory. +          - ``cd -<n>``: changes to the n-th directory in the directory history. +          - ``cd --foo``: change to directory that matches 'foo' in history +          - ``cd -b <bookmark_name>``: jump to a bookmark set by %bookmark +          - Hitting a tab key after ``cd -b`` allows you to tab-complete +            bookmark names. + +          .. note:: +            ``cd <bookmark_name>`` is enough if there is no directory +            ``<bookmark_name>``, but a bookmark with the name exists. + +        Options: + +        -q               Be quiet. Do not print the working directory after the +                         cd command is executed. By default IPython's cd +                         command does print this directory, since the default +                         prompts do not display path information. + +        .. note:: +           Note that ``!cd`` doesn't work for this purpose because the shell +           where ``!command`` runs is immediately discarded after executing +           'command'. + +        Examples +        -------- +        :: + +          In [10]: cd parent/child +          /home/tsuser/parent/child +        """ + +        try: +            oldcwd = os.getcwd() +        except FileNotFoundError: +            # Happens if the CWD has been deleted. +            oldcwd = None + +        numcd = re.match(r'(-)(\d+)$',parameter_s) +        # jump in directory history by number +        if numcd: +            nn = int(numcd.group(2)) +            try: +                ps = self.shell.user_ns['_dh'][nn] +            except IndexError: +                print('The requested directory does not exist in history.') +                return +            else: +                opts = {} +        elif parameter_s.startswith('--'): +            ps = None +            fallback = None +            pat = parameter_s[2:] +            dh = self.shell.user_ns['_dh'] +            # first search only by basename (last component) +            for ent in reversed(dh): +                if pat in os.path.basename(ent) and os.path.isdir(ent): +                    ps = ent +                    break + +                if fallback is None and pat in ent and os.path.isdir(ent): +                    fallback = ent + +            # if we have no last part match, pick the first full path match +            if ps is None: +                ps = fallback + +            if ps is None: +                print("No matching entry in directory history") +                return +            else: +                opts = {} + + +        else: +            opts, ps = self.parse_options(parameter_s, 'qb', mode='string') +        # jump to previous +        if ps == '-': +            try: +                ps = self.shell.user_ns['_dh'][-2] +            except IndexError as e: +                raise UsageError('%cd -: No previous directory to change to.') from e +        # jump to bookmark if needed +        else: +            if not os.path.isdir(ps) or 'b' in opts: +                bkms = self.shell.db.get('bookmarks', {}) + +                if ps in bkms: +                    target = bkms[ps] +                    print('(bookmark:%s) -> %s' % (ps, target)) +                    ps = target +                else: +                    if 'b' in opts: +                        raise UsageError("Bookmark '%s' not found.  " +                              "Use '%%bookmark -l' to see your bookmarks." % ps) + +        # at this point ps should point to the target dir +        if ps: +            try: +                os.chdir(os.path.expanduser(ps)) +                if hasattr(self.shell, 'term_title') and self.shell.term_title: +                    set_term_title(self.shell.term_title_format.format(cwd=abbrev_cwd())) +            except OSError: +                print(sys.exc_info()[1]) +            else: +                cwd = pathlib.Path.cwd() +                dhist = self.shell.user_ns['_dh'] +                if oldcwd != cwd: +                    dhist.append(cwd) +                    self.shell.db['dhist'] = compress_dhist(dhist)[-100:] + +        else: +            os.chdir(self.shell.home_dir) +            if hasattr(self.shell, 'term_title') and self.shell.term_title: +                set_term_title(self.shell.term_title_format.format(cwd="~")) +            cwd = pathlib.Path.cwd() +            dhist = self.shell.user_ns['_dh'] + +            if oldcwd != cwd: +                dhist.append(cwd) +                self.shell.db['dhist'] = compress_dhist(dhist)[-100:] +        if not 'q' in opts and not self.cd_force_quiet and self.shell.user_ns['_dh']: +            print(self.shell.user_ns['_dh'][-1]) + +    @line_magic +    def env(self, parameter_s=''): +        """Get, set, or list environment variables. + +        Usage:\\ + +          :``%env``: lists all environment variables/values +          :``%env var``: get value for var +          :``%env var val``: set value for var +          :``%env var=val``: set value for var +          :``%env var=$val``: set value for var, using python expansion if possible +        """ +        if parameter_s.strip(): +            split = '=' if '=' in parameter_s else ' ' +            bits = parameter_s.split(split) +            if len(bits) == 1: +                key = parameter_s.strip() +                if key in os.environ: +                    return os.environ[key] +                else: +                    err = "Environment does not have key: {0}".format(key) +                    raise UsageError(err) +            if len(bits) > 1: +                return self.set_env(parameter_s) +        env = dict(os.environ) +        # hide likely secrets when printing the whole environment +        for key in list(env): +            if any(s in key.lower() for s in ('key', 'token', 'secret')): +                env[key] = '<hidden>' + +        return env + +    @line_magic +    def set_env(self, parameter_s): +        """Set environment variables.  Assumptions are that either "val" is a +        name in the user namespace, or val is something that evaluates to a +        string. + +        Usage:\\ +          :``%set_env var val``: set value for var +          :``%set_env var=val``: set value for var +          :``%set_env var=$val``: set value for var, using python expansion if possible +        """ +        split = '=' if '=' in parameter_s else ' ' +        bits = parameter_s.split(split, 1) +        if not parameter_s.strip() or len(bits)<2: +            raise UsageError("usage is 'set_env var=val'") +        var = bits[0].strip() +        val = bits[1].strip() +        if re.match(r'.*\s.*', var): +            # an environment variable with whitespace is almost certainly +            # not what the user intended.  what's more likely is the wrong +            # split was chosen, ie for "set_env cmd_args A=B", we chose +            # '=' for the split and should have chosen ' '.  to get around +            # this, users should just assign directly to os.environ or use +            # standard magic {var} expansion. +            err = "refusing to set env var with whitespace: '{0}'" +            err = err.format(val) +            raise UsageError(err) +        os.environ[var] = val +        print('env: {0}={1}'.format(var,val)) + +    @line_magic +    def pushd(self, parameter_s=''): +        """Place the current dir on stack and change directory. + +        Usage:\\ +          %pushd ['dirname'] +        """ + +        dir_s = self.shell.dir_stack +        tgt = os.path.expanduser(parameter_s) +        cwd = os.getcwd().replace(self.shell.home_dir,'~') +        if tgt: +            self.cd(parameter_s) +        dir_s.insert(0,cwd) +        return self.shell.run_line_magic('dirs', '') + +    @line_magic +    def popd(self, parameter_s=''): +        """Change to directory popped off the top of the stack. +        """ +        if not self.shell.dir_stack: +            raise UsageError("%popd on empty stack") +        top = self.shell.dir_stack.pop(0) +        self.cd(top) +        print("popd ->",top) + +    @line_magic +    def dirs(self, parameter_s=''): +        """Return the current directory stack.""" + +        return self.shell.dir_stack + +    @line_magic +    def dhist(self, parameter_s=''): +        """Print your history of visited directories. + +        %dhist       -> print full history\\ +        %dhist n     -> print last n entries only\\ +        %dhist n1 n2 -> print entries between n1 and n2 (n2 not included)\\ + +        This history is automatically maintained by the %cd command, and +        always available as the global list variable _dh. You can use %cd -<n> +        to go to directory number <n>. + +        Note that most of time, you should view directory history by entering +        cd -<TAB>. + +        """ + +        dh = self.shell.user_ns['_dh'] +        if parameter_s: +            try: +                args = map(int,parameter_s.split()) +            except: +                self.arg_err(self.dhist) +                return +            if len(args) == 1: +                ini,fin = max(len(dh)-(args[0]),0),len(dh) +            elif len(args) == 2: +                ini,fin = args +                fin = min(fin, len(dh)) +            else: +                self.arg_err(self.dhist) +                return +        else: +            ini,fin = 0,len(dh) +        print('Directory history (kept in _dh)') +        for i in range(ini, fin): +            print("%d: %s" % (i, dh[i])) + +    @skip_doctest +    @line_magic +    def sc(self, parameter_s=''): +        """Shell capture - run shell command and capture output (DEPRECATED use !). + +        DEPRECATED. Suboptimal, retained for backwards compatibility. + +        You should use the form 'var = !command' instead. Example: + +         "%sc -l myfiles = ls ~" should now be written as + +         "myfiles = !ls ~" + +        myfiles.s, myfiles.l and myfiles.n still apply as documented +        below. + +        -- +        %sc [options] varname=command + +        IPython will run the given command using commands.getoutput(), and +        will then update the user's interactive namespace with a variable +        called varname, containing the value of the call.  Your command can +        contain shell wildcards, pipes, etc. + +        The '=' sign in the syntax is mandatory, and the variable name you +        supply must follow Python's standard conventions for valid names. + +        (A special format without variable name exists for internal use) + +        Options: + +          -l: list output.  Split the output on newlines into a list before +          assigning it to the given variable.  By default the output is stored +          as a single string. + +          -v: verbose.  Print the contents of the variable. + +        In most cases you should not need to split as a list, because the +        returned value is a special type of string which can automatically +        provide its contents either as a list (split on newlines) or as a +        space-separated string.  These are convenient, respectively, either +        for sequential processing or to be passed to a shell command. + +        For example:: + +            # Capture into variable a +            In [1]: sc a=ls *py + +            # a is a string with embedded newlines +            In [2]: a +            Out[2]: 'setup.py\\nwin32_manual_post_install.py' + +            # which can be seen as a list: +            In [3]: a.l +            Out[3]: ['setup.py', 'win32_manual_post_install.py'] + +            # or as a whitespace-separated string: +            In [4]: a.s +            Out[4]: 'setup.py win32_manual_post_install.py' + +            # a.s is useful to pass as a single command line: +            In [5]: !wc -l $a.s +              146 setup.py +              130 win32_manual_post_install.py +              276 total + +            # while the list form is useful to loop over: +            In [6]: for f in a.l: +               ...:      !wc -l $f +               ...: +            146 setup.py +            130 win32_manual_post_install.py + +        Similarly, the lists returned by the -l option are also special, in +        the sense that you can equally invoke the .s attribute on them to +        automatically get a whitespace-separated string from their contents:: + +            In [7]: sc -l b=ls *py + +            In [8]: b +            Out[8]: ['setup.py', 'win32_manual_post_install.py'] + +            In [9]: b.s +            Out[9]: 'setup.py win32_manual_post_install.py' + +        In summary, both the lists and strings used for output capture have +        the following special attributes:: + +            .l (or .list) : value as list. +            .n (or .nlstr): value as newline-separated string. +            .s (or .spstr): value as space-separated string. +        """ + +        opts,args = self.parse_options(parameter_s, 'lv') +        # Try to get a variable name and command to run +        try: +            # the variable name must be obtained from the parse_options +            # output, which uses shlex.split to strip options out. +            var,_ = args.split('=', 1) +            var = var.strip() +            # But the command has to be extracted from the original input +            # parameter_s, not on what parse_options returns, to avoid the +            # quote stripping which shlex.split performs on it. +            _,cmd = parameter_s.split('=', 1) +        except ValueError: +            var,cmd = '','' +        # If all looks ok, proceed +        split = 'l' in opts +        out = self.shell.getoutput(cmd, split=split) +        if 'v' in opts: +            print('%s ==\n%s' % (var, pformat(out))) +        if var: +            self.shell.user_ns.update({var:out}) +        else: +            return out + +    @line_cell_magic +    def sx(self, line='', cell=None): +        """Shell execute - run shell command and capture output (!! is short-hand). + +        %sx command + +        IPython will run the given command using commands.getoutput(), and +        return the result formatted as a list (split on '\\n').  Since the +        output is _returned_, it will be stored in ipython's regular output +        cache Out[N] and in the '_N' automatic variables. + +        Notes: + +        1) If an input line begins with '!!', then %sx is automatically +        invoked.  That is, while:: + +          !ls + +        causes ipython to simply issue system('ls'), typing:: + +          !!ls + +        is a shorthand equivalent to:: + +          %sx ls + +        2) %sx differs from %sc in that %sx automatically splits into a list, +        like '%sc -l'.  The reason for this is to make it as easy as possible +        to process line-oriented shell output via further python commands. +        %sc is meant to provide much finer control, but requires more +        typing. + +        3) Just like %sc -l, this is a list with special attributes: +        :: + +          .l (or .list) : value as list. +          .n (or .nlstr): value as newline-separated string. +          .s (or .spstr): value as whitespace-separated string. + +        This is very useful when trying to use such lists as arguments to +        system commands.""" +         +        if cell is None: +            # line magic +            return self.shell.getoutput(line) +        else: +            opts,args = self.parse_options(line, '', 'out=') +            output = self.shell.getoutput(cell) +            out_name = opts.get('out', opts.get('o')) +            if out_name: +                self.shell.user_ns[out_name] = output +            else: +                return output + +    system = line_cell_magic('system')(sx) +    bang = cell_magic('!')(sx) + +    @line_magic +    def bookmark(self, parameter_s=''): +        """Manage IPython's bookmark system. + +        %bookmark <name>       - set bookmark to current dir +        %bookmark <name> <dir> - set bookmark to <dir> +        %bookmark -l           - list all bookmarks +        %bookmark -d <name>    - remove bookmark +        %bookmark -r           - remove all bookmarks + +        You can later on access a bookmarked folder with:: + +          %cd -b <name> + +        or simply '%cd <name>' if there is no directory called <name> AND +        there is such a bookmark defined. + +        Your bookmarks persist through IPython sessions, but they are +        associated with each profile.""" + +        opts,args = self.parse_options(parameter_s,'drl',mode='list') +        if len(args) > 2: +            raise UsageError("%bookmark: too many arguments") + +        bkms = self.shell.db.get('bookmarks',{}) + +        if 'd' in opts: +            try: +                todel = args[0] +            except IndexError as e: +                raise UsageError( +                    "%bookmark -d: must provide a bookmark to delete") from e +            else: +                try: +                    del bkms[todel] +                except KeyError as e: +                    raise UsageError( +                        "%%bookmark -d: Can't delete bookmark '%s'" % todel) from e + +        elif 'r' in opts: +            bkms = {} +        elif 'l' in opts: +            bks = sorted(bkms) +            if bks: +                size = max(map(len, bks)) +            else: +                size = 0 +            fmt = '%-'+str(size)+'s -> %s' +            print('Current bookmarks:') +            for bk in bks: +                print(fmt % (bk, bkms[bk])) +        else: +            if not args: +                raise UsageError("%bookmark: You must specify the bookmark name") +            elif len(args)==1: +                bkms[args[0]] = os.getcwd() +            elif len(args)==2: +                bkms[args[0]] = args[1] +        self.shell.db['bookmarks'] = bkms + +    @line_magic +    def pycat(self, parameter_s=''): +        """Show a syntax-highlighted file through a pager. + +        This magic is similar to the cat utility, but it will assume the file +        to be Python source and will show it with syntax highlighting. + +        This magic command can either take a local filename, an url, +        an history range (see %history) or a macro as argument. + +        If no parameter is given, prints out history of current session up to +        this point. :: + +        %pycat myscript.py +        %pycat 7-27 +        %pycat myMacro +        %pycat http://www.example.com/myscript.py +        """ +        try: +            cont = self.shell.find_user_code(parameter_s, skip_encoding_cookie=False) +        except (ValueError, IOError): +            print("Error: no such file, variable, URL, history range or macro") +            return + +        page.page(self.shell.pycolorize(source_to_unicode(cont))) + +    @magic_arguments.magic_arguments() +    @magic_arguments.argument( +        '-a', '--append', action='store_true', default=False, +        help='Append contents of the cell to an existing file. ' +             'The file will be created if it does not exist.' +    ) +    @magic_arguments.argument( +        'filename', type=str, +        help='file to write' +    ) +    @cell_magic +    def writefile(self, line, cell): +        """Write the contents of the cell to a file. + +        The file will be overwritten unless the -a (--append) flag is specified. +        """ +        args = magic_arguments.parse_argstring(self.writefile, line) +        if re.match(r'^(\'.*\')|(".*")$', args.filename): +            filename = os.path.expanduser(args.filename[1:-1]) +        else: +            filename = os.path.expanduser(args.filename) +             +        if os.path.exists(filename): +            if args.append: +                print("Appending to %s" % filename) +            else: +                print("Overwriting %s" % filename) +        else: +            print("Writing %s" % filename) +         +        mode = 'a' if args.append else 'w' +        with io.open(filename, mode, encoding='utf-8') as f: +            f.write(cell) diff --git a/contrib/python/ipython/py3/IPython/core/magics/packaging.py b/contrib/python/ipython/py3/IPython/core/magics/packaging.py new file mode 100644 index 00000000000..2f7652c169b --- /dev/null +++ b/contrib/python/ipython/py3/IPython/core/magics/packaging.py @@ -0,0 +1,112 @@ +"""Implementation of packaging-related magic functions. +""" +#----------------------------------------------------------------------------- +#  Copyright (c) 2018 The IPython Development Team. +# +#  Distributed under the terms of the Modified BSD License. +# +#  The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +import re +import shlex +import sys +from pathlib import Path + +from IPython.core.magic import Magics, magics_class, line_magic + + +def _is_conda_environment(): +    """Return True if the current Python executable is in a conda env""" +    # TODO: does this need to change on windows? +    return Path(sys.prefix, "conda-meta", "history").exists() + + +def _get_conda_executable(): +    """Find the path to the conda executable""" +    # Check if there is a conda executable in the same directory as the Python executable. +    # This is the case within conda's root environment. +    conda = Path(sys.executable).parent / "conda" +    if conda.is_file(): +        return str(conda) + +    # Otherwise, attempt to extract the executable from conda history. +    # This applies in any conda environment. +    history = Path(sys.prefix, "conda-meta", "history").read_text(encoding="utf-8") +    match = re.search( +        r"^#\s*cmd:\s*(?P<command>.*conda)\s[create|install]", +        history, +        flags=re.MULTILINE, +    ) +    if match: +        return match.groupdict()["command"] + +    # Fallback: assume conda is available on the system path. +    return "conda" + + +CONDA_COMMANDS_REQUIRING_PREFIX = { +    'install', 'list', 'remove', 'uninstall', 'update', 'upgrade', +} +CONDA_COMMANDS_REQUIRING_YES = { +    'install', 'remove', 'uninstall', 'update', 'upgrade', +} +CONDA_ENV_FLAGS = {'-p', '--prefix', '-n', '--name'} +CONDA_YES_FLAGS = {'-y', '--y'} + + +@magics_class +class PackagingMagics(Magics): +    """Magics related to packaging & installation""" + +    @line_magic +    def pip(self, line): +        """Run the pip package manager within the current kernel. + +        Usage: +          %pip install [pkgs] +        """ +        python = sys.executable +        if sys.platform == "win32": +            python = '"' + python + '"' +        else: +            python = shlex.quote(python) + +        self.shell.system(" ".join([python, "-m", "pip", line])) + +        print("Note: you may need to restart the kernel to use updated packages.") + +    @line_magic +    def conda(self, line): +        """Run the conda package manager within the current kernel. + +        Usage: +          %conda install [pkgs] +        """ +        if not _is_conda_environment(): +            raise ValueError("The python kernel does not appear to be a conda environment.  " +                             "Please use ``%pip install`` instead.") + +        conda = _get_conda_executable() +        args = shlex.split(line) +        command = args[0] if len(args) > 0 else "" +        args = args[1:] if len(args) > 1 else [""] + +        extra_args = [] + +        # When the subprocess does not allow us to respond "yes" during the installation, +        # we need to insert --yes in the argument list for some commands +        stdin_disabled = getattr(self.shell, 'kernel', None) is not None +        needs_yes = command in CONDA_COMMANDS_REQUIRING_YES +        has_yes = set(args).intersection(CONDA_YES_FLAGS) +        if stdin_disabled and needs_yes and not has_yes: +            extra_args.append("--yes") + +        # Add --prefix to point conda installation to the current environment +        needs_prefix = command in CONDA_COMMANDS_REQUIRING_PREFIX +        has_prefix = set(args).intersection(CONDA_ENV_FLAGS) +        if needs_prefix and not has_prefix: +            extra_args.extend(["--prefix", sys.prefix]) + +        self.shell.system(' '.join([conda, command] + extra_args + args)) +        print("\nNote: you may need to restart the kernel to use updated packages.") diff --git a/contrib/python/ipython/py3/IPython/core/magics/pylab.py b/contrib/python/ipython/py3/IPython/core/magics/pylab.py new file mode 100644 index 00000000000..2a69453ac98 --- /dev/null +++ b/contrib/python/ipython/py3/IPython/core/magics/pylab.py @@ -0,0 +1,169 @@ +"""Implementation of magic functions for matplotlib/pylab support. +""" +#----------------------------------------------------------------------------- +#  Copyright (c) 2012 The IPython Development Team. +# +#  Distributed under the terms of the Modified BSD License. +# +#  The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +# Our own packages +from traitlets.config.application import Application +from IPython.core import magic_arguments +from IPython.core.magic import Magics, magics_class, line_magic +from IPython.testing.skipdoctest import skip_doctest +from warnings import warn +from IPython.core.pylabtools import backends + +#----------------------------------------------------------------------------- +# Magic implementation classes +#----------------------------------------------------------------------------- + +magic_gui_arg = magic_arguments.argument( +        'gui', nargs='?', +        help="""Name of the matplotlib backend to use %s. +        If given, the corresponding matplotlib backend is used, +        otherwise it will be matplotlib's default +        (which you can set in your matplotlib config file). +        """ % str(tuple(sorted(backends.keys()))) +) + + +@magics_class +class PylabMagics(Magics): +    """Magics related to matplotlib's pylab support""" + +    @skip_doctest +    @line_magic +    @magic_arguments.magic_arguments() +    @magic_arguments.argument('-l', '--list', action='store_true', +                              help='Show available matplotlib backends') +    @magic_gui_arg +    def matplotlib(self, line=''): +        """Set up matplotlib to work interactively. + +        This function lets you activate matplotlib interactive support +        at any point during an IPython session. It does not import anything +        into the interactive namespace. + +        If you are using the inline matplotlib backend in the IPython Notebook +        you can set which figure formats are enabled using the following:: + +            In [1]: from matplotlib_inline.backend_inline import set_matplotlib_formats + +            In [2]: set_matplotlib_formats('pdf', 'svg') + +        The default for inline figures sets `bbox_inches` to 'tight'. This can +        cause discrepancies between the displayed image and the identical +        image created using `savefig`. This behavior can be disabled using the +        `%config` magic:: + +            In [3]: %config InlineBackend.print_figure_kwargs = {'bbox_inches':None} + +        In addition, see the docstrings of +        `matplotlib_inline.backend_inline.set_matplotlib_formats` and +        `matplotlib_inline.backend_inline.set_matplotlib_close` for more information on +        changing additional behaviors of the inline backend. + +        Examples +        -------- +        To enable the inline backend for usage with the IPython Notebook:: + +            In [1]: %matplotlib inline + +        In this case, where the matplotlib default is TkAgg:: + +            In [2]: %matplotlib +            Using matplotlib backend: TkAgg + +        But you can explicitly request a different GUI backend:: + +            In [3]: %matplotlib qt + +        You can list the available backends using the -l/--list option:: + +           In [4]: %matplotlib --list +           Available matplotlib backends: ['osx', 'qt4', 'qt5', 'gtk3', 'gtk4', 'notebook', 'wx', 'qt', 'nbagg', +           'gtk', 'tk', 'inline'] +        """ +        args = magic_arguments.parse_argstring(self.matplotlib, line) +        if args.list: +            backends_list = list(backends.keys()) +            print("Available matplotlib backends: %s" % backends_list) +        else: +            gui, backend = self.shell.enable_matplotlib(args.gui.lower() if isinstance(args.gui, str) else args.gui) +            self._show_matplotlib_backend(args.gui, backend) + +    @skip_doctest +    @line_magic +    @magic_arguments.magic_arguments() +    @magic_arguments.argument( +        '--no-import-all', action='store_true', default=None, +        help="""Prevent IPython from performing ``import *`` into the interactive namespace. + +        You can govern the default behavior of this flag with the +        InteractiveShellApp.pylab_import_all configurable. +        """ +    ) +    @magic_gui_arg +    def pylab(self, line=''): +        """Load numpy and matplotlib to work interactively. + +        This function lets you activate pylab (matplotlib, numpy and +        interactive support) at any point during an IPython session. + +        %pylab makes the following imports:: + +            import numpy +            import matplotlib +            from matplotlib import pylab, mlab, pyplot +            np = numpy +            plt = pyplot + +            from IPython.display import display +            from IPython.core.pylabtools import figsize, getfigs + +            from pylab import * +            from numpy import * + +        If you pass `--no-import-all`, the last two `*` imports will be excluded. + +        See the %matplotlib magic for more details about activating matplotlib +        without affecting the interactive namespace. +        """ +        args = magic_arguments.parse_argstring(self.pylab, line) +        if args.no_import_all is None: +            # get default from Application +            if Application.initialized(): +                app = Application.instance() +                try: +                    import_all = app.pylab_import_all +                except AttributeError: +                    import_all = True +            else: +                # nothing specified, no app - default True +                import_all = True +        else: +            # invert no-import flag +            import_all = not args.no_import_all + +        gui, backend, clobbered = self.shell.enable_pylab(args.gui, import_all=import_all) +        self._show_matplotlib_backend(args.gui, backend) +        print( +            "%pylab is deprecated, use %matplotlib inline and import the required libraries." +        ) +        print("Populating the interactive namespace from numpy and matplotlib") +        if clobbered: +            warn("pylab import has clobbered these variables: %s"  % clobbered + +            "\n`%matplotlib` prevents importing * from pylab and numpy" +            ) + +    def _show_matplotlib_backend(self, gui, backend): +        """show matplotlib message backend message""" +        if not gui or gui == 'auto': +            print("Using matplotlib backend: %s" % backend) diff --git a/contrib/python/ipython/py3/IPython/core/magics/script.py b/contrib/python/ipython/py3/IPython/core/magics/script.py new file mode 100644 index 00000000000..a858c6489c8 --- /dev/null +++ b/contrib/python/ipython/py3/IPython/core/magics/script.py @@ -0,0 +1,371 @@ +"""Magic functions for running cells in various scripts.""" + +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. + +import asyncio +import asyncio.exceptions +import atexit +import errno +import os +import signal +import sys +import time +from subprocess import CalledProcessError +from threading import Thread + +from traitlets import Any, Dict, List, default + +from IPython.core import magic_arguments +from IPython.core.async_helpers import _AsyncIOProxy +from IPython.core.magic import Magics, cell_magic, line_magic, magics_class +from IPython.utils.process import arg_split + +#----------------------------------------------------------------------------- +# Magic implementation classes +#----------------------------------------------------------------------------- + +def script_args(f): +    """single decorator for adding script args""" +    args = [ +        magic_arguments.argument( +            '--out', type=str, +            help="""The variable in which to store stdout from the script. +            If the script is backgrounded, this will be the stdout *pipe*, +            instead of the stderr text itself and will not be auto closed. +            """ +        ), +        magic_arguments.argument( +            '--err', type=str, +            help="""The variable in which to store stderr from the script. +            If the script is backgrounded, this will be the stderr *pipe*, +            instead of the stderr text itself and will not be autoclosed. +            """ +        ), +        magic_arguments.argument( +            '--bg', action="store_true", +            help="""Whether to run the script in the background. +            If given, the only way to see the output of the command is +            with --out/err. +            """ +        ), +        magic_arguments.argument( +            '--proc', type=str, +            help="""The variable in which to store Popen instance. +            This is used only when --bg option is given. +            """ +        ), +        magic_arguments.argument( +            '--no-raise-error', action="store_false", dest='raise_error', +            help="""Whether you should raise an error message in addition to +            a stream on stderr if you get a nonzero exit code. +            """, +        ), +    ] +    for arg in args: +        f = arg(f) +    return f + + +@magics_class +class ScriptMagics(Magics): +    """Magics for talking to scripts +     +    This defines a base `%%script` cell magic for running a cell +    with a program in a subprocess, and registers a few top-level +    magics that call %%script with common interpreters. +    """ + +    event_loop = Any( +        help=""" +        The event loop on which to run subprocesses + +        Not the main event loop, +        because we want to be able to make blocking calls +        and have certain requirements we don't want to impose on the main loop. +        """ +    ) + +    script_magics = List( +        help="""Extra script cell magics to define +         +        This generates simple wrappers of `%%script foo` as `%%foo`. +         +        If you want to add script magics that aren't on your path, +        specify them in script_paths +        """, +    ).tag(config=True) +    @default('script_magics') +    def _script_magics_default(self): +        """default to a common list of programs""" +         +        defaults = [ +            'sh', +            'bash', +            'perl', +            'ruby', +            'python', +            'python2', +            'python3', +            'pypy', +        ] +        if os.name == 'nt': +            defaults.extend([ +                'cmd', +            ]) +         +        return defaults +     +    script_paths = Dict( +        help="""Dict mapping short 'ruby' names to full paths, such as '/opt/secret/bin/ruby' +         +        Only necessary for items in script_magics where the default path will not +        find the right interpreter. +        """ +    ).tag(config=True) +     +    def __init__(self, shell=None): +        super(ScriptMagics, self).__init__(shell=shell) +        self._generate_script_magics() +        self.bg_processes = [] +        atexit.register(self.kill_bg_processes) + +    def __del__(self): +        self.kill_bg_processes() +     +    def _generate_script_magics(self): +        cell_magics = self.magics['cell'] +        for name in self.script_magics: +            cell_magics[name] = self._make_script_magic(name) +     +    def _make_script_magic(self, name): +        """make a named magic, that calls %%script with a particular program""" +        # expand to explicit path if necessary: +        script = self.script_paths.get(name, name) +         +        @magic_arguments.magic_arguments() +        @script_args +        def named_script_magic(line, cell): +            # if line, add it as cl-flags +            if line: +                line = "%s %s" % (script, line) +            else: +                line = script +            return self.shebang(line, cell) +         +        # write a basic docstring: +        named_script_magic.__doc__ = \ +        """%%{name} script magic +         +        Run cells with {script} in a subprocess. +         +        This is a shortcut for `%%script {script}` +        """.format(**locals()) +         +        return named_script_magic +     +    @magic_arguments.magic_arguments() +    @script_args +    @cell_magic("script") +    def shebang(self, line, cell): +        """Run a cell via a shell command + +        The `%%script` line is like the #! line of script, +        specifying a program (bash, perl, ruby, etc.) with which to run. + +        The rest of the cell is run by that program. + +        Examples +        -------- +        :: + +            In [1]: %%script bash +               ...: for i in 1 2 3; do +               ...:   echo $i +               ...: done +            1 +            2 +            3 +        """ + +        # Create the event loop in which to run script magics +        # this operates on a background thread +        if self.event_loop is None: +            if sys.platform == "win32": +                # don't override the current policy, +                # just create an event loop +                event_loop = asyncio.WindowsProactorEventLoopPolicy().new_event_loop() +            else: +                event_loop = asyncio.new_event_loop() +            self.event_loop = event_loop + +            # start the loop in a background thread +            asyncio_thread = Thread(target=event_loop.run_forever, daemon=True) +            asyncio_thread.start() +        else: +            event_loop = self.event_loop + +        def in_thread(coro): +            """Call a coroutine on the asyncio thread""" +            return asyncio.run_coroutine_threadsafe(coro, event_loop).result() + +        async def _readchunk(stream): +            try: +                return await stream.readuntil(b"\n") +            except asyncio.exceptions.IncompleteReadError as e: +                return e.partial +            except asyncio.exceptions.LimitOverrunError as e: +                return await stream.read(e.consumed) + +        async def _handle_stream(stream, stream_arg, file_object): +            while True: +                chunk = (await _readchunk(stream)).decode("utf8", errors="replace") +                if not chunk: +                    break +                if stream_arg: +                    self.shell.user_ns[stream_arg] = chunk +                else: +                    file_object.write(chunk) +                    file_object.flush() + +        async def _stream_communicate(process, cell): +            process.stdin.write(cell) +            process.stdin.close() +            stdout_task = asyncio.create_task( +                _handle_stream(process.stdout, args.out, sys.stdout) +            ) +            stderr_task = asyncio.create_task( +                _handle_stream(process.stderr, args.err, sys.stderr) +            ) +            await asyncio.wait([stdout_task, stderr_task]) +            await process.wait() + +        argv = arg_split(line, posix=not sys.platform.startswith("win")) +        args, cmd = self.shebang.parser.parse_known_args(argv) + +        try: +            p = in_thread( +                asyncio.create_subprocess_exec( +                    *cmd, +                    stdout=asyncio.subprocess.PIPE, +                    stderr=asyncio.subprocess.PIPE, +                    stdin=asyncio.subprocess.PIPE, +                ) +            ) +        except OSError as e: +            if e.errno == errno.ENOENT: +                print("Couldn't find program: %r" % cmd[0]) +                return +            else: +                raise + +        if not cell.endswith('\n'): +            cell += '\n' +        cell = cell.encode('utf8', 'replace') +        if args.bg: +            self.bg_processes.append(p) +            self._gc_bg_processes() +            to_close = [] +            if args.out: +                self.shell.user_ns[args.out] = _AsyncIOProxy(p.stdout, event_loop) +            else: +                to_close.append(p.stdout) +            if args.err: +                self.shell.user_ns[args.err] = _AsyncIOProxy(p.stderr, event_loop) +            else: +                to_close.append(p.stderr) +            event_loop.call_soon_threadsafe( +                lambda: asyncio.Task(self._run_script(p, cell, to_close)) +            ) +            if args.proc: +                proc_proxy = _AsyncIOProxy(p, event_loop) +                proc_proxy.stdout = _AsyncIOProxy(p.stdout, event_loop) +                proc_proxy.stderr = _AsyncIOProxy(p.stderr, event_loop) +                self.shell.user_ns[args.proc] = proc_proxy +            return + +        try: +            in_thread(_stream_communicate(p, cell)) +        except KeyboardInterrupt: +            try: +                p.send_signal(signal.SIGINT) +                in_thread(asyncio.wait_for(p.wait(), timeout=0.1)) +                if p.returncode is not None: +                    print("Process is interrupted.") +                    return +                p.terminate() +                in_thread(asyncio.wait_for(p.wait(), timeout=0.1)) +                if p.returncode is not None: +                    print("Process is terminated.") +                    return +                p.kill() +                print("Process is killed.") +            except OSError: +                pass +            except Exception as e: +                print("Error while terminating subprocess (pid=%i): %s" % (p.pid, e)) +            return + +        if args.raise_error and p.returncode != 0: +            # If we get here and p.returncode is still None, we must have +            # killed it but not yet seen its return code. We don't wait for it, +            # in case it's stuck in uninterruptible sleep. -9 = SIGKILL +            rc = p.returncode or -9 +            raise CalledProcessError(rc, cell) + +    shebang.__skip_doctest__ = os.name != "posix" + +    async def _run_script(self, p, cell, to_close): +        """callback for running the script in the background""" + +        p.stdin.write(cell) +        await p.stdin.drain() +        p.stdin.close() +        await p.stdin.wait_closed() +        await p.wait() +        # asyncio read pipes have no close +        # but we should drain the data anyway +        for s in to_close: +            await s.read() +        self._gc_bg_processes() + +    @line_magic("killbgscripts") +    def killbgscripts(self, _nouse_=''): +        """Kill all BG processes started by %%script and its family.""" +        self.kill_bg_processes() +        print("All background processes were killed.") + +    def kill_bg_processes(self): +        """Kill all BG processes which are still running.""" +        if not self.bg_processes: +            return +        for p in self.bg_processes: +            if p.returncode is None: +                try: +                    p.send_signal(signal.SIGINT) +                except: +                    pass +        time.sleep(0.1) +        self._gc_bg_processes() +        if not self.bg_processes: +            return +        for p in self.bg_processes: +            if p.returncode is None: +                try: +                    p.terminate() +                except: +                    pass +        time.sleep(0.1) +        self._gc_bg_processes() +        if not self.bg_processes: +            return +        for p in self.bg_processes: +            if p.returncode is None: +                try: +                    p.kill() +                except: +                    pass +        self._gc_bg_processes() + +    def _gc_bg_processes(self): +        self.bg_processes = [p for p in self.bg_processes if p.returncode is None]  | 
