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