##// END OF EJS Templates
Further prefilter refinement -- now aliases, like magics, can get run even if there are python ops on the line after them
dan.milstein -
Show More
@@ -1,313 +1,313 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
75
75
76 def splitUserInput(line, pattern=None):
76 def splitUserInput(line, pattern=None):
77 """Split user input into pre-char/whitespace, function part and rest.
77 """Split user input into pre-char/whitespace, function part and rest.
78
78
79 Mostly internal to this module, but also used by iplib.expand_aliases,
79 Mostly internal to this module, but also used by iplib.expand_aliases,
80 which passes in a shell pattern.
80 which passes in a shell pattern.
81 """
81 """
82 # It seems to me that the shell splitting should be a separate method.
82 # It seems to me that the shell splitting should be a separate method.
83
83
84 if not pattern:
84 if not pattern:
85 pattern = line_split
85 pattern = line_split
86 match = pattern.match(line)
86 match = pattern.match(line)
87 if not match:
87 if not match:
88 #print "match failed for line '%s'" % line
88 #print "match failed for line '%s'" % line
89 try:
89 try:
90 iFun,theRest = line.split(None,1)
90 iFun,theRest = line.split(None,1)
91 except ValueError:
91 except ValueError:
92 #print "split failed for line '%s'" % line
92 #print "split failed for line '%s'" % line
93 iFun,theRest = line,''
93 iFun,theRest = line,''
94 pre = re.match('^(\s*)(.*)',line).groups()[0]
94 pre = re.match('^(\s*)(.*)',line).groups()[0]
95 else:
95 else:
96 pre,iFun,theRest = match.groups()
96 pre,iFun,theRest = match.groups()
97
97
98 # iFun has to be a valid python identifier, so it better be only pure
98 # iFun has to be a valid python identifier, so it better be only pure
99 # ascii, no unicode:
99 # ascii, no unicode:
100 try:
100 try:
101 iFun = iFun.encode('ascii')
101 iFun = iFun.encode('ascii')
102 except UnicodeEncodeError:
102 except UnicodeEncodeError:
103 theRest = iFun + u' ' + theRest
103 theRest = iFun + u' ' + theRest
104 iFun = u''
104 iFun = u''
105
105
106 #print 'line:<%s>' % line # dbg
106 #print 'line:<%s>' % line # dbg
107 #print 'pre <%s> iFun <%s> rest <%s>' % (pre,iFun.strip(),theRest) # dbg
107 #print 'pre <%s> iFun <%s> rest <%s>' % (pre,iFun.strip(),theRest) # dbg
108 return pre,iFun.strip(),theRest
108 return pre,iFun.strip(),theRest
109
109
110
110
111 # RegExp for splitting line contents into pre-char//first word-method//rest.
111 # RegExp for splitting line contents into pre-char//first word-method//rest.
112 # For clarity, each group in on one line.
112 # For clarity, each group in on one line.
113
113
114 # WARNING: update the regexp if the escapes in iplib are changed, as they
114 # WARNING: update the regexp if the escapes in iplib are changed, as they
115 # are hardwired in.
115 # are hardwired in.
116
116
117 # Although it's not solely driven by the regex, note that:
117 # Although it's not solely driven by the regex, note that:
118 # ,;/% only trigger if they are the first character on the line
118 # ,;/% only trigger if they are the first character on the line
119 # ! and !! trigger if they are first char(s) *or* follow an indent
119 # ! and !! trigger if they are first char(s) *or* follow an indent
120 # ? triggers as first or last char.
120 # ? triggers as first or last char.
121
121
122 # The three parts of the regex are:
122 # The three parts of the regex are:
123 # 1) pre: pre_char *or* initial whitespace
123 # 1) pre: pre_char *or* initial whitespace
124 # 2) iFun: first word/method (mix of \w and '.')
124 # 2) iFun: first word/method (mix of \w and '.')
125 # 3) theRest: rest of line
125 # 3) theRest: rest of line
126 line_split = re.compile(r'^([,;/%?]|!!?|\s*)'
126 line_split = re.compile(r'^([,;/%?]|!!?|\s*)'
127 r'\s*([\w\.]+)\s*'
127 r'\s*([\w\.]+)\s*'
128 r'(.*)$')
128 r'(.*)$')
129
129
130 shell_line_split = re.compile(r'^(\s*)(\S*\s*)(.*$)')
130 shell_line_split = re.compile(r'^(\s*)(\S*\s*)(.*$)')
131
131
132 def prefilter(line_info, ip):
132 def prefilter(line_info, ip):
133 """Call one of the passed-in InteractiveShell's handler preprocessors,
133 """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
134 depending on the form of the line. Return the results, which must be a
135 value, even if it's a blank ('')."""
135 value, even if it's a blank ('')."""
136 # Note: the order of these checks does matter.
136 # Note: the order of these checks does matter.
137 for check in [ checkEmacs,
137 for check in [ checkEmacs,
138 checkIPyAutocall,
138 checkIPyAutocall,
139 checkMultiLineShell,
139 checkMultiLineShell,
140 checkEscChars,
140 checkEscChars,
141 checkAssignment,
141 checkAssignment,
142 checkAutomagic,
142 checkAutomagic,
143 checkPythonOps,
144 checkAlias,
143 checkAlias,
144 checkPythonOps,
145 checkAutocall,
145 checkAutocall,
146 ]:
146 ]:
147 handler = check(line_info, ip)
147 handler = check(line_info, ip)
148 if handler:
148 if handler:
149 return handler(line_info)
149 return handler(line_info)
150
150
151 return ip.handle_normal(line_info)
151 return ip.handle_normal(line_info)
152
152
153 # Handler checks
153 # Handler checks
154 #
154 #
155 # All have the same interface: they take a LineInfo object and a ref to the
155 # 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
156 # 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
157 # handler should be called, and return either a handler or None. The
158 # handlers which they return are *bound* methods of the InteractiveShell
158 # handlers which they return are *bound* methods of the InteractiveShell
159 # object.
159 # object.
160 #
160 #
161 # In general, these checks should only take responsibility for their 'own'
161 # 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
162 # handler. If it doesn't get triggered, they should just return None and
163 # let the rest of the check sequence run.
163 # let the rest of the check sequence run.
164 def checkEmacs(l_info,ip):
164 def checkEmacs(l_info,ip):
165 "Emacs ipython-mode tags certain input lines."
165 "Emacs ipython-mode tags certain input lines."
166 if l_info.line.endswith('# PYTHON-MODE'):
166 if l_info.line.endswith('# PYTHON-MODE'):
167 return ip.handle_emacs
167 return ip.handle_emacs
168 else:
168 else:
169 return None
169 return None
170
170
171 def checkIPyAutocall(l_info,ip):
171 def checkIPyAutocall(l_info,ip):
172 "Instances of IPyAutocall in user_ns get autocalled immediately"
172 "Instances of IPyAutocall in user_ns get autocalled immediately"
173 obj = ip.user_ns.get(l_info.iFun, None)
173 obj = ip.user_ns.get(l_info.iFun, None)
174 if isinstance(obj, IPython.ipapi.IPyAutocall):
174 if isinstance(obj, IPython.ipapi.IPyAutocall):
175 obj.set_ip(ip.api)
175 obj.set_ip(ip.api)
176 return ip.handle_auto
176 return ip.handle_auto
177 else:
177 else:
178 return None
178 return None
179
179
180
180
181 def checkMultiLineShell(l_info,ip):
181 def checkMultiLineShell(l_info,ip):
182 "Allow ! and !! in multi-line statements if multi_line_specials is on"
182 "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
183 # 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
184 # iFun and *not* the preChar. Also note that the below test matches
185 # both ! and !!.
185 # both ! and !!.
186 if l_info.continue_prompt \
186 if l_info.continue_prompt \
187 and ip.rc.multi_line_specials \
187 and ip.rc.multi_line_specials \
188 and l_info.iFun.startswith(ip.ESC_SHELL):
188 and l_info.iFun.startswith(ip.ESC_SHELL):
189 return ip.handle_shell_escape
189 return ip.handle_shell_escape
190 else:
190 else:
191 return None
191 return None
192
192
193 def checkEscChars(l_info,ip):
193 def checkEscChars(l_info,ip):
194 """Check for escape character and return either a handler to handle it,
194 """Check for escape character and return either a handler to handle it,
195 or None if there is no escape char."""
195 or None if there is no escape char."""
196 if l_info.line[-1] == ip.ESC_HELP \
196 if l_info.line[-1] == ip.ESC_HELP \
197 and l_info.preChar != ip.ESC_SHELL \
197 and l_info.preChar != ip.ESC_SHELL \
198 and l_info.preChar != ip.ESC_SH_CAP:
198 and l_info.preChar != ip.ESC_SH_CAP:
199 # the ? can be at the end, but *not* for either kind of shell escape,
199 # 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
200 # because a ? can be a vaild final char in a shell cmd
201 return ip.handle_help
201 return ip.handle_help
202 elif l_info.preChar in ip.esc_handlers:
202 elif l_info.preChar in ip.esc_handlers:
203 return ip.esc_handlers[l_info.preChar]
203 return ip.esc_handlers[l_info.preChar]
204 else:
204 else:
205 return None
205 return None
206
206
207
207
208 def checkAssignment(l_info,ip):
208 def checkAssignment(l_info,ip):
209 """Check to see if user is assigning to a var for the first time, in
209 """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.
210 which case we want to avoid any sort of automagic / autocall games.
211
211
212 This allows users to assign to either alias or magic names true python
212 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
213 variables (the magic/alias systems always take second seat to true
214 python code). E.g. ls='hi', or ls,that=1,2"""
214 python code). E.g. ls='hi', or ls,that=1,2"""
215 if l_info.theRest and l_info.theRest[0] in '=,':
215 if l_info.theRest and l_info.theRest[0] in '=,':
216 return ip.handle_normal
216 return ip.handle_normal
217 else:
217 else:
218 return None
218 return None
219
219
220
220
221 def checkAutomagic(l_info,ip):
221 def checkAutomagic(l_info,ip):
222 """If the iFun is magic, and automagic is on, run it. Note: normal,
222 """If the iFun is magic, and automagic is on, run it. Note: normal,
223 non-auto magic would already have been triggered via '%' in
223 non-auto magic would already have been triggered via '%' in
224 check_esc_chars. This just checks for automagic. Also, before
224 check_esc_chars. This just checks for automagic. Also, before
225 triggering the magic handler, make sure that there is nothing in the
225 triggering the magic handler, make sure that there is nothing in the
226 user namespace which could shadow it."""
226 user namespace which could shadow it."""
227 if not ip.rc.automagic or not hasattr(ip,'magic_'+l_info.iFun):
227 if not ip.rc.automagic or not hasattr(ip,'magic_'+l_info.iFun):
228 return None
228 return None
229
229
230 # We have a likely magic method. Make sure we should actually call it.
230 # 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:
231 if l_info.continue_prompt and not ip.rc.multi_line_specials:
232 return None
232 return None
233
233
234 head = l_info.iFun.split('.',1)[0]
234 head = l_info.iFun.split('.',1)[0]
235 if isShadowed(head,ip):
235 if isShadowed(head,ip):
236 return None
236 return None
237
237
238 return ip.handle_magic
238 return ip.handle_magic
239
239
240
240
241 def checkPythonOps(l_info,ip):
242 """If the 'rest' of the line begins with a function call or pretty much
243 any python operator, we should simply execute the line (regardless of
244 whether or not there's a possible alias or autocall expansion). This
245 avoids spurious (and very confusing) geattr() accesses."""
246 if l_info.theRest and l_info.theRest[0] in '!=()<>,+*/%^&|':
247 return ip.handle_normal
248 else:
249 return None
250
251
252 def checkAlias(l_info,ip):
241 def checkAlias(l_info,ip):
253 "Check if the initital identifier on the line is an alias."
242 "Check if the initital identifier on the line is an alias."
254 # Note: aliases can not contain '.'
243 # Note: aliases can not contain '.'
255 head = l_info.iFun.split('.',1)[0]
244 head = l_info.iFun.split('.',1)[0]
256
245
257 if l_info.iFun not in ip.alias_table \
246 if l_info.iFun not in ip.alias_table \
258 or head not in ip.alias_table \
247 or head not in ip.alias_table \
259 or isShadowed(head,ip):
248 or isShadowed(head,ip):
260 return None
249 return None
261
250
262 return ip.handle_alias
251 return ip.handle_alias
263
252
264
253
254 def checkPythonOps(l_info,ip):
255 """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
257 whether or not there's a possible autocall expansion). This avoids
258 spurious (and very confusing) geattr() accesses."""
259 if l_info.theRest and l_info.theRest[0] in '!=()<>,+*/%^&|':
260 return ip.handle_normal
261 else:
262 return None
263
264
265 def checkAutocall(l_info,ip):
265 def checkAutocall(l_info,ip):
266 "Check if the initial word/function is callable and autocall is on."
266 "Check if the initial word/function is callable and autocall is on."
267 if not ip.rc.autocall:
267 if not ip.rc.autocall:
268 return None
268 return None
269
269
270 oinfo = l_info.ofind(ip) # This can mutate state via getattr
270 oinfo = l_info.ofind(ip) # This can mutate state via getattr
271 if not oinfo['found']:
271 if not oinfo['found']:
272 return None
272 return None
273
273
274 if callable(oinfo['obj']) \
274 if callable(oinfo['obj']) \
275 and (not re_exclude_auto.match(l_info.theRest)) \
275 and (not re_exclude_auto.match(l_info.theRest)) \
276 and re_fun_name.match(l_info.iFun):
276 and re_fun_name.match(l_info.iFun):
277 #print 'going auto' # dbg
277 #print 'going auto' # dbg
278 return ip.handle_auto
278 return ip.handle_auto
279 else:
279 else:
280 #print 'was callable?', callable(l_info.oinfo['obj']) # dbg
280 #print 'was callable?', callable(l_info.oinfo['obj']) # dbg
281 return None
281 return None
282
282
283 # RegExp to identify potential function names
283 # RegExp to identify potential function names
284 re_fun_name = re.compile(r'[a-zA-Z_]([a-zA-Z0-9_.]*) *$')
284 re_fun_name = re.compile(r'[a-zA-Z_]([a-zA-Z0-9_.]*) *$')
285
285
286 # RegExp to exclude strings with this start from autocalling. In
286 # RegExp to exclude strings with this start from autocalling. In
287 # particular, all binary operators should be excluded, so that if foo is
287 # 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
288 # 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
289 # characters '!=()' don't need to be checked for, as the checkPythonChars
290 # routine explicitely does so, to catch direct calls and rebindings of
290 # routine explicitely does so, to catch direct calls and rebindings of
291 # existing names.
291 # existing names.
292
292
293 # Warning: the '-' HAS TO BE AT THE END of the first group, otherwise
293 # Warning: the '-' HAS TO BE AT THE END of the first group, otherwise
294 # it affects the rest of the group in square brackets.
294 # it affects the rest of the group in square brackets.
295 re_exclude_auto = re.compile(r'^[,&^\|\*/\+-]'
295 re_exclude_auto = re.compile(r'^[,&^\|\*/\+-]'
296 r'|^is |^not |^in |^and |^or ')
296 r'|^is |^not |^in |^and |^or ')
297
297
298 # try to catch also methods for stuff in lists/tuples/dicts: off
298 # try to catch also methods for stuff in lists/tuples/dicts: off
299 # (experimental). For this to work, the line_split regexp would need
299 # (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
300 # 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_.
301 # 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_.\[\]]*) ?$')
302 #self.re_fun_name = re.compile (r'[a-zA-Z_]([a-zA-Z0-9_.\[\]]*) ?$')
303
303
304 # Handler Check Utilities
304 # Handler Check Utilities
305 def isShadowed(identifier,ip):
305 def isShadowed(identifier,ip):
306 """Is the given identifier defined in one of the namespaces which shadow
306 """Is the given identifier defined in one of the namespaces which shadow
307 the alias and magic namespaces? Note that an identifier is different
307 the alias and magic namespaces? Note that an identifier is different
308 than iFun, because it can not contain a '.' character."""
308 than iFun, because it can not contain a '.' character."""
309 # This is much safer than calling ofind, which can change state
309 # This is much safer than calling ofind, which can change state
310 return (identifier in ip.user_ns \
310 return (identifier in ip.user_ns \
311 or identifier in ip.internal_ns \
311 or identifier in ip.internal_ns \
312 or identifier in ip.ns_table['builtin'])
312 or identifier in ip.ns_table['builtin'])
313
313
@@ -1,427 +1,428 b''
1 """
1 """
2 Test which prefilter transformations get called for various input lines.
2 Test which prefilter transformations get called for various input lines.
3 Note that this does *not* test the transformations themselves -- it's just
3 Note that this does *not* test the transformations themselves -- it's just
4 verifying that a particular combination of, e.g. config options and escape
4 verifying that a particular combination of, e.g. config options and escape
5 chars trigger the proper handle_X transform of the input line.
5 chars trigger the proper handle_X transform of the input line.
6
6
7 Usage: run from the command line with *normal* python, not ipython:
7 Usage: run from the command line with *normal* python, not ipython:
8 > python test_prefilter.py
8 > python test_prefilter.py
9
9
10 Fairly quiet output by default. Pass in -v to get everyone's favorite dots.
10 Fairly quiet output by default. Pass in -v to get everyone's favorite dots.
11 """
11 """
12
12
13 # The prefilter always ends in a call to some self.handle_X method. We swap
13 # The prefilter always ends in a call to some self.handle_X method. We swap
14 # all of those out so that we can capture which one was called.
14 # all of those out so that we can capture which one was called.
15
15
16 import sys
16 import sys
17 sys.path.append('..')
17 sys.path.append('..')
18 import IPython
18 import IPython
19 import IPython.ipapi
19 import IPython.ipapi
20
20
21 verbose = False
21 verbose = False
22 if len(sys.argv) > 1:
22 if len(sys.argv) > 1:
23 if sys.argv[1] == '-v':
23 if sys.argv[1] == '-v':
24 sys.argv = sys.argv[:-1] # IPython is confused by -v, apparently
24 sys.argv = sys.argv[:-1] # IPython is confused by -v, apparently
25 verbose = True
25 verbose = True
26
26
27 IPython.Shell.start()
27 IPython.Shell.start()
28
28
29 ip = IPython.ipapi.get()
29 ip = IPython.ipapi.get()
30
30
31 # Collect failed tests + stats and print them at the end
31 # Collect failed tests + stats and print them at the end
32 failures = []
32 failures = []
33 num_tests = 0
33 num_tests = 0
34
34
35 # Store the results in module vars as we go
35 # Store the results in module vars as we go
36 last_line = None
36 last_line = None
37 handler_called = None
37 handler_called = None
38 def install_mock_handler(name):
38 def install_mock_handler(name):
39 """Swap out one of the IP.handle_x methods with a function which can
39 """Swap out one of the IP.handle_x methods with a function which can
40 record which handler was called and what line was produced. The mock
40 record which handler was called and what line was produced. The mock
41 handler func always returns '', which causes ipython to cease handling
41 handler func always returns '', which causes ipython to cease handling
42 the string immediately. That way, that it doesn't echo output, raise
42 the string immediately. That way, that it doesn't echo output, raise
43 exceptions, etc. But do note that testing multiline strings thus gets
43 exceptions, etc. But do note that testing multiline strings thus gets
44 a bit hard."""
44 a bit hard."""
45 def mock_handler(self, line, continue_prompt=None,
45 def mock_handler(self, line, continue_prompt=None,
46 pre=None,iFun=None,theRest=None,
46 pre=None,iFun=None,theRest=None,
47 obj=None):
47 obj=None):
48 #print "Inside %s with '%s'" % (name, line)
48 #print "Inside %s with '%s'" % (name, line)
49 global last_line, handler_called
49 global last_line, handler_called
50 last_line = line
50 last_line = line
51 handler_called = name
51 handler_called = name
52 return ''
52 return ''
53 mock_handler.name = name
53 mock_handler.name = name
54 setattr(IPython.iplib.InteractiveShell, name, mock_handler)
54 setattr(IPython.iplib.InteractiveShell, name, mock_handler)
55
55
56 install_mock_handler('handle_normal')
56 install_mock_handler('handle_normal')
57 install_mock_handler('handle_auto')
57 install_mock_handler('handle_auto')
58 install_mock_handler('handle_magic')
58 install_mock_handler('handle_magic')
59 install_mock_handler('handle_help')
59 install_mock_handler('handle_help')
60 install_mock_handler('handle_shell_escape')
60 install_mock_handler('handle_shell_escape')
61 install_mock_handler('handle_alias')
61 install_mock_handler('handle_alias')
62 install_mock_handler('handle_emacs')
62 install_mock_handler('handle_emacs')
63
63
64
64
65 def reset_esc_handlers():
65 def reset_esc_handlers():
66 """The escape handlers are stored in a hash (as an attribute of the
66 """The escape handlers are stored in a hash (as an attribute of the
67 InteractiveShell *instance*), so we have to rebuild that hash to get our
67 InteractiveShell *instance*), so we have to rebuild that hash to get our
68 new handlers in there."""
68 new handlers in there."""
69 s = ip.IP
69 s = ip.IP
70 s.esc_handlers = {s.ESC_PAREN : s.handle_auto,
70 s.esc_handlers = {s.ESC_PAREN : s.handle_auto,
71 s.ESC_QUOTE : s.handle_auto,
71 s.ESC_QUOTE : s.handle_auto,
72 s.ESC_QUOTE2 : s.handle_auto,
72 s.ESC_QUOTE2 : s.handle_auto,
73 s.ESC_MAGIC : s.handle_magic,
73 s.ESC_MAGIC : s.handle_magic,
74 s.ESC_HELP : s.handle_help,
74 s.ESC_HELP : s.handle_help,
75 s.ESC_SHELL : s.handle_shell_escape,
75 s.ESC_SHELL : s.handle_shell_escape,
76 s.ESC_SH_CAP : s.handle_shell_escape,
76 s.ESC_SH_CAP : s.handle_shell_escape,
77 }
77 }
78 reset_esc_handlers()
78 reset_esc_handlers()
79
79
80 # This is so I don't have to quote over and over. Gotta be a better way.
80 # This is so I don't have to quote over and over. Gotta be a better way.
81 handle_normal = 'handle_normal'
81 handle_normal = 'handle_normal'
82 handle_auto = 'handle_auto'
82 handle_auto = 'handle_auto'
83 handle_magic = 'handle_magic'
83 handle_magic = 'handle_magic'
84 handle_help = 'handle_help'
84 handle_help = 'handle_help'
85 handle_shell_escape = 'handle_shell_escape'
85 handle_shell_escape = 'handle_shell_escape'
86 handle_alias = 'handle_alias'
86 handle_alias = 'handle_alias'
87 handle_emacs = 'handle_emacs'
87 handle_emacs = 'handle_emacs'
88
88
89 def check(assertion, failure_msg):
89 def check(assertion, failure_msg):
90 """Check a boolean assertion and fail with a message if necessary. Store
90 """Check a boolean assertion and fail with a message if necessary. Store
91 an error essage in module-level failures list in case of failure. Print
91 an error essage in module-level failures list in case of failure. Print
92 '.' or 'F' if module var Verbose is true.
92 '.' or 'F' if module var Verbose is true.
93 """
93 """
94 global num_tests
94 global num_tests
95 num_tests += 1
95 num_tests += 1
96 if assertion:
96 if assertion:
97 if verbose:
97 if verbose:
98 sys.stdout.write('.')
98 sys.stdout.write('.')
99 sys.stdout.flush()
99 sys.stdout.flush()
100 else:
100 else:
101 if verbose:
101 if verbose:
102 sys.stdout.write('F')
102 sys.stdout.write('F')
103 sys.stdout.flush()
103 sys.stdout.flush()
104 failures.append(failure_msg)
104 failures.append(failure_msg)
105
105
106
106
107 def check_handler(expected_handler, line):
107 def check_handler(expected_handler, line):
108 """Verify that the expected hander was called (for the given line,
108 """Verify that the expected hander was called (for the given line,
109 passed in for failure reporting).
109 passed in for failure reporting).
110
110
111 Pulled out to its own function so that tests which don't use
111 Pulled out to its own function so that tests which don't use
112 run_handler_tests can still take advantage of it."""
112 run_handler_tests can still take advantage of it."""
113 check(handler_called == expected_handler,
113 check(handler_called == expected_handler,
114 "Expected %s to be called for %s, "
114 "Expected %s to be called for %s, "
115 "instead %s called" % (expected_handler,
115 "instead %s called" % (expected_handler,
116 repr(line),
116 repr(line),
117 handler_called))
117 handler_called))
118
118
119
119
120 def run_handler_tests(h_tests):
120 def run_handler_tests(h_tests):
121 """Loop through a series of (input_line, handler_name) pairs, verifying
121 """Loop through a series of (input_line, handler_name) pairs, verifying
122 that, for each ip calls the given handler for the given line.
122 that, for each ip calls the given handler for the given line.
123
123
124 The verbose complaint includes the line passed in, so if that line can
124 The verbose complaint includes the line passed in, so if that line can
125 include enough info to find the error, the tests are modestly
125 include enough info to find the error, the tests are modestly
126 self-documenting.
126 self-documenting.
127 """
127 """
128 for ln, expected_handler in h_tests:
128 for ln, expected_handler in h_tests:
129 global handler_called
129 global handler_called
130 handler_called = None
130 handler_called = None
131 ip.runlines(ln)
131 ip.runlines(ln)
132 check_handler(expected_handler, ln)
132 check_handler(expected_handler, ln)
133
133
134 def run_one_test(ln, expected_handler):
134 def run_one_test(ln, expected_handler):
135 run_handler_tests([(ln, expected_handler)])
135 run_handler_tests([(ln, expected_handler)])
136
136
137
137
138 # =========================================
138 # =========================================
139 # Tests
139 # Tests
140 # =========================================
140 # =========================================
141
141
142
142
143 # Fundamental escape characters + whitespace & misc
143 # Fundamental escape characters + whitespace & misc
144 # =================================================
144 # =================================================
145 esc_handler_tests = [
145 esc_handler_tests = [
146 ( '?thing', handle_help, ),
146 ( '?thing', handle_help, ),
147 ( 'thing?', handle_help ), # '?' can trail...
147 ( 'thing?', handle_help ), # '?' can trail...
148 ( 'thing!', handle_normal), # but only '?' can trail
148 ( 'thing!', handle_normal), # but only '?' can trail
149 ( ' ?thing', handle_normal), # leading whitespace turns off esc chars
149 ( ' ?thing', handle_normal), # leading whitespace turns off esc chars
150 ( '!ls', handle_shell_escape),
150 ( '!ls', handle_shell_escape),
151 ( '! true', handle_shell_escape),
151 ( '! true', handle_shell_escape),
152 ( '!! true', handle_shell_escape),
152 ( '!! true', handle_shell_escape),
153 ( '%magic', handle_magic),
153 ( '%magic', handle_magic),
154 # XXX Possibly, add test for /,; once those are unhooked from %autocall
154 # XXX Possibly, add test for /,; once those are unhooked from %autocall
155 ( 'emacs_mode # PYTHON-MODE', handle_emacs ),
155 ( 'emacs_mode # PYTHON-MODE', handle_emacs ),
156 ( ' ', handle_normal),
156 ( ' ', handle_normal),
157 # Trailing qmark combos. Odd special cases abound
157 # Trailing qmark combos. Odd special cases abound
158 ( '!thing?', handle_shell_escape), # trailing '?' loses to shell esc
158 ( '!thing?', handle_shell_escape), # trailing '?' loses to shell esc
159 ( '!thing ?', handle_shell_escape),
159 ( '!thing ?', handle_shell_escape),
160 ( '!!thing?', handle_shell_escape),
160 ( '!!thing?', handle_shell_escape),
161 ( '%cmd?', handle_help),
161 ( '%cmd?', handle_help),
162 ( '/cmd?', handle_help),
162 ( '/cmd?', handle_help),
163 ( ';cmd?', handle_help),
163 ( ';cmd?', handle_help),
164 ( ',cmd?', handle_help),
164 ( ',cmd?', handle_help),
165 ]
165 ]
166 run_handler_tests(esc_handler_tests)
166 run_handler_tests(esc_handler_tests)
167
167
168
168
169
169
170 # Shell Escapes in Multi-line statements
170 # Shell Escapes in Multi-line statements
171 # ======================================
171 # ======================================
172 #
172 #
173 # We can't test this via runlines, since the hacked-over-for-testing
173 # We can't test this via runlines, since the hacked-over-for-testing
174 # handlers all return None, so continue_prompt never becomes true. Instead
174 # handlers all return None, so continue_prompt never becomes true. Instead
175 # we drop into prefilter directly and pass in continue_prompt.
175 # we drop into prefilter directly and pass in continue_prompt.
176
176
177 old_mls = ip.options.multi_line_specials
177 old_mls = ip.options.multi_line_specials
178 for ln in [ ' !ls $f multi_line_specials %s',
178 for ln in [ ' !ls $f multi_line_specials %s',
179 ' !!ls $f multi_line_specials %s', # !! escapes work on mls
179 ' !!ls $f multi_line_specials %s', # !! escapes work on mls
180 # Trailing ? doesn't trigger help:
180 # Trailing ? doesn't trigger help:
181 ' !ls $f multi_line_specials %s ?',
181 ' !ls $f multi_line_specials %s ?',
182 ' !!ls $f multi_line_specials %s ?',
182 ' !!ls $f multi_line_specials %s ?',
183 ]:
183 ]:
184 ip.options.multi_line_specials = 1
184 ip.options.multi_line_specials = 1
185 on_ln = ln % 'on'
185 on_ln = ln % 'on'
186 ignore = ip.IP.prefilter(on_ln, continue_prompt=True)
186 ignore = ip.IP.prefilter(on_ln, continue_prompt=True)
187 check_handler(handle_shell_escape, on_ln)
187 check_handler(handle_shell_escape, on_ln)
188
188
189 ip.options.multi_line_specials = 0
189 ip.options.multi_line_specials = 0
190 off_ln = ln % 'off'
190 off_ln = ln % 'off'
191 ignore = ip.IP.prefilter(off_ln, continue_prompt=True)
191 ignore = ip.IP.prefilter(off_ln, continue_prompt=True)
192 check_handler(handle_normal, off_ln)
192 check_handler(handle_normal, off_ln)
193
193
194 ip.options.multi_line_specials = old_mls
194 ip.options.multi_line_specials = old_mls
195
195
196
196
197 # Automagic
197 # Automagic
198 # =========
198 # =========
199
199
200 # Pick one magic fun and one non_magic fun, make sure both exist
200 # Pick one magic fun and one non_magic fun, make sure both exist
201 assert hasattr(ip.IP, "magic_cpaste")
201 assert hasattr(ip.IP, "magic_cpaste")
202 assert not hasattr(ip.IP, "magic_does_not_exist")
202 assert not hasattr(ip.IP, "magic_does_not_exist")
203 ip.options.autocall = 0 # gotta have this off to get handle_normal
203 ip.options.autocall = 0 # gotta have this off to get handle_normal
204 ip.options.automagic = 0
204 ip.options.automagic = 0
205 run_handler_tests([
205 run_handler_tests([
206 # Without automagic, only shows up with explicit escape
206 # Without automagic, only shows up with explicit escape
207 ( 'cpaste', handle_normal),
207 ( 'cpaste', handle_normal),
208 ( '%cpaste', handle_magic),
208 ( '%cpaste', handle_magic),
209 ( '%does_not_exist', handle_magic),
209 ( '%does_not_exist', handle_magic),
210 ])
210 ])
211 ip.options.automagic = 1
211 ip.options.automagic = 1
212 run_handler_tests([
212 run_handler_tests([
213 ( 'cpaste', handle_magic),
213 ( 'cpaste', handle_magic),
214 ( '%cpaste', handle_magic),
214 ( '%cpaste', handle_magic),
215 ( 'does_not_exist', handle_normal),
215 ( 'does_not_exist', handle_normal),
216 ( '%does_not_exist', handle_magic),
216 ( '%does_not_exist', handle_magic),
217 ( 'cd /', handle_magic),
217 ( 'cd /', handle_magic),
218 ( 'cd = 2', handle_normal),
218 ( 'cd = 2', handle_normal),
219 ])
219 ])
220
220
221 # If next elt starts with anything that could be an assignment, func call,
221 # If next elt starts with anything that could be an assignment, func call,
222 # etc, we don't call the magic func, unless explicitly escaped to do so.
222 # etc, we don't call the magic func, unless explicitly escaped to do so.
223 #magic_killing_tests = []
223 #magic_killing_tests = []
224 #for c in list('!=()<>,'):
224 #for c in list('!=()<>,'):
225 # magic_killing_tests.append(('cpaste %s killed_automagic' % c, handle_normal))
225 # magic_killing_tests.append(('cpaste %s killed_automagic' % c, handle_normal))
226 # magic_killing_tests.append(('%%cpaste %s escaped_magic' % c, handle_magic))
226 # magic_killing_tests.append(('%%cpaste %s escaped_magic' % c, handle_magic))
227 #run_handler_tests(magic_killing_tests)
227 #run_handler_tests(magic_killing_tests)
228
228
229 # magic on indented continuation lines -- on iff multi_line_specials == 1
229 # magic on indented continuation lines -- on iff multi_line_specials == 1
230 ip.options.multi_line_specials = 0
230 ip.options.multi_line_specials = 0
231 ln = ' cpaste multi_line off kills magic'
231 ln = ' cpaste multi_line off kills magic'
232 ignore = ip.IP.prefilter(ln, continue_prompt=True)
232 ignore = ip.IP.prefilter(ln, continue_prompt=True)
233 check_handler(handle_normal, ln)
233 check_handler(handle_normal, ln)
234
234
235 ip.options.multi_line_specials = 1
235 ip.options.multi_line_specials = 1
236 ln = ' cpaste multi_line on enables magic'
236 ln = ' cpaste multi_line on enables magic'
237 ignore = ip.IP.prefilter(ln, continue_prompt=True)
237 ignore = ip.IP.prefilter(ln, continue_prompt=True)
238 check_handler(handle_magic, ln)
238 check_handler(handle_magic, ln)
239
239
240 # user namespace shadows the magic one unless shell escaped
240 # user namespace shadows the magic one unless shell escaped
241 ip.user_ns['cpaste'] = 'user_ns'
241 ip.user_ns['cpaste'] = 'user_ns'
242 run_handler_tests([
242 run_handler_tests([
243 ( 'cpaste', handle_normal),
243 ( 'cpaste', handle_normal),
244 ( '%cpaste', handle_magic)])
244 ( '%cpaste', handle_magic)])
245 del ip.user_ns['cpaste']
245 del ip.user_ns['cpaste']
246
246
247
247
248
248
249 # Check for !=() turning off .ofind
249 # Check for !=() turning off .ofind
250 # =================================
250 # =================================
251 class AttributeMutator(object):
251 class AttributeMutator(object):
252 """A class which will be modified on attribute access, to test ofind"""
252 """A class which will be modified on attribute access, to test ofind"""
253 def __init__(self):
253 def __init__(self):
254 self.called = False
254 self.called = False
255
255
256 def getFoo(self): self.called = True
256 def getFoo(self): self.called = True
257 foo = property(getFoo)
257 foo = property(getFoo)
258
258
259 attr_mutator = AttributeMutator()
259 attr_mutator = AttributeMutator()
260 ip.to_user_ns('attr_mutator')
260 ip.to_user_ns('attr_mutator')
261
261
262 ip.options.autocall = 1
262 ip.options.autocall = 1
263
263
264 run_one_test('attr_mutator.foo should mutate', handle_normal)
264 run_one_test('attr_mutator.foo should mutate', handle_normal)
265 check(attr_mutator.called, 'ofind should be called in absence of assign characters')
265 check(attr_mutator.called, 'ofind should be called in absence of assign characters')
266
266
267 for c in list('!=()<>+*/%^&|'):
267 for c in list('!=()<>+*/%^&|'):
268 attr_mutator.called = False
268 attr_mutator.called = False
269 run_one_test('attr_mutator.foo %s should *not* mutate' % c, handle_normal)
269 run_one_test('attr_mutator.foo %s should *not* mutate' % c, handle_normal)
270 run_one_test('attr_mutator.foo%s should *not* mutate' % c, handle_normal)
270 run_one_test('attr_mutator.foo%s should *not* mutate' % c, handle_normal)
271
271
272 check(not attr_mutator.called,
272 check(not attr_mutator.called,
273 'ofind should not be called near character %s' % c)
273 'ofind should not be called near character %s' % c)
274
274
275
275
276
276
277 # Alias expansion
277 # Alias expansion
278 # ===============
278 # ===============
279
279
280 # With autocall on or off, aliases should be shadowed by user, internal and
280 # With autocall on or off, aliases should be shadowed by user, internal and
281 # __builtin__ namespaces
281 # __builtin__ namespaces
282 #
282 #
283 # XXX Can aliases have '.' in their name? With autocall off, that works,
283 # XXX Can aliases have '.' in their name? With autocall off, that works,
284 # with autocall on, it doesn't. Hmmm.
284 # with autocall on, it doesn't. Hmmm.
285 import __builtin__
285 import __builtin__
286 for ac_state in [0,1]:
286 for ac_state in [0,1]:
287 ip.options.autocall = ac_state
287 ip.options.autocall = ac_state
288 ip.IP.alias_table['alias_cmd'] = 'alias_result'
288 ip.IP.alias_table['alias_cmd'] = 'alias_result'
289 ip.IP.alias_table['alias_head.with_dot'] = 'alias_result'
289 ip.IP.alias_table['alias_head.with_dot'] = 'alias_result'
290 run_handler_tests([
290 run_handler_tests([
291 ("alias_cmd", handle_alias),
291 ("alias_cmd", handle_alias),
292 # XXX See note above
292 # XXX See note above
293 #("alias_head.with_dot unshadowed, autocall=%s" % ac_state, handle_alias),
293 #("alias_head.with_dot unshadowed, autocall=%s" % ac_state, handle_alias),
294 ("alias_cmd.something aliases must match whole expr", handle_normal),
294 ("alias_cmd.something aliases must match whole expr", handle_normal),
295 ("alias_cmd /", handle_alias),
295 ])
296 ])
296
297
297 for ns in [ip.user_ns, ip.IP.internal_ns, __builtin__.__dict__ ]:
298 for ns in [ip.user_ns, ip.IP.internal_ns, __builtin__.__dict__ ]:
298 ns['alias_cmd'] = 'a user value'
299 ns['alias_cmd'] = 'a user value'
299 ns['alias_head'] = 'a user value'
300 ns['alias_head'] = 'a user value'
300 run_handler_tests([
301 run_handler_tests([
301 ("alias_cmd", handle_normal),
302 ("alias_cmd", handle_normal),
302 ("alias_head.with_dot", handle_normal)])
303 ("alias_head.with_dot", handle_normal)])
303 del ns['alias_cmd']
304 del ns['alias_cmd']
304 del ns['alias_head']
305 del ns['alias_head']
305
306
306 ip.options.autocall = 1
307 ip.options.autocall = 1
307
308
308
309
309
310
310
311
311 # Autocall
312 # Autocall
312 # ========
313 # ========
313
314
314 # For all the tests below, 'len' is callable / 'thing' is not
315 # For all the tests below, 'len' is callable / 'thing' is not
315
316
316 # Objects which are instances of IPyAutocall are *always* autocalled
317 # Objects which are instances of IPyAutocall are *always* autocalled
317 import IPython.ipapi
318 import IPython.ipapi
318 class Autocallable(IPython.ipapi.IPyAutocall):
319 class Autocallable(IPython.ipapi.IPyAutocall):
319 def __call__(self):
320 def __call__(self):
320 return "called"
321 return "called"
321
322
322 autocallable = Autocallable()
323 autocallable = Autocallable()
323 ip.to_user_ns('autocallable')
324 ip.to_user_ns('autocallable')
324
325
325
326
326 # First, with autocalling fully off
327 # First, with autocalling fully off
327 ip.options.autocall = 0
328 ip.options.autocall = 0
328 run_handler_tests( [
329 run_handler_tests( [
329 # With no escapes, no autocalling expansions happen, callable or not,
330 # With no escapes, no autocalling expansions happen, callable or not,
330 # unless the obj extends IPyAutocall
331 # unless the obj extends IPyAutocall
331 ( 'len autocall_0', handle_normal),
332 ( 'len autocall_0', handle_normal),
332 ( 'thing autocall_0', handle_normal),
333 ( 'thing autocall_0', handle_normal),
333 ( 'autocallable', handle_auto),
334 ( 'autocallable', handle_auto),
334
335
335 # With explicit escapes, callable and non-callables both get expanded,
336 # With explicit escapes, callable and non-callables both get expanded,
336 # regardless of the %autocall setting:
337 # regardless of the %autocall setting:
337 ( '/len autocall_0', handle_auto),
338 ( '/len autocall_0', handle_auto),
338 ( ',len autocall_0 b0', handle_auto),
339 ( ',len autocall_0 b0', handle_auto),
339 ( ';len autocall_0 b0', handle_auto),
340 ( ';len autocall_0 b0', handle_auto),
340
341
341 ( '/thing autocall_0', handle_auto),
342 ( '/thing autocall_0', handle_auto),
342 ( ',thing autocall_0 b0', handle_auto),
343 ( ',thing autocall_0 b0', handle_auto),
343 ( ';thing autocall_0 b0', handle_auto),
344 ( ';thing autocall_0 b0', handle_auto),
344
345
345 # Explicit autocall should not trigger if there is leading whitespace
346 # Explicit autocall should not trigger if there is leading whitespace
346 ( ' /len autocall_0', handle_normal),
347 ( ' /len autocall_0', handle_normal),
347 ( ' ;len autocall_0', handle_normal),
348 ( ' ;len autocall_0', handle_normal),
348 ( ' ,len autocall_0', handle_normal),
349 ( ' ,len autocall_0', handle_normal),
349 ( ' / len autocall_0', handle_normal),
350 ( ' / len autocall_0', handle_normal),
350
351
351 # But should work if the whitespace comes after the esc char
352 # But should work if the whitespace comes after the esc char
352 ( '/ len autocall_0', handle_auto),
353 ( '/ len autocall_0', handle_auto),
353 ( '; len autocall_0', handle_auto),
354 ( '; len autocall_0', handle_auto),
354 ( ', len autocall_0', handle_auto),
355 ( ', len autocall_0', handle_auto),
355 ( '/ len autocall_0', handle_auto),
356 ( '/ len autocall_0', handle_auto),
356 ])
357 ])
357
358
358
359
359 # Now, with autocall in default, 'smart' mode
360 # Now, with autocall in default, 'smart' mode
360 ip.options.autocall = 1
361 ip.options.autocall = 1
361 run_handler_tests( [
362 run_handler_tests( [
362 # Autocalls without escapes -- only expand if it's callable
363 # Autocalls without escapes -- only expand if it's callable
363 ( 'len a1', handle_auto),
364 ( 'len a1', handle_auto),
364 ( 'thing a1', handle_normal),
365 ( 'thing a1', handle_normal),
365 ( 'autocallable', handle_auto),
366 ( 'autocallable', handle_auto),
366
367
367 # As above, all explicit escapes generate auto-calls, callable or not
368 # As above, all explicit escapes generate auto-calls, callable or not
368 ( '/len a1', handle_auto),
369 ( '/len a1', handle_auto),
369 ( ',len a1 b1', handle_auto),
370 ( ',len a1 b1', handle_auto),
370 ( ';len a1 b1', handle_auto),
371 ( ';len a1 b1', handle_auto),
371 ( '/thing a1', handle_auto),
372 ( '/thing a1', handle_auto),
372 ( ',thing a1 b1', handle_auto),
373 ( ',thing a1 b1', handle_auto),
373 ( ';thing a1 b1', handle_auto),
374 ( ';thing a1 b1', handle_auto),
374
375
375 # Autocalls only happen on things which look like funcs, even if
376 # Autocalls only happen on things which look like funcs, even if
376 # explicitly requested. Which, in this case means they look like a
377 # explicitly requested. Which, in this case means they look like a
377 # sequence of identifiers and . attribute references. Possibly the
378 # sequence of identifiers and . attribute references. Possibly the
378 # second of these two should trigger handle_auto. But not for now.
379 # second of these two should trigger handle_auto. But not for now.
379 ( '"abc".join range(4)', handle_normal),
380 ( '"abc".join range(4)', handle_normal),
380 ( '/"abc".join range(4)', handle_normal),
381 ( '/"abc".join range(4)', handle_normal),
381 ])
382 ])
382
383
383
384
384 # No tests for autocall = 2, since the extra magic there happens inside the
385 # No tests for autocall = 2, since the extra magic there happens inside the
385 # handle_auto function, which our test doesn't examine.
386 # handle_auto function, which our test doesn't examine.
386
387
387 # Note that we leave autocall in default, 1, 'smart' mode
388 # Note that we leave autocall in default, 1, 'smart' mode
388
389
389
390
390 # Autocall / Binary operators
391 # Autocall / Binary operators
391 # ==========================
392 # ==========================
392
393
393 # Even with autocall on, 'len in thing' won't transform.
394 # Even with autocall on, 'len in thing' won't transform.
394 # But ';len in thing' will
395 # But ';len in thing' will
395
396
396 # Note, the tests below don't check for multi-char ops. It could.
397 # Note, the tests below don't check for multi-char ops. It could.
397
398
398 # XXX % is a binary op and should be in the list, too, but fails
399 # XXX % is a binary op and should be in the list, too, but fails
399 bin_ops = list(r'<>,&^|*/+-') + 'is not in and or'.split()
400 bin_ops = list(r'<>,&^|*/+-') + 'is not in and or'.split()
400 bin_tests = []
401 bin_tests = []
401 for b in bin_ops:
402 for b in bin_ops:
402 bin_tests.append(('len %s binop_autocall' % b, handle_normal))
403 bin_tests.append(('len %s binop_autocall' % b, handle_normal))
403 bin_tests.append((';len %s binop_autocall' % b, handle_auto))
404 bin_tests.append((';len %s binop_autocall' % b, handle_auto))
404 bin_tests.append((',len %s binop_autocall' % b, handle_auto))
405 bin_tests.append((',len %s binop_autocall' % b, handle_auto))
405 bin_tests.append(('/len %s binop_autocall' % b, handle_auto))
406 bin_tests.append(('/len %s binop_autocall' % b, handle_auto))
406
407
407 # Who loves auto-generating tests?
408 # Who loves auto-generating tests?
408 run_handler_tests(bin_tests)
409 run_handler_tests(bin_tests)
409
410
410
411
411 # Possibly add tests for namespace shadowing (really ofind's business?).
412 # Possibly add tests for namespace shadowing (really ofind's business?).
412 #
413 #
413 # user > ipython internal > python builtin > alias > magic
414 # user > ipython internal > python builtin > alias > magic
414
415
415
416
416 # ============
417 # ============
417 # Test Summary
418 # Test Summary
418 # ============
419 # ============
419 num_f = len(failures)
420 num_f = len(failures)
420 if verbose:
421 if verbose:
421 print
422 print
422 print "%s tests run, %s failure%s" % (num_tests,
423 print "%s tests run, %s failure%s" % (num_tests,
423 num_f,
424 num_f,
424 num_f != 1 and "s" or "")
425 num_f != 1 and "s" or "")
425 for f in failures:
426 for f in failures:
426 print f
427 print f
427
428
General Comments 0
You need to be logged in to leave comments. Login now