##// END OF EJS Templates
prefilter: magics on multiline structuress now work again
vivainio -
Show More
@@ -1,313 +1,316 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 Classes and functions for prefiltering (transforming) a line of user input.
3 Classes and functions for prefiltering (transforming) a line of user input.
4 This module is responsible, primarily, for breaking the line up into useful
4 This module is responsible, primarily, for breaking the line up into useful
5 pieces and triggering the appropriate handlers in iplib to do the actual
5 pieces and triggering the appropriate handlers in iplib to do the actual
6 transforming work.
6 transforming work.
7 """
7 """
8 __docformat__ = "restructuredtext en"
8 __docformat__ = "restructuredtext en"
9
9
10 import re
10 import re
11 import IPython.ipapi
11 import IPython.ipapi
12
12
13 class LineInfo(object):
13 class LineInfo(object):
14 """A single line of input and associated info.
14 """A single line of input and associated info.
15
15
16 Includes the following as properties:
16 Includes the following as properties:
17
17
18 line
18 line
19 The original, raw line
19 The original, raw line
20
20
21 continue_prompt
21 continue_prompt
22 Is this line a continuation in a sequence of multiline input?
22 Is this line a continuation in a sequence of multiline input?
23
23
24 pre
24 pre
25 The initial esc character or whitespace.
25 The initial esc character or whitespace.
26
26
27 preChar
27 preChar
28 The escape character(s) in pre or the empty string if there isn't one.
28 The escape character(s) in pre or the empty string if there isn't one.
29 Note that '!!' is a possible value for preChar. Otherwise it will
29 Note that '!!' is a possible value for preChar. Otherwise it will
30 always be a single character.
30 always be a single character.
31
31
32 preWhitespace
32 preWhitespace
33 The leading whitespace from pre if it exists. If there is a preChar,
33 The leading whitespace from pre if it exists. If there is a preChar,
34 this is just ''.
34 this is just ''.
35
35
36 iFun
36 iFun
37 The 'function part', which is basically the maximal initial sequence
37 The 'function part', which is basically the maximal initial sequence
38 of valid python identifiers and the '.' character. This is what is
38 of valid python identifiers and the '.' character. This is what is
39 checked for alias and magic transformations, used for auto-calling,
39 checked for alias and magic transformations, used for auto-calling,
40 etc.
40 etc.
41
41
42 theRest
42 theRest
43 Everything else on the line.
43 Everything else on the line.
44 """
44 """
45 def __init__(self, line, continue_prompt):
45 def __init__(self, line, continue_prompt):
46 self.line = line
46 self.line = line
47 self.continue_prompt = continue_prompt
47 self.continue_prompt = continue_prompt
48 self.pre, self.iFun, self.theRest = splitUserInput(line)
48 self.pre, self.iFun, self.theRest = splitUserInput(line)
49
49
50 self.preChar = self.pre.strip()
50 self.preChar = self.pre.strip()
51 if self.preChar:
51 if self.preChar:
52 self.preWhitespace = '' # No whitespace allowd before esc chars
52 self.preWhitespace = '' # No whitespace allowd before esc chars
53 else:
53 else:
54 self.preWhitespace = self.pre
54 self.preWhitespace = self.pre
55
55
56 self._oinfo = None
56 self._oinfo = None
57
57
58 def ofind(self, ip):
58 def ofind(self, ip):
59 """Do a full, attribute-walking lookup of the iFun in the various
59 """Do a full, attribute-walking lookup of the iFun in the various
60 namespaces for the given IPython InteractiveShell instance.
60 namespaces for the given IPython InteractiveShell instance.
61
61
62 Return a dict with keys: found,obj,ospace,ismagic
62 Return a dict with keys: found,obj,ospace,ismagic
63
63
64 Note: can cause state changes because of calling getattr, but should
64 Note: can cause state changes because of calling getattr, but should
65 only be run if autocall is on and if the line hasn't matched any
65 only be run if autocall is on and if the line hasn't matched any
66 other, less dangerous handlers.
66 other, less dangerous handlers.
67
67
68 Does cache the results of the call, so can be called multiple times
68 Does cache the results of the call, so can be called multiple times
69 without worrying about *further* damaging state.
69 without worrying about *further* damaging state.
70 """
70 """
71 if not self._oinfo:
71 if not self._oinfo:
72 self._oinfo = ip._ofind(self.iFun)
72 self._oinfo = ip._ofind(self.iFun)
73 return self._oinfo
73 return self._oinfo
74
74 def __str__(self):
75 return "Lineinfo [%s|%s|%s]" %(self.pre,self.iFun,self.theRest)
75
76
76 def splitUserInput(line, pattern=None):
77 def splitUserInput(line, pattern=None):
77 """Split user input into pre-char/whitespace, function part and rest.
78 """Split user input into pre-char/whitespace, function part and rest.
78
79
79 Mostly internal to this module, but also used by iplib.expand_aliases,
80 Mostly internal to this module, but also used by iplib.expand_aliases,
80 which passes in a shell pattern.
81 which passes in a shell pattern.
81 """
82 """
82 # It seems to me that the shell splitting should be a separate method.
83 # It seems to me that the shell splitting should be a separate method.
83
84
84 if not pattern:
85 if not pattern:
85 pattern = line_split
86 pattern = line_split
86 match = pattern.match(line)
87 match = pattern.match(line)
87 if not match:
88 if not match:
88 #print "match failed for line '%s'" % line
89 #print "match failed for line '%s'" % line
89 try:
90 try:
90 iFun,theRest = line.split(None,1)
91 iFun,theRest = line.split(None,1)
91 except ValueError:
92 except ValueError:
92 #print "split failed for line '%s'" % line
93 #print "split failed for line '%s'" % line
93 iFun,theRest = line,''
94 iFun,theRest = line,''
94 pre = re.match('^(\s*)(.*)',line).groups()[0]
95 pre = re.match('^(\s*)(.*)',line).groups()[0]
95 else:
96 else:
96 pre,iFun,theRest = match.groups()
97 pre,iFun,theRest = match.groups()
97
98
98 # iFun has to be a valid python identifier, so it better be only pure
99 # iFun has to be a valid python identifier, so it better be only pure
99 # ascii, no unicode:
100 # ascii, no unicode:
100 try:
101 try:
101 iFun = iFun.encode('ascii')
102 iFun = iFun.encode('ascii')
102 except UnicodeEncodeError:
103 except UnicodeEncodeError:
103 theRest = iFun + u' ' + theRest
104 theRest = iFun + u' ' + theRest
104 iFun = u''
105 iFun = u''
105
106
106 #print 'line:<%s>' % line # dbg
107 #print 'line:<%s>' % line # dbg
107 #print 'pre <%s> iFun <%s> rest <%s>' % (pre,iFun.strip(),theRest) # dbg
108 #print 'pre <%s> iFun <%s> rest <%s>' % (pre,iFun.strip(),theRest) # dbg
108 return pre,iFun.strip(),theRest.lstrip()
109 return pre,iFun.strip(),theRest.lstrip()
109
110
110
111
111 # RegExp for splitting line contents into pre-char//first word-method//rest.
112 # RegExp for splitting line contents into pre-char//first word-method//rest.
112 # For clarity, each group in on one line.
113 # For clarity, each group in on one line.
113
114
114 # WARNING: update the regexp if the escapes in iplib are changed, as they
115 # WARNING: update the regexp if the escapes in iplib are changed, as they
115 # are hardwired in.
116 # are hardwired in.
116
117
117 # Although it's not solely driven by the regex, note that:
118 # Although it's not solely driven by the regex, note that:
118 # ,;/% only trigger if they are the first character on the line
119 # ,;/% only trigger if they are the first character on the line
119 # ! and !! trigger if they are first char(s) *or* follow an indent
120 # ! and !! trigger if they are first char(s) *or* follow an indent
120 # ? triggers as first or last char.
121 # ? triggers as first or last char.
121
122
122 # The three parts of the regex are:
123 # The three parts of the regex are:
123 # 1) pre: pre_char *or* initial whitespace
124 # 1) pre: pre_char *or* initial whitespace
124 # 2) iFun: first word/method (mix of \w and '.')
125 # 2) iFun: first word/method (mix of \w and '.')
125 # 3) theRest: rest of line (separated from iFun by space if non-empty)
126 # 3) theRest: rest of line (separated from iFun by space if non-empty)
126 line_split = re.compile(r'^([,;/%?]|!!?|\s*)'
127 line_split = re.compile(r'^([,;/%?]|!!?|\s*)'
127 r'\s*([\w\.]+)'
128 r'\s*([\w\.]+)'
128 r'(\s+.*$|$)')
129 r'(\s+.*$|$)')
129
130
130 shell_line_split = re.compile(r'^(\s*)(\S*\s*)(.*$)')
131 shell_line_split = re.compile(r'^(\s*)(\S*\s*)(.*$)')
131
132
132 def prefilter(line_info, ip):
133 def prefilter(line_info, ip):
133 """Call one of the passed-in InteractiveShell's handler preprocessors,
134 """Call one of the passed-in InteractiveShell's handler preprocessors,
134 depending on the form of the line. Return the results, which must be a
135 depending on the form of the line. Return the results, which must be a
135 value, even if it's a blank ('')."""
136 value, even if it's a blank ('')."""
136 # Note: the order of these checks does matter.
137 # Note: the order of these checks does matter.
137 for check in [ checkEmacs,
138 for check in [ checkEmacs,
138 checkIPyAutocall,
139 checkIPyAutocall,
139 checkMultiLineShell,
140 checkMultiLineShell,
140 checkEscChars,
141 checkEscChars,
141 checkAssignment,
142 checkAssignment,
142 checkAutomagic,
143 checkAutomagic,
143 checkAlias,
144 checkAlias,
144 checkPythonOps,
145 checkPythonOps,
145 checkAutocall,
146 checkAutocall,
146 ]:
147 ]:
147 handler = check(line_info, ip)
148 handler = check(line_info, ip)
148 if handler:
149 if handler:
149 return handler(line_info)
150 return handler(line_info)
150
151
151 return ip.handle_normal(line_info)
152 return ip.handle_normal(line_info)
152
153
153 # Handler checks
154 # Handler checks
154 #
155 #
155 # All have the same interface: they take a LineInfo object and a ref to the
156 # All have the same interface: they take a LineInfo object and a ref to the
156 # iplib.InteractiveShell object. They check the line to see if a particular
157 # iplib.InteractiveShell object. They check the line to see if a particular
157 # handler should be called, and return either a handler or None. The
158 # handler should be called, and return either a handler or None. The
158 # handlers which they return are *bound* methods of the InteractiveShell
159 # handlers which they return are *bound* methods of the InteractiveShell
159 # object.
160 # object.
160 #
161 #
161 # In general, these checks should only take responsibility for their 'own'
162 # In general, these checks should only take responsibility for their 'own'
162 # handler. If it doesn't get triggered, they should just return None and
163 # handler. If it doesn't get triggered, they should just return None and
163 # let the rest of the check sequence run.
164 # let the rest of the check sequence run.
164 def checkEmacs(l_info,ip):
165 def checkEmacs(l_info,ip):
165 "Emacs ipython-mode tags certain input lines."
166 "Emacs ipython-mode tags certain input lines."
166 if l_info.line.endswith('# PYTHON-MODE'):
167 if l_info.line.endswith('# PYTHON-MODE'):
167 return ip.handle_emacs
168 return ip.handle_emacs
168 else:
169 else:
169 return None
170 return None
170
171
171 def checkIPyAutocall(l_info,ip):
172 def checkIPyAutocall(l_info,ip):
172 "Instances of IPyAutocall in user_ns get autocalled immediately"
173 "Instances of IPyAutocall in user_ns get autocalled immediately"
173 obj = ip.user_ns.get(l_info.iFun, None)
174 obj = ip.user_ns.get(l_info.iFun, None)
174 if isinstance(obj, IPython.ipapi.IPyAutocall):
175 if isinstance(obj, IPython.ipapi.IPyAutocall):
175 obj.set_ip(ip.api)
176 obj.set_ip(ip.api)
176 return ip.handle_auto
177 return ip.handle_auto
177 else:
178 else:
178 return None
179 return None
179
180
180
181
181 def checkMultiLineShell(l_info,ip):
182 def checkMultiLineShell(l_info,ip):
182 "Allow ! and !! in multi-line statements if multi_line_specials is on"
183 "Allow ! and !! in multi-line statements if multi_line_specials is on"
183 # Note that this one of the only places we check the first character of
184 # Note that this one of the only places we check the first character of
184 # iFun and *not* the preChar. Also note that the below test matches
185 # iFun and *not* the preChar. Also note that the below test matches
185 # both ! and !!.
186 # both ! and !!.
186 if l_info.continue_prompt \
187 if l_info.continue_prompt \
187 and ip.rc.multi_line_specials \
188 and ip.rc.multi_line_specials:
188 and l_info.iFun.startswith(ip.ESC_SHELL):
189 if l_info.iFun.startswith(ip.ESC_SHELL):
189 return ip.handle_shell_escape
190 return ip.handle_shell_escape
191 if l_info.iFun.startswith(ip.ESC_MAGIC):
192 return ip.handle_magic
190 else:
193 else:
191 return None
194 return None
192
195
193 def checkEscChars(l_info,ip):
196 def checkEscChars(l_info,ip):
194 """Check for escape character and return either a handler to handle it,
197 """Check for escape character and return either a handler to handle it,
195 or None if there is no escape char."""
198 or None if there is no escape char."""
196 if l_info.line[-1] == ip.ESC_HELP \
199 if l_info.line[-1] == ip.ESC_HELP \
197 and l_info.preChar != ip.ESC_SHELL \
200 and l_info.preChar != ip.ESC_SHELL \
198 and l_info.preChar != ip.ESC_SH_CAP:
201 and l_info.preChar != ip.ESC_SH_CAP:
199 # the ? can be at the end, but *not* for either kind of shell escape,
202 # the ? can be at the end, but *not* for either kind of shell escape,
200 # because a ? can be a vaild final char in a shell cmd
203 # because a ? can be a vaild final char in a shell cmd
201 return ip.handle_help
204 return ip.handle_help
202 elif l_info.preChar in ip.esc_handlers:
205 elif l_info.preChar in ip.esc_handlers:
203 return ip.esc_handlers[l_info.preChar]
206 return ip.esc_handlers[l_info.preChar]
204 else:
207 else:
205 return None
208 return None
206
209
207
210
208 def checkAssignment(l_info,ip):
211 def checkAssignment(l_info,ip):
209 """Check to see if user is assigning to a var for the first time, in
212 """Check to see if user is assigning to a var for the first time, in
210 which case we want to avoid any sort of automagic / autocall games.
213 which case we want to avoid any sort of automagic / autocall games.
211
214
212 This allows users to assign to either alias or magic names true python
215 This allows users to assign to either alias or magic names true python
213 variables (the magic/alias systems always take second seat to true
216 variables (the magic/alias systems always take second seat to true
214 python code). E.g. ls='hi', or ls,that=1,2"""
217 python code). E.g. ls='hi', or ls,that=1,2"""
215 if l_info.theRest and l_info.theRest[0] in '=,':
218 if l_info.theRest and l_info.theRest[0] in '=,':
216 return ip.handle_normal
219 return ip.handle_normal
217 else:
220 else:
218 return None
221 return None
219
222
220
223
221 def checkAutomagic(l_info,ip):
224 def checkAutomagic(l_info,ip):
222 """If the iFun is magic, and automagic is on, run it. Note: normal,
225 """If the iFun is magic, and automagic is on, run it. Note: normal,
223 non-auto magic would already have been triggered via '%' in
226 non-auto magic would already have been triggered via '%' in
224 check_esc_chars. This just checks for automagic. Also, before
227 check_esc_chars. This just checks for automagic. Also, before
225 triggering the magic handler, make sure that there is nothing in the
228 triggering the magic handler, make sure that there is nothing in the
226 user namespace which could shadow it."""
229 user namespace which could shadow it."""
227 if not ip.rc.automagic or not hasattr(ip,'magic_'+l_info.iFun):
230 if not ip.rc.automagic or not hasattr(ip,'magic_'+l_info.iFun):
228 return None
231 return None
229
232
230 # We have a likely magic method. Make sure we should actually call it.
233 # We have a likely magic method. Make sure we should actually call it.
231 if l_info.continue_prompt and not ip.rc.multi_line_specials:
234 if l_info.continue_prompt and not ip.rc.multi_line_specials:
232 return None
235 return None
233
236
234 head = l_info.iFun.split('.',1)[0]
237 head = l_info.iFun.split('.',1)[0]
235 if isShadowed(head,ip):
238 if isShadowed(head,ip):
236 return None
239 return None
237
240
238 return ip.handle_magic
241 return ip.handle_magic
239
242
240
243
241 def checkAlias(l_info,ip):
244 def checkAlias(l_info,ip):
242 "Check if the initital identifier on the line is an alias."
245 "Check if the initital identifier on the line is an alias."
243 # Note: aliases can not contain '.'
246 # Note: aliases can not contain '.'
244 head = l_info.iFun.split('.',1)[0]
247 head = l_info.iFun.split('.',1)[0]
245
248
246 if l_info.iFun not in ip.alias_table \
249 if l_info.iFun not in ip.alias_table \
247 or head not in ip.alias_table \
250 or head not in ip.alias_table \
248 or isShadowed(head,ip):
251 or isShadowed(head,ip):
249 return None
252 return None
250
253
251 return ip.handle_alias
254 return ip.handle_alias
252
255
253
256
254 def checkPythonOps(l_info,ip):
257 def checkPythonOps(l_info,ip):
255 """If the 'rest' of the line begins with a function call or pretty much
258 """If the 'rest' of the line begins with a function call or pretty much
256 any python operator, we should simply execute the line (regardless of
259 any python operator, we should simply execute the line (regardless of
257 whether or not there's a possible autocall expansion). This avoids
260 whether or not there's a possible autocall expansion). This avoids
258 spurious (and very confusing) geattr() accesses."""
261 spurious (and very confusing) geattr() accesses."""
259 if l_info.theRest and l_info.theRest[0] in '!=()<>,+*/%^&|':
262 if l_info.theRest and l_info.theRest[0] in '!=()<>,+*/%^&|':
260 return ip.handle_normal
263 return ip.handle_normal
261 else:
264 else:
262 return None
265 return None
263
266
264
267
265 def checkAutocall(l_info,ip):
268 def checkAutocall(l_info,ip):
266 "Check if the initial word/function is callable and autocall is on."
269 "Check if the initial word/function is callable and autocall is on."
267 if not ip.rc.autocall:
270 if not ip.rc.autocall:
268 return None
271 return None
269
272
270 oinfo = l_info.ofind(ip) # This can mutate state via getattr
273 oinfo = l_info.ofind(ip) # This can mutate state via getattr
271 if not oinfo['found']:
274 if not oinfo['found']:
272 return None
275 return None
273
276
274 if callable(oinfo['obj']) \
277 if callable(oinfo['obj']) \
275 and (not re_exclude_auto.match(l_info.theRest)) \
278 and (not re_exclude_auto.match(l_info.theRest)) \
276 and re_fun_name.match(l_info.iFun):
279 and re_fun_name.match(l_info.iFun):
277 #print 'going auto' # dbg
280 #print 'going auto' # dbg
278 return ip.handle_auto
281 return ip.handle_auto
279 else:
282 else:
280 #print 'was callable?', callable(l_info.oinfo['obj']) # dbg
283 #print 'was callable?', callable(l_info.oinfo['obj']) # dbg
281 return None
284 return None
282
285
283 # RegExp to identify potential function names
286 # RegExp to identify potential function names
284 re_fun_name = re.compile(r'[a-zA-Z_]([a-zA-Z0-9_.]*) *$')
287 re_fun_name = re.compile(r'[a-zA-Z_]([a-zA-Z0-9_.]*) *$')
285
288
286 # RegExp to exclude strings with this start from autocalling. In
289 # RegExp to exclude strings with this start from autocalling. In
287 # particular, all binary operators should be excluded, so that if foo is
290 # particular, all binary operators should be excluded, so that if foo is
288 # callable, foo OP bar doesn't become foo(OP bar), which is invalid. The
291 # callable, foo OP bar doesn't become foo(OP bar), which is invalid. The
289 # characters '!=()' don't need to be checked for, as the checkPythonChars
292 # characters '!=()' don't need to be checked for, as the checkPythonChars
290 # routine explicitely does so, to catch direct calls and rebindings of
293 # routine explicitely does so, to catch direct calls and rebindings of
291 # existing names.
294 # existing names.
292
295
293 # Warning: the '-' HAS TO BE AT THE END of the first group, otherwise
296 # Warning: the '-' HAS TO BE AT THE END of the first group, otherwise
294 # it affects the rest of the group in square brackets.
297 # it affects the rest of the group in square brackets.
295 re_exclude_auto = re.compile(r'^[,&^\|\*/\+-]'
298 re_exclude_auto = re.compile(r'^[,&^\|\*/\+-]'
296 r'|^is |^not |^in |^and |^or ')
299 r'|^is |^not |^in |^and |^or ')
297
300
298 # try to catch also methods for stuff in lists/tuples/dicts: off
301 # try to catch also methods for stuff in lists/tuples/dicts: off
299 # (experimental). For this to work, the line_split regexp would need
302 # (experimental). For this to work, the line_split regexp would need
300 # to be modified so it wouldn't break things at '['. That line is
303 # to be modified so it wouldn't break things at '['. That line is
301 # nasty enough that I shouldn't change it until I can test it _well_.
304 # nasty enough that I shouldn't change it until I can test it _well_.
302 #self.re_fun_name = re.compile (r'[a-zA-Z_]([a-zA-Z0-9_.\[\]]*) ?$')
305 #self.re_fun_name = re.compile (r'[a-zA-Z_]([a-zA-Z0-9_.\[\]]*) ?$')
303
306
304 # Handler Check Utilities
307 # Handler Check Utilities
305 def isShadowed(identifier,ip):
308 def isShadowed(identifier,ip):
306 """Is the given identifier defined in one of the namespaces which shadow
309 """Is the given identifier defined in one of the namespaces which shadow
307 the alias and magic namespaces? Note that an identifier is different
310 the alias and magic namespaces? Note that an identifier is different
308 than iFun, because it can not contain a '.' character."""
311 than iFun, because it can not contain a '.' character."""
309 # This is much safer than calling ofind, which can change state
312 # This is much safer than calling ofind, which can change state
310 return (identifier in ip.user_ns \
313 return (identifier in ip.user_ns \
311 or identifier in ip.internal_ns \
314 or identifier in ip.internal_ns \
312 or identifier in ip.ns_table['builtin'])
315 or identifier in ip.ns_table['builtin'])
313
316
General Comments 0
You need to be logged in to leave comments. Login now