Show More
@@ -79,7 +79,7 b' from IPython.core.inputtransformer import (leading_indent,' | |||||
79 | cellmagic, |
|
79 | cellmagic, | |
80 | assemble_logical_lines, |
|
80 | assemble_logical_lines, | |
81 | help_end, |
|
81 | help_end, | |
82 |
escaped_ |
|
82 | escaped_commands, | |
83 | assign_from_magic, |
|
83 | assign_from_magic, | |
84 | assign_from_system, |
|
84 | assign_from_system, | |
85 | assemble_python_lines, |
|
85 | assemble_python_lines, | |
@@ -526,7 +526,7 b' class IPythonInputSplitter(InputSplitter):' | |||||
526 | self.logical_line_transforms = logical_line_transforms or \ |
|
526 | self.logical_line_transforms = logical_line_transforms or \ | |
527 | [cellmagic(), |
|
527 | [cellmagic(), | |
528 | help_end(), |
|
528 | help_end(), | |
529 |
escaped_ |
|
529 | escaped_commands(), | |
530 | assign_from_magic(), |
|
530 | assign_from_magic(), | |
531 | assign_from_system(), |
|
531 | assign_from_system(), | |
532 | ] |
|
532 | ] |
@@ -61,9 +61,6 b' class InputTransformer(object):' | |||||
61 | """ |
|
61 | """ | |
62 | pass |
|
62 | pass | |
63 |
|
63 | |||
64 | # Set this to True to allow the transformer to act on lines inside strings. |
|
|||
65 | look_in_string = False |
|
|||
66 |
|
||||
67 | @classmethod |
|
64 | @classmethod | |
68 | def wrap(cls, func): |
|
65 | def wrap(cls, func): | |
69 | """Can be used by subclasses as a decorator, to return a factory that |
|
66 | """Can be used by subclasses as a decorator, to return a factory that | |
@@ -71,10 +68,7 b' class InputTransformer(object):' | |||||
71 | """ |
|
68 | """ | |
72 | @functools.wraps(func) |
|
69 | @functools.wraps(func) | |
73 | def transformer_factory(): |
|
70 | def transformer_factory(): | |
74 |
tr |
|
71 | return cls(func) | |
75 | if getattr(transformer_factory, 'look_in_string', False): |
|
|||
76 | transformer.look_in_string = True |
|
|||
77 | return transformer |
|
|||
78 |
|
72 | |||
79 | return transformer_factory |
|
73 | return transformer_factory | |
80 |
|
74 | |||
@@ -214,83 +208,67 b' def _make_help_call(target, esc, lspace, next_input=None):' | |||||
214 | else: |
|
208 | else: | |
215 | return '%sget_ipython().set_next_input(%r);get_ipython().magic(%r)' % \ |
|
209 | return '%sget_ipython().set_next_input(%r);get_ipython().magic(%r)' % \ | |
216 | (lspace, next_input, arg) |
|
210 | (lspace, next_input, arg) | |
217 |
|
||||
218 | @CoroutineInputTransformer.wrap |
|
|||
219 | def escaped_transformer(): |
|
|||
220 | """Translate lines beginning with one of IPython's escape characters. |
|
|||
221 |
|
211 | |||
222 | This is stateful to allow magic commands etc. to be continued over several |
|
212 | # These define the transformations for the different escape characters. | |
223 | lines using explicit line continuations (\ at the end of a line). |
|
213 | def _tr_system(line_info): | |
|
214 | "Translate lines escaped with: !" | |||
|
215 | cmd = line_info.line.lstrip().lstrip(ESC_SHELL) | |||
|
216 | return '%sget_ipython().system(%r)' % (line_info.pre, cmd) | |||
|
217 | ||||
|
218 | def _tr_system2(line_info): | |||
|
219 | "Translate lines escaped with: !!" | |||
|
220 | cmd = line_info.line.lstrip()[2:] | |||
|
221 | return '%sget_ipython().getoutput(%r)' % (line_info.pre, cmd) | |||
|
222 | ||||
|
223 | def _tr_help(line_info): | |||
|
224 | "Translate lines escaped with: ?/??" | |||
|
225 | # A naked help line should just fire the intro help screen | |||
|
226 | if not line_info.line[1:]: | |||
|
227 | return 'get_ipython().show_usage()' | |||
|
228 | ||||
|
229 | return _make_help_call(line_info.ifun, line_info.esc, line_info.pre) | |||
|
230 | ||||
|
231 | def _tr_magic(line_info): | |||
|
232 | "Translate lines escaped with: %" | |||
|
233 | tpl = '%sget_ipython().magic(%r)' | |||
|
234 | cmd = ' '.join([line_info.ifun, line_info.the_rest]).strip() | |||
|
235 | return tpl % (line_info.pre, cmd) | |||
|
236 | ||||
|
237 | def _tr_quote(line_info): | |||
|
238 | "Translate lines escaped with: ," | |||
|
239 | return '%s%s("%s")' % (line_info.pre, line_info.ifun, | |||
|
240 | '", "'.join(line_info.the_rest.split()) ) | |||
|
241 | ||||
|
242 | def _tr_quote2(line_info): | |||
|
243 | "Translate lines escaped with: ;" | |||
|
244 | return '%s%s("%s")' % (line_info.pre, line_info.ifun, | |||
|
245 | line_info.the_rest) | |||
|
246 | ||||
|
247 | def _tr_paren(line_info): | |||
|
248 | "Translate lines escaped with: /" | |||
|
249 | return '%s%s(%s)' % (line_info.pre, line_info.ifun, | |||
|
250 | ", ".join(line_info.the_rest.split())) | |||
|
251 | ||||
|
252 | tr = { ESC_SHELL : _tr_system, | |||
|
253 | ESC_SH_CAP : _tr_system2, | |||
|
254 | ESC_HELP : _tr_help, | |||
|
255 | ESC_HELP2 : _tr_help, | |||
|
256 | ESC_MAGIC : _tr_magic, | |||
|
257 | ESC_QUOTE : _tr_quote, | |||
|
258 | ESC_QUOTE2 : _tr_quote2, | |||
|
259 | ESC_PAREN : _tr_paren } | |||
|
260 | ||||
|
261 | @StatelessInputTransformer.wrap | |||
|
262 | def escaped_commands(line): | |||
|
263 | """Transform escaped commands - %magic, !system, ?help + various autocalls. | |||
224 | """ |
|
264 | """ | |
|
265 | if not line or line.isspace(): | |||
|
266 | return line | |||
|
267 | lineinf = LineInfo(line) | |||
|
268 | if lineinf.esc not in tr: | |||
|
269 | return line | |||
225 |
|
270 | |||
226 | # These define the transformations for the different escape characters. |
|
271 | return tr[lineinf.esc](lineinf) | |
227 | def _tr_system(line_info): |
|
|||
228 | "Translate lines escaped with: !" |
|
|||
229 | cmd = line_info.line.lstrip().lstrip(ESC_SHELL) |
|
|||
230 | return '%sget_ipython().system(%r)' % (line_info.pre, cmd) |
|
|||
231 |
|
||||
232 | def _tr_system2(line_info): |
|
|||
233 | "Translate lines escaped with: !!" |
|
|||
234 | cmd = line_info.line.lstrip()[2:] |
|
|||
235 | return '%sget_ipython().getoutput(%r)' % (line_info.pre, cmd) |
|
|||
236 |
|
||||
237 | def _tr_help(line_info): |
|
|||
238 | "Translate lines escaped with: ?/??" |
|
|||
239 | # A naked help line should just fire the intro help screen |
|
|||
240 | if not line_info.line[1:]: |
|
|||
241 | return 'get_ipython().show_usage()' |
|
|||
242 |
|
||||
243 | return _make_help_call(line_info.ifun, line_info.esc, line_info.pre) |
|
|||
244 |
|
||||
245 | def _tr_magic(line_info): |
|
|||
246 | "Translate lines escaped with: %" |
|
|||
247 | tpl = '%sget_ipython().magic(%r)' |
|
|||
248 | cmd = ' '.join([line_info.ifun, line_info.the_rest]).strip() |
|
|||
249 | return tpl % (line_info.pre, cmd) |
|
|||
250 |
|
||||
251 | def _tr_quote(line_info): |
|
|||
252 | "Translate lines escaped with: ," |
|
|||
253 | return '%s%s("%s")' % (line_info.pre, line_info.ifun, |
|
|||
254 | '", "'.join(line_info.the_rest.split()) ) |
|
|||
255 |
|
||||
256 | def _tr_quote2(line_info): |
|
|||
257 | "Translate lines escaped with: ;" |
|
|||
258 | return '%s%s("%s")' % (line_info.pre, line_info.ifun, |
|
|||
259 | line_info.the_rest) |
|
|||
260 |
|
||||
261 | def _tr_paren(line_info): |
|
|||
262 | "Translate lines escaped with: /" |
|
|||
263 | return '%s%s(%s)' % (line_info.pre, line_info.ifun, |
|
|||
264 | ", ".join(line_info.the_rest.split())) |
|
|||
265 |
|
||||
266 | tr = { ESC_SHELL : _tr_system, |
|
|||
267 | ESC_SH_CAP : _tr_system2, |
|
|||
268 | ESC_HELP : _tr_help, |
|
|||
269 | ESC_HELP2 : _tr_help, |
|
|||
270 | ESC_MAGIC : _tr_magic, |
|
|||
271 | ESC_QUOTE : _tr_quote, |
|
|||
272 | ESC_QUOTE2 : _tr_quote2, |
|
|||
273 | ESC_PAREN : _tr_paren } |
|
|||
274 |
|
||||
275 | line = '' |
|
|||
276 | while True: |
|
|||
277 | line = (yield line) |
|
|||
278 | if not line or line.isspace(): |
|
|||
279 | continue |
|
|||
280 | lineinf = LineInfo(line) |
|
|||
281 | if lineinf.esc not in tr: |
|
|||
282 | continue |
|
|||
283 |
|
||||
284 | parts = [] |
|
|||
285 | while line is not None: |
|
|||
286 | parts.append(line.rstrip('\\')) |
|
|||
287 | if not line.endswith('\\'): |
|
|||
288 | break |
|
|||
289 | line = (yield None) |
|
|||
290 |
|
||||
291 | # Output |
|
|||
292 | lineinf = LineInfo(' '.join(parts)) |
|
|||
293 | line = tr[lineinf.esc](lineinf) |
|
|||
294 |
|
272 | |||
295 | _initial_space_re = re.compile(r'\s*') |
|
273 | _initial_space_re = re.compile(r'\s*') | |
296 |
|
274 | |||
@@ -401,8 +379,6 b' def classic_prompt():' | |||||
401 | prompt2_re = re.compile(r'^(>>> |^\.\.\. )') |
|
379 | prompt2_re = re.compile(r'^(>>> |^\.\.\. )') | |
402 | return _strip_prompts(prompt1_re, prompt2_re) |
|
380 | return _strip_prompts(prompt1_re, prompt2_re) | |
403 |
|
381 | |||
404 | classic_prompt.look_in_string = True |
|
|||
405 |
|
||||
406 | @CoroutineInputTransformer.wrap |
|
382 | @CoroutineInputTransformer.wrap | |
407 | def ipy_prompt(): |
|
383 | def ipy_prompt(): | |
408 | """Strip IPython's In [1]:/...: prompts.""" |
|
384 | """Strip IPython's In [1]:/...: prompts.""" | |
@@ -410,8 +386,6 b' def ipy_prompt():' | |||||
410 | prompt2_re = re.compile(r'^(In \[\d+\]: |^\ \ \ \.\.\.+: )') |
|
386 | prompt2_re = re.compile(r'^(In \[\d+\]: |^\ \ \ \.\.\.+: )') | |
411 | return _strip_prompts(prompt1_re, prompt2_re) |
|
387 | return _strip_prompts(prompt1_re, prompt2_re) | |
412 |
|
388 | |||
413 | ipy_prompt.look_in_string = True |
|
|||
414 |
|
||||
415 |
|
389 | |||
416 | @CoroutineInputTransformer.wrap |
|
390 | @CoroutineInputTransformer.wrap | |
417 | def leading_indent(): |
|
391 | def leading_indent(): | |
@@ -440,48 +414,27 b' def leading_indent():' | |||||
440 | while line is not None: |
|
414 | while line is not None: | |
441 | line = (yield line) |
|
415 | line = (yield line) | |
442 |
|
416 | |||
443 | leading_indent.look_in_string = True |
|
|||
444 |
|
||||
445 |
|
417 | |||
446 | def _special_assignment(assignment_re, template): |
|
418 | assign_system_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))' | |
447 | """Transform assignment from system & magic commands. |
|
419 | r'\s*=\s*!\s*(?P<cmd>.*)') | |
448 |
|
420 | assign_system_template = '%s = get_ipython().getoutput(%r)' | ||
449 | This is stateful so that it can handle magic commands continued on several |
|
421 | @StatelessInputTransformer.wrap | |
450 | lines. |
|
422 | def assign_from_system(line): | |
451 | """ |
|
|||
452 | line = '' |
|
|||
453 | while True: |
|
|||
454 | line = (yield line) |
|
|||
455 | if not line or line.isspace(): |
|
|||
456 | continue |
|
|||
457 |
|
||||
458 | m = assignment_re.match(line) |
|
|||
459 | if not m: |
|
|||
460 | continue |
|
|||
461 |
|
||||
462 | parts = [] |
|
|||
463 | while line is not None: |
|
|||
464 | parts.append(line.rstrip('\\')) |
|
|||
465 | if not line.endswith('\\'): |
|
|||
466 | break |
|
|||
467 | line = (yield None) |
|
|||
468 |
|
||||
469 | # Output |
|
|||
470 | whole = assignment_re.match(' '.join(parts)) |
|
|||
471 | line = template % (whole.group('lhs'), whole.group('cmd')) |
|
|||
472 |
|
||||
473 | @CoroutineInputTransformer.wrap |
|
|||
474 | def assign_from_system(): |
|
|||
475 | """Transform assignment from system commands (e.g. files = !ls)""" |
|
423 | """Transform assignment from system commands (e.g. files = !ls)""" | |
476 | assignment_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))' |
|
424 | m = assign_system_re.match(line) | |
477 | r'\s*=\s*!\s*(?P<cmd>.*)') |
|
425 | if m is None: | |
478 | template = '%s = get_ipython().getoutput(%r)' |
|
426 | return line | |
479 | return _special_assignment(assignment_re, template) |
|
427 | ||
|
428 | return assign_system_template % m.group('lhs', 'cmd') | |||
480 |
|
429 | |||
481 | @CoroutineInputTransformer.wrap |
|
430 | assign_magic_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))' | |
482 | def assign_from_magic(): |
|
431 | r'\s*=\s*%\s*(?P<cmd>.*)') | |
|
432 | assign_magic_template = '%s = get_ipython().magic(%r)' | |||
|
433 | @StatelessInputTransformer.wrap | |||
|
434 | def assign_from_magic(line): | |||
483 | """Transform assignment from magic commands (e.g. a = %who_ls)""" |
|
435 | """Transform assignment from magic commands (e.g. a = %who_ls)""" | |
484 | assignment_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))' |
|
436 | m = assign_magic_re.match(line) | |
485 | r'\s*=\s*%\s*(?P<cmd>.*)') |
|
437 | if m is None: | |
486 | template = '%s = get_ipython().magic(%r)' |
|
438 | return line | |
487 | return _special_assignment(assignment_re, template) |
|
439 | ||
|
440 | return assign_magic_template % m.group('lhs', 'cmd') |
@@ -254,14 +254,9 b' syntax_ml = \\' | |||||
254 |
|
254 | |||
255 | def test_assign_system(): |
|
255 | def test_assign_system(): | |
256 | tt.check_pairs(transform_and_reset(ipt.assign_from_system), syntax['assign_system']) |
|
256 | tt.check_pairs(transform_and_reset(ipt.assign_from_system), syntax['assign_system']) | |
257 | for example in syntax_ml['assign_system']: |
|
|||
258 | transform_checker(example, ipt.assign_from_system) |
|
|||
259 |
|
257 | |||
260 | def test_assign_magic(): |
|
258 | def test_assign_magic(): | |
261 | tt.check_pairs(transform_and_reset(ipt.assign_from_magic), syntax['assign_magic']) |
|
259 | tt.check_pairs(transform_and_reset(ipt.assign_from_magic), syntax['assign_magic']) | |
262 | for example in syntax_ml['assign_magic']: |
|
|||
263 | transform_checker(example, ipt.assign_from_magic) |
|
|||
264 |
|
||||
265 |
|
260 | |||
266 | def test_classic_prompt(): |
|
261 | def test_classic_prompt(): | |
267 | tt.check_pairs(transform_and_reset(ipt.classic_prompt), syntax['classic_prompt']) |
|
262 | tt.check_pairs(transform_and_reset(ipt.classic_prompt), syntax['classic_prompt']) | |
@@ -276,39 +271,70 b' def test_ipy_prompt():' | |||||
276 | for example in syntax_ml['ipy_prompt']: |
|
271 | for example in syntax_ml['ipy_prompt']: | |
277 | transform_checker(example, ipt.ipy_prompt) |
|
272 | transform_checker(example, ipt.ipy_prompt) | |
278 |
|
273 | |||
|
274 | def test_assemble_logical_lines(): | |||
|
275 | tests = \ | |||
|
276 | [ [(u"a = \\", None), | |||
|
277 | (u"123", u"a = 123"), | |||
|
278 | ], | |||
|
279 | [(u"a = \\", None), # Test resetting when within a multi-line string | |||
|
280 | (u"12 *\\", None), | |||
|
281 | (None, u"a = 12 *"), | |||
|
282 | ], | |||
|
283 | ] | |||
|
284 | for example in tests: | |||
|
285 | transform_checker(example, ipt.assemble_logical_lines) | |||
|
286 | ||||
|
287 | def test_assemble_python_lines(): | |||
|
288 | tests = \ | |||
|
289 | [ [(u"a = '''", None), | |||
|
290 | (u"abc'''", u"a = '''\nabc'''"), | |||
|
291 | ], | |||
|
292 | [(u"a = '''", None), # Test resetting when within a multi-line string | |||
|
293 | (u"def", None), | |||
|
294 | (None, u"a = '''\ndef"), | |||
|
295 | ], | |||
|
296 | [(u"a = [1,", None), | |||
|
297 | (u"2]", u"a = [1,\n2]"), | |||
|
298 | ], | |||
|
299 | [(u"a = [1,", None), # Test resetting when within a multi-line string | |||
|
300 | (u"2,", None), | |||
|
301 | (None, u"a = [1,\n2,"), | |||
|
302 | ], | |||
|
303 | ] | |||
|
304 | for example in tests: | |||
|
305 | transform_checker(example, ipt.assemble_python_lines) | |||
|
306 | ||||
|
307 | ||||
279 | def test_help_end(): |
|
308 | def test_help_end(): | |
280 | tt.check_pairs(transform_and_reset(ipt.help_end), syntax['end_help']) |
|
309 | tt.check_pairs(transform_and_reset(ipt.help_end), syntax['end_help']) | |
281 |
|
310 | |||
282 | def test_escaped_noesc(): |
|
311 | def test_escaped_noesc(): | |
283 |
tt.check_pairs(transform_and_reset(ipt.escaped_ |
|
312 | tt.check_pairs(transform_and_reset(ipt.escaped_commands), syntax['escaped_noesc']) | |
284 |
|
313 | |||
285 |
|
314 | |||
286 | def test_escaped_shell(): |
|
315 | def test_escaped_shell(): | |
287 |
tt.check_pairs(transform_and_reset(ipt.escaped_ |
|
316 | tt.check_pairs(transform_and_reset(ipt.escaped_commands), syntax['escaped_shell']) | |
288 |
|
317 | |||
289 |
|
318 | |||
290 | def test_escaped_help(): |
|
319 | def test_escaped_help(): | |
291 |
tt.check_pairs(transform_and_reset(ipt.escaped_ |
|
320 | tt.check_pairs(transform_and_reset(ipt.escaped_commands), syntax['escaped_help']) | |
292 |
|
321 | |||
293 |
|
322 | |||
294 | def test_escaped_magic(): |
|
323 | def test_escaped_magic(): | |
295 |
tt.check_pairs(transform_and_reset(ipt.escaped_ |
|
324 | tt.check_pairs(transform_and_reset(ipt.escaped_commands), syntax['escaped_magic']) | |
296 |
|
325 | |||
297 |
|
326 | |||
298 | def test_escaped_quote(): |
|
327 | def test_escaped_quote(): | |
299 |
tt.check_pairs(transform_and_reset(ipt.escaped_ |
|
328 | tt.check_pairs(transform_and_reset(ipt.escaped_commands), syntax['escaped_quote']) | |
300 |
|
329 | |||
301 |
|
330 | |||
302 | def test_escaped_quote2(): |
|
331 | def test_escaped_quote2(): | |
303 |
tt.check_pairs(transform_and_reset(ipt.escaped_ |
|
332 | tt.check_pairs(transform_and_reset(ipt.escaped_commands), syntax['escaped_quote2']) | |
304 |
|
333 | |||
305 |
|
334 | |||
306 | def test_escaped_paren(): |
|
335 | def test_escaped_paren(): | |
307 |
tt.check_pairs(transform_and_reset(ipt.escaped_ |
|
336 | tt.check_pairs(transform_and_reset(ipt.escaped_commands), syntax['escaped_paren']) | |
308 |
|
337 | |||
309 | def test_escaped_multiline(): |
|
|||
310 | for example in syntax_ml['escaped']: |
|
|||
311 | transform_checker(example, ipt.escaped_transformer) |
|
|||
312 |
|
338 | |||
313 | def test_cellmagic(): |
|
339 | def test_cellmagic(): | |
314 | for example in syntax_ml['cellmagic']: |
|
340 | for example in syntax_ml['cellmagic']: |
General Comments 0
You need to be logged in to leave comments.
Login now