##// END OF EJS Templates
Refined checking for assignment vs other python operators in the new input transformation/prefiltering system. This makes it once again possible for magics to do useful things which involve python operator characters (e.g. 'cd /'). Also added tests to verify this.
dan.milstein -
Show More
@@ -1,298 +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 checkPythonChars,
141 checkAssignment,
142 checkAutomagic,
142 checkAutomagic,
143 checkPythonOps,
143 checkAlias,
144 checkAlias,
144 checkAutocall,
145 checkAutocall,
145 ]:
146 ]:
146 handler = check(line_info, ip)
147 handler = check(line_info, ip)
147 if handler:
148 if handler:
148 return handler(line_info)
149 return handler(line_info)
149
150
150 return ip.handle_normal(line_info)
151 return ip.handle_normal(line_info)
151
152
152 # Handler checks
153 # Handler checks
153 #
154 #
154 # 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
155 # 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
156 # 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
157 # handlers which they return are *bound* methods of the InteractiveShell
158 # handlers which they return are *bound* methods of the InteractiveShell
158 # object.
159 # object.
159 #
160 #
160 # In general, these checks should only take responsibility for their 'own'
161 # In general, these checks should only take responsibility for their 'own'
161 # 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
162 # let the rest of the check sequence run.
163 # let the rest of the check sequence run.
163 def checkEmacs(l_info,ip):
164 def checkEmacs(l_info,ip):
164 "Emacs ipython-mode tags certain input lines."
165 "Emacs ipython-mode tags certain input lines."
165 if l_info.line.endswith('# PYTHON-MODE'):
166 if l_info.line.endswith('# PYTHON-MODE'):
166 return ip.handle_emacs
167 return ip.handle_emacs
167 else:
168 else:
168 return None
169 return None
169
170
170 def checkIPyAutocall(l_info,ip):
171 def checkIPyAutocall(l_info,ip):
171 "Instances of IPyAutocall in user_ns get autocalled immediately"
172 "Instances of IPyAutocall in user_ns get autocalled immediately"
172 obj = ip.user_ns.get(l_info.iFun, None)
173 obj = ip.user_ns.get(l_info.iFun, None)
173 if isinstance(obj, IPython.ipapi.IPyAutocall):
174 if isinstance(obj, IPython.ipapi.IPyAutocall):
174 obj.set_ip(ip.api)
175 obj.set_ip(ip.api)
175 return ip.handle_auto
176 return ip.handle_auto
176 else:
177 else:
177 return None
178 return None
178
179
179
180
180 def checkMultiLineShell(l_info,ip):
181 def checkMultiLineShell(l_info,ip):
181 "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"
182 # 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
183 # 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
184 # both ! and !!.
185 # both ! and !!.
185 if l_info.continue_prompt \
186 if l_info.continue_prompt \
186 and ip.rc.multi_line_specials \
187 and ip.rc.multi_line_specials \
187 and l_info.iFun.startswith(ip.ESC_SHELL):
188 and l_info.iFun.startswith(ip.ESC_SHELL):
188 return ip.handle_shell_escape
189 return ip.handle_shell_escape
189 else:
190 else:
190 return None
191 return None
191
192
192 def checkEscChars(l_info,ip):
193 def checkEscChars(l_info,ip):
193 """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,
194 or None if there is no escape char."""
195 or None if there is no escape char."""
195 if l_info.line[-1] == ip.ESC_HELP \
196 if l_info.line[-1] == ip.ESC_HELP \
196 and l_info.preChar != ip.ESC_SHELL \
197 and l_info.preChar != ip.ESC_SHELL \
197 and l_info.preChar != ip.ESC_SH_CAP:
198 and l_info.preChar != ip.ESC_SH_CAP:
198 # 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,
199 # 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
200 return ip.handle_help
201 return ip.handle_help
201 elif l_info.preChar in ip.esc_handlers:
202 elif l_info.preChar in ip.esc_handlers:
202 return ip.esc_handlers[l_info.preChar]
203 return ip.esc_handlers[l_info.preChar]
203 else:
204 else:
204 return None
205 return None
206
207
208 def checkAssignment(l_info,ip):
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.
205
211
206 def checkPythonChars(l_info,ip):
212 This allows users to assign to either alias or magic names true python
207 """If the 'rest' of the line begins with an (in)equality, assginment,
213 variables (the magic/alias systems always take second seat to true
208 function call or tuple comma, we should simply execute the line
214 python code). E.g. ls='hi', or ls,that=1,2"""
209 (regardless of whether or not there's a possible alias, automagic or
215 if l_info.theRest and l_info.theRest[0] in '=,':
210 autocall expansion). This both avoids spurious geattr() accesses on
211 objects upon assignment, and also allows users to assign to either alias
212 or magic names true python variables (the magic/alias systems always
213 take second seat to true python code). E.g. ls='hi', or ls,that=1,2"""
214 if l_info.theRest and l_info.theRest[0] in '!=()<>,+*/%^&|':
215 return ip.handle_normal
216 return ip.handle_normal
216 else:
217 else:
217 return None
218 return None
218
219
220
219 def checkAutomagic(l_info,ip):
221 def checkAutomagic(l_info,ip):
220 """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,
221 non-auto magic would already have been triggered via '%' in
223 non-auto magic would already have been triggered via '%' in
222 check_esc_chars. This just checks for automagic."""
224 check_esc_chars. This just checks for automagic. Also, before
225 triggering the magic handler, make sure that there is nothing in the
226 user namespace which could shadow it."""
223 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):
224 return None
228 return None
225
229
226 # 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.
227 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:
228 return None
232 return None
229
233
230 head = l_info.iFun.split('.',1)[0]
234 head = l_info.iFun.split('.',1)[0]
231 if isShadowed(head,ip):
235 if isShadowed(head,ip):
232 return None
236 return None
233
237
234 return ip.handle_magic
238 return ip.handle_magic
235
239
236
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
237 def checkAlias(l_info,ip):
252 def checkAlias(l_info,ip):
238 "Check if the initital identifier on the line is an alias."
253 "Check if the initital identifier on the line is an alias."
239 # Note: aliases can not contain '.'
254 # Note: aliases can not contain '.'
240 head = l_info.iFun.split('.',1)[0]
255 head = l_info.iFun.split('.',1)[0]
241
256
242 if l_info.iFun not in ip.alias_table \
257 if l_info.iFun not in ip.alias_table \
243 or head not in ip.alias_table \
258 or head not in ip.alias_table \
244 or isShadowed(head,ip):
259 or isShadowed(head,ip):
245 return None
260 return None
246
261
247 return ip.handle_alias
262 return ip.handle_alias
248
263
249
264
250 def checkAutocall(l_info,ip):
265 def checkAutocall(l_info,ip):
251 "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."
252 if not ip.rc.autocall:
267 if not ip.rc.autocall:
253 return None
268 return None
254
269
255 oinfo = l_info.ofind(ip) # This can mutate state via getattr
270 oinfo = l_info.ofind(ip) # This can mutate state via getattr
256 if not oinfo['found']:
271 if not oinfo['found']:
257 return None
272 return None
258
273
259 if callable(oinfo['obj']) \
274 if callable(oinfo['obj']) \
260 and (not re_exclude_auto.match(l_info.theRest)) \
275 and (not re_exclude_auto.match(l_info.theRest)) \
261 and re_fun_name.match(l_info.iFun):
276 and re_fun_name.match(l_info.iFun):
262 #print 'going auto' # dbg
277 #print 'going auto' # dbg
263 return ip.handle_auto
278 return ip.handle_auto
264 else:
279 else:
265 #print 'was callable?', callable(l_info.oinfo['obj']) # dbg
280 #print 'was callable?', callable(l_info.oinfo['obj']) # dbg
266 return None
281 return None
267
282
268 # RegExp to identify potential function names
283 # RegExp to identify potential function names
269 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_.]*) *$')
270
285
271 # RegExp to exclude strings with this start from autocalling. In
286 # RegExp to exclude strings with this start from autocalling. In
272 # 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
273 # 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
274 # characters '!=()' don't need to be checked for, as the checkPythonChars
289 # characters '!=()' don't need to be checked for, as the checkPythonChars
275 # routine explicitely does so, to catch direct calls and rebindings of
290 # routine explicitely does so, to catch direct calls and rebindings of
276 # existing names.
291 # existing names.
277
292
278 # 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
279 # it affects the rest of the group in square brackets.
294 # it affects the rest of the group in square brackets.
280 re_exclude_auto = re.compile(r'^[,&^\|\*/\+-]'
295 re_exclude_auto = re.compile(r'^[,&^\|\*/\+-]'
281 r'|^is |^not |^in |^and |^or ')
296 r'|^is |^not |^in |^and |^or ')
282
297
283 # 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
284 # (experimental). For this to work, the line_split regexp would need
299 # (experimental). For this to work, the line_split regexp would need
285 # 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
286 # 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_.
287 #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_.\[\]]*) ?$')
288
303
289 # Handler Check Utilities
304 # Handler Check Utilities
290 def isShadowed(identifier,ip):
305 def isShadowed(identifier,ip):
291 """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
292 the alias and magic namespaces? Note that an identifier is different
307 the alias and magic namespaces? Note that an identifier is different
293 than iFun, because it can not contain a '.' character."""
308 than iFun, because it can not contain a '.' character."""
294 # This is much safer than calling ofind, which can change state
309 # This is much safer than calling ofind, which can change state
295 return (identifier in ip.user_ns \
310 return (identifier in ip.user_ns \
296 or identifier in ip.internal_ns \
311 or identifier in ip.internal_ns \
297 or identifier in ip.ns_table['builtin'])
312 or identifier in ip.ns_table['builtin'])
298
313
@@ -1,424 +1,427 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),
218 ( 'cd = 2', handle_normal),
219 ])
217
220
218 # 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,
219 # 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.
220 magic_killing_tests = []
223 #magic_killing_tests = []
221 for c in list('!=()<>,'):
224 #for c in list('!=()<>,'):
222 magic_killing_tests.append(('cpaste %s killed_automagic' % c, handle_normal))
225 # magic_killing_tests.append(('cpaste %s killed_automagic' % c, handle_normal))
223 magic_killing_tests.append(('%%cpaste %s escaped_magic' % c, handle_magic))
226 # magic_killing_tests.append(('%%cpaste %s escaped_magic' % c, handle_magic))
224 run_handler_tests(magic_killing_tests)
227 #run_handler_tests(magic_killing_tests)
225
228
226 # magic on indented continuation lines -- on iff multi_line_specials == 1
229 # magic on indented continuation lines -- on iff multi_line_specials == 1
227 ip.options.multi_line_specials = 0
230 ip.options.multi_line_specials = 0
228 ln = ' cpaste multi_line off kills magic'
231 ln = ' cpaste multi_line off kills magic'
229 ignore = ip.IP.prefilter(ln, continue_prompt=True)
232 ignore = ip.IP.prefilter(ln, continue_prompt=True)
230 check_handler(handle_normal, ln)
233 check_handler(handle_normal, ln)
231
234
232 ip.options.multi_line_specials = 1
235 ip.options.multi_line_specials = 1
233 ln = ' cpaste multi_line on enables magic'
236 ln = ' cpaste multi_line on enables magic'
234 ignore = ip.IP.prefilter(ln, continue_prompt=True)
237 ignore = ip.IP.prefilter(ln, continue_prompt=True)
235 check_handler(handle_magic, ln)
238 check_handler(handle_magic, ln)
236
239
237 # user namespace shadows the magic one unless shell escaped
240 # user namespace shadows the magic one unless shell escaped
238 ip.user_ns['cpaste'] = 'user_ns'
241 ip.user_ns['cpaste'] = 'user_ns'
239 run_handler_tests([
242 run_handler_tests([
240 ( 'cpaste', handle_normal),
243 ( 'cpaste', handle_normal),
241 ( '%cpaste', handle_magic)])
244 ( '%cpaste', handle_magic)])
242 del ip.user_ns['cpaste']
245 del ip.user_ns['cpaste']
243
246
244
247
245
248
246 # Check for !=() turning off .ofind
249 # Check for !=() turning off .ofind
247 # =================================
250 # =================================
248 class AttributeMutator(object):
251 class AttributeMutator(object):
249 """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"""
250 def __init__(self):
253 def __init__(self):
251 self.called = False
254 self.called = False
252
255
253 def getFoo(self): self.called = True
256 def getFoo(self): self.called = True
254 foo = property(getFoo)
257 foo = property(getFoo)
255
258
256 attr_mutator = AttributeMutator()
259 attr_mutator = AttributeMutator()
257 ip.to_user_ns('attr_mutator')
260 ip.to_user_ns('attr_mutator')
258
261
259 ip.options.autocall = 1
262 ip.options.autocall = 1
260
263
261 run_one_test('attr_mutator.foo should mutate', handle_normal)
264 run_one_test('attr_mutator.foo should mutate', handle_normal)
262 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')
263
266
264 for c in list('!=()<>+*/%^&|'):
267 for c in list('!=()<>+*/%^&|'):
265 attr_mutator.called = False
268 attr_mutator.called = False
266 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)
267 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)
268
271
269 check(not attr_mutator.called,
272 check(not attr_mutator.called,
270 'ofind should not be called near character %s' % c)
273 'ofind should not be called near character %s' % c)
271
274
272
275
273
276
274 # Alias expansion
277 # Alias expansion
275 # ===============
278 # ===============
276
279
277 # 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
278 # __builtin__ namespaces
281 # __builtin__ namespaces
279 #
282 #
280 # 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,
281 # with autocall on, it doesn't. Hmmm.
284 # with autocall on, it doesn't. Hmmm.
282 import __builtin__
285 import __builtin__
283 for ac_state in [0,1]:
286 for ac_state in [0,1]:
284 ip.options.autocall = ac_state
287 ip.options.autocall = ac_state
285 ip.IP.alias_table['alias_cmd'] = 'alias_result'
288 ip.IP.alias_table['alias_cmd'] = 'alias_result'
286 ip.IP.alias_table['alias_head.with_dot'] = 'alias_result'
289 ip.IP.alias_table['alias_head.with_dot'] = 'alias_result'
287 run_handler_tests([
290 run_handler_tests([
288 ("alias_cmd", handle_alias),
291 ("alias_cmd", handle_alias),
289 # XXX See note above
292 # XXX See note above
290 #("alias_head.with_dot unshadowed, autocall=%s" % ac_state, handle_alias),
293 #("alias_head.with_dot unshadowed, autocall=%s" % ac_state, handle_alias),
291 ("alias_cmd.something aliases must match whole expr", handle_normal),
294 ("alias_cmd.something aliases must match whole expr", handle_normal),
292 ])
295 ])
293
296
294 for ns in [ip.user_ns, ip.IP.internal_ns, __builtin__.__dict__ ]:
297 for ns in [ip.user_ns, ip.IP.internal_ns, __builtin__.__dict__ ]:
295 ns['alias_cmd'] = 'a user value'
298 ns['alias_cmd'] = 'a user value'
296 ns['alias_head'] = 'a user value'
299 ns['alias_head'] = 'a user value'
297 run_handler_tests([
300 run_handler_tests([
298 ("alias_cmd", handle_normal),
301 ("alias_cmd", handle_normal),
299 ("alias_head.with_dot", handle_normal)])
302 ("alias_head.with_dot", handle_normal)])
300 del ns['alias_cmd']
303 del ns['alias_cmd']
301 del ns['alias_head']
304 del ns['alias_head']
302
305
303 ip.options.autocall = 1
306 ip.options.autocall = 1
304
307
305
308
306
309
307
310
308 # Autocall
311 # Autocall
309 # ========
312 # ========
310
313
311 # For all the tests below, 'len' is callable / 'thing' is not
314 # For all the tests below, 'len' is callable / 'thing' is not
312
315
313 # Objects which are instances of IPyAutocall are *always* autocalled
316 # Objects which are instances of IPyAutocall are *always* autocalled
314 import IPython.ipapi
317 import IPython.ipapi
315 class Autocallable(IPython.ipapi.IPyAutocall):
318 class Autocallable(IPython.ipapi.IPyAutocall):
316 def __call__(self):
319 def __call__(self):
317 return "called"
320 return "called"
318
321
319 autocallable = Autocallable()
322 autocallable = Autocallable()
320 ip.to_user_ns('autocallable')
323 ip.to_user_ns('autocallable')
321
324
322
325
323 # First, with autocalling fully off
326 # First, with autocalling fully off
324 ip.options.autocall = 0
327 ip.options.autocall = 0
325 run_handler_tests( [
328 run_handler_tests( [
326 # With no escapes, no autocalling expansions happen, callable or not,
329 # With no escapes, no autocalling expansions happen, callable or not,
327 # unless the obj extends IPyAutocall
330 # unless the obj extends IPyAutocall
328 ( 'len autocall_0', handle_normal),
331 ( 'len autocall_0', handle_normal),
329 ( 'thing autocall_0', handle_normal),
332 ( 'thing autocall_0', handle_normal),
330 ( 'autocallable', handle_auto),
333 ( 'autocallable', handle_auto),
331
334
332 # With explicit escapes, callable and non-callables both get expanded,
335 # With explicit escapes, callable and non-callables both get expanded,
333 # regardless of the %autocall setting:
336 # regardless of the %autocall setting:
334 ( '/len autocall_0', handle_auto),
337 ( '/len autocall_0', handle_auto),
335 ( ',len autocall_0 b0', handle_auto),
338 ( ',len autocall_0 b0', handle_auto),
336 ( ';len autocall_0 b0', handle_auto),
339 ( ';len autocall_0 b0', handle_auto),
337
340
338 ( '/thing autocall_0', handle_auto),
341 ( '/thing autocall_0', handle_auto),
339 ( ',thing autocall_0 b0', handle_auto),
342 ( ',thing autocall_0 b0', handle_auto),
340 ( ';thing autocall_0 b0', handle_auto),
343 ( ';thing autocall_0 b0', handle_auto),
341
344
342 # Explicit autocall should not trigger if there is leading whitespace
345 # Explicit autocall should not trigger if there is leading whitespace
343 ( ' /len autocall_0', handle_normal),
346 ( ' /len autocall_0', handle_normal),
344 ( ' ;len autocall_0', handle_normal),
347 ( ' ;len autocall_0', handle_normal),
345 ( ' ,len autocall_0', handle_normal),
348 ( ' ,len autocall_0', handle_normal),
346 ( ' / len autocall_0', handle_normal),
349 ( ' / len autocall_0', handle_normal),
347
350
348 # But should work if the whitespace comes after the esc char
351 # But should work if the whitespace comes after the esc char
349 ( '/ len autocall_0', handle_auto),
352 ( '/ len autocall_0', handle_auto),
350 ( '; len autocall_0', handle_auto),
353 ( '; len autocall_0', handle_auto),
351 ( ', len autocall_0', handle_auto),
354 ( ', len autocall_0', handle_auto),
352 ( '/ len autocall_0', handle_auto),
355 ( '/ len autocall_0', handle_auto),
353 ])
356 ])
354
357
355
358
356 # Now, with autocall in default, 'smart' mode
359 # Now, with autocall in default, 'smart' mode
357 ip.options.autocall = 1
360 ip.options.autocall = 1
358 run_handler_tests( [
361 run_handler_tests( [
359 # Autocalls without escapes -- only expand if it's callable
362 # Autocalls without escapes -- only expand if it's callable
360 ( 'len a1', handle_auto),
363 ( 'len a1', handle_auto),
361 ( 'thing a1', handle_normal),
364 ( 'thing a1', handle_normal),
362 ( 'autocallable', handle_auto),
365 ( 'autocallable', handle_auto),
363
366
364 # As above, all explicit escapes generate auto-calls, callable or not
367 # As above, all explicit escapes generate auto-calls, callable or not
365 ( '/len a1', handle_auto),
368 ( '/len a1', handle_auto),
366 ( ',len a1 b1', handle_auto),
369 ( ',len a1 b1', handle_auto),
367 ( ';len a1 b1', handle_auto),
370 ( ';len a1 b1', handle_auto),
368 ( '/thing a1', handle_auto),
371 ( '/thing a1', handle_auto),
369 ( ',thing a1 b1', handle_auto),
372 ( ',thing a1 b1', handle_auto),
370 ( ';thing a1 b1', handle_auto),
373 ( ';thing a1 b1', handle_auto),
371
374
372 # Autocalls only happen on things which look like funcs, even if
375 # Autocalls only happen on things which look like funcs, even if
373 # explicitly requested. Which, in this case means they look like a
376 # explicitly requested. Which, in this case means they look like a
374 # sequence of identifiers and . attribute references. Possibly the
377 # sequence of identifiers and . attribute references. Possibly the
375 # second of these two should trigger handle_auto. But not for now.
378 # second of these two should trigger handle_auto. But not for now.
376 ( '"abc".join range(4)', handle_normal),
379 ( '"abc".join range(4)', handle_normal),
377 ( '/"abc".join range(4)', handle_normal),
380 ( '/"abc".join range(4)', handle_normal),
378 ])
381 ])
379
382
380
383
381 # No tests for autocall = 2, since the extra magic there happens inside the
384 # No tests for autocall = 2, since the extra magic there happens inside the
382 # handle_auto function, which our test doesn't examine.
385 # handle_auto function, which our test doesn't examine.
383
386
384 # Note that we leave autocall in default, 1, 'smart' mode
387 # Note that we leave autocall in default, 1, 'smart' mode
385
388
386
389
387 # Autocall / Binary operators
390 # Autocall / Binary operators
388 # ==========================
391 # ==========================
389
392
390 # Even with autocall on, 'len in thing' won't transform.
393 # Even with autocall on, 'len in thing' won't transform.
391 # But ';len in thing' will
394 # But ';len in thing' will
392
395
393 # Note, the tests below don't check for multi-char ops. It could.
396 # Note, the tests below don't check for multi-char ops. It could.
394
397
395 # XXX % is a binary op and should be in the list, too, but fails
398 # XXX % is a binary op and should be in the list, too, but fails
396 bin_ops = list(r'<>,&^|*/+-') + 'is not in and or'.split()
399 bin_ops = list(r'<>,&^|*/+-') + 'is not in and or'.split()
397 bin_tests = []
400 bin_tests = []
398 for b in bin_ops:
401 for b in bin_ops:
399 bin_tests.append(('len %s binop_autocall' % b, handle_normal))
402 bin_tests.append(('len %s binop_autocall' % b, handle_normal))
400 bin_tests.append((';len %s binop_autocall' % b, handle_auto))
403 bin_tests.append((';len %s binop_autocall' % b, handle_auto))
401 bin_tests.append((',len %s binop_autocall' % b, handle_auto))
404 bin_tests.append((',len %s binop_autocall' % b, handle_auto))
402 bin_tests.append(('/len %s binop_autocall' % b, handle_auto))
405 bin_tests.append(('/len %s binop_autocall' % b, handle_auto))
403
406
404 # Who loves auto-generating tests?
407 # Who loves auto-generating tests?
405 run_handler_tests(bin_tests)
408 run_handler_tests(bin_tests)
406
409
407
410
408 # Possibly add tests for namespace shadowing (really ofind's business?).
411 # Possibly add tests for namespace shadowing (really ofind's business?).
409 #
412 #
410 # user > ipython internal > python builtin > alias > magic
413 # user > ipython internal > python builtin > alias > magic
411
414
412
415
413 # ============
416 # ============
414 # Test Summary
417 # Test Summary
415 # ============
418 # ============
416 num_f = len(failures)
419 num_f = len(failures)
417 if verbose:
420 if verbose:
418 print
421 print
419 print "%s tests run, %s failure%s" % (num_tests,
422 print "%s tests run, %s failure%s" % (num_tests,
420 num_f,
423 num_f,
421 num_f != 1 and "s" or "")
424 num_f != 1 and "s" or "")
422 for f in failures:
425 for f in failures:
423 print f
426 print f
424
427
General Comments 0
You need to be logged in to leave comments. Login now