##// END OF EJS Templates
Properly clean up input for interactive pasting....
Fernando Perez -
Show More
@@ -29,6 +29,7 b' except:'
29
29
30 from IPython.core.error import TryNext, UsageError
30 from IPython.core.error import TryNext, UsageError
31 from IPython.core.usage import interactive_usage, default_banner
31 from IPython.core.usage import interactive_usage, default_banner
32 from IPython.core.inputsplitter import IPythonInputSplitter
32 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
33 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
33 from IPython.core.magic import Magics, magics_class, line_magic
34 from IPython.core.magic import Magics, magics_class, line_magic
34 from IPython.testing.skipdoctest import skip_doctest
35 from IPython.testing.skipdoctest import skip_doctest
@@ -37,7 +38,7 b' from IPython.utils import py3compat'
37 from IPython.utils.terminal import toggle_set_term_title, set_term_title
38 from IPython.utils.terminal import toggle_set_term_title, set_term_title
38 from IPython.utils.process import abbrev_cwd
39 from IPython.utils.process import abbrev_cwd
39 from IPython.utils.warn import warn, error
40 from IPython.utils.warn import warn, error
40 from IPython.utils.text import num_ini_spaces, SList
41 from IPython.utils.text import num_ini_spaces, SList, strip_email_quotes
41 from IPython.utils.traitlets import Integer, CBool, Unicode
42 from IPython.utils.traitlets import Integer, CBool, Unicode
42
43
43 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
@@ -72,39 +73,91 b' def get_pasted_lines(sentinel, l_input=py3compat.input):'
72 return
73 return
73
74
74
75
75 def strip_email_quotes(raw_lines):
76 """ Strip email quotation marks at the beginning of each line.
77
78 We don't do any more input transofrmations here because the main shell's
79 prefiltering handles other cases.
80 """
81 lines = [re.sub(r'^\s*(\s?>)+', '', l) for l in raw_lines]
82 return '\n'.join(lines) + '\n'
83
84
85 #------------------------------------------------------------------------
76 #------------------------------------------------------------------------
86 # Terminal-specific magics
77 # Terminal-specific magics
87 #------------------------------------------------------------------------
78 #------------------------------------------------------------------------
88
79
89 @magics_class
80 @magics_class
90 class TerminalMagics(Magics):
81 class TerminalMagics(Magics):
82 def __init__(self, shell):
83 super(TerminalMagics, self).__init__(shell)
84 self.input_splitter = IPythonInputSplitter(input_mode='line')
85
86 def cleanup_input(block):
87 """Apply all possible IPython cleanups to an input block.
91
88
89 This means:
90
91 - remove any global leading whitespace (dedent)
92 - remove any email quotes ('>') if they are present in *all* lines
93 - apply all static inputsplitter transforms and break into sub-blocks
94 - apply prefilter() to each sub-block that is a single line.
95
96 Parameters
97 ----------
98 block : str
99 A possibly multiline input string of code.
100
101 Returns
102 -------
103 transformed block : str
104 The input, with all transformations above applied.
105 """
106 # We have to effectively implement client-side the loop that is done by
107 # the terminal frontend, and furthermore do it on a block that can
108 # possibly contain multiple statments pasted in one go.
109
110 # First, run the input through the block splitting code. We should
111 # eventually make this a self-contained method in the inputsplitter.
112 isp = self.input_splitter
113 isp.reset()
114 b = textwrap.dedent(block)
115
116 # Remove email quotes first. These must be consistently applied to
117 # *all* lines to be removed
118 b = strip_email_quotes(b)
119
120 # Split the input into independent sub-blocks so we can later do
121 # prefiltering (which must be done *only* to single-line inputs)
122 blocks = []
123 last_block = []
124 for line in b.splitlines():
125 isp.push(line)
126 last_block.append(line)
127 if not isp.push_accepts_more():
128 blocks.append(isp.source_reset())
129 last_block = []
130 if last_block:
131 blocks.append('\n'.join(last_block))
132
133 # Now, apply prefiltering to any one-line block to match the behavior
134 # of the interactive terminal
135 final_blocks = []
136 for block in blocks:
137 lines = block.splitlines()
138 if len(lines) == 1:
139 final_blocks.append(self.shell.prefilter(lines[0]))
140 else:
141 final_blocks.append(block)
142
143 # We now have the final version of the input code as a list of blocks,
144 # with all inputsplitter transformations applied and single-line blocks
145 # run through prefilter. For further processing, turn into a single
146 # string as the rest of our apis use string inputs.
147 return '\n'.join(final_blocks)
148
92 def store_or_execute(self, block, name):
149 def store_or_execute(self, block, name):
93 """ Execute a block, or store it in a variable, per the user's request.
150 """ Execute a block, or store it in a variable, per the user's request.
94 """
151 """
95 # Dedent and prefilter so what we store matches what is executed by
152 b = self.cleanup_input(block)
96 # run_cell.
97 b = self.shell.prefilter(textwrap.dedent(block))
98
99 if name:
153 if name:
100 # If storing it for further editing, run the prefilter on it
154 # If storing it for further editing
101 self.shell.user_ns[name] = SList(b.splitlines())
155 self.shell.user_ns[name] = SList(b.splitlines())
102 print "Block assigned to '%s'" % name
156 print "Block assigned to '%s'" % name
103 else:
157 else:
104 self.shell.user_ns['pasted_block'] = b
158 self.shell.user_ns['pasted_block'] = b
105 self.shell.run_cell(b)
159 self.shell.run_cell(b)
106
160
107
108 def rerun_pasted(self, name='pasted_block'):
161 def rerun_pasted(self, name='pasted_block'):
109 """ Rerun a previously pasted command.
162 """ Rerun a previously pasted command.
110 """
163 """
@@ -170,14 +223,13 b' class TerminalMagics(Magics):'
170 :--
223 :--
171 Hello world!
224 Hello world!
172 """
225 """
173
174 opts, name = self.parse_options(parameter_s, 'rs:', mode='string')
226 opts, name = self.parse_options(parameter_s, 'rs:', mode='string')
175 if 'r' in opts:
227 if 'r' in opts:
176 self.rerun_pasted()
228 self.rerun_pasted()
177 return
229 return
178
230
179 sentinel = opts.get('s', '--')
231 sentinel = opts.get('s', '--')
180 block = strip_email_quotes(get_pasted_lines(sentinel))
232 block = '\n'.join(get_pasted_lines(sentinel))
181 self.store_or_execute(block, name)
233 self.store_or_execute(block, name)
182
234
183 @line_magic
235 @line_magic
@@ -197,7 +249,7 b' class TerminalMagics(Magics):'
197
249
198 You can also pass a variable name as an argument, e.g. '%paste foo'.
250 You can also pass a variable name as an argument, e.g. '%paste foo'.
199 This assigns the pasted block to variable 'foo' as string, without
251 This assigns the pasted block to variable 'foo' as string, without
200 dedenting or executing it (preceding >>> and + is still stripped)
252 executing it (preceding >>> and + is still stripped).
201
253
202 Options
254 Options
203 -------
255 -------
@@ -217,8 +269,7 b' class TerminalMagics(Magics):'
217 self.rerun_pasted()
269 self.rerun_pasted()
218 return
270 return
219 try:
271 try:
220 text = self.shell.hooks.clipboard_get()
272 block = self.shell.hooks.clipboard_get()
221 block = strip_email_quotes(text.splitlines())
222 except TryNext as clipboard_exc:
273 except TryNext as clipboard_exc:
223 message = getattr(clipboard_exc, 'args')
274 message = getattr(clipboard_exc, 'args')
224 if message:
275 if message:
General Comments 0
You need to be logged in to leave comments. Login now