Show More
@@ -3,11 +3,15 b'' | |||
|
3 | 3 | """ |
|
4 | 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 | 9 | Authors: |
|
7 | 10 | |
|
8 | 11 | * Brian Granger |
|
9 | 12 | * Fernando Perez |
|
10 | 13 | * Dan Milstein |
|
14 | * Ville Vainio | |
|
11 | 15 | """ |
|
12 | 16 | |
|
13 | 17 | #----------------------------------------------------------------------------- |
@@ -34,7 +38,7 b' from IPython.core.component import Component' | |||
|
34 | 38 | from IPython.core.splitinput import split_user_input |
|
35 | 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 | 42 | from IPython.utils.genutils import make_quoted_expr |
|
39 | 43 | from IPython.utils.autoattr import auto_attr |
|
40 | 44 | |
@@ -171,15 +175,46 b' class PrefilterManager(Component):' | |||
|
171 | 175 | |
|
172 | 176 | The IPython prefilter is run on all user input before it is run. The |
|
173 | 177 | prefilter consumes lines of input and produces transformed lines of |
|
174 | input. The implementation consists of checkers and handlers. The | |
|
175 | checkers inspect the input line and select which handler will be used | |
|
176 | to transform the input line. | |
|
178 | input. | |
|
179 | ||
|
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 | 213 | multi_line_specials = CBool(True, config=True) |
|
180 | 214 | |
|
181 | 215 | def __init__(self, parent, config=None): |
|
182 | 216 | super(PrefilterManager, self).__init__(parent, config=config) |
|
217 | self.init_transformers() | |
|
183 | 218 | self.init_handlers() |
|
184 | 219 | self.init_checkers() |
|
185 | 220 | |
@@ -189,21 +224,89 b' class PrefilterManager(Component):' | |||
|
189 | 224 | root=self.root, |
|
190 | 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 | 265 | def init_checkers(self): |
|
266 | """Create the default checkers.""" | |
|
193 | 267 | self._checkers = [] |
|
194 | 268 | for checker in _default_checkers: |
|
195 |
|
|
|
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 | 299 | def init_handlers(self): |
|
300 | """Create the default handlers.""" | |
|
198 | 301 | self._handlers = {} |
|
199 | 302 | self._esc_handlers = {} |
|
200 | 303 | for handler in _default_handlers: |
|
201 | 304 | handler(self, config=self.config) |
|
202 | 305 | |
|
203 | 306 | @property |
|
204 |
def |
|
|
205 |
"""Return a |
|
|
206 | return sorted(self._checkers, cmp=lambda x,y: x.priority-y.priority) | |
|
307 | def handlers(self): | |
|
308 | """Return a dict of all the handlers.""" | |
|
309 | return self._handlers | |
|
207 | 310 | |
|
208 | 311 | def register_handler(self, name, handler, esc_strings): |
|
209 | 312 | """Register a handler instance by name with esc_strings.""" |
@@ -230,24 +333,41 b' class PrefilterManager(Component):' | |||
|
230 | 333 | """Get a handler by its escape string.""" |
|
231 | 334 | return self._esc_handlers.get(esc_str) |
|
232 | 335 | |
|
336 | #------------------------------------------------------------------------- | |
|
337 | # Main prefiltering API | |
|
338 | #------------------------------------------------------------------------- | |
|
339 | ||
|
233 | 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 | 345 | # print "prefilter_line_info: ", line_info |
|
236 | 346 | handler = self.find_handler(line_info) |
|
237 | 347 | return handler.handle(line_info) |
|
238 | 348 | |
|
239 | 349 | def find_handler(self, line_info): |
|
240 | 350 | """Find a handler for the line_info by trying checkers.""" |
|
241 |
for checker in self. |
|
|
242 |
|
|
|
243 | if handler: | |
|
244 | # print "Used checker: ", checker | |
|
245 |
|
|
|
246 | return handler | |
|
351 | for checker in self.checkers: | |
|
352 | if checker.enabled: | |
|
353 | handler = checker.check(line_info) | |
|
354 | if handler: | |
|
355 | return handler | |
|
247 | 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 | 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 | 372 | # print "prefilter_line: ", line, continue_prompt |
|
253 | 373 | # All handlers *must* return a value, even if it's blank (''). |
@@ -256,8 +376,6 b' class PrefilterManager(Component):' | |||
|
256 | 376 | # needed, update the cache AND log it (so that the input cache array |
|
257 | 377 | # stays synced). |
|
258 | 378 | |
|
259 | # growl.notify("_prefilter: ", "line = %s\ncontinue_prompt = %s" % (line, continue_prompt)) | |
|
260 | ||
|
261 | 379 | # save the line away in case we crash, so the post-mortem handler can |
|
262 | 380 | # record it |
|
263 | 381 | self.shell._last_input_line = line |
@@ -272,13 +390,18 b' class PrefilterManager(Component):' | |||
|
272 | 390 | if ''.join(self.shell.buffer).isspace(): |
|
273 | 391 | self.shell.buffer[:] = [] |
|
274 | 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 | 399 | line_info = LineInfo(line, continue_prompt) |
|
277 | 400 | |
|
278 | 401 | # the input history needs to track even empty lines |
|
279 | 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 | 405 | if not stripped: |
|
283 | 406 | if not continue_prompt: |
|
284 | 407 | self.shell.outputcache.prompt_count -= 1 |
@@ -296,19 +419,96 b' class PrefilterManager(Component):' | |||
|
296 | 419 | def prefilter_lines(self, lines, continue_prompt): |
|
297 | 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 | 427 | which is the case when the user goes back to a multiline history |
|
301 | 428 | entry and presses enter. |
|
302 | 429 | """ |
|
303 | # growl.notify("multiline_prefilter: ", "%s\n%s" % (line, continue_prompt)) | |
|
304 | 430 | out = [] |
|
305 | 431 | for line in lines.rstrip('\n').split('\n'): |
|
306 | 432 | out.append(self.prefilter_line(line, continue_prompt)) |
|
307 | # growl.notify("multiline_prefilter return: ", '\n'.join(out)) | |
|
308 | 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 | 512 | # Prefilter checkers |
|
313 | 513 | #----------------------------------------------------------------------------- |
|
314 | 514 | |
@@ -319,9 +519,11 b' class PrefilterChecker(Component):' | |||
|
319 | 519 | priority = Int(100, config=True) |
|
320 | 520 | shell = Any |
|
321 | 521 | prefilter_manager = Any |
|
522 | enabled = Bool(True, config=True) | |
|
322 | 523 | |
|
323 | 524 | def __init__(self, parent, config=None): |
|
324 | 525 | super(PrefilterChecker, self).__init__(parent, config=config) |
|
526 | self.prefilter_manager.register_checker(self) | |
|
325 | 527 | |
|
326 | 528 | @auto_attr |
|
327 | 529 | def shell(self): |
@@ -334,15 +536,18 b' class PrefilterChecker(Component):' | |||
|
334 | 536 | return PrefilterManager.get_instances(root=self.root)[0] |
|
335 | 537 | |
|
336 | 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 | 540 | return None |
|
339 | 541 | |
|
340 |
def __ |
|
|
341 | return "<%s(priority=%i)>" % (self.__class__.__name__, self.priority) | |
|
542 | def __repr__(self): | |
|
543 | return "<%s(priority=%r, enabled=%r)>" % ( | |
|
544 | self.__class__.__name__, self.priority, self.enabled) | |
|
545 | ||
|
342 | 546 | |
|
343 | 547 | class EmacsChecker(PrefilterChecker): |
|
344 | 548 | |
|
345 | 549 | priority = Int(100, config=True) |
|
550 | enabled = Bool(False, config=True) | |
|
346 | 551 | |
|
347 | 552 | def check(self, line_info): |
|
348 | 553 | "Emacs ipython-mode tags certain input lines." |
@@ -410,10 +615,6 b' class EscCharsChecker(PrefilterChecker):' | |||
|
410 | 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 | 618 | class AssignmentChecker(PrefilterChecker): |
|
418 | 619 | |
|
419 | 620 | priority = Int(600, config=True) |
@@ -427,12 +628,6 b' class AssignmentChecker(PrefilterChecker):' | |||
|
427 | 628 | python code). E.g. ls='hi', or ls,that=1,2""" |
|
428 | 629 | if line_info.the_rest: |
|
429 | 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 | 631 | return self.prefilter_manager.get_handler_by_name('normal') |
|
437 | 632 | else: |
|
438 | 633 | return None |
@@ -571,45 +766,6 b' class PrefilterHandler(Component):' | |||
|
571 | 766 | def __str__(self): |
|
572 | 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 | 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 | 973 | _default_checkers = [ |
|
813 | 974 | EmacsChecker, |
|
814 | 975 | ShellEscapeChecker, |
@@ -830,7 +991,5 b' _default_handlers = [' | |||
|
830 | 991 | AutoHandler, |
|
831 | 992 | HelpHandler, |
|
832 | 993 | EmacsHandler |
|
833 | # AssignSystemHandler, | |
|
834 | # AssignMagicHandler | |
|
835 | 994 | ] |
|
836 | 995 |
General Comments 0
You need to be logged in to leave comments.
Login now