##// END OF EJS Templates
Transformers have been added to the prefilter....
Brian Granger -
Show More
@@ -3,11 +3,15 b''
3 """
3 """
4 Prefiltering components.
4 Prefiltering components.
5
5
6 Prefilters transform user input before it is exec'd by Python. These
7 transforms are used to implement additional syntax such as !ls and %magic.
8
6 Authors:
9 Authors:
7
10
8 * Brian Granger
11 * Brian Granger
9 * Fernando Perez
12 * Fernando Perez
10 * Dan Milstein
13 * Dan Milstein
14 * Ville Vainio
11 """
15 """
12
16
13 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
@@ -34,7 +38,7 b' from IPython.core.component import Component'
34 from IPython.core.splitinput import split_user_input
38 from IPython.core.splitinput import split_user_input
35 from IPython.core.page import page
39 from IPython.core.page import page
36
40
37 from IPython.utils.traitlets import List, Int, Any, Str, CBool
41 from IPython.utils.traitlets import List, Int, Any, Str, CBool, Bool
38 from IPython.utils.genutils import make_quoted_expr
42 from IPython.utils.genutils import make_quoted_expr
39 from IPython.utils.autoattr import auto_attr
43 from IPython.utils.autoattr import auto_attr
40
44
@@ -171,15 +175,46 b' class PrefilterManager(Component):'
171
175
172 The IPython prefilter is run on all user input before it is run. The
176 The IPython prefilter is run on all user input before it is run. The
173 prefilter consumes lines of input and produces transformed lines of
177 prefilter consumes lines of input and produces transformed lines of
174 input. The implementation consists of checkers and handlers. The
178 input.
175 checkers inspect the input line and select which handler will be used
179
176 to transform the input line.
180 The iplementation consists of two phases:
181
182 1. Transformers
183 2. Checkers and handlers
184
185 Over time, we plan on deprecating the checkers and handlers and doing
186 everything in the transformers.
187
188 The transformers are instances of :class:`PrefilterTransformer` and have
189 a single method :meth:`transform` that takes a line and returns a
190 transformed line. The transformation can be accomplished using any
191 tool, but our current ones use regular expressions for speed. We also
192 ship :mod:`pyparsing` in :mod:`IPython.external` for use in transformers.
193
194 After all the transformers have been run, the line is fed to the checkers,
195 which are instances of :class:`PrefilterChecker`. The line is passed to
196 the :meth:`check` method, which either returns `None` or a
197 :class:`PrefilterHandler` instance. If `None` is returned, the other
198 checkers are tried. If an :class:`PrefilterHandler` instance is returned,
199 the line is passed to the :meth:`handle` method of the returned
200 handler and no further checkers are tried.
201
202 Both transformers and checkers have a `priority` attribute, that determines
203 the order in which they are called. Smaller priorities are tried first.
204
205 Both transformers and checkers also have `enabled` attribute, which is
206 a boolean that determines if the instance is used.
207
208 Users or developers can change the priority or enabled attribute of
209 transformers or checkers, but they must call the :meth:`sort_checkers`
210 or :meth`sort_transformers` method after changing the priority.
177 """
211 """
178
212
179 multi_line_specials = CBool(True, config=True)
213 multi_line_specials = CBool(True, config=True)
180
214
181 def __init__(self, parent, config=None):
215 def __init__(self, parent, config=None):
182 super(PrefilterManager, self).__init__(parent, config=config)
216 super(PrefilterManager, self).__init__(parent, config=config)
217 self.init_transformers()
183 self.init_handlers()
218 self.init_handlers()
184 self.init_checkers()
219 self.init_checkers()
185
220
@@ -189,21 +224,89 b' class PrefilterManager(Component):'
189 root=self.root,
224 root=self.root,
190 klass='IPython.core.iplib.InteractiveShell')[0]
225 klass='IPython.core.iplib.InteractiveShell')[0]
191
226
227 #-------------------------------------------------------------------------
228 # API for managing transformers
229 #-------------------------------------------------------------------------
230
231 def init_transformers(self):
232 """Create the default transformers."""
233 self._transformers = []
234 for transformer_cls in _default_transformers:
235 transformer_cls(self, config=self.config)
236
237 def sort_transformers(self):
238 """Sort the transformers by priority.
239
240 This must be called after the priority of a transformer is changed.
241 The :meth:`register_transformer` method calls this automatically.
242 """
243 self._transformers.sort(cmp=lambda x,y: x.priority-y.priority)
244
245 @property
246 def transformers(self):
247 """Return a list of checkers, sorted by priority."""
248 return self._transformers
249
250 def register_transformer(self, transformer):
251 """Register a transformer instance."""
252 if transformer not in self._transformers:
253 self._transformers.append(transformer)
254 self.sort_transformers()
255
256 def unregister_transformer(self, transformer):
257 """Unregister a transformer instance."""
258 if transformer in self._transformers:
259 self._transformers.remove(transformer)
260
261 #-------------------------------------------------------------------------
262 # API for managing checkers
263 #-------------------------------------------------------------------------
264
192 def init_checkers(self):
265 def init_checkers(self):
266 """Create the default checkers."""
193 self._checkers = []
267 self._checkers = []
194 for checker in _default_checkers:
268 for checker in _default_checkers:
195 self._checkers.append(checker(self, config=self.config))
269 checker(self, config=self.config)
270
271 def sort_checkers(self):
272 """Sort the checkers by priority.
273
274 This must be called after the priority of a checker is changed.
275 The :meth:`register_checker` method calls this automatically.
276 """
277 self._checkers.sort(cmp=lambda x,y: x.priority-y.priority)
278
279 @property
280 def checkers(self):
281 """Return a list of checkers, sorted by priority."""
282 return self._checkers
283
284 def register_checker(self, checker):
285 """Register a checker instance."""
286 if checker not in self._checkers:
287 self._checkers.append(checker)
288 self.sort_checkers()
289
290 def unregister_checker(self, checker):
291 """Unregister a checker instance."""
292 if checker in self._checkers:
293 self._checkers.remove(checker)
294
295 #-------------------------------------------------------------------------
296 # API for managing checkers
297 #-------------------------------------------------------------------------
196
298
197 def init_handlers(self):
299 def init_handlers(self):
300 """Create the default handlers."""
198 self._handlers = {}
301 self._handlers = {}
199 self._esc_handlers = {}
302 self._esc_handlers = {}
200 for handler in _default_handlers:
303 for handler in _default_handlers:
201 handler(self, config=self.config)
304 handler(self, config=self.config)
202
305
203 @property
306 @property
204 def sorted_checkers(self):
307 def handlers(self):
205 """Return a list of checkers, sorted by priority."""
308 """Return a dict of all the handlers."""
206 return sorted(self._checkers, cmp=lambda x,y: x.priority-y.priority)
309 return self._handlers
207
310
208 def register_handler(self, name, handler, esc_strings):
311 def register_handler(self, name, handler, esc_strings):
209 """Register a handler instance by name with esc_strings."""
312 """Register a handler instance by name with esc_strings."""
@@ -230,24 +333,41 b' class PrefilterManager(Component):'
230 """Get a handler by its escape string."""
333 """Get a handler by its escape string."""
231 return self._esc_handlers.get(esc_str)
334 return self._esc_handlers.get(esc_str)
232
335
336 #-------------------------------------------------------------------------
337 # Main prefiltering API
338 #-------------------------------------------------------------------------
339
233 def prefilter_line_info(self, line_info):
340 def prefilter_line_info(self, line_info):
234 """Prefilter a line that has been converted to a LineInfo object."""
341 """Prefilter a line that has been converted to a LineInfo object.
342
343 This implements the checker/handler part of the prefilter pipe.
344 """
235 # print "prefilter_line_info: ", line_info
345 # print "prefilter_line_info: ", line_info
236 handler = self.find_handler(line_info)
346 handler = self.find_handler(line_info)
237 return handler.handle(line_info)
347 return handler.handle(line_info)
238
348
239 def find_handler(self, line_info):
349 def find_handler(self, line_info):
240 """Find a handler for the line_info by trying checkers."""
350 """Find a handler for the line_info by trying checkers."""
241 for checker in self.sorted_checkers:
351 for checker in self.checkers:
242 handler = checker.check(line_info)
352 if checker.enabled:
243 if handler:
353 handler = checker.check(line_info)
244 # print "Used checker: ", checker
354 if handler:
245 # print "Using handler: ", handler
355 return handler
246 return handler
247 return self.get_handler_by_name('normal')
356 return self.get_handler_by_name('normal')
248
357
358 def transform_line(self, line, continue_prompt):
359 """Calls the enabled transformers in order of increasing priority."""
360 for transformer in self.transformers:
361 if transformer.enabled:
362 line = transformer.transform(line, continue_prompt)
363 return line
364
249 def prefilter_line(self, line, continue_prompt):
365 def prefilter_line(self, line, continue_prompt):
250 """Prefilter a single input line as text."""
366 """Prefilter a single input line as text.
367
368 This method prefilters a single line of text by calling the
369 transformers and then the checkers/handlers.
370 """
251
371
252 # print "prefilter_line: ", line, continue_prompt
372 # print "prefilter_line: ", line, continue_prompt
253 # All handlers *must* return a value, even if it's blank ('').
373 # All handlers *must* return a value, even if it's blank ('').
@@ -256,8 +376,6 b' class PrefilterManager(Component):'
256 # needed, update the cache AND log it (so that the input cache array
376 # needed, update the cache AND log it (so that the input cache array
257 # stays synced).
377 # stays synced).
258
378
259 # growl.notify("_prefilter: ", "line = %s\ncontinue_prompt = %s" % (line, continue_prompt))
260
261 # save the line away in case we crash, so the post-mortem handler can
379 # save the line away in case we crash, so the post-mortem handler can
262 # record it
380 # record it
263 self.shell._last_input_line = line
381 self.shell._last_input_line = line
@@ -272,13 +390,18 b' class PrefilterManager(Component):'
272 if ''.join(self.shell.buffer).isspace():
390 if ''.join(self.shell.buffer).isspace():
273 self.shell.buffer[:] = []
391 self.shell.buffer[:] = []
274 return ''
392 return ''
275
393
394 # At this point, we invoke our transformers.
395 if not continue_prompt or (continue_prompt and self.multi_line_specials):
396 line = self.transform_line(line, continue_prompt)
397
398 # Now we compute line_info for the checkers and handlers
276 line_info = LineInfo(line, continue_prompt)
399 line_info = LineInfo(line, continue_prompt)
277
400
278 # the input history needs to track even empty lines
401 # the input history needs to track even empty lines
279 stripped = line.strip()
402 stripped = line.strip()
280
403
281 normal_handler = self.get_handler_by_name('normal')
404 normal_handler = self.get_handler_by_name('normal')
282 if not stripped:
405 if not stripped:
283 if not continue_prompt:
406 if not continue_prompt:
284 self.shell.outputcache.prompt_count -= 1
407 self.shell.outputcache.prompt_count -= 1
@@ -296,19 +419,96 b' class PrefilterManager(Component):'
296 def prefilter_lines(self, lines, continue_prompt):
419 def prefilter_lines(self, lines, continue_prompt):
297 """Prefilter multiple input lines of text.
420 """Prefilter multiple input lines of text.
298
421
299 Covers cases where there are multiple lines in the user entry,
422 This is the main entry point for prefiltering multiple lines of
423 input. This simply calls :meth:`prefilter_line` for each line of
424 input.
425
426 This covers cases where there are multiple lines in the user entry,
300 which is the case when the user goes back to a multiline history
427 which is the case when the user goes back to a multiline history
301 entry and presses enter.
428 entry and presses enter.
302 """
429 """
303 # growl.notify("multiline_prefilter: ", "%s\n%s" % (line, continue_prompt))
304 out = []
430 out = []
305 for line in lines.rstrip('\n').split('\n'):
431 for line in lines.rstrip('\n').split('\n'):
306 out.append(self.prefilter_line(line, continue_prompt))
432 out.append(self.prefilter_line(line, continue_prompt))
307 # growl.notify("multiline_prefilter return: ", '\n'.join(out))
308 return '\n'.join(out)
433 return '\n'.join(out)
309
434
310
435
311 #-----------------------------------------------------------------------------
436 #-----------------------------------------------------------------------------
437 # Prefilter transformers
438 #-----------------------------------------------------------------------------
439
440
441 class PrefilterTransformer(Component):
442 """Transform a line of user input."""
443
444 priority = Int(100, config=True)
445 shell = Any
446 prefilter_manager = Any
447 enabled = Bool(True, config=True)
448
449 def __init__(self, parent, config=None):
450 super(PrefilterTransformer, self).__init__(parent, config=config)
451 self.prefilter_manager.register_transformer(self)
452
453 @auto_attr
454 def shell(self):
455 return Component.get_instances(
456 root=self.root,
457 klass='IPython.core.iplib.InteractiveShell')[0]
458
459 @auto_attr
460 def prefilter_manager(self):
461 return PrefilterManager.get_instances(root=self.root)[0]
462
463 def transform(self, line, continue_prompt):
464 """Transform a line, returning the new one."""
465 return None
466
467 def __repr__(self):
468 return "<%s(priority=%r, enabled=%r)>" % (
469 self.__class__.__name__, self.priority, self.enabled)
470
471
472 _assign_system_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
473 r'\s*=\s*!(?P<cmd>.*)')
474
475
476 class AssignSystemTransformer(PrefilterTransformer):
477 """Handle the `files = !ls` syntax."""
478
479 priority = Int(100, config=True)
480
481 def transform(self, line, continue_prompt):
482 m = _assign_system_re.match(line)
483 if m is not None:
484 cmd = m.group('cmd')
485 lhs = m.group('lhs')
486 expr = make_quoted_expr("sc -l =%s" % cmd)
487 new_line = '%s = get_ipython().magic(%s)' % (lhs, expr)
488 return new_line
489 return line
490
491
492 _assign_magic_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
493 r'\s*=\s*%(?P<cmd>.*)')
494
495 class AssignMagicTransformer(PrefilterTransformer):
496 """Handle the `a = %who` syntax."""
497
498 priority = Int(200, config=True)
499
500 def transform(self, line, continue_prompt):
501 m = _assign_magic_re.match(line)
502 if m is not None:
503 cmd = m.group('cmd')
504 lhs = m.group('lhs')
505 expr = make_quoted_expr(cmd)
506 new_line = '%s = get_ipython().magic(%s)' % (lhs, expr)
507 return new_line
508 return line
509
510
511 #-----------------------------------------------------------------------------
312 # Prefilter checkers
512 # Prefilter checkers
313 #-----------------------------------------------------------------------------
513 #-----------------------------------------------------------------------------
314
514
@@ -319,9 +519,11 b' class PrefilterChecker(Component):'
319 priority = Int(100, config=True)
519 priority = Int(100, config=True)
320 shell = Any
520 shell = Any
321 prefilter_manager = Any
521 prefilter_manager = Any
522 enabled = Bool(True, config=True)
322
523
323 def __init__(self, parent, config=None):
524 def __init__(self, parent, config=None):
324 super(PrefilterChecker, self).__init__(parent, config=config)
525 super(PrefilterChecker, self).__init__(parent, config=config)
526 self.prefilter_manager.register_checker(self)
325
527
326 @auto_attr
528 @auto_attr
327 def shell(self):
529 def shell(self):
@@ -334,15 +536,18 b' class PrefilterChecker(Component):'
334 return PrefilterManager.get_instances(root=self.root)[0]
536 return PrefilterManager.get_instances(root=self.root)[0]
335
537
336 def check(self, line_info):
538 def check(self, line_info):
337 """Inspect line_info and return a handler or None."""
539 """Inspect line_info and return a handler instance or None."""
338 return None
540 return None
339
541
340 def __str__(self):
542 def __repr__(self):
341 return "<%s(priority=%i)>" % (self.__class__.__name__, self.priority)
543 return "<%s(priority=%r, enabled=%r)>" % (
544 self.__class__.__name__, self.priority, self.enabled)
545
342
546
343 class EmacsChecker(PrefilterChecker):
547 class EmacsChecker(PrefilterChecker):
344
548
345 priority = Int(100, config=True)
549 priority = Int(100, config=True)
550 enabled = Bool(False, config=True)
346
551
347 def check(self, line_info):
552 def check(self, line_info):
348 "Emacs ipython-mode tags certain input lines."
553 "Emacs ipython-mode tags certain input lines."
@@ -410,10 +615,6 b' class EscCharsChecker(PrefilterChecker):'
410 return self.prefilter_manager.get_handler_by_esc(line_info.pre_char)
615 return self.prefilter_manager.get_handler_by_esc(line_info.pre_char)
411
616
412
617
413 _assign_system_re = re.compile('\s*=\s*!(?P<cmd>.*)')
414 _assign_magic_re = re.compile('\s*=\s*%(?P<cmd>.*)')
415
416
417 class AssignmentChecker(PrefilterChecker):
618 class AssignmentChecker(PrefilterChecker):
418
619
419 priority = Int(600, config=True)
620 priority = Int(600, config=True)
@@ -427,12 +628,6 b' class AssignmentChecker(PrefilterChecker):'
427 python code). E.g. ls='hi', or ls,that=1,2"""
628 python code). E.g. ls='hi', or ls,that=1,2"""
428 if line_info.the_rest:
629 if line_info.the_rest:
429 if line_info.the_rest[0] in '=,':
630 if line_info.the_rest[0] in '=,':
430 # m = _assign_system_re.match(line_info.the_rest)
431 # if m is not None:
432 # return self.prefilter_manager.get_handler_by_name('assign_system')
433 # m = _assign_magic_re.match(line_info.the_rest)
434 # if m is not None:
435 # return self.prefilter_manager.get_handler_by_name('assign_magic')
436 return self.prefilter_manager.get_handler_by_name('normal')
631 return self.prefilter_manager.get_handler_by_name('normal')
437 else:
632 else:
438 return None
633 return None
@@ -571,45 +766,6 b' class PrefilterHandler(Component):'
571 def __str__(self):
766 def __str__(self):
572 return "<%s(name=%s)>" % (self.__class__.__name__, self.handler_name)
767 return "<%s(name=%s)>" % (self.__class__.__name__, self.handler_name)
573
768
574 class AssignSystemHandler(PrefilterHandler):
575
576 handler_name = Str('assign_system')
577
578 @auto_attr
579 def normal_handler(self):
580 return self.prefilter_manager.get_handler_by_name('normal')
581
582 def handle(self, line_info):
583 new_line = line_info.line
584 m = _assign_system_re.match(line_info.the_rest)
585 if m is not None:
586 cmd = m.group('cmd')
587 expr = make_quoted_expr("sc -l =%s" % cmd)
588 new_line = '%s%s = get_ipython().magic(%s)' % (line_info.pre_whitespace,
589 line_info.ifun, expr)
590 self.shell.log(line_info.line, new_line, line_info.continue_prompt)
591 return new_line
592
593
594 class AssignMagicHandler(PrefilterHandler):
595
596 handler_name = Str('assign_magic')
597
598 @auto_attr
599 def normal_handler(self):
600 return self.prefilter_manager.get_handler_by_name('normal')
601
602 def handle(self, line_info):
603 new_line = line_info.line
604 m = _assign_magic_re.match(line_info.the_rest)
605 if m is not None:
606 cmd = m.group('cmd')
607 expr = make_quoted_expr(cmd)
608 new_line = '%s%s = get_ipython().magic(%s)' % (line_info.pre_whitespace,
609 line_info.ifun, expr)
610 self.shell.log(line_info.line, new_line, line_info.continue_prompt)
611 return new_line
612
613
769
614 class AliasHandler(PrefilterHandler):
770 class AliasHandler(PrefilterHandler):
615
771
@@ -809,6 +965,11 b' class EmacsHandler(PrefilterHandler):'
809 #-----------------------------------------------------------------------------
965 #-----------------------------------------------------------------------------
810
966
811
967
968 _default_transformers = [
969 AssignSystemTransformer,
970 AssignMagicTransformer
971 ]
972
812 _default_checkers = [
973 _default_checkers = [
813 EmacsChecker,
974 EmacsChecker,
814 ShellEscapeChecker,
975 ShellEscapeChecker,
@@ -830,7 +991,5 b' _default_handlers = ['
830 AutoHandler,
991 AutoHandler,
831 HelpHandler,
992 HelpHandler,
832 EmacsHandler
993 EmacsHandler
833 # AssignSystemHandler,
834 # AssignMagicHandler
835 ]
994 ]
836
995
General Comments 0
You need to be logged in to leave comments. Login now