204 lines
7.1 KiB
Python
204 lines
7.1 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.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)
|
||
|
|
||
|
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.
|
||
|
|
||
|
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")
|