##// END OF EJS Templates
Properly clean up input for interactive pasting....
Fernando Perez -
Show More
@@ -29,6 +29,7 b' except:'
29 29
30 30 from IPython.core.error import TryNext, UsageError
31 31 from IPython.core.usage import interactive_usage, default_banner
32 from IPython.core.inputsplitter import IPythonInputSplitter
32 33 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
33 34 from IPython.core.magic import Magics, magics_class, line_magic
34 35 from IPython.testing.skipdoctest import skip_doctest
@@ -37,7 +38,7 b' from IPython.utils import py3compat'
37 38 from IPython.utils.terminal import toggle_set_term_title, set_term_title
38 39 from IPython.utils.process import abbrev_cwd
39 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 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 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 77 # Terminal-specific magics
87 78 #------------------------------------------------------------------------
88 79
89 80 @magics_class
90 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 149 def store_or_execute(self, block, name):
93 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
96 # run_cell.
97 b = self.shell.prefilter(textwrap.dedent(block))
98
152 b = self.cleanup_input(block)
99 153 if name:
100 # If storing it for further editing, run the prefilter on it
154 # If storing it for further editing
101 155 self.shell.user_ns[name] = SList(b.splitlines())
102 156 print "Block assigned to '%s'" % name
103 157 else:
104 158 self.shell.user_ns['pasted_block'] = b
105 159 self.shell.run_cell(b)
106 160
107
108 161 def rerun_pasted(self, name='pasted_block'):
109 162 """ Rerun a previously pasted command.
110 163 """
@@ -170,14 +223,13 b' class TerminalMagics(Magics):'
170 223 :--
171 224 Hello world!
172 225 """
173
174 226 opts, name = self.parse_options(parameter_s, 'rs:', mode='string')
175 227 if 'r' in opts:
176 228 self.rerun_pasted()
177 229 return
178 230
179 231 sentinel = opts.get('s', '--')
180 block = strip_email_quotes(get_pasted_lines(sentinel))
232 block = '\n'.join(get_pasted_lines(sentinel))
181 233 self.store_or_execute(block, name)
182 234
183 235 @line_magic
@@ -197,7 +249,7 b' class TerminalMagics(Magics):'
197 249
198 250 You can also pass a variable name as an argument, e.g. '%paste foo'.
199 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 254 Options
203 255 -------
@@ -217,8 +269,7 b' class TerminalMagics(Magics):'
217 269 self.rerun_pasted()
218 270 return
219 271 try:
220 text = self.shell.hooks.clipboard_get()
221 block = strip_email_quotes(text.splitlines())
272 block = self.shell.hooks.clipboard_get()
222 273 except TryNext as clipboard_exc:
223 274 message = getattr(clipboard_exc, 'args')
224 275 if message:
General Comments 0
You need to be logged in to leave comments. Login now