|
@@
-1,994
+1,994
b''
|
|
1
|
#!/usr/bin/env python
|
|
1
|
#!/usr/bin/env python
|
|
2
|
# encoding: utf-8
|
|
2
|
# encoding: utf-8
|
|
3
|
"""
|
|
3
|
"""
|
|
4
|
Prefiltering components.
|
|
4
|
Prefiltering components.
|
|
5
|
|
|
5
|
|
|
6
|
Prefilters transform user input before it is exec'd by Python. These
|
|
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.
|
|
7
|
transforms are used to implement additional syntax such as !ls and %magic.
|
|
8
|
|
|
8
|
|
|
9
|
Authors:
|
|
9
|
Authors:
|
|
10
|
|
|
10
|
|
|
11
|
* Brian Granger
|
|
11
|
* Brian Granger
|
|
12
|
* Fernando Perez
|
|
12
|
* Fernando Perez
|
|
13
|
* Dan Milstein
|
|
13
|
* Dan Milstein
|
|
14
|
* Ville Vainio
|
|
14
|
* Ville Vainio
|
|
15
|
"""
|
|
15
|
"""
|
|
16
|
|
|
16
|
|
|
17
|
#-----------------------------------------------------------------------------
|
|
17
|
#-----------------------------------------------------------------------------
|
|
18
|
# Copyright (C) 2008-2009 The IPython Development Team
|
|
18
|
# Copyright (C) 2008-2009 The IPython Development Team
|
|
19
|
#
|
|
19
|
#
|
|
20
|
# Distributed under the terms of the BSD License. The full license is in
|
|
20
|
# Distributed under the terms of the BSD License. The full license is in
|
|
21
|
# the file COPYING, distributed as part of this software.
|
|
21
|
# the file COPYING, distributed as part of this software.
|
|
22
|
#-----------------------------------------------------------------------------
|
|
22
|
#-----------------------------------------------------------------------------
|
|
23
|
|
|
23
|
|
|
24
|
#-----------------------------------------------------------------------------
|
|
24
|
#-----------------------------------------------------------------------------
|
|
25
|
# Imports
|
|
25
|
# Imports
|
|
26
|
#-----------------------------------------------------------------------------
|
|
26
|
#-----------------------------------------------------------------------------
|
|
27
|
|
|
27
|
|
|
28
|
import __builtin__
|
|
28
|
import __builtin__
|
|
29
|
import codeop
|
|
29
|
import codeop
|
|
30
|
import keyword
|
|
30
|
import keyword
|
|
31
|
import os
|
|
31
|
import os
|
|
32
|
import re
|
|
32
|
import re
|
|
33
|
import sys
|
|
33
|
import sys
|
|
34
|
|
|
34
|
|
|
35
|
from IPython.core.alias import AliasManager
|
|
35
|
from IPython.core.alias import AliasManager
|
|
36
|
from IPython.core.autocall import IPyAutocall
|
|
36
|
from IPython.core.autocall import IPyAutocall
|
|
37
|
from IPython.core.component import Component
|
|
37
|
from IPython.core.component import Component
|
|
38
|
from IPython.core.splitinput import split_user_input
|
|
38
|
from IPython.core.splitinput import split_user_input
|
|
39
|
from IPython.core.page import page
|
|
39
|
from IPython.core.page import page
|
|
40
|
|
|
40
|
|
|
41
|
from IPython.utils.traitlets import List, Int, Any, Str, CBool, Bool
|
|
41
|
from IPython.utils.traitlets import List, Int, Any, Str, CBool, Bool
|
|
42
|
from IPython.utils.genutils import make_quoted_expr, Term
|
|
42
|
from IPython.utils.genutils import make_quoted_expr, Term
|
|
43
|
from IPython.utils.autoattr import auto_attr
|
|
43
|
from IPython.utils.autoattr import auto_attr
|
|
44
|
|
|
44
|
|
|
45
|
#-----------------------------------------------------------------------------
|
|
45
|
#-----------------------------------------------------------------------------
|
|
46
|
# Global utilities, errors and constants
|
|
46
|
# Global utilities, errors and constants
|
|
47
|
#-----------------------------------------------------------------------------
|
|
47
|
#-----------------------------------------------------------------------------
|
|
48
|
|
|
48
|
|
|
49
|
# Warning, these cannot be changed unless various regular expressions
|
|
49
|
# Warning, these cannot be changed unless various regular expressions
|
|
50
|
# are updated in a number of places. Not great, but at least we told you.
|
|
50
|
# are updated in a number of places. Not great, but at least we told you.
|
|
51
|
ESC_SHELL = '!'
|
|
51
|
ESC_SHELL = '!'
|
|
52
|
ESC_SH_CAP = '!!'
|
|
52
|
ESC_SH_CAP = '!!'
|
|
53
|
ESC_HELP = '?'
|
|
53
|
ESC_HELP = '?'
|
|
54
|
ESC_MAGIC = '%'
|
|
54
|
ESC_MAGIC = '%'
|
|
55
|
ESC_QUOTE = ','
|
|
55
|
ESC_QUOTE = ','
|
|
56
|
ESC_QUOTE2 = ';'
|
|
56
|
ESC_QUOTE2 = ';'
|
|
57
|
ESC_PAREN = '/'
|
|
57
|
ESC_PAREN = '/'
|
|
58
|
|
|
58
|
|
|
59
|
|
|
59
|
|
|
60
|
class PrefilterError(Exception):
|
|
60
|
class PrefilterError(Exception):
|
|
61
|
pass
|
|
61
|
pass
|
|
62
|
|
|
62
|
|
|
63
|
|
|
63
|
|
|
64
|
# RegExp to identify potential function names
|
|
64
|
# RegExp to identify potential function names
|
|
65
|
re_fun_name = re.compile(r'[a-zA-Z_]([a-zA-Z0-9_.]*) *$')
|
|
65
|
re_fun_name = re.compile(r'[a-zA-Z_]([a-zA-Z0-9_.]*) *$')
|
|
66
|
|
|
66
|
|
|
67
|
# RegExp to exclude strings with this start from autocalling. In
|
|
67
|
# RegExp to exclude strings with this start from autocalling. In
|
|
68
|
# particular, all binary operators should be excluded, so that if foo is
|
|
68
|
# particular, all binary operators should be excluded, so that if foo is
|
|
69
|
# callable, foo OP bar doesn't become foo(OP bar), which is invalid. The
|
|
69
|
# callable, foo OP bar doesn't become foo(OP bar), which is invalid. The
|
|
70
|
# characters '!=()' don't need to be checked for, as the checkPythonChars
|
|
70
|
# characters '!=()' don't need to be checked for, as the checkPythonChars
|
|
71
|
# routine explicitely does so, to catch direct calls and rebindings of
|
|
71
|
# routine explicitely does so, to catch direct calls and rebindings of
|
|
72
|
# existing names.
|
|
72
|
# existing names.
|
|
73
|
|
|
73
|
|
|
74
|
# Warning: the '-' HAS TO BE AT THE END of the first group, otherwise
|
|
74
|
# Warning: the '-' HAS TO BE AT THE END of the first group, otherwise
|
|
75
|
# it affects the rest of the group in square brackets.
|
|
75
|
# it affects the rest of the group in square brackets.
|
|
76
|
re_exclude_auto = re.compile(r'^[,&^\|\*/\+-]'
|
|
76
|
re_exclude_auto = re.compile(r'^[,&^\|\*/\+-]'
|
|
77
|
r'|^is |^not |^in |^and |^or ')
|
|
77
|
r'|^is |^not |^in |^and |^or ')
|
|
78
|
|
|
78
|
|
|
79
|
# try to catch also methods for stuff in lists/tuples/dicts: off
|
|
79
|
# try to catch also methods for stuff in lists/tuples/dicts: off
|
|
80
|
# (experimental). For this to work, the line_split regexp would need
|
|
80
|
# (experimental). For this to work, the line_split regexp would need
|
|
81
|
# to be modified so it wouldn't break things at '['. That line is
|
|
81
|
# to be modified so it wouldn't break things at '['. That line is
|
|
82
|
# nasty enough that I shouldn't change it until I can test it _well_.
|
|
82
|
# nasty enough that I shouldn't change it until I can test it _well_.
|
|
83
|
#self.re_fun_name = re.compile (r'[a-zA-Z_]([a-zA-Z0-9_.\[\]]*) ?$')
|
|
83
|
#self.re_fun_name = re.compile (r'[a-zA-Z_]([a-zA-Z0-9_.\[\]]*) ?$')
|
|
84
|
|
|
84
|
|
|
85
|
|
|
85
|
|
|
86
|
# Handler Check Utilities
|
|
86
|
# Handler Check Utilities
|
|
87
|
def is_shadowed(identifier, ip):
|
|
87
|
def is_shadowed(identifier, ip):
|
|
88
|
"""Is the given identifier defined in one of the namespaces which shadow
|
|
88
|
"""Is the given identifier defined in one of the namespaces which shadow
|
|
89
|
the alias and magic namespaces? Note that an identifier is different
|
|
89
|
the alias and magic namespaces? Note that an identifier is different
|
|
90
|
than ifun, because it can not contain a '.' character."""
|
|
90
|
than ifun, because it can not contain a '.' character."""
|
|
91
|
# This is much safer than calling ofind, which can change state
|
|
91
|
# This is much safer than calling ofind, which can change state
|
|
92
|
return (identifier in ip.user_ns \
|
|
92
|
return (identifier in ip.user_ns \
|
|
93
|
or identifier in ip.internal_ns \
|
|
93
|
or identifier in ip.internal_ns \
|
|
94
|
or identifier in ip.ns_table['builtin'])
|
|
94
|
or identifier in ip.ns_table['builtin'])
|
|
95
|
|
|
95
|
|
|
96
|
|
|
96
|
|
|
97
|
#-----------------------------------------------------------------------------
|
|
97
|
#-----------------------------------------------------------------------------
|
|
98
|
# The LineInfo class used throughout
|
|
98
|
# The LineInfo class used throughout
|
|
99
|
#-----------------------------------------------------------------------------
|
|
99
|
#-----------------------------------------------------------------------------
|
|
100
|
|
|
100
|
|
|
101
|
|
|
101
|
|
|
102
|
class LineInfo(object):
|
|
102
|
class LineInfo(object):
|
|
103
|
"""A single line of input and associated info.
|
|
103
|
"""A single line of input and associated info.
|
|
104
|
|
|
104
|
|
|
105
|
Includes the following as properties:
|
|
105
|
Includes the following as properties:
|
|
106
|
|
|
106
|
|
|
107
|
line
|
|
107
|
line
|
|
108
|
The original, raw line
|
|
108
|
The original, raw line
|
|
109
|
|
|
109
|
|
|
110
|
continue_prompt
|
|
110
|
continue_prompt
|
|
111
|
Is this line a continuation in a sequence of multiline input?
|
|
111
|
Is this line a continuation in a sequence of multiline input?
|
|
112
|
|
|
112
|
|
|
113
|
pre
|
|
113
|
pre
|
|
114
|
The initial esc character or whitespace.
|
|
114
|
The initial esc character or whitespace.
|
|
115
|
|
|
115
|
|
|
116
|
pre_char
|
|
116
|
pre_char
|
|
117
|
The escape character(s) in pre or the empty string if there isn't one.
|
|
117
|
The escape character(s) in pre or the empty string if there isn't one.
|
|
118
|
Note that '!!' is a possible value for pre_char. Otherwise it will
|
|
118
|
Note that '!!' is a possible value for pre_char. Otherwise it will
|
|
119
|
always be a single character.
|
|
119
|
always be a single character.
|
|
120
|
|
|
120
|
|
|
121
|
pre_whitespace
|
|
121
|
pre_whitespace
|
|
122
|
The leading whitespace from pre if it exists. If there is a pre_char,
|
|
122
|
The leading whitespace from pre if it exists. If there is a pre_char,
|
|
123
|
this is just ''.
|
|
123
|
this is just ''.
|
|
124
|
|
|
124
|
|
|
125
|
ifun
|
|
125
|
ifun
|
|
126
|
The 'function part', which is basically the maximal initial sequence
|
|
126
|
The 'function part', which is basically the maximal initial sequence
|
|
127
|
of valid python identifiers and the '.' character. This is what is
|
|
127
|
of valid python identifiers and the '.' character. This is what is
|
|
128
|
checked for alias and magic transformations, used for auto-calling,
|
|
128
|
checked for alias and magic transformations, used for auto-calling,
|
|
129
|
etc.
|
|
129
|
etc.
|
|
130
|
|
|
130
|
|
|
131
|
the_rest
|
|
131
|
the_rest
|
|
132
|
Everything else on the line.
|
|
132
|
Everything else on the line.
|
|
133
|
"""
|
|
133
|
"""
|
|
134
|
def __init__(self, line, continue_prompt):
|
|
134
|
def __init__(self, line, continue_prompt):
|
|
135
|
self.line = line
|
|
135
|
self.line = line
|
|
136
|
self.continue_prompt = continue_prompt
|
|
136
|
self.continue_prompt = continue_prompt
|
|
137
|
self.pre, self.ifun, self.the_rest = split_user_input(line)
|
|
137
|
self.pre, self.ifun, self.the_rest = split_user_input(line)
|
|
138
|
|
|
138
|
|
|
139
|
self.pre_char = self.pre.strip()
|
|
139
|
self.pre_char = self.pre.strip()
|
|
140
|
if self.pre_char:
|
|
140
|
if self.pre_char:
|
|
141
|
self.pre_whitespace = '' # No whitespace allowd before esc chars
|
|
141
|
self.pre_whitespace = '' # No whitespace allowd before esc chars
|
|
142
|
else:
|
|
142
|
else:
|
|
143
|
self.pre_whitespace = self.pre
|
|
143
|
self.pre_whitespace = self.pre
|
|
144
|
|
|
144
|
|
|
145
|
self._oinfo = None
|
|
145
|
self._oinfo = None
|
|
146
|
|
|
146
|
|
|
147
|
def ofind(self, ip):
|
|
147
|
def ofind(self, ip):
|
|
148
|
"""Do a full, attribute-walking lookup of the ifun in the various
|
|
148
|
"""Do a full, attribute-walking lookup of the ifun in the various
|
|
149
|
namespaces for the given IPython InteractiveShell instance.
|
|
149
|
namespaces for the given IPython InteractiveShell instance.
|
|
150
|
|
|
150
|
|
|
151
|
Return a dict with keys: found,obj,ospace,ismagic
|
|
151
|
Return a dict with keys: found,obj,ospace,ismagic
|
|
152
|
|
|
152
|
|
|
153
|
Note: can cause state changes because of calling getattr, but should
|
|
153
|
Note: can cause state changes because of calling getattr, but should
|
|
154
|
only be run if autocall is on and if the line hasn't matched any
|
|
154
|
only be run if autocall is on and if the line hasn't matched any
|
|
155
|
other, less dangerous handlers.
|
|
155
|
other, less dangerous handlers.
|
|
156
|
|
|
156
|
|
|
157
|
Does cache the results of the call, so can be called multiple times
|
|
157
|
Does cache the results of the call, so can be called multiple times
|
|
158
|
without worrying about *further* damaging state.
|
|
158
|
without worrying about *further* damaging state.
|
|
159
|
"""
|
|
159
|
"""
|
|
160
|
if not self._oinfo:
|
|
160
|
if not self._oinfo:
|
|
161
|
self._oinfo = ip.shell._ofind(self.ifun)
|
|
161
|
self._oinfo = ip.shell._ofind(self.ifun)
|
|
162
|
return self._oinfo
|
|
162
|
return self._oinfo
|
|
163
|
|
|
163
|
|
|
164
|
def __str__(self):
|
|
164
|
def __str__(self):
|
|
165
|
return "Lineinfo [%s|%s|%s]" %(self.pre,self.ifun,self.the_rest)
|
|
165
|
return "Lineinfo [%s|%s|%s]" %(self.pre,self.ifun,self.the_rest)
|
|
166
|
|
|
166
|
|
|
167
|
|
|
167
|
|
|
168
|
#-----------------------------------------------------------------------------
|
|
168
|
#-----------------------------------------------------------------------------
|
|
169
|
# Main Prefilter manager
|
|
169
|
# Main Prefilter manager
|
|
170
|
#-----------------------------------------------------------------------------
|
|
170
|
#-----------------------------------------------------------------------------
|
|
171
|
|
|
171
|
|
|
172
|
|
|
172
|
|
|
173
|
class PrefilterManager(Component):
|
|
173
|
class PrefilterManager(Component):
|
|
174
|
"""Main prefilter component.
|
|
174
|
"""Main prefilter component.
|
|
175
|
|
|
175
|
|
|
176
|
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
|
|
177
|
prefilter consumes lines of input and produces transformed lines of
|
|
177
|
prefilter consumes lines of input and produces transformed lines of
|
|
178
|
input.
|
|
178
|
input.
|
|
179
|
|
|
179
|
|
|
180
|
The iplementation consists of two phases:
|
|
180
|
The iplementation consists of two phases:
|
|
181
|
|
|
181
|
|
|
182
|
1. Transformers
|
|
182
|
1. Transformers
|
|
183
|
2. Checkers and handlers
|
|
183
|
2. Checkers and handlers
|
|
184
|
|
|
184
|
|
|
185
|
Over time, we plan on deprecating the checkers and handlers and doing
|
|
185
|
Over time, we plan on deprecating the checkers and handlers and doing
|
|
186
|
everything in the transformers.
|
|
186
|
everything in the transformers.
|
|
187
|
|
|
187
|
|
|
188
|
The transformers are instances of :class:`PrefilterTransformer` and have
|
|
188
|
The transformers are instances of :class:`PrefilterTransformer` and have
|
|
189
|
a single method :meth:`transform` that takes a line and returns a
|
|
189
|
a single method :meth:`transform` that takes a line and returns a
|
|
190
|
transformed line. The transformation can be accomplished using any
|
|
190
|
transformed line. The transformation can be accomplished using any
|
|
191
|
tool, but our current ones use regular expressions for speed. We also
|
|
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.
|
|
192
|
ship :mod:`pyparsing` in :mod:`IPython.external` for use in transformers.
|
|
193
|
|
|
193
|
|
|
194
|
After all the transformers have been run, the line is fed to the checkers,
|
|
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
|
|
195
|
which are instances of :class:`PrefilterChecker`. The line is passed to
|
|
196
|
the :meth:`check` method, which either returns `None` or a
|
|
196
|
the :meth:`check` method, which either returns `None` or a
|
|
197
|
:class:`PrefilterHandler` instance. If `None` is returned, the other
|
|
197
|
:class:`PrefilterHandler` instance. If `None` is returned, the other
|
|
198
|
checkers are tried. If an :class:`PrefilterHandler` instance is returned,
|
|
198
|
checkers are tried. If an :class:`PrefilterHandler` instance is returned,
|
|
199
|
the line is passed to the :meth:`handle` method of the returned
|
|
199
|
the line is passed to the :meth:`handle` method of the returned
|
|
200
|
handler and no further checkers are tried.
|
|
200
|
handler and no further checkers are tried.
|
|
201
|
|
|
201
|
|
|
202
|
Both transformers and checkers have a `priority` attribute, that determines
|
|
202
|
Both transformers and checkers have a `priority` attribute, that determines
|
|
203
|
the order in which they are called. Smaller priorities are tried first.
|
|
203
|
the order in which they are called. Smaller priorities are tried first.
|
|
204
|
|
|
204
|
|
|
205
|
Both transformers and checkers also have `enabled` attribute, which is
|
|
205
|
Both transformers and checkers also have `enabled` attribute, which is
|
|
206
|
a boolean that determines if the instance is used.
|
|
206
|
a boolean that determines if the instance is used.
|
|
207
|
|
|
207
|
|
|
208
|
Users or developers can change the priority or enabled attribute of
|
|
208
|
Users or developers can change the priority or enabled attribute of
|
|
209
|
transformers or checkers, but they must call the :meth:`sort_checkers`
|
|
209
|
transformers or checkers, but they must call the :meth:`sort_checkers`
|
|
210
|
or :meth:`sort_transformers` method after changing the priority.
|
|
210
|
or :meth:`sort_transformers` method after changing the priority.
|
|
211
|
"""
|
|
211
|
"""
|
|
212
|
|
|
212
|
|
|
213
|
multi_line_specials = CBool(True, config=True)
|
|
213
|
multi_line_specials = CBool(True, config=True)
|
|
214
|
|
|
214
|
|
|
215
|
def __init__(self, parent, config=None):
|
|
215
|
def __init__(self, parent, config=None):
|
|
216
|
super(PrefilterManager, self).__init__(parent, config=config)
|
|
216
|
super(PrefilterManager, self).__init__(parent, config=config)
|
|
217
|
self.init_transformers()
|
|
217
|
self.init_transformers()
|
|
218
|
self.init_handlers()
|
|
218
|
self.init_handlers()
|
|
219
|
self.init_checkers()
|
|
219
|
self.init_checkers()
|
|
220
|
|
|
220
|
|
|
221
|
@auto_attr
|
|
221
|
@auto_attr
|
|
222
|
def shell(self):
|
|
222
|
def shell(self):
|
|
223
|
return Component.get_instances(
|
|
223
|
return Component.get_instances(
|
|
224
|
root=self.root,
|
|
224
|
root=self.root,
|
|
225
|
klass='IPython.core.iplib.InteractiveShell')[0]
|
|
225
|
klass='IPython.core.iplib.InteractiveShell')[0]
|
|
226
|
|
|
226
|
|
|
227
|
#-------------------------------------------------------------------------
|
|
227
|
#-------------------------------------------------------------------------
|
|
228
|
# API for managing transformers
|
|
228
|
# API for managing transformers
|
|
229
|
#-------------------------------------------------------------------------
|
|
229
|
#-------------------------------------------------------------------------
|
|
230
|
|
|
230
|
|
|
231
|
def init_transformers(self):
|
|
231
|
def init_transformers(self):
|
|
232
|
"""Create the default transformers."""
|
|
232
|
"""Create the default transformers."""
|
|
233
|
self._transformers = []
|
|
233
|
self._transformers = []
|
|
234
|
for transformer_cls in _default_transformers:
|
|
234
|
for transformer_cls in _default_transformers:
|
|
235
|
transformer_cls(self, config=self.config)
|
|
235
|
transformer_cls(self, config=self.config)
|
|
236
|
|
|
236
|
|
|
237
|
def sort_transformers(self):
|
|
237
|
def sort_transformers(self):
|
|
238
|
"""Sort the transformers by priority.
|
|
238
|
"""Sort the transformers by priority.
|
|
239
|
|
|
239
|
|
|
240
|
This must be called after the priority of a transformer is changed.
|
|
240
|
This must be called after the priority of a transformer is changed.
|
|
241
|
The :meth:`register_transformer` method calls this automatically.
|
|
241
|
The :meth:`register_transformer` method calls this automatically.
|
|
242
|
"""
|
|
242
|
"""
|
|
243
|
self._transformers.sort(cmp=lambda x,y: x.priority-y.priority)
|
|
243
|
self._transformers.sort(cmp=lambda x,y: x.priority-y.priority)
|
|
244
|
|
|
244
|
|
|
245
|
@property
|
|
245
|
@property
|
|
246
|
def transformers(self):
|
|
246
|
def transformers(self):
|
|
247
|
"""Return a list of checkers, sorted by priority."""
|
|
247
|
"""Return a list of checkers, sorted by priority."""
|
|
248
|
return self._transformers
|
|
248
|
return self._transformers
|
|
249
|
|
|
249
|
|
|
250
|
def register_transformer(self, transformer):
|
|
250
|
def register_transformer(self, transformer):
|
|
251
|
"""Register a transformer instance."""
|
|
251
|
"""Register a transformer instance."""
|
|
252
|
if transformer not in self._transformers:
|
|
252
|
if transformer not in self._transformers:
|
|
253
|
self._transformers.append(transformer)
|
|
253
|
self._transformers.append(transformer)
|
|
254
|
self.sort_transformers()
|
|
254
|
self.sort_transformers()
|
|
255
|
|
|
255
|
|
|
256
|
def unregister_transformer(self, transformer):
|
|
256
|
def unregister_transformer(self, transformer):
|
|
257
|
"""Unregister a transformer instance."""
|
|
257
|
"""Unregister a transformer instance."""
|
|
258
|
if transformer in self._transformers:
|
|
258
|
if transformer in self._transformers:
|
|
259
|
self._transformers.remove(transformer)
|
|
259
|
self._transformers.remove(transformer)
|
|
260
|
|
|
260
|
|
|
261
|
#-------------------------------------------------------------------------
|
|
261
|
#-------------------------------------------------------------------------
|
|
262
|
# API for managing checkers
|
|
262
|
# API for managing checkers
|
|
263
|
#-------------------------------------------------------------------------
|
|
263
|
#-------------------------------------------------------------------------
|
|
264
|
|
|
264
|
|
|
265
|
def init_checkers(self):
|
|
265
|
def init_checkers(self):
|
|
266
|
"""Create the default checkers."""
|
|
266
|
"""Create the default checkers."""
|
|
267
|
self._checkers = []
|
|
267
|
self._checkers = []
|
|
268
|
for checker in _default_checkers:
|
|
268
|
for checker in _default_checkers:
|
|
269
|
checker(self, config=self.config)
|
|
269
|
checker(self, config=self.config)
|
|
270
|
|
|
270
|
|
|
271
|
def sort_checkers(self):
|
|
271
|
def sort_checkers(self):
|
|
272
|
"""Sort the checkers by priority.
|
|
272
|
"""Sort the checkers by priority.
|
|
273
|
|
|
273
|
|
|
274
|
This must be called after the priority of a checker is changed.
|
|
274
|
This must be called after the priority of a checker is changed.
|
|
275
|
The :meth:`register_checker` method calls this automatically.
|
|
275
|
The :meth:`register_checker` method calls this automatically.
|
|
276
|
"""
|
|
276
|
"""
|
|
277
|
self._checkers.sort(cmp=lambda x,y: x.priority-y.priority)
|
|
277
|
self._checkers.sort(cmp=lambda x,y: x.priority-y.priority)
|
|
278
|
|
|
278
|
|
|
279
|
@property
|
|
279
|
@property
|
|
280
|
def checkers(self):
|
|
280
|
def checkers(self):
|
|
281
|
"""Return a list of checkers, sorted by priority."""
|
|
281
|
"""Return a list of checkers, sorted by priority."""
|
|
282
|
return self._checkers
|
|
282
|
return self._checkers
|
|
283
|
|
|
283
|
|
|
284
|
def register_checker(self, checker):
|
|
284
|
def register_checker(self, checker):
|
|
285
|
"""Register a checker instance."""
|
|
285
|
"""Register a checker instance."""
|
|
286
|
if checker not in self._checkers:
|
|
286
|
if checker not in self._checkers:
|
|
287
|
self._checkers.append(checker)
|
|
287
|
self._checkers.append(checker)
|
|
288
|
self.sort_checkers()
|
|
288
|
self.sort_checkers()
|
|
289
|
|
|
289
|
|
|
290
|
def unregister_checker(self, checker):
|
|
290
|
def unregister_checker(self, checker):
|
|
291
|
"""Unregister a checker instance."""
|
|
291
|
"""Unregister a checker instance."""
|
|
292
|
if checker in self._checkers:
|
|
292
|
if checker in self._checkers:
|
|
293
|
self._checkers.remove(checker)
|
|
293
|
self._checkers.remove(checker)
|
|
294
|
|
|
294
|
|
|
295
|
#-------------------------------------------------------------------------
|
|
295
|
#-------------------------------------------------------------------------
|
|
296
|
# API for managing checkers
|
|
296
|
# API for managing checkers
|
|
297
|
#-------------------------------------------------------------------------
|
|
297
|
#-------------------------------------------------------------------------
|
|
298
|
|
|
298
|
|
|
299
|
def init_handlers(self):
|
|
299
|
def init_handlers(self):
|
|
300
|
"""Create the default handlers."""
|
|
300
|
"""Create the default handlers."""
|
|
301
|
self._handlers = {}
|
|
301
|
self._handlers = {}
|
|
302
|
self._esc_handlers = {}
|
|
302
|
self._esc_handlers = {}
|
|
303
|
for handler in _default_handlers:
|
|
303
|
for handler in _default_handlers:
|
|
304
|
handler(self, config=self.config)
|
|
304
|
handler(self, config=self.config)
|
|
305
|
|
|
305
|
|
|
306
|
@property
|
|
306
|
@property
|
|
307
|
def handlers(self):
|
|
307
|
def handlers(self):
|
|
308
|
"""Return a dict of all the handlers."""
|
|
308
|
"""Return a dict of all the handlers."""
|
|
309
|
return self._handlers
|
|
309
|
return self._handlers
|
|
310
|
|
|
310
|
|
|
311
|
def register_handler(self, name, handler, esc_strings):
|
|
311
|
def register_handler(self, name, handler, esc_strings):
|
|
312
|
"""Register a handler instance by name with esc_strings."""
|
|
312
|
"""Register a handler instance by name with esc_strings."""
|
|
313
|
self._handlers[name] = handler
|
|
313
|
self._handlers[name] = handler
|
|
314
|
for esc_str in esc_strings:
|
|
314
|
for esc_str in esc_strings:
|
|
315
|
self._esc_handlers[esc_str] = handler
|
|
315
|
self._esc_handlers[esc_str] = handler
|
|
316
|
|
|
316
|
|
|
317
|
def unregister_handler(self, name, handler, esc_strings):
|
|
317
|
def unregister_handler(self, name, handler, esc_strings):
|
|
318
|
"""Unregister a handler instance by name with esc_strings."""
|
|
318
|
"""Unregister a handler instance by name with esc_strings."""
|
|
319
|
try:
|
|
319
|
try:
|
|
320
|
del self._handlers[name]
|
|
320
|
del self._handlers[name]
|
|
321
|
except KeyError:
|
|
321
|
except KeyError:
|
|
322
|
pass
|
|
322
|
pass
|
|
323
|
for esc_str in esc_strings:
|
|
323
|
for esc_str in esc_strings:
|
|
324
|
h = self._esc_handlers.get(esc_str)
|
|
324
|
h = self._esc_handlers.get(esc_str)
|
|
325
|
if h is handler:
|
|
325
|
if h is handler:
|
|
326
|
del self._esc_handlers[esc_str]
|
|
326
|
del self._esc_handlers[esc_str]
|
|
327
|
|
|
327
|
|
|
328
|
def get_handler_by_name(self, name):
|
|
328
|
def get_handler_by_name(self, name):
|
|
329
|
"""Get a handler by its name."""
|
|
329
|
"""Get a handler by its name."""
|
|
330
|
return self._handlers.get(name)
|
|
330
|
return self._handlers.get(name)
|
|
331
|
|
|
331
|
|
|
332
|
def get_handler_by_esc(self, esc_str):
|
|
332
|
def get_handler_by_esc(self, esc_str):
|
|
333
|
"""Get a handler by its escape string."""
|
|
333
|
"""Get a handler by its escape string."""
|
|
334
|
return self._esc_handlers.get(esc_str)
|
|
334
|
return self._esc_handlers.get(esc_str)
|
|
335
|
|
|
335
|
|
|
336
|
#-------------------------------------------------------------------------
|
|
336
|
#-------------------------------------------------------------------------
|
|
337
|
# Main prefiltering API
|
|
337
|
# Main prefiltering API
|
|
338
|
#-------------------------------------------------------------------------
|
|
338
|
#-------------------------------------------------------------------------
|
|
339
|
|
|
339
|
|
|
340
|
def prefilter_line_info(self, line_info):
|
|
340
|
def prefilter_line_info(self, line_info):
|
|
341
|
"""Prefilter a line that has been converted to a LineInfo object.
|
|
341
|
"""Prefilter a line that has been converted to a LineInfo object.
|
|
342
|
|
|
342
|
|
|
343
|
This implements the checker/handler part of the prefilter pipe.
|
|
343
|
This implements the checker/handler part of the prefilter pipe.
|
|
344
|
"""
|
|
344
|
"""
|
|
345
|
# print "prefilter_line_info: ", line_info
|
|
345
|
# print "prefilter_line_info: ", line_info
|
|
346
|
handler = self.find_handler(line_info)
|
|
346
|
handler = self.find_handler(line_info)
|
|
347
|
return handler.handle(line_info)
|
|
347
|
return handler.handle(line_info)
|
|
348
|
|
|
348
|
|
|
349
|
def find_handler(self, line_info):
|
|
349
|
def find_handler(self, line_info):
|
|
350
|
"""Find a handler for the line_info by trying checkers."""
|
|
350
|
"""Find a handler for the line_info by trying checkers."""
|
|
351
|
for checker in self.checkers:
|
|
351
|
for checker in self.checkers:
|
|
352
|
if checker.enabled:
|
|
352
|
if checker.enabled:
|
|
353
|
handler = checker.check(line_info)
|
|
353
|
handler = checker.check(line_info)
|
|
354
|
if handler:
|
|
354
|
if handler:
|
|
355
|
return handler
|
|
355
|
return handler
|
|
356
|
return self.get_handler_by_name('normal')
|
|
356
|
return self.get_handler_by_name('normal')
|
|
357
|
|
|
357
|
|
|
358
|
def transform_line(self, line, continue_prompt):
|
|
358
|
def transform_line(self, line, continue_prompt):
|
|
359
|
"""Calls the enabled transformers in order of increasing priority."""
|
|
359
|
"""Calls the enabled transformers in order of increasing priority."""
|
|
360
|
for transformer in self.transformers:
|
|
360
|
for transformer in self.transformers:
|
|
361
|
if transformer.enabled:
|
|
361
|
if transformer.enabled:
|
|
362
|
line = transformer.transform(line, continue_prompt)
|
|
362
|
line = transformer.transform(line, continue_prompt)
|
|
363
|
return line
|
|
363
|
return line
|
|
364
|
|
|
364
|
|
|
365
|
def prefilter_line(self, line, continue_prompt):
|
|
365
|
def prefilter_line(self, line, continue_prompt):
|
|
366
|
"""Prefilter a single input line as text.
|
|
366
|
"""Prefilter a single input line as text.
|
|
367
|
|
|
367
|
|
|
368
|
This method prefilters a single line of text by calling the
|
|
368
|
This method prefilters a single line of text by calling the
|
|
369
|
transformers and then the checkers/handlers.
|
|
369
|
transformers and then the checkers/handlers.
|
|
370
|
"""
|
|
370
|
"""
|
|
371
|
|
|
371
|
|
|
372
|
# print "prefilter_line: ", line, continue_prompt
|
|
372
|
# print "prefilter_line: ", line, continue_prompt
|
|
373
|
# All handlers *must* return a value, even if it's blank ('').
|
|
373
|
# All handlers *must* return a value, even if it's blank ('').
|
|
374
|
|
|
374
|
|
|
375
|
# Lines are NOT logged here. Handlers should process the line as
|
|
375
|
# Lines are NOT logged here. Handlers should process the line as
|
|
376
|
# 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
|
|
377
|
# stays synced).
|
|
377
|
# stays synced).
|
|
378
|
|
|
378
|
|
|
379
|
# 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
|
|
380
|
# record it
|
|
380
|
# record it
|
|
381
|
self.shell._last_input_line = line
|
|
381
|
self.shell._last_input_line = line
|
|
382
|
|
|
382
|
|
|
383
|
if not line:
|
|
383
|
if not line:
|
|
384
|
# Return immediately on purely empty lines, so that if the user
|
|
384
|
# Return immediately on purely empty lines, so that if the user
|
|
385
|
# previously typed some whitespace that started a continuation
|
|
385
|
# previously typed some whitespace that started a continuation
|
|
386
|
# prompt, he can break out of that loop with just an empty line.
|
|
386
|
# prompt, he can break out of that loop with just an empty line.
|
|
387
|
# This is how the default python prompt works.
|
|
387
|
# This is how the default python prompt works.
|
|
388
|
|
|
388
|
|
|
389
|
# Only return if the accumulated input buffer was just whitespace!
|
|
389
|
# Only return if the accumulated input buffer was just whitespace!
|
|
390
|
if ''.join(self.shell.buffer).isspace():
|
|
390
|
if ''.join(self.shell.buffer).isspace():
|
|
391
|
self.shell.buffer[:] = []
|
|
391
|
self.shell.buffer[:] = []
|
|
392
|
return ''
|
|
392
|
return ''
|
|
393
|
|
|
393
|
|
|
394
|
# At this point, we invoke our transformers.
|
|
394
|
# At this point, we invoke our transformers.
|
|
395
|
if not continue_prompt or (continue_prompt and self.multi_line_specials):
|
|
395
|
if not continue_prompt or (continue_prompt and self.multi_line_specials):
|
|
396
|
line = self.transform_line(line, continue_prompt)
|
|
396
|
line = self.transform_line(line, continue_prompt)
|
|
397
|
|
|
397
|
|
|
398
|
# Now we compute line_info for the checkers and handlers
|
|
398
|
# Now we compute line_info for the checkers and handlers
|
|
399
|
line_info = LineInfo(line, continue_prompt)
|
|
399
|
line_info = LineInfo(line, continue_prompt)
|
|
400
|
|
|
400
|
|
|
401
|
# the input history needs to track even empty lines
|
|
401
|
# the input history needs to track even empty lines
|
|
402
|
stripped = line.strip()
|
|
402
|
stripped = line.strip()
|
|
403
|
|
|
403
|
|
|
404
|
normal_handler = self.get_handler_by_name('normal')
|
|
404
|
normal_handler = self.get_handler_by_name('normal')
|
|
405
|
if not stripped:
|
|
405
|
if not stripped:
|
|
406
|
if not continue_prompt:
|
|
406
|
if not continue_prompt:
|
|
407
|
self.shell.outputcache.prompt_count -= 1
|
|
407
|
self.shell.outputcache.prompt_count -= 1
|
|
408
|
|
|
408
|
|
|
409
|
return normal_handler.handle(line_info)
|
|
409
|
return normal_handler.handle(line_info)
|
|
410
|
|
|
410
|
|
|
411
|
# special handlers are only allowed for single line statements
|
|
411
|
# special handlers are only allowed for single line statements
|
|
412
|
if continue_prompt and not self.multi_line_specials:
|
|
412
|
if continue_prompt and not self.multi_line_specials:
|
|
413
|
return normal_handler.handle(line_info)
|
|
413
|
return normal_handler.handle(line_info)
|
|
414
|
|
|
414
|
|
|
415
|
prefiltered = self.prefilter_line_info(line_info)
|
|
415
|
prefiltered = self.prefilter_line_info(line_info)
|
|
416
|
# print "prefiltered line: %r" % prefiltered
|
|
416
|
# print "prefiltered line: %r" % prefiltered
|
|
417
|
return prefiltered
|
|
417
|
return prefiltered
|
|
418
|
|
|
418
|
|
|
419
|
def prefilter_lines(self, lines, continue_prompt):
|
|
419
|
def prefilter_lines(self, lines, continue_prompt=False):
|
|
420
|
"""Prefilter multiple input lines of text.
|
|
420
|
"""Prefilter multiple input lines of text.
|
|
421
|
|
|
421
|
|
|
422
|
This is the main entry point for prefiltering multiple lines of
|
|
422
|
This is the main entry point for prefiltering multiple lines of
|
|
423
|
input. This simply calls :meth:`prefilter_line` for each line of
|
|
423
|
input. This simply calls :meth:`prefilter_line` for each line of
|
|
424
|
input.
|
|
424
|
input.
|
|
425
|
|
|
425
|
|
|
426
|
This covers cases where there are multiple lines in the user entry,
|
|
426
|
This covers cases where there are multiple lines in the user entry,
|
|
427
|
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
|
|
428
|
entry and presses enter.
|
|
428
|
entry and presses enter.
|
|
429
|
"""
|
|
429
|
"""
|
|
430
|
out = []
|
|
430
|
out = []
|
|
431
|
for line in lines.rstrip('\n').split('\n'):
|
|
431
|
for line in lines.rstrip('\n').split('\n'):
|
|
432
|
out.append(self.prefilter_line(line, continue_prompt))
|
|
432
|
out.append(self.prefilter_line(line, continue_prompt))
|
|
433
|
return '\n'.join(out)
|
|
433
|
return '\n'.join(out)
|
|
434
|
|
|
434
|
|
|
435
|
|
|
435
|
|
|
436
|
#-----------------------------------------------------------------------------
|
|
436
|
#-----------------------------------------------------------------------------
|
|
437
|
# Prefilter transformers
|
|
437
|
# Prefilter transformers
|
|
438
|
#-----------------------------------------------------------------------------
|
|
438
|
#-----------------------------------------------------------------------------
|
|
439
|
|
|
439
|
|
|
440
|
|
|
440
|
|
|
441
|
class PrefilterTransformer(Component):
|
|
441
|
class PrefilterTransformer(Component):
|
|
442
|
"""Transform a line of user input."""
|
|
442
|
"""Transform a line of user input."""
|
|
443
|
|
|
443
|
|
|
444
|
priority = Int(100, config=True)
|
|
444
|
priority = Int(100, config=True)
|
|
445
|
shell = Any
|
|
445
|
shell = Any
|
|
446
|
prefilter_manager = Any
|
|
446
|
prefilter_manager = Any
|
|
447
|
enabled = Bool(True, config=True)
|
|
447
|
enabled = Bool(True, config=True)
|
|
448
|
|
|
448
|
|
|
449
|
def __init__(self, parent, config=None):
|
|
449
|
def __init__(self, parent, config=None):
|
|
450
|
super(PrefilterTransformer, self).__init__(parent, config=config)
|
|
450
|
super(PrefilterTransformer, self).__init__(parent, config=config)
|
|
451
|
self.prefilter_manager.register_transformer(self)
|
|
451
|
self.prefilter_manager.register_transformer(self)
|
|
452
|
|
|
452
|
|
|
453
|
@auto_attr
|
|
453
|
@auto_attr
|
|
454
|
def shell(self):
|
|
454
|
def shell(self):
|
|
455
|
return Component.get_instances(
|
|
455
|
return Component.get_instances(
|
|
456
|
root=self.root,
|
|
456
|
root=self.root,
|
|
457
|
klass='IPython.core.iplib.InteractiveShell')[0]
|
|
457
|
klass='IPython.core.iplib.InteractiveShell')[0]
|
|
458
|
|
|
458
|
|
|
459
|
@auto_attr
|
|
459
|
@auto_attr
|
|
460
|
def prefilter_manager(self):
|
|
460
|
def prefilter_manager(self):
|
|
461
|
return PrefilterManager.get_instances(root=self.root)[0]
|
|
461
|
return PrefilterManager.get_instances(root=self.root)[0]
|
|
462
|
|
|
462
|
|
|
463
|
def transform(self, line, continue_prompt):
|
|
463
|
def transform(self, line, continue_prompt):
|
|
464
|
"""Transform a line, returning the new one."""
|
|
464
|
"""Transform a line, returning the new one."""
|
|
465
|
return None
|
|
465
|
return None
|
|
466
|
|
|
466
|
|
|
467
|
def __repr__(self):
|
|
467
|
def __repr__(self):
|
|