1"""Implementation of magic functions related to History. 
    2""" 
    3#----------------------------------------------------------------------------- 
    4#  Copyright (c) 2012, IPython Development Team. 
    5# 
    6#  Distributed under the terms of the Modified BSD License. 
    7# 
    8#  The full license is in the file COPYING.txt, distributed with this software. 
    9#----------------------------------------------------------------------------- 
    10 
    11#----------------------------------------------------------------------------- 
    12# Imports 
    13#----------------------------------------------------------------------------- 
    14 
    15# Stdlib 
    16import os 
    17import sys 
    18from io import open as io_open 
    19import fnmatch 
    20 
    21# Our own packages 
    22from IPython.core.error import StdinNotImplementedError 
    23from IPython.core.magic import Magics, magics_class, line_magic 
    24from IPython.core.magic_arguments import (argument, magic_arguments, 
    25                                          parse_argstring) 
    26from IPython.testing.skipdoctest import skip_doctest 
    27from IPython.utils import io 
    28 
    29#----------------------------------------------------------------------------- 
    30# Magics class implementation 
    31#----------------------------------------------------------------------------- 
    32 
    33 
    34_unspecified = object() 
    35 
    36 
    37@magics_class 
    38class HistoryMagics(Magics): 
    39 
    40    @magic_arguments() 
    41    @argument( 
    42        '-n', dest='print_nums', action='store_true', default=False, 
    43        help=""" 
    44        print line numbers for each input. 
    45        This feature is only available if numbered prompts are in use. 
    46        """) 
    47    @argument( 
    48        '-o', dest='get_output', action='store_true', default=False, 
    49        help="also print outputs for each input.") 
    50    @argument( 
    51        '-p', dest='pyprompts', action='store_true', default=False, 
    52        help=""" 
    53        print classic '>>>' python prompts before each input. 
    54        This is useful for making documentation, and in conjunction 
    55        with -o, for producing doctest-ready output. 
    56        """) 
    57    @argument( 
    58        '-t', dest='raw', action='store_false', default=True, 
    59        help=""" 
    60        print the 'translated' history, as IPython understands it. 
    61        IPython filters your input and converts it all into valid Python 
    62        source before executing it (things like magics or aliases are turned 
    63        into function calls, for example). With this option, you'll see the 
    64        native history instead of the user-entered version: '%%cd /' will be 
    65        seen as 'get_ipython().run_line_magic("cd", "/")' instead of '%%cd /'. 
    66        """) 
    67    @argument( 
    68        '-f', dest='filename', 
    69        help=""" 
    70        FILENAME: instead of printing the output to the screen, redirect 
    71        it to the given file.  The file is always overwritten, though *when 
    72        it can*, IPython asks for confirmation first. In particular, running 
    73        the command 'history -f FILENAME' from the IPython Notebook 
    74        interface will replace FILENAME even if it already exists *without* 
    75        confirmation. 
    76        """, 
    77    ) 
    78    @argument( 
    79        "-y", 
    80        dest="overwrite", 
    81        help="yes, overwrite filename even if exists", 
    82        action="store_true", 
    83        default=None, 
    84    ) 
    85    @argument( 
    86        "-g", 
    87        dest="pattern", 
    88        nargs="*", 
    89        default=None, 
    90        help=""" 
    91        treat the arg as a glob pattern to search for in (full) history. 
    92        This includes the saved history (almost all commands ever written). 
    93        The pattern may contain '?' to match one unknown character and '*' 
    94        to match any number of unknown characters. Use '%%hist -g' to show 
    95        full saved history (may be very long). 
    96        """) 
    97    @argument( 
    98        '-l', dest='limit', type=int, nargs='?', default=_unspecified, 
    99        help=""" 
    100        get the last n lines from all sessions. Specify n as a single 
    101        arg, or the default is the last 10 lines. 
    102        """) 
    103    @argument( 
    104        '-u', dest='unique', action='store_true', 
    105        help=""" 
    106        when searching history using `-g`, show only unique history. 
    107        """) 
    108    @argument('range', nargs='*') 
    109    @skip_doctest 
    110    @line_magic 
    111    def history(self, parameter_s = ''): 
    112        """Print input history (_i<n> variables), with most recent last. 
    113 
    114        By default, input history is printed without line numbers so it can be 
    115        directly pasted into an editor. Use -n to show them. 
    116 
    117        By default, all input history from the current session is displayed. 
    118        Ranges of history can be indicated using the syntax: 
    119 
    120        ``4`` 
    121            Line 4, current session 
    122        ``4-6`` 
    123            Lines 4-6, current session 
    124        ``243/1-5`` 
    125            Lines 1-5, session 243 
    126        ``~2/7`` 
    127            Line 7, session 2 before current 
    128        ``~8/1-~6/5`` 
    129            From the first line of 8 sessions ago, to the fifth line of 6 
    130            sessions ago. 
    131 
    132        Multiple ranges can be entered, separated by spaces 
    133 
    134        The same syntax is used by %macro, %save, %edit, %rerun 
    135 
    136        Examples 
    137        -------- 
    138        :: 
    139 
    140          In [6]: %history -n 4-6 
    141          4:a = 12 
    142          5:print(a**2) 
    143          6:%history -n 4-6 
    144 
    145        """ 
    146 
    147        args = parse_argstring(self.history, parameter_s) 
    148 
    149        # For brevity 
    150        history_manager = self.shell.history_manager 
    151 
    152        def _format_lineno(session, line): 
    153            """Helper function to format line numbers properly.""" 
    154            if session in (0, history_manager.session_number): 
    155                return str(line) 
    156            return "%s/%s" % (session, line) 
    157 
    158        # Check if output to specific file was requested. 
    159        outfname = args.filename 
    160        if not outfname: 
    161            outfile = sys.stdout  # default 
    162            # We don't want to close stdout at the end! 
    163            close_at_end = False 
    164        else: 
    165            outfname = os.path.expanduser(outfname) 
    166            if args.overwrite is True: 
    167                ans = True 
    168            elif os.path.exists(outfname): 
    169                ans = True 
    170                if sys.stdin.isatty(): 
    171                    ans = io.ask_yes_no("File %r exists. Overwrite?" % outfname) 
    172                else: 
    173                    try: 
    174                        ans = io.ask_yes_no("File %r exists. Overwrite?" % outfname) 
    175                    except StdinNotImplementedError: 
    176                        ans = True 
    177                if not ans: 
    178                    print("Aborting.") 
    179                    return 
    180                print("Overwriting file.") 
    181            outfile = io_open(outfname, 'w', encoding='utf-8') 
    182            close_at_end = True 
    183 
    184        print_nums = args.print_nums 
    185        get_output = args.get_output 
    186        pyprompts = args.pyprompts 
    187        raw = args.raw 
    188 
    189        pattern = None 
    190        limit = None if args.limit is _unspecified else args.limit 
    191 
    192        range_pattern = False 
    193        if args.pattern is not None and not args.range: 
    194            if args.pattern: 
    195                pattern = "*" + " ".join(args.pattern) + "*" 
    196            else: 
    197                pattern = "*" 
    198            hist = history_manager.search(pattern, raw=raw, output=get_output, 
    199                                          n=limit, unique=args.unique) 
    200            print_nums = True 
    201        elif args.limit is not _unspecified: 
    202            n = 10 if limit is None else limit 
    203            hist = history_manager.get_tail(n, raw=raw, output=get_output) 
    204        else: 
    205            if args.pattern: 
    206                range_pattern = "*" + " ".join(args.pattern) + "*" 
    207                print_nums = True 
    208            hist = history_manager.get_range_by_str( 
    209                " ".join(args.range), raw, get_output 
    210            ) 
    211 
    212        # We could be displaying the entire history, so let's not try to pull 
    213        # it into a list in memory. Anything that needs more space will just 
    214        # misalign. 
    215        width = 4 
    216 
    217        for session, lineno, inline in hist: 
    218            # Print user history with tabs expanded to 4 spaces.  The GUI 
    219            # clients use hard tabs for easier usability in auto-indented code, 
    220            # but we want to produce PEP-8 compliant history for safe pasting 
    221            # into an editor. 
    222            if get_output: 
    223                inline, output = inline 
    224            if range_pattern: 
    225                if not fnmatch.fnmatch(inline, range_pattern): 
    226                    continue 
    227            inline = inline.expandtabs(4).rstrip() 
    228 
    229            multiline = "\n" in inline 
    230            line_sep = '\n' if multiline else ' ' 
    231            if print_nums: 
    232                print(u'%s:%s' % (_format_lineno(session, lineno).rjust(width), 
    233                        line_sep),  file=outfile, end=u'') 
    234            if pyprompts: 
    235                print(u">>> ", end=u"", file=outfile) 
    236                if multiline: 
    237                    inline = "\n... ".join(inline.splitlines()) + "\n..." 
    238            print(inline, file=outfile) 
    239            if get_output and output: 
    240                print(output, file=outfile) 
    241 
    242        if close_at_end: 
    243            outfile.close() 
    244 
    245    @line_magic 
    246    def recall(self, arg): 
    247        r"""Repeat a command, or get command to input line for editing. 
    248 
    249        %recall and %rep are equivalent. 
    250 
    251        - %recall (no arguments): 
    252 
    253        Place a string version of last computation result (stored in the 
    254        special '_' variable) to the next input prompt. Allows you to create 
    255        elaborate command lines without using copy-paste:: 
    256 
    257             In[1]: l = ["hei", "vaan"] 
    258             In[2]: "".join(l) 
    259            Out[2]: heivaan 
    260             In[3]: %recall 
    261             In[4]: heivaan_ <== cursor blinking 
    262 
    263        %recall 45 
    264 
    265        Place history line 45 on the next input prompt. Use %hist to find 
    266        out the number. 
    267 
    268        %recall 1-4 
    269 
    270        Combine the specified lines into one cell, and place it on the next 
    271        input prompt. See %history for the slice syntax. 
    272 
    273        %recall foo+bar 
    274 
    275        If foo+bar can be evaluated in the user namespace, the result is 
    276        placed at the next input prompt. Otherwise, the history is searched 
    277        for lines which contain that substring, and the most recent one is 
    278        placed at the next input prompt. 
    279        """ 
    280        if not arg:                 # Last output 
    281            self.shell.set_next_input(str(self.shell.user_ns["_"])) 
    282            return 
    283                                    # Get history range 
    284        histlines = self.shell.history_manager.get_range_by_str(arg) 
    285        cmd = "\n".join(x[2] for x in histlines) 
    286        if cmd: 
    287            self.shell.set_next_input(cmd.rstrip()) 
    288            return 
    289 
    290        try:                        # Variable in user namespace 
    291            cmd = str(eval(arg, self.shell.user_ns)) 
    292        except Exception:           # Search for term in history 
    293            histlines = self.shell.history_manager.search("*"+arg+"*") 
    294            for h in reversed([x[2] for x in histlines]): 
    295                if 'recall' in h or 'rep' in h: 
    296                    continue 
    297                self.shell.set_next_input(h.rstrip()) 
    298                return 
    299        else: 
    300            self.shell.set_next_input(cmd.rstrip()) 
    301            return 
    302        print("Couldn't evaluate or find in history:", arg) 
    303 
    304    @line_magic 
    305    def rerun(self, parameter_s=''): 
    306        """Re-run previous input 
    307 
    308        By default, you can specify ranges of input history to be repeated 
    309        (as with %history). With no arguments, it will repeat the last line. 
    310 
    311        Options: 
    312 
    313          -l <n> : Repeat the last n lines of input, not including the 
    314          current command. 
    315 
    316          -g foo : Repeat the most recent line which contains foo 
    317        """ 
    318        opts, args = self.parse_options(parameter_s, 'l:g:', mode='string') 
    319        if "l" in opts:         # Last n lines 
    320            try: 
    321                n = int(opts["l"]) 
    322            except ValueError: 
    323                print("Number of lines must be an integer") 
    324                return 
    325 
    326            if n == 0: 
    327                print("Requested 0 last lines - nothing to run") 
    328                return 
    329            elif n < 0: 
    330                print("Number of lines to rerun cannot be negative") 
    331                return 
    332 
    333            hist = self.shell.history_manager.get_tail(n) 
    334        elif "g" in opts:       # Search 
    335            p = "*"+opts['g']+"*" 
    336            hist = list(self.shell.history_manager.search(p)) 
    337            for l in reversed(hist): 
    338                if "rerun" not in l[2]: 
    339                    hist = [l]     # The last match which isn't a %rerun 
    340                    break 
    341            else: 
    342                hist = []          # No matches except %rerun 
    343        elif args:              # Specify history ranges 
    344            hist = self.shell.history_manager.get_range_by_str(args) 
    345        else:                   # Last line 
    346            hist = self.shell.history_manager.get_tail(1) 
    347        hist = [x[2] for x in hist] 
    348        if not hist: 
    349            print("No lines in history match specification") 
    350            return 
    351        histlines = "\n".join(hist) 
    352        print("=== Executing: ===") 
    353        print(histlines) 
    354        print("=== Output: ===") 
    355        self.shell.run_cell("\n".join(hist), store_history=False)