##// END OF EJS Templates
Simplify input transformers...
Thomas Kluyver -
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_transformer,
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_transformer(),
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 transformer = cls(func)
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_transformer), syntax['escaped_noesc'])
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_transformer), syntax['escaped_shell'])
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_transformer), syntax['escaped_help'])
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_transformer), syntax['escaped_magic'])
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_transformer), syntax['escaped_quote'])
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_transformer), syntax['escaped_quote2'])
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_transformer), syntax['escaped_paren'])
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