magics.py
206 lines
| 7.2 KiB
| text/x-python
|
PythonLexer
Thomas Kluyver
|
r22436 | """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 = py3compat.str_to_unicode(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, py3compat.string_types): | ||||
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 (if available).""" | ||||
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") | ||||