1"""Extra magics for terminal use.""" 
    2 
    3# Copyright (c) IPython Development Team. 
    4# Distributed under the terms of the Modified BSD License. 
    5 
    6 
    7from logging import error 
    8import os 
    9import sys 
    10 
    11from IPython.core.error import TryNext, UsageError 
    12from IPython.core.magic import Magics, magics_class, line_magic 
    13from IPython.lib.clipboard import ClipboardEmpty 
    14from IPython.testing.skipdoctest import skip_doctest 
    15from IPython.utils.text import SList, strip_email_quotes 
    16from IPython.utils import py3compat 
    17 
    18def get_pasted_lines(sentinel, l_input=py3compat.input, quiet=False): 
    19    """ Yield pasted lines until the user enters the given sentinel value. 
    20    """ 
    21    if not quiet: 
    22        print("Pasting code; enter '%s' alone on the line to stop or use Ctrl-D." \ 
    23              % sentinel) 
    24        prompt = ":" 
    25    else: 
    26        prompt = "" 
    27    while True: 
    28        try: 
    29            l = l_input(prompt) 
    30            if l == sentinel: 
    31                return 
    32            else: 
    33                yield l 
    34        except EOFError: 
    35            print('<EOF>') 
    36            return 
    37 
    38 
    39@magics_class 
    40class TerminalMagics(Magics): 
    41    def __init__(self, shell): 
    42        super(TerminalMagics, self).__init__(shell) 
    43 
    44    def store_or_execute(self, block, name, store_history=False): 
    45        """ Execute a block, or store it in a variable, per the user's request. 
    46        """ 
    47        if name: 
    48            # If storing it for further editing 
    49            self.shell.user_ns[name] = SList(block.splitlines()) 
    50            print("Block assigned to '%s'" % name) 
    51        else: 
    52            b = self.preclean_input(block) 
    53            self.shell.user_ns['pasted_block'] = b 
    54            self.shell.using_paste_magics = True 
    55            try: 
    56                self.shell.run_cell(b, store_history) 
    57            finally: 
    58                self.shell.using_paste_magics = False 
    59 
    60    def preclean_input(self, block): 
    61        lines = block.splitlines() 
    62        while lines and not lines[0].strip(): 
    63            lines = lines[1:] 
    64        return strip_email_quotes('\n'.join(lines)) 
    65 
    66    def rerun_pasted(self, name='pasted_block'): 
    67        """ Rerun a previously pasted command. 
    68        """ 
    69        b = self.shell.user_ns.get(name) 
    70 
    71        # Sanity checks 
    72        if b is None: 
    73            raise UsageError('No previous pasted block available') 
    74        if not isinstance(b, str): 
    75            raise UsageError( 
    76                "Variable 'pasted_block' is not a string, can't execute") 
    77 
    78        print("Re-executing '%s...' (%d chars)"% (b.split('\n',1)[0], len(b))) 
    79        self.shell.run_cell(b) 
    80 
    81    @line_magic 
    82    def autoindent(self, parameter_s = ''): 
    83        """Toggle autoindent on/off (deprecated)""" 
    84        self.shell.set_autoindent() 
    85        print("Automatic indentation is:",['OFF','ON'][self.shell.autoindent]) 
    86 
    87    @skip_doctest 
    88    @line_magic 
    89    def cpaste(self, parameter_s=''): 
    90        """Paste & execute a pre-formatted code block from clipboard. 
    91 
    92        You must terminate the block with '--' (two minus-signs) or Ctrl-D 
    93        alone on the line. You can also provide your own sentinel with '%paste 
    94        -s %%' ('%%' is the new sentinel for this operation). 
    95 
    96        The block is dedented prior to execution to enable execution of method 
    97        definitions. '>' and '+' characters at the beginning of a line are 
    98        ignored, to allow pasting directly from e-mails, diff files and 
    99        doctests (the '...' continuation prompt is also stripped).  The 
    100        executed block is also assigned to variable named 'pasted_block' for 
    101        later editing with '%edit pasted_block'. 
    102 
    103        You can also pass a variable name as an argument, e.g. '%cpaste foo'. 
    104        This assigns the pasted block to variable 'foo' as string, without 
    105        dedenting or executing it (preceding >>> and + is still stripped) 
    106 
    107        '%cpaste -r' re-executes the block previously entered by cpaste. 
    108        '%cpaste -q' suppresses any additional output messages. 
    109 
    110        Do not be alarmed by garbled output on Windows (it's a readline bug). 
    111        Just press enter and type -- (and press enter again) and the block 
    112        will be what was just pasted. 
    113 
    114        Shell escapes are not supported (yet). 
    115 
    116        See Also 
    117        -------- 
    118        paste : automatically pull code from clipboard. 
    119 
    120        Examples 
    121        -------- 
    122        :: 
    123 
    124          In [8]: %cpaste 
    125          Pasting code; enter '--' alone on the line to stop. 
    126          :>>> a = ["world!", "Hello"] 
    127          :>>> print(" ".join(sorted(a))) 
    128          :-- 
    129          Hello world! 
    130 
    131        :: 
    132          In [8]: %cpaste 
    133          Pasting code; enter '--' alone on the line to stop. 
    134          :>>> %alias_magic t timeit 
    135          :>>> %t -n1 pass 
    136          :-- 
    137          Created `%t` as an alias for `%timeit`. 
    138          Created `%%t` as an alias for `%%timeit`. 
    139          354 ns ± 224 ns per loop (mean ± std. dev. of 7 runs, 1 loop each) 
    140        """ 
    141        opts, name = self.parse_options(parameter_s, 'rqs:', mode='string') 
    142        if 'r' in opts: 
    143            self.rerun_pasted() 
    144            return 
    145 
    146        quiet = ('q' in opts) 
    147 
    148        sentinel = opts.get('s', u'--') 
    149        block = '\n'.join(get_pasted_lines(sentinel, quiet=quiet)) 
    150        self.store_or_execute(block, name, store_history=True) 
    151 
    152    @line_magic 
    153    def paste(self, parameter_s=''): 
    154        """Paste & execute a pre-formatted code block from clipboard. 
    155 
    156        The text is pulled directly from the clipboard without user 
    157        intervention and printed back on the screen before execution (unless 
    158        the -q flag is given to force quiet mode). 
    159 
    160        The block is dedented prior to execution to enable execution of method 
    161        definitions. '>' and '+' characters at the beginning of a line are 
    162        ignored, to allow pasting directly from e-mails, diff files and 
    163        doctests (the '...' continuation prompt is also stripped).  The 
    164        executed block is also assigned to variable named 'pasted_block' for 
    165        later editing with '%edit pasted_block'. 
    166 
    167        You can also pass a variable name as an argument, e.g. '%paste foo'. 
    168        This assigns the pasted block to variable 'foo' as string, without 
    169        executing it (preceding >>> and + is still stripped). 
    170 
    171        Options: 
    172 
    173          -r: re-executes the block previously entered by cpaste. 
    174 
    175          -q: quiet mode: do not echo the pasted text back to the terminal. 
    176 
    177        IPython statements (magics, shell escapes) are not supported (yet). 
    178 
    179        See Also 
    180        -------- 
    181        cpaste : manually paste code into terminal until you mark its end. 
    182        """ 
    183        opts, name = self.parse_options(parameter_s, 'rq', mode='string') 
    184        if 'r' in opts: 
    185            self.rerun_pasted() 
    186            return 
    187        try: 
    188            block = self.shell.hooks.clipboard_get() 
    189        except TryNext as clipboard_exc: 
    190            message = getattr(clipboard_exc, 'args') 
    191            if message: 
    192                error(message[0]) 
    193            else: 
    194                error('Could not get text from the clipboard.') 
    195            return 
    196        except ClipboardEmpty as e: 
    197            raise UsageError("The clipboard appears to be empty") from e 
    198 
    199        # By default, echo back to terminal unless quiet mode is requested 
    200        if 'q' not in opts: 
    201            sys.stdout.write(self.shell.pycolorize(block)) 
    202            if not block.endswith("\n"): 
    203                sys.stdout.write("\n") 
    204            sys.stdout.write("## -- End pasted text --\n") 
    205 
    206        self.store_or_execute(block, name, store_history=True) 
    207 
    208    # Class-level: add a '%cls' magic only on Windows 
    209    if sys.platform == 'win32': 
    210        @line_magic 
    211        def cls(self, s): 
    212            """Clear screen. 
    213            """ 
    214            os.system("cls")