"""Extra magics for terminal use.""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. from logging import error import os import sys from IPython.core.error import TryNext, UsageError from IPython.core.inputsplitter import IPythonInputSplitter from IPython.core.magic import Magics, magics_class, line_magic from IPython.lib.clipboard import ClipboardEmpty from IPython.utils.text import SList, strip_email_quotes from IPython.utils import py3compat def get_pasted_lines(sentinel, l_input=py3compat.input, quiet=False): """ Yield pasted lines until the user enters the given sentinel value. """ if not quiet: print("Pasting code; enter '%s' alone on the line to stop or use Ctrl-D." \ % sentinel) prompt = ":" else: prompt = "" while True: try: l = l_input(prompt) if l == sentinel: return else: yield l except EOFError: print('') return @magics_class class TerminalMagics(Magics): def __init__(self, shell): super(TerminalMagics, self).__init__(shell) self.input_splitter = IPythonInputSplitter() def store_or_execute(self, block, name): """ Execute a block, or store it in a variable, per the user's request. """ if name: # If storing it for further editing self.shell.user_ns[name] = SList(block.splitlines()) print("Block assigned to '%s'" % name) else: b = self.preclean_input(block) self.shell.user_ns['pasted_block'] = b self.shell.using_paste_magics = True try: self.shell.run_cell(b) finally: self.shell.using_paste_magics = False def preclean_input(self, block): lines = block.splitlines() while lines and not lines[0].strip(): lines = lines[1:] return strip_email_quotes('\n'.join(lines)) def rerun_pasted(self, name='pasted_block'): """ Rerun a previously pasted command. """ b = self.shell.user_ns.get(name) # Sanity checks if b is None: raise UsageError('No previous pasted block available') if not isinstance(b, str): raise UsageError( "Variable 'pasted_block' is not a string, can't execute") print("Re-executing '%s...' (%d chars)"% (b.split('\n',1)[0], len(b))) self.shell.run_cell(b) @line_magic def autoindent(self, parameter_s = ''): """Toggle autoindent on/off (deprecated)""" self.shell.set_autoindent() print("Automatic indentation is:",['OFF','ON'][self.shell.autoindent]) @line_magic def cpaste(self, parameter_s=''): """Paste & execute a pre-formatted code block from clipboard. You must terminate the block with '--' (two minus-signs) or Ctrl-D alone on the line. You can also provide your own sentinel with '%paste -s %%' ('%%' is the new sentinel for this operation). The block is dedented prior to execution to enable execution of method definitions. '>' and '+' characters at the beginning of a line are ignored, to allow pasting directly from e-mails, diff files and doctests (the '...' continuation prompt is also stripped). The executed block is also assigned to variable named 'pasted_block' for later editing with '%edit pasted_block'. You can also pass a variable name as an argument, e.g. '%cpaste foo'. This assigns the pasted block to variable 'foo' as string, without dedenting or executing it (preceding >>> and + is still stripped) '%cpaste -r' re-executes the block previously entered by cpaste. '%cpaste -q' suppresses any additional output messages. Do not be alarmed by garbled output on Windows (it's a readline bug). Just press enter and type -- (and press enter again) and the block will be what was just pasted. IPython statements (magics, shell escapes) are not supported (yet). See also -------- paste: automatically pull code from clipboard. Examples -------- :: In [8]: %cpaste Pasting code; enter '--' alone on the line to stop. :>>> a = ["world!", "Hello"] :>>> print " ".join(sorted(a)) :-- Hello world! """ opts, name = self.parse_options(parameter_s, 'rqs:', mode='string') if 'r' in opts: self.rerun_pasted() return quiet = ('q' in opts) sentinel = opts.get('s', u'--') block = '\n'.join(get_pasted_lines(sentinel, quiet=quiet)) self.store_or_execute(block, name) @line_magic def paste(self, parameter_s=''): """Paste & execute a pre-formatted code block from clipboard. The text is pulled directly from the clipboard without user intervention and printed back on the screen before execution (unless the -q flag is given to force quiet mode). The block is dedented prior to execution to enable execution of method definitions. '>' and '+' characters at the beginning of a line are ignored, to allow pasting directly from e-mails, diff files and doctests (the '...' continuation prompt is also stripped). The executed block is also assigned to variable named 'pasted_block' for later editing with '%edit pasted_block'. You can also pass a variable name as an argument, e.g. '%paste foo'. This assigns the pasted block to variable 'foo' as string, without executing it (preceding >>> and + is still stripped). Options: -r: re-executes the block previously entered by cpaste. -q: quiet mode: do not echo the pasted text back to the terminal. IPython statements (magics, shell escapes) are not supported (yet). See also -------- cpaste: manually paste code into terminal until you mark its end. """ opts, name = self.parse_options(parameter_s, 'rq', mode='string') if 'r' in opts: self.rerun_pasted() return try: block = self.shell.hooks.clipboard_get() except TryNext as clipboard_exc: message = getattr(clipboard_exc, 'args') if message: error(message[0]) else: error('Could not get text from the clipboard.') return except ClipboardEmpty: raise UsageError("The clipboard appears to be empty") # By default, echo back to terminal unless quiet mode is requested if 'q' not in opts: write = self.shell.write write(self.shell.pycolorize(block)) if not block.endswith('\n'): write('\n') write("## -- End pasted text --\n") self.store_or_execute(block, name) # Class-level: add a '%cls' magic only on Windows if sys.platform == 'win32': @line_magic def cls(self, s): """Clear screen. """ os.system("cls")