prefilter.py
700 lines
| 25.0 KiB
| text/x-python
|
PythonLexer
Brian Granger
|
r2244 | # encoding: utf-8 | ||
dan.milstein
|
r657 | """ | ||
Brian Granger
|
r2244 | Prefiltering components. | ||
Brian Granger
|
r2273 | Prefilters transform user input before it is exec'd by Python. These | ||
transforms are used to implement additional syntax such as !ls and %magic. | ||||
dan.milstein
|
r657 | """ | ||
Min RK
|
r22340 | # Copyright (c) IPython Development Team. | ||
# Distributed under the terms of the Modified BSD License. | ||||
Brian Granger
|
r2244 | |||
Thomas Kluyver
|
r12760 | from keyword import iskeyword | ||
dan.milstein
|
r657 | import re | ||
Brian Granger
|
r2244 | |||
Srinivas Reddy Thatiparthy
|
r25227 | from .autocall import IPyAutocall | ||
Min RK
|
r21253 | from traitlets.config.configurable import Configurable | ||
Srinivas Reddy Thatiparthy
|
r25227 | from .inputtransformer2 import ( | ||
MinRK
|
r7437 | ESC_MAGIC, | ||
ESC_QUOTE, | ||||
ESC_QUOTE2, | ||||
ESC_PAREN, | ||||
) | ||||
Srinivas Reddy Thatiparthy
|
r25227 | from .macro import Macro | ||
from .splitinput import LineInfo | ||||
Brian Granger
|
r2244 | |||
Min RK
|
r21253 | from traitlets import ( | ||
Min RK
|
r22340 | List, Integer, Unicode, Bool, Instance, CRegExp | ||
Bradley M. Froehle
|
r6733 | ) | ||
Brian Granger
|
r2244 | |||
#----------------------------------------------------------------------------- | ||||
# Global utilities, errors and constants | ||||
#----------------------------------------------------------------------------- | ||||
class PrefilterError(Exception): | ||||
pass | ||||
# RegExp to identify potential function names | ||||
Markus Wageringel
|
r25595 | re_fun_name = re.compile(r'[^\W\d]([\w.]*) *$') | ||
Brian Granger
|
r2244 | |||
# RegExp to exclude strings with this start from autocalling. In | ||||
# particular, all binary operators should be excluded, so that if foo is | ||||
# callable, foo OP bar doesn't become foo(OP bar), which is invalid. The | ||||
# characters '!=()' don't need to be checked for, as the checkPythonChars | ||||
luzpaz
|
r24084 | # routine explicitly does so, to catch direct calls and rebindings of | ||
Brian Granger
|
r2244 | # existing names. | ||
# Warning: the '-' HAS TO BE AT THE END of the first group, otherwise | ||||
# it affects the rest of the group in square brackets. | ||||
re_exclude_auto = re.compile(r'^[,&^\|\*/\+-]' | ||||
r'|^is |^not |^in |^and |^or ') | ||||
# try to catch also methods for stuff in lists/tuples/dicts: off | ||||
# (experimental). For this to work, the line_split regexp would need | ||||
# to be modified so it wouldn't break things at '['. That line is | ||||
# nasty enough that I shouldn't change it until I can test it _well_. | ||||
#self.re_fun_name = re.compile (r'[a-zA-Z_]([a-zA-Z0-9_.\[\]]*) ?$') | ||||
# Handler Check Utilities | ||||
def is_shadowed(identifier, ip): | ||||
"""Is the given identifier defined in one of the namespaces which shadow | ||||
the alias and magic namespaces? Note that an identifier is different | ||||
than ifun, because it can not contain a '.' character.""" | ||||
# This is much safer than calling ofind, which can change state | ||||
return (identifier in ip.user_ns \ | ||||
Thomas Kluyver
|
r5454 | or identifier in ip.user_global_ns \ | ||
Thomas Kluyver
|
r12760 | or identifier in ip.ns_table['builtin']\ | ||
or iskeyword(identifier)) | ||||
Brian Granger
|
r2244 | |||
#----------------------------------------------------------------------------- | ||||
# Main Prefilter manager | ||||
#----------------------------------------------------------------------------- | ||||
dan.milstein
|
r657 | |||
Brian Granger
|
r2244 | |||
Brian Granger
|
r2731 | class PrefilterManager(Configurable): | ||
Brian Granger
|
r2244 | """Main prefilter component. | ||
The IPython prefilter is run on all user input before it is run. The | ||||
Bernardo B. Marques
|
r4872 | prefilter consumes lines of input and produces transformed lines of | ||
Brian Granger
|
r2273 | input. | ||
luz.paz
|
r24493 | The implementation consists of two phases: | ||
Brian Granger
|
r2273 | |||
1. Transformers | ||||
2. Checkers and handlers | ||||
Over time, we plan on deprecating the checkers and handlers and doing | ||||
everything in the transformers. | ||||
The transformers are instances of :class:`PrefilterTransformer` and have | ||||
a single method :meth:`transform` that takes a line and returns a | ||||
transformed line. The transformation can be accomplished using any | ||||
Julian Taylor
|
r7273 | tool, but our current ones use regular expressions for speed. | ||
Brian Granger
|
r2273 | |||
After all the transformers have been run, the line is fed to the checkers, | ||||
which are instances of :class:`PrefilterChecker`. The line is passed to | ||||
Bernardo B. Marques
|
r4872 | the :meth:`check` method, which either returns `None` or a | ||
Brian Granger
|
r2273 | :class:`PrefilterHandler` instance. If `None` is returned, the other | ||
checkers are tried. If an :class:`PrefilterHandler` instance is returned, | ||||
the line is passed to the :meth:`handle` method of the returned | ||||
handler and no further checkers are tried. | ||||
Bernardo B. Marques
|
r4872 | |||
Brian Granger
|
r2273 | Both transformers and checkers have a `priority` attribute, that determines | ||
the order in which they are called. Smaller priorities are tried first. | ||||
Both transformers and checkers also have `enabled` attribute, which is | ||||
a boolean that determines if the instance is used. | ||||
Users or developers can change the priority or enabled attribute of | ||||
transformers or checkers, but they must call the :meth:`sort_checkers` | ||||
Brian Granger
|
r2277 | or :meth:`sort_transformers` method after changing the priority. | ||
dan.milstein
|
r657 | """ | ||
Brian Granger
|
r2244 | |||
Min RK
|
r22340 | multi_line_specials = Bool(True).tag(config=True) | ||
Sylvain Corlay
|
r20940 | shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', allow_none=True) | ||
Brian Granger
|
r2244 | |||
MinRK
|
r11064 | def __init__(self, shell=None, **kwargs): | ||
super(PrefilterManager, self).__init__(shell=shell, **kwargs) | ||||
Brian Granger
|
r2731 | self.shell = shell | ||
Matthias Bussonnier
|
r25725 | self._transformers = [] | ||
Brian Granger
|
r2244 | self.init_handlers() | ||
self.init_checkers() | ||||
Brian Granger
|
r2273 | #------------------------------------------------------------------------- | ||
# API for managing transformers | ||||
#------------------------------------------------------------------------- | ||||
def sort_transformers(self): | ||||
"""Sort the transformers by priority. | ||||
This must be called after the priority of a transformer is changed. | ||||
The :meth:`register_transformer` method calls this automatically. | ||||
""" | ||||
Thomas Kluyver
|
r3108 | self._transformers.sort(key=lambda x: x.priority) | ||
Brian Granger
|
r2273 | |||
@property | ||||
def transformers(self): | ||||
"""Return a list of checkers, sorted by priority.""" | ||||
return self._transformers | ||||
def register_transformer(self, transformer): | ||||
"""Register a transformer instance.""" | ||||
if transformer not in self._transformers: | ||||
self._transformers.append(transformer) | ||||
self.sort_transformers() | ||||
def unregister_transformer(self, transformer): | ||||
"""Unregister a transformer instance.""" | ||||
if transformer in self._transformers: | ||||
self._transformers.remove(transformer) | ||||
#------------------------------------------------------------------------- | ||||
# API for managing checkers | ||||
#------------------------------------------------------------------------- | ||||
Brian Granger
|
r2244 | def init_checkers(self): | ||
Brian Granger
|
r2273 | """Create the default checkers.""" | ||
Brian Granger
|
r2244 | self._checkers = [] | ||
for checker in _default_checkers: | ||||
Brian Granger
|
r2740 | checker( | ||
MinRK
|
r11064 | shell=self.shell, prefilter_manager=self, parent=self | ||
Brian Granger
|
r2740 | ) | ||
Brian Granger
|
r2273 | |||
def sort_checkers(self): | ||||
"""Sort the checkers by priority. | ||||
This must be called after the priority of a checker is changed. | ||||
The :meth:`register_checker` method calls this automatically. | ||||
""" | ||||
Thomas Kluyver
|
r3108 | self._checkers.sort(key=lambda x: x.priority) | ||
Brian Granger
|
r2273 | |||
@property | ||||
def checkers(self): | ||||
"""Return a list of checkers, sorted by priority.""" | ||||
return self._checkers | ||||
def register_checker(self, checker): | ||||
"""Register a checker instance.""" | ||||
if checker not in self._checkers: | ||||
self._checkers.append(checker) | ||||
self.sort_checkers() | ||||
def unregister_checker(self, checker): | ||||
"""Unregister a checker instance.""" | ||||
if checker in self._checkers: | ||||
self._checkers.remove(checker) | ||||
#------------------------------------------------------------------------- | ||||
Jonathan Frederic
|
r13905 | # API for managing handlers | ||
Brian Granger
|
r2273 | #------------------------------------------------------------------------- | ||
Brian Granger
|
r2244 | |||
def init_handlers(self): | ||||
Brian Granger
|
r2273 | """Create the default handlers.""" | ||
Brian Granger
|
r2244 | self._handlers = {} | ||
self._esc_handlers = {} | ||||
for handler in _default_handlers: | ||||
Brian Granger
|
r2740 | handler( | ||
MinRK
|
r11064 | shell=self.shell, prefilter_manager=self, parent=self | ||
Brian Granger
|
r2740 | ) | ||
Brian Granger
|
r2244 | |||
@property | ||||
Brian Granger
|
r2273 | def handlers(self): | ||
"""Return a dict of all the handlers.""" | ||||
return self._handlers | ||||
Brian Granger
|
r2244 | |||
def register_handler(self, name, handler, esc_strings): | ||||
"""Register a handler instance by name with esc_strings.""" | ||||
self._handlers[name] = handler | ||||
for esc_str in esc_strings: | ||||
self._esc_handlers[esc_str] = handler | ||||
def unregister_handler(self, name, handler, esc_strings): | ||||
"""Unregister a handler instance by name with esc_strings.""" | ||||
dan.milstein
|
r657 | try: | ||
Brian Granger
|
r2244 | del self._handlers[name] | ||
except KeyError: | ||||
pass | ||||
for esc_str in esc_strings: | ||||
h = self._esc_handlers.get(esc_str) | ||||
if h is handler: | ||||
del self._esc_handlers[esc_str] | ||||
def get_handler_by_name(self, name): | ||||
"""Get a handler by its name.""" | ||||
return self._handlers.get(name) | ||||
def get_handler_by_esc(self, esc_str): | ||||
"""Get a handler by its escape string.""" | ||||
return self._esc_handlers.get(esc_str) | ||||
Brian Granger
|
r2273 | #------------------------------------------------------------------------- | ||
# Main prefiltering API | ||||
#------------------------------------------------------------------------- | ||||
Brian Granger
|
r2244 | def prefilter_line_info(self, line_info): | ||
Brian Granger
|
r2273 | """Prefilter a line that has been converted to a LineInfo object. | ||
This implements the checker/handler part of the prefilter pipe. | ||||
""" | ||||
Brian Granger
|
r2256 | # print "prefilter_line_info: ", line_info | ||
Brian Granger
|
r2244 | handler = self.find_handler(line_info) | ||
return handler.handle(line_info) | ||||
def find_handler(self, line_info): | ||||
"""Find a handler for the line_info by trying checkers.""" | ||||
Brian Granger
|
r2273 | for checker in self.checkers: | ||
if checker.enabled: | ||||
handler = checker.check(line_info) | ||||
if handler: | ||||
return handler | ||||
Brian Granger
|
r2244 | return self.get_handler_by_name('normal') | ||
Brian Granger
|
r2273 | def transform_line(self, line, continue_prompt): | ||
"""Calls the enabled transformers in order of increasing priority.""" | ||||
for transformer in self.transformers: | ||||
if transformer.enabled: | ||||
line = transformer.transform(line, continue_prompt) | ||||
return line | ||||
Fernando Perez
|
r2426 | def prefilter_line(self, line, continue_prompt=False): | ||
Brian Granger
|
r2273 | """Prefilter a single input line as text. | ||
This method prefilters a single line of text by calling the | ||||
transformers and then the checkers/handlers. | ||||
""" | ||||
Brian Granger
|
r2244 | |||
Brian Granger
|
r2256 | # print "prefilter_line: ", line, continue_prompt | ||
Brian Granger
|
r2244 | # All handlers *must* return a value, even if it's blank (''). | ||
# save the line away in case we crash, so the post-mortem handler can | ||||
Brian Granger
|
r2256 | # record it | ||
Brian Granger
|
r2244 | self.shell._last_input_line = line | ||
if not line: | ||||
# Return immediately on purely empty lines, so that if the user | ||||
# previously typed some whitespace that started a continuation | ||||
# prompt, he can break out of that loop with just an empty line. | ||||
# This is how the default python prompt works. | ||||
return '' | ||||
Brian Granger
|
r2273 | |||
# At this point, we invoke our transformers. | ||||
if not continue_prompt or (continue_prompt and self.multi_line_specials): | ||||
line = self.transform_line(line, continue_prompt) | ||||
# Now we compute line_info for the checkers and handlers | ||||
Brian Granger
|
r2244 | line_info = LineInfo(line, continue_prompt) | ||
Bernardo B. Marques
|
r4872 | |||
Brian Granger
|
r2244 | # the input history needs to track even empty lines | ||
stripped = line.strip() | ||||
dan.milstein
|
r657 | |||
Brian Granger
|
r2273 | normal_handler = self.get_handler_by_name('normal') | ||
Brian Granger
|
r2244 | if not stripped: | ||
Brian Granger
|
r2245 | return normal_handler.handle(line_info) | ||
dan.milstein
|
r670 | |||
Brian Granger
|
r2244 | # special handlers are only allowed for single line statements | ||
if continue_prompt and not self.multi_line_specials: | ||||
Brian Granger
|
r2245 | return normal_handler.handle(line_info) | ||
dan.milstein
|
r670 | |||
Brian Granger
|
r2256 | prefiltered = self.prefilter_line_info(line_info) | ||
# print "prefiltered line: %r" % prefiltered | ||||
return prefiltered | ||||
dan.milstein
|
r657 | |||
Fernando Perez
|
r2414 | def prefilter_lines(self, lines, continue_prompt=False): | ||
Brian Granger
|
r2244 | """Prefilter multiple input lines of text. | ||
dan.milstein
|
r670 | |||
Brian Granger
|
r2273 | This is the main entry point for prefiltering multiple lines of | ||
input. This simply calls :meth:`prefilter_line` for each line of | ||||
input. | ||||
This covers cases where there are multiple lines in the user entry, | ||||
Brian Granger
|
r2244 | which is the case when the user goes back to a multiline history | ||
entry and presses enter. | ||||
""" | ||||
Fernando Perez
|
r2435 | llines = lines.rstrip('\n').split('\n') | ||
# We can get multiple lines in one shot, where multiline input 'blends' | ||||
# into one line, in cases like recalling from the readline history | ||||
# buffer. We need to make sure that in such cases, we correctly | ||||
# communicate downstream which line is first and which are continuation | ||||
# ones. | ||||
if len(llines) > 1: | ||||
out = '\n'.join([self.prefilter_line(line, lnum>0) | ||||
for lnum, line in enumerate(llines) ]) | ||||
else: | ||||
out = self.prefilter_line(llines[0], continue_prompt) | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2435 | return out | ||
dan.milstein
|
r657 | |||
Brian Granger
|
r2244 | #----------------------------------------------------------------------------- | ||
Brian Granger
|
r2273 | # Prefilter transformers | ||
#----------------------------------------------------------------------------- | ||||
Brian Granger
|
r2731 | class PrefilterTransformer(Configurable): | ||
Brian Granger
|
r2273 | """Transform a line of user input.""" | ||
Matthias Bussonnier
|
r22330 | priority = Integer(100).tag(config=True) | ||
Brian Granger
|
r2731 | # Transformers don't currently use shell or prefilter_manager, but as we | ||
# move away from checkers and handlers, they will need them. | ||||
Sylvain Corlay
|
r20940 | shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', allow_none=True) | ||
prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager', allow_none=True) | ||||
Matthias Bussonnier
|
r22330 | enabled = Bool(True).tag(config=True) | ||
Brian Granger
|
r2273 | |||
MinRK
|
r11064 | def __init__(self, shell=None, prefilter_manager=None, **kwargs): | ||
Brian Granger
|
r2740 | super(PrefilterTransformer, self).__init__( | ||
MinRK
|
r11064 | shell=shell, prefilter_manager=prefilter_manager, **kwargs | ||
Brian Granger
|
r2740 | ) | ||
Brian Granger
|
r2273 | self.prefilter_manager.register_transformer(self) | ||
def transform(self, line, continue_prompt): | ||||
"""Transform a line, returning the new one.""" | ||||
return None | ||||
def __repr__(self): | ||||
return "<%s(priority=%r, enabled=%r)>" % ( | ||||
self.__class__.__name__, self.priority, self.enabled) | ||||
#----------------------------------------------------------------------------- | ||||
Brian Granger
|
r2244 | # Prefilter checkers | ||
#----------------------------------------------------------------------------- | ||||
dan.milstein
|
r657 | |||
Brian Granger
|
r2731 | class PrefilterChecker(Configurable): | ||
Brian Granger
|
r2244 | """Inspect an input line and return a handler for that line.""" | ||
dan.milstein
|
r657 | |||
Matthias Bussonnier
|
r22330 | priority = Integer(100).tag(config=True) | ||
Sylvain Corlay
|
r20940 | shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', allow_none=True) | ||
prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager', allow_none=True) | ||||
Matthias Bussonnier
|
r22330 | enabled = Bool(True).tag(config=True) | ||
dan.milstein
|
r657 | |||
MinRK
|
r11064 | def __init__(self, shell=None, prefilter_manager=None, **kwargs): | ||
Brian Granger
|
r2740 | super(PrefilterChecker, self).__init__( | ||
MinRK
|
r11064 | shell=shell, prefilter_manager=prefilter_manager, **kwargs | ||
Brian Granger
|
r2740 | ) | ||
Brian Granger
|
r2273 | self.prefilter_manager.register_checker(self) | ||
dan.milstein
|
r657 | |||
Brian Granger
|
r2244 | def check(self, line_info): | ||
Brian Granger
|
r2273 | """Inspect line_info and return a handler instance or None.""" | ||
dan.milstein
|
r657 | return None | ||
Brian Granger
|
r2273 | def __repr__(self): | ||
return "<%s(priority=%r, enabled=%r)>" % ( | ||||
self.__class__.__name__, self.priority, self.enabled) | ||||
Brian Granger
|
r2244 | |||
class EmacsChecker(PrefilterChecker): | ||||
Matthias Bussonnier
|
r22330 | priority = Integer(100).tag(config=True) | ||
enabled = Bool(False).tag(config=True) | ||||
Brian Granger
|
r2244 | |||
def check(self, line_info): | ||||
"Emacs ipython-mode tags certain input lines." | ||||
if line_info.line.endswith('# PYTHON-MODE'): | ||||
return self.prefilter_manager.get_handler_by_name('emacs') | ||||
else: | ||||
return None | ||||
Thomas Kluyver
|
r3406 | class MacroChecker(PrefilterChecker): | ||
Bernardo B. Marques
|
r4872 | |||
Matthias Bussonnier
|
r22330 | priority = Integer(250).tag(config=True) | ||
Bernardo B. Marques
|
r4872 | |||
Thomas Kluyver
|
r3406 | def check(self, line_info): | ||
obj = self.shell.user_ns.get(line_info.ifun) | ||||
if isinstance(obj, Macro): | ||||
return self.prefilter_manager.get_handler_by_name('macro') | ||||
else: | ||||
return None | ||||
Brian Granger
|
r2244 | class IPyAutocallChecker(PrefilterChecker): | ||
Matthias Bussonnier
|
r22330 | priority = Integer(300).tag(config=True) | ||
Brian Granger
|
r2244 | |||
def check(self, line_info): | ||||
"Instances of IPyAutocall in user_ns get autocalled immediately" | ||||
obj = self.shell.user_ns.get(line_info.ifun, None) | ||||
if isinstance(obj, IPyAutocall): | ||||
obj.set_ip(self.shell) | ||||
return self.prefilter_manager.get_handler_by_name('auto') | ||||
else: | ||||
return None | ||||
class AssignmentChecker(PrefilterChecker): | ||||
Matthias Bussonnier
|
r22330 | priority = Integer(600).tag(config=True) | ||
Brian Granger
|
r2244 | |||
def check(self, line_info): | ||||
"""Check to see if user is assigning to a var for the first time, in | ||||
which case we want to avoid any sort of automagic / autocall games. | ||||
Bernardo B. Marques
|
r4872 | |||
Brian Granger
|
r2244 | This allows users to assign to either alias or magic names true python | ||
variables (the magic/alias systems always take second seat to true | ||||
python code). E.g. ls='hi', or ls,that=1,2""" | ||||
Brian Granger
|
r2256 | if line_info.the_rest: | ||
if line_info.the_rest[0] in '=,': | ||||
return self.prefilter_manager.get_handler_by_name('normal') | ||||
Brian Granger
|
r2244 | else: | ||
return None | ||||
dan.milstein
|
r657 | |||
Brian Granger
|
r2244 | class AutoMagicChecker(PrefilterChecker): | ||
dan.milstein
|
r657 | |||
Matthias Bussonnier
|
r22330 | priority = Integer(700).tag(config=True) | ||
dan.milstein
|
r657 | |||
Brian Granger
|
r2244 | def check(self, line_info): | ||
"""If the ifun is magic, and automagic is on, run it. Note: normal, | ||||
non-auto magic would already have been triggered via '%' in | ||||
check_esc_chars. This just checks for automagic. Also, before | ||||
triggering the magic handler, make sure that there is nothing in the | ||||
user namespace which could shadow it.""" | ||||
Fernando Perez
|
r6908 | if not self.shell.automagic or not self.shell.find_magic(line_info.ifun): | ||
Brian Granger
|
r2244 | return None | ||
# We have a likely magic method. Make sure we should actually call it. | ||||
Paul Ivanov
|
r2619 | if line_info.continue_prompt and not self.prefilter_manager.multi_line_specials: | ||
Brian Granger
|
r2244 | return None | ||
head = line_info.ifun.split('.',1)[0] | ||||
if is_shadowed(head, self.shell): | ||||
return None | ||||
return self.prefilter_manager.get_handler_by_name('magic') | ||||
class PythonOpsChecker(PrefilterChecker): | ||||
Matthias Bussonnier
|
r22330 | priority = Integer(900).tag(config=True) | ||
Brian Granger
|
r2244 | |||
def check(self, line_info): | ||||
"""If the 'rest' of the line begins with a function call or pretty much | ||||
any python operator, we should simply execute the line (regardless of | ||||
whether or not there's a possible autocall expansion). This avoids | ||||
spurious (and very confusing) geattr() accesses.""" | ||||
if line_info.the_rest and line_info.the_rest[0] in '!=()<>,+*/%^&|': | ||||
return self.prefilter_manager.get_handler_by_name('normal') | ||||
else: | ||||
return None | ||||
class AutocallChecker(PrefilterChecker): | ||||
Matthias Bussonnier
|
r22330 | priority = Integer(1000).tag(config=True) | ||
Brian Granger
|
r2244 | |||
Matthias Bussonnier
|
r22338 | function_name_regexp = CRegExp(re_fun_name, | ||
help="RegExp to identify potential function names." | ||||
).tag(config=True) | ||||
exclude_regexp = CRegExp(re_exclude_auto, | ||||
help="RegExp to exclude strings with this start from autocalling." | ||||
).tag(config=True) | ||||
Bradley M. Froehle
|
r6733 | |||
Brian Granger
|
r2244 | def check(self, line_info): | ||
"Check if the initial word/function is callable and autocall is on." | ||||
if not self.shell.autocall: | ||||
return None | ||||
oinfo = line_info.ofind(self.shell) # This can mutate state via getattr | ||||
Matthias Bussonnier
|
r28165 | if not oinfo.found: | ||
Brian Granger
|
r2244 | return None | ||
Bernardo B. Marques
|
r4872 | |||
chillaranand
|
r23493 | ignored_funs = ['b', 'f', 'r', 'u', 'br', 'rb', 'fr', 'rf'] | ||
ifun = line_info.ifun | ||||
line = line_info.line | ||||
if ifun.lower() in ignored_funs and (line.startswith(ifun + "'") or line.startswith(ifun + '"')): | ||||
return None | ||||
Matthias Bussonnier
|
r28165 | if ( | ||
callable(oinfo.obj) | ||||
and (not self.exclude_regexp.match(line_info.the_rest)) | ||||
and self.function_name_regexp.match(line_info.ifun) | ||||
): | ||||
return self.prefilter_manager.get_handler_by_name("auto") | ||||
Brian Granger
|
r2244 | else: | ||
return None | ||||
#----------------------------------------------------------------------------- | ||||
# Prefilter handlers | ||||
#----------------------------------------------------------------------------- | ||||
Brian Granger
|
r2731 | class PrefilterHandler(Configurable): | ||
Brian Granger
|
r2244 | |||
Thomas Kluyver
|
r4046 | handler_name = Unicode('normal') | ||
Brian Granger
|
r2244 | esc_strings = List([]) | ||
Sylvain Corlay
|
r20940 | shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', allow_none=True) | ||
prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager', allow_none=True) | ||||
Brian Granger
|
r2244 | |||
MinRK
|
r11064 | def __init__(self, shell=None, prefilter_manager=None, **kwargs): | ||
Brian Granger
|
r2740 | super(PrefilterHandler, self).__init__( | ||
MinRK
|
r11064 | shell=shell, prefilter_manager=prefilter_manager, **kwargs | ||
Brian Granger
|
r2740 | ) | ||
Brian Granger
|
r2244 | self.prefilter_manager.register_handler( | ||
self.handler_name, | ||||
self, | ||||
self.esc_strings | ||||
) | ||||
def handle(self, line_info): | ||||
Brian Granger
|
r2256 | # print "normal: ", line_info | ||
Brian Granger
|
r2244 | """Handle normal input lines. Use as a template for handlers.""" | ||
# With autoindent on, we need some way to exit the input loop, and I | ||||
# don't want to force the user to have to backspace all the way to | ||||
# clear the line. The rule will be in this case, that either two | ||||
# lines of pure whitespace in a row, or a line of pure whitespace but | ||||
# of a size different to the indent level, will exit the input loop. | ||||
line = line_info.line | ||||
continue_prompt = line_info.continue_prompt | ||||
Fernando Perez
|
r2419 | if (continue_prompt and | ||
self.shell.autoindent and | ||||
line.isspace() and | ||||
Thomas Kluyver
|
r3756 | 0 < abs(len(line) - self.shell.indent_current_nsp) <= 2): | ||
Brian Granger
|
r2244 | line = '' | ||
return line | ||||
Brian Granger
|
r2256 | def __str__(self): | ||
return "<%s(name=%s)>" % (self.__class__.__name__, self.handler_name) | ||||
Brian Granger
|
r2244 | |||
Thomas Kluyver
|
r3406 | class MacroHandler(PrefilterHandler): | ||
Thomas Kluyver
|
r4046 | handler_name = Unicode("macro") | ||
Bernardo B. Marques
|
r4872 | |||
Thomas Kluyver
|
r3406 | def handle(self, line_info): | ||
obj = self.shell.user_ns.get(line_info.ifun) | ||||
pre_space = line_info.pre_whitespace | ||||
line_sep = "\n" + pre_space | ||||
return pre_space + line_sep.join(obj.value.splitlines()) | ||||
Brian Granger
|
r2244 | class MagicHandler(PrefilterHandler): | ||
Thomas Kluyver
|
r4046 | handler_name = Unicode('magic') | ||
Brian Granger
|
r2245 | esc_strings = List([ESC_MAGIC]) | ||
Brian Granger
|
r2244 | |||
def handle(self, line_info): | ||||
"""Execute magic functions.""" | ||||
ifun = line_info.ifun | ||||
the_rest = line_info.the_rest | ||||
adityausathe
|
r23750 | #Prepare arguments for get_ipython().run_line_magic(magic_name, magic_args) | ||
t_arg_s = ifun + " " + the_rest | ||||
t_magic_name, _, t_magic_arg_s = t_arg_s.partition(' ') | ||||
t_magic_name = t_magic_name.lstrip(ESC_MAGIC) | ||||
cmd = '%sget_ipython().run_line_magic(%r, %r)' % (line_info.pre_whitespace, t_magic_name, t_magic_arg_s) | ||||
Brian Granger
|
r2244 | return cmd | ||
class AutoHandler(PrefilterHandler): | ||||
Thomas Kluyver
|
r4046 | handler_name = Unicode('auto') | ||
Brian Granger
|
r2244 | esc_strings = List([ESC_PAREN, ESC_QUOTE, ESC_QUOTE2]) | ||
def handle(self, line_info): | ||||
Brian Granger
|
r2731 | """Handle lines which can be auto-executed, quoting if requested.""" | ||
Brian Granger
|
r2244 | line = line_info.line | ||
ifun = line_info.ifun | ||||
the_rest = line_info.the_rest | ||||
Thomas Kluyver
|
r4744 | esc = line_info.esc | ||
Brian Granger
|
r2244 | continue_prompt = line_info.continue_prompt | ||
Matthias Bussonnier
|
r28165 | obj = line_info.ofind(self.shell).obj | ||
Brian Granger
|
r2244 | |||
# This should only be active for single-line input! | ||||
if continue_prompt: | ||||
return line | ||||
force_auto = isinstance(obj, IPyAutocall) | ||||
Fernando Perez
|
r5298 | |||
# User objects sometimes raise exceptions on attribute access other | ||||
# than AttributeError (we've seen it in the past), so it's safest to be | ||||
# ultra-conservative here and catch all. | ||||
try: | ||||
auto_rewrite = obj.rewrite | ||||
except Exception: | ||||
auto_rewrite = True | ||||
Bernardo B. Marques
|
r4872 | |||
Thomas Kluyver
|
r4744 | if esc == ESC_QUOTE: | ||
Brian Granger
|
r2244 | # Auto-quote splitting on whitespace | ||
newcmd = '%s("%s")' % (ifun,'", "'.join(the_rest.split()) ) | ||||
Thomas Kluyver
|
r4744 | elif esc == ESC_QUOTE2: | ||
Brian Granger
|
r2244 | # Auto-quote whole string | ||
newcmd = '%s("%s")' % (ifun,the_rest) | ||||
Thomas Kluyver
|
r4744 | elif esc == ESC_PAREN: | ||
Brian Granger
|
r2244 | newcmd = '%s(%s)' % (ifun,",".join(the_rest.split())) | ||
else: | ||||
chillaranand
|
r23493 | # Auto-paren. | ||
Bradley Froehle
|
r5654 | if force_auto: | ||
# Don't rewrite if it is already a call. | ||||
do_rewrite = not the_rest.startswith('(') | ||||
Brian Granger
|
r2244 | else: | ||
Bradley Froehle
|
r5654 | if not the_rest: | ||
# We only apply it to argument-less calls if the autocall | ||||
# parameter is set to 2. | ||||
do_rewrite = (self.shell.autocall >= 2) | ||||
elif the_rest.startswith('[') and hasattr(obj, '__getitem__'): | ||||
# Don't autocall in this case: item access for an object | ||||
# which is BOTH callable and implements __getitem__. | ||||
do_rewrite = False | ||||
Brian Granger
|
r2244 | else: | ||
Bradley Froehle
|
r5654 | do_rewrite = True | ||
Brian Granger
|
r2244 | |||
Bradley Froehle
|
r5654 | # Figure out the rewritten command | ||
if do_rewrite: | ||||
if the_rest.endswith(';'): | ||||
newcmd = '%s(%s);' % (ifun.rstrip(),the_rest[:-1]) | ||||
else: | ||||
chillaranand
|
r23493 | newcmd = '%s(%s)' % (ifun.rstrip(), the_rest) | ||
Bradley Froehle
|
r5654 | else: | ||
normal_handler = self.prefilter_manager.get_handler_by_name('normal') | ||||
return normal_handler.handle(line_info) | ||||
chillaranand
|
r23493 | |||
Bradley Froehle
|
r5654 | # Display the rewritten call | ||
Brian Granger
|
r2244 | if auto_rewrite: | ||
Fernando Perez
|
r2951 | self.shell.auto_rewrite_input(newcmd) | ||
Bernardo B. Marques
|
r4872 | |||
Brian Granger
|
r2244 | return newcmd | ||
class EmacsHandler(PrefilterHandler): | ||||
Thomas Kluyver
|
r4046 | handler_name = Unicode('emacs') | ||
Brian Granger
|
r2244 | esc_strings = List([]) | ||
def handle(self, line_info): | ||||
"""Handle input lines marked by python-mode.""" | ||||
# Currently, nothing is done. Later more functionality can be added | ||||
# here if needed. | ||||
# The input cache shouldn't be updated | ||||
return line_info.line | ||||
#----------------------------------------------------------------------------- | ||||
# Defaults | ||||
#----------------------------------------------------------------------------- | ||||
_default_checkers = [ | ||||
EmacsChecker, | ||||
Thomas Kluyver
|
r3406 | MacroChecker, | ||
Brian Granger
|
r2244 | IPyAutocallChecker, | ||
AssignmentChecker, | ||||
AutoMagicChecker, | ||||
PythonOpsChecker, | ||||
AutocallChecker | ||||
] | ||||
_default_handlers = [ | ||||
PrefilterHandler, | ||||
Thomas Kluyver
|
r3406 | MacroHandler, | ||
Brian Granger
|
r2244 | MagicHandler, | ||
AutoHandler, | ||||
EmacsHandler | ||||
] | ||||