208 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			208 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """Extra magics for terminal use."""
 | |
| 
 | |
| # Copyright (c) yap_ipython Development Team.
 | |
| # Distributed under the terms of the Modified BSD License.
 | |
| 
 | |
| 
 | |
| from logging import error
 | |
| import os
 | |
| import sys
 | |
| 
 | |
| from yap_ipython.core.error import TryNext, UsageError
 | |
| from yap_ipython.core.inputsplitter import IPythonInputSplitter
 | |
| from yap_ipython.core.magic import Magics, magics_class, line_magic
 | |
| from yap_ipython.lib.clipboard import ClipboardEmpty
 | |
| from yap_ipython.utils.text import SList, strip_email_quotes
 | |
| from yap_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('<EOF>')
 | |
|             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)"""
 | |
|         print("%autoindent is deprecated since yap_ipython 5: you can now paste "
 | |
|               "multiple lines without turning autoindentation off.")
 | |
|         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.
 | |
| 
 | |
|         yap_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.
 | |
| 
 | |
|         yap_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")
 |