##// END OF EJS Templates
Changing input filtering to require whitespace separation between the initial command (alias, magic, autocall) and rest of line. ...
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.lstrip()
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 (separated from iFun by space if non-empty)
126 line_split = re.compile(r'^([,;/%?]|!!?|\s*)'
126 line_split = re.compile(r'^([,;/%?]|!!?|\s*)'
127 r'\s*([\w\.]+)\s*'
127 r'\s*([\w\.]+)'
128 r'(.*)$')
128 r'(\s+.*$|$)')
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 checkAlias,
143 checkAlias,
144 checkPythonOps,
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 checkAlias(l_info,ip):
241 def checkAlias(l_info,ip):
242 "Check if the initital identifier on the line is an alias."
242 "Check if the initital identifier on the line is an alias."
243 # Note: aliases can not contain '.'
243 # Note: aliases can not contain '.'
244 head = l_info.iFun.split('.',1)[0]
244 head = l_info.iFun.split('.',1)[0]
245
245
246 if l_info.iFun not in ip.alias_table \
246 if l_info.iFun not in ip.alias_table \
247 or head not in ip.alias_table \
247 or head not in ip.alias_table \
248 or isShadowed(head,ip):
248 or isShadowed(head,ip):
249 return None
249 return None
250
250
251 return ip.handle_alias
251 return ip.handle_alias
252
252
253
253
254 def checkPythonOps(l_info,ip):
254 def checkPythonOps(l_info,ip):
255 """If the 'rest' of the line begins with a function call or pretty much
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
256 any python operator, we should simply execute the line (regardless of
257 whether or not there's a possible autocall expansion). This avoids
257 whether or not there's a possible autocall expansion). This avoids
258 spurious (and very confusing) geattr() accesses."""
258 spurious (and very confusing) geattr() accesses."""
259 if l_info.theRest and l_info.theRest[0] in '!=()<>,+*/%^&|':
259 if l_info.theRest and l_info.theRest[0] in '!=()<>,+*/%^&|':
260 return ip.handle_normal
260 return ip.handle_normal
261 else:
261 else:
262 return None
262 return None
263
263
264
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,201 +1,202 b''
1 """Test the various handlers which do the actual rewriting of the line."""
1 """Test the various handlers which do the actual rewriting of the line."""
2
2
3 from StringIO import StringIO
3 from StringIO import StringIO
4 import sys
4 import sys
5 sys.path.append('..')
5 sys.path.append('..')
6
6
7 failures = []
7 failures = []
8 num_tests = 0
8 num_tests = 0
9
9
10 def run(tests):
10 def run(tests):
11 """Loop through a list of (pre, post) inputs, where pre is the string
11 """Loop through a list of (pre, post) inputs, where pre is the string
12 handed to ipython, and post is how that string looks after it's been
12 handed to ipython, and post is how that string looks after it's been
13 transformed (i.e. ipython's notion of _i)"""
13 transformed (i.e. ipython's notion of _i)"""
14 for pre, post in tests:
14 for pre, post in tests:
15 global num_tests
15 global num_tests
16 num_tests += 1
16 num_tests += 1
17 ip.runlines(pre)
17 ip.runlines(pre)
18 ip.runlines('_i') # Not sure why I need this...
18 ip.runlines('_i') # Not sure why I need this...
19 actual = ip.user_ns['_i']
19 actual = ip.user_ns['_i']
20 if actual != None: actual = actual.rstrip('\n')
20 if actual != None:
21 actual = actual.rstrip('\n')
21 if actual != post:
22 if actual != post:
22 failures.append('Expected %r to become %r, found %r' % (
23 failures.append('Expected %r to become %r, found %r' % (
23 pre, post, actual))
24 pre, post, actual))
24
25
25
26
26 # Shutdown stdout/stderr so that ipython isn't noisy during tests. Have to
27 # Shutdown stdout/stderr so that ipython isn't noisy during tests. Have to
27 # do this *before* importing IPython below.
28 # do this *before* importing IPython below.
28 #
29 #
29 # NOTE: this means that, if you stick print statements into code as part of
30 # NOTE: this means that, if you stick print statements into code as part of
30 # debugging, you won't see the results (unless you comment out some of the
31 # debugging, you won't see the results (unless you comment out some of the
31 # below). I keep on doing this, so apparently it's easy. Or I am an idiot.
32 # below). I keep on doing this, so apparently it's easy. Or I am an idiot.
32 old_stdout = sys.stdout
33 old_stdout = sys.stdout
33 old_stderr = sys.stderr
34 old_stderr = sys.stderr
34
35
35 sys.stdout = StringIO()
36 sys.stdout = StringIO()
36 sys.stderr = StringIO()
37 sys.stderr = StringIO()
37
38
38 import IPython
39 import IPython
39 import IPython.ipapi
40 import IPython.ipapi
40
41
41 IPython.Shell.start()
42 IPython.Shell.start()
42 ip = IPython.ipapi.get()
43 ip = IPython.ipapi.get()
43
44
44 class CallableIndexable(object):
45 class CallableIndexable(object):
45 def __getitem__(self, idx): return True
46 def __getitem__(self, idx): return True
46 def __call__(self, *args, **kws): return True
47 def __call__(self, *args, **kws): return True
47
48
48
49
49 try:
50 try:
50 # alias expansion
51 # alias expansion
51
52
52 # We're using 'true' as our syscall of choice because it doesn't
53 # We're using 'true' as our syscall of choice because it doesn't
53 # write anything to stdout.
54 # write anything to stdout.
54
55
55 # Turn off actual execution of aliases, because it's noisy
56 # Turn off actual execution of aliases, because it's noisy
56 old_system_cmd = ip.IP.system
57 old_system_cmd = ip.system
57 ip.IP.system = lambda cmd: None
58 ip.system = lambda cmd: None
58
59
59
60
60 ip.IP.alias_table['an_alias'] = (0, 'true')
61 ip.IP.alias_table['an_alias'] = (0, 'true')
61 # These are useful for checking a particular recursive alias issue
62 # These are useful for checking a particular recursive alias issue
62 ip.IP.alias_table['top'] = (0, 'd:/cygwin/top')
63 ip.IP.alias_table['top'] = (0, 'd:/cygwin/top')
63 ip.IP.alias_table['d'] = (0, 'true')
64 ip.IP.alias_table['d'] = (0, 'true')
64 run([("an_alias", '_ip.system("true ")'), # alias
65 run([("an_alias", '_ip.system("true ")'), # alias
65 # Below: recursive aliases should expand whitespace-surrounded
66 # Below: recursive aliases should expand whitespace-surrounded
66 # chars, *not* initial chars which happen to be aliases:
67 # chars, *not* initial chars which happen to be aliases:
67 ("top", '_ip.system("d:/cygwin/top ")'),
68 ("top", '_ip.system("d:/cygwin/top ")'),
68 ])
69 ])
69 ip.IP.system = old_system_cmd
70 ip.system = old_system_cmd
70
71
71
72
72 call_idx = CallableIndexable()
73 call_idx = CallableIndexable()
73 ip.to_user_ns('call_idx')
74 ip.to_user_ns('call_idx')
74
75
75 # For many of the below, we're also checking that leading whitespace
76 # For many of the below, we're also checking that leading whitespace
76 # turns off the esc char, which it should unless there is a continuation
77 # turns off the esc char, which it should unless there is a continuation
77 # line.
78 # line.
78 run([('"no change"', '"no change"'), # normal
79 run([('"no change"', '"no change"'), # normal
79 ("!true", '_ip.system("true")'), # shell_escapes
80 ("!true", '_ip.system("true")'), # shell_escapes
80 ("!! true", '_ip.magic("sx true")'), # shell_escapes + magic
81 ("!! true", '_ip.magic("sx true")'), # shell_escapes + magic
81 ("!!true", '_ip.magic("sx true")'), # shell_escapes + magic
82 ("!!true", '_ip.magic("sx true")'), # shell_escapes + magic
82 ("%lsmagic", '_ip.magic("lsmagic ")'), # magic
83 ("%lsmagic", '_ip.magic("lsmagic ")'), # magic
83 ("lsmagic", '_ip.magic("lsmagic ")'), # magic
84 ("lsmagic", '_ip.magic("lsmagic ")'), # magic
84 ("a = b # PYTHON-MODE", '_i'), # emacs -- avoids _in cache
85 ("a = b # PYTHON-MODE", '_i'), # emacs -- avoids _in cache
85
86
86 # post-esc-char whitespace goes inside
87 # post-esc-char whitespace goes inside
87 ("! true", '_ip.system(" true")'),
88 ("! true", '_ip.system(" true")'),
88
89
89 # Leading whitespace generally turns off escape characters
90 # Leading whitespace generally turns off escape characters
90 (" ! true", ' ! true'),
91 (" ! true", ' ! true'),
91 (" !true", ' !true'),
92 (" !true", ' !true'),
92
93
93 # handle_help
94 # handle_help
94
95
95 # These are weak tests -- just looking at what the help handlers
96 # These are weak tests -- just looking at what the help handlers
96 # logs, which is not how it really does its work. But it still
97 # logs, which is not how it really does its work. But it still
97 # lets us check the key paths through the handler.
98 # lets us check the key paths through the handler.
98
99
99 ("x=1 # what?", "x=1 # what?"), # no help if valid python
100 ("x=1 # what?", "x=1 # what?"), # no help if valid python
100 ("len?", "#?len"), # this is what help logs when it runs
101 ("len?", "#?len"), # this is what help logs when it runs
101 ("len??", "#?len?"),
102 ("len??", "#?len?"),
102 ("?len", "#?len"),
103 ("?len", "#?len"),
103 ])
104 ])
104
105
105 # multi_line_specials
106 # multi_line_specials
106 ip.options.multi_line_specials = 0
107 ip.options.multi_line_specials = 0
107 # W/ multi_line_specials off, leading ws kills esc chars/autoexpansion
108 # W/ multi_line_specials off, leading ws kills esc chars/autoexpansion
108 run([
109 run([
109 ('if 1:\n !true', 'if 1:\n !true'),
110 ('if 1:\n !true', 'if 1:\n !true'),
110 ('if 1:\n lsmagic', 'if 1:\n lsmagic'),
111 ('if 1:\n lsmagic', 'if 1:\n lsmagic'),
111 ('if 1:\n an_alias', 'if 1:\n an_alias'),
112 ('if 1:\n an_alias', 'if 1:\n an_alias'),
112 ])
113 ])
113
114
114 ip.options.multi_line_specials = 1
115 ip.options.multi_line_specials = 1
115 # initial indents must be preserved.
116 # initial indents must be preserved.
116 run([
117 run([
117 ('if 1:\n !true', 'if 1:\n _ip.system("true")'),
118 ('if 1:\n !true', 'if 1:\n _ip.system("true")'),
118 ('if 1:\n lsmagic', 'if 1:\n _ip.magic("lsmagic ")'),
119 ('if 1:\n lsmagic', 'if 1:\n _ip.magic("lsmagic ")'),
119 ('if 1:\n an_alias', 'if 1:\n _ip.system("true ")'),
120 ('if 1:\n an_alias', 'if 1:\n _ip.system("true ")'),
120 # Weird one
121 # Weird one
121 ('if 1:\n !!true', 'if 1:\n _ip.magic("sx true")'),
122 ('if 1:\n !!true', 'if 1:\n _ip.magic("sx true")'),
122
123
123
124
124 # Even with m_l_s on, all esc_chars except ! are off
125 # Even with m_l_s on, all esc_chars except ! are off
125 ('if 1:\n %lsmagic', 'if 1:\n %lsmagic'),
126 ('if 1:\n %lsmagic', 'if 1:\n %lsmagic'),
126 ('if 1:\n /fun 1 2', 'if 1:\n /fun 1 2'),
127 ('if 1:\n /fun 1 2', 'if 1:\n /fun 1 2'),
127 ('if 1:\n ;fun 1 2', 'if 1:\n ;fun 1 2'),
128 ('if 1:\n ;fun 1 2', 'if 1:\n ;fun 1 2'),
128 ('if 1:\n ,fun 1 2', 'if 1:\n ,fun 1 2'),
129 ('if 1:\n ,fun 1 2', 'if 1:\n ,fun 1 2'),
129 ('if 1:\n ?fun 1 2', 'if 1:\n ?fun 1 2'),
130 ('if 1:\n ?fun 1 2', 'if 1:\n ?fun 1 2'),
130 # What about !!
131 # What about !!
131 ])
132 ])
132
133
133
134
134 # Objects which are instances of IPyAutocall are *always* autocalled
135 # Objects which are instances of IPyAutocall are *always* autocalled
135 import IPython.ipapi
136 import IPython.ipapi
136 class Autocallable(IPython.ipapi.IPyAutocall):
137 class Autocallable(IPython.ipapi.IPyAutocall):
137 def __call__(self):
138 def __call__(self):
138 return "called"
139 return "called"
139
140
140 autocallable = Autocallable()
141 autocallable = Autocallable()
141 ip.to_user_ns('autocallable')
142 ip.to_user_ns('autocallable')
142
143
143 # auto
144 # auto
144 ip.options.autocall = 0
145 ip.options.autocall = 0
145 # Only explicit escapes or instances of IPyAutocallable should get
146 # Only explicit escapes or instances of IPyAutocallable should get
146 # expanded
147 # expanded
147 run([
148 run([
148 ('len "abc"', 'len "abc"'),
149 ('len "abc"', 'len "abc"'),
149 ('autocallable', 'autocallable()'),
150 ('autocallable', 'autocallable()'),
150 (",list 1 2 3", 'list("1", "2", "3")'),
151 (",list 1 2 3", 'list("1", "2", "3")'),
151 (";list 1 2 3", 'list("1 2 3")'),
152 (";list 1 2 3", 'list("1 2 3")'),
152 ("/len range(1,4)", 'len(range(1,4))'),
153 ("/len range(1,4)", 'len(range(1,4))'),
153 ])
154 ])
154 ip.options.autocall = 1
155 ip.options.autocall = 1
155 run([
156 run([
156 (",list 1 2 3", 'list("1", "2", "3")'),
157 (",list 1 2 3", 'list("1", "2", "3")'),
157 (";list 1 2 3", 'list("1 2 3")'),
158 (";list 1 2 3", 'list("1 2 3")'),
158 ("/len range(1,4)", 'len(range(1,4))'),
159 ("/len range(1,4)", 'len(range(1,4))'),
159 ('len "abc"', 'len("abc")'),
160 ('len "abc"', 'len("abc")'),
160 ('len "abc";', 'len("abc");'), # ; is special -- moves out of parens
161 ('len "abc";', 'len("abc");'), # ; is special -- moves out of parens
161 # Autocall is turned off if first arg is [] and the object
162 # Autocall is turned off if first arg is [] and the object
162 # is both callable and indexable. Like so:
163 # is both callable and indexable. Like so:
163 ('len [1,2]', 'len([1,2])'), # len doesn't support __getitem__...
164 ('len [1,2]', 'len([1,2])'), # len doesn't support __getitem__...
164 ('call_idx [1]', 'call_idx [1]'), # call_idx *does*..
165 ('call_idx [1]', 'call_idx [1]'), # call_idx *does*..
165 ('call_idx 1', 'call_idx(1)'),
166 ('call_idx 1', 'call_idx(1)'),
166 ('len', 'len '), # only at 2 does it auto-call on single args
167 ('len', 'len '), # only at 2 does it auto-call on single args
167 ])
168 ])
168
169
169 ip.options.autocall = 2
170 ip.options.autocall = 2
170 run([
171 run([
171 (",list 1 2 3", 'list("1", "2", "3")'),
172 (",list 1 2 3", 'list("1", "2", "3")'),
172 (";list 1 2 3", 'list("1 2 3")'),
173 (";list 1 2 3", 'list("1 2 3")'),
173 ("/len range(1,4)", 'len(range(1,4))'),
174 ("/len range(1,4)", 'len(range(1,4))'),
174 ('len "abc"', 'len("abc")'),
175 ('len "abc"', 'len("abc")'),
175 ('len "abc";', 'len("abc");'),
176 ('len "abc";', 'len("abc");'),
176 ('len [1,2]', 'len([1,2])'),
177 ('len [1,2]', 'len([1,2])'),
177 ('call_idx [1]', 'call_idx [1]'),
178 ('call_idx [1]', 'call_idx [1]'),
178 ('call_idx 1', 'call_idx(1)'),
179 ('call_idx 1', 'call_idx(1)'),
179 # This is what's different:
180 # This is what's different:
180 ('len', 'len()'), # only at 2 does it auto-call on single args
181 ('len', 'len()'), # only at 2 does it auto-call on single args
181 ])
182 ])
182 ip.options.autocall = 1
183 ip.options.autocall = 1
183
184
184 # Ignoring handle_emacs, 'cause it doesn't do anything.
185 # Ignoring handle_emacs, 'cause it doesn't do anything.
185 finally:
186 finally:
186 sys.stdout = old_stdout
187 sys.stdout = old_stdout
187 sys.stderr = old_stderr
188 sys.stderr = old_stderr
188
189
189
190
190
191
191
192
192 num_f = len(failures)
193 num_f = len(failures)
193 #if verbose:
194 #if verbose:
194 # print
195 # print
195
196
196
197
197 print "%s tests run, %s failure%s" % (num_tests,
198 print "%s tests run, %s failure%s" % (num_tests,
198 num_f,
199 num_f,
199 num_f != 1 and "s" or "")
200 num_f != 1 and "s" or "")
200 for f in failures:
201 for f in failures:
201 print f
202 print f
@@ -1,428 +1,443 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
157 # Trailing qmark combos. Odd special cases abound
158 # Trailing qmark combos. Odd special cases abound
158 ( '!thing?', handle_shell_escape), # trailing '?' loses to shell esc
159
159 ( '!thing ?', handle_shell_escape),
160 # The key is: we don't want the trailing ? to trigger help if it's a
160 ( '!!thing?', handle_shell_escape),
161 # part of a shell glob (like, e.g. '!ls file.?'). Instead, we want the
162 # shell handler to be called. Due to subtleties of the input string
163 # parsing, however, we only call the shell handler if the trailing ? is
164 # part of something whitespace-separated from the !cmd. See examples.
165 ( '!thing?', handle_help),
166 ( '!thing arg?', handle_shell_escape),
167 ( '!!thing?', handle_help),
168 ( '!!thing arg?', handle_shell_escape),
169
170 # For all other leading esc chars, we always trigger help
161 ( '%cmd?', handle_help),
171 ( '%cmd?', handle_help),
172 ( '%cmd ?', handle_help),
162 ( '/cmd?', handle_help),
173 ( '/cmd?', handle_help),
174 ( '/cmd ?', handle_help),
163 ( ';cmd?', handle_help),
175 ( ';cmd?', handle_help),
164 ( ',cmd?', handle_help),
176 ( ',cmd?', handle_help),
165 ]
177 ]
166 run_handler_tests(esc_handler_tests)
178 run_handler_tests(esc_handler_tests)
167
179
168
180
169
181
170 # Shell Escapes in Multi-line statements
182 # Shell Escapes in Multi-line statements
171 # ======================================
183 # ======================================
172 #
184 #
173 # We can't test this via runlines, since the hacked-over-for-testing
185 # 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
186 # handlers all return None, so continue_prompt never becomes true. Instead
175 # we drop into prefilter directly and pass in continue_prompt.
187 # we drop into prefilter directly and pass in continue_prompt.
176
188
177 old_mls = ip.options.multi_line_specials
189 old_mls = ip.options.multi_line_specials
178 for ln in [ ' !ls $f multi_line_specials %s',
190 for ln in [ ' !ls $f multi_line_specials %s',
179 ' !!ls $f multi_line_specials %s', # !! escapes work on mls
191 ' !!ls $f multi_line_specials %s', # !! escapes work on mls
180 # Trailing ? doesn't trigger help:
192 # Trailing ? doesn't trigger help:
181 ' !ls $f multi_line_specials %s ?',
193 ' !ls $f multi_line_specials %s ?',
182 ' !!ls $f multi_line_specials %s ?',
194 ' !!ls $f multi_line_specials %s ?',
183 ]:
195 ]:
184 ip.options.multi_line_specials = 1
196 ip.options.multi_line_specials = 1
185 on_ln = ln % 'on'
197 on_ln = ln % 'on'
186 ignore = ip.IP.prefilter(on_ln, continue_prompt=True)
198 ignore = ip.IP.prefilter(on_ln, continue_prompt=True)
187 check_handler(handle_shell_escape, on_ln)
199 check_handler(handle_shell_escape, on_ln)
188
200
189 ip.options.multi_line_specials = 0
201 ip.options.multi_line_specials = 0
190 off_ln = ln % 'off'
202 off_ln = ln % 'off'
191 ignore = ip.IP.prefilter(off_ln, continue_prompt=True)
203 ignore = ip.IP.prefilter(off_ln, continue_prompt=True)
192 check_handler(handle_normal, off_ln)
204 check_handler(handle_normal, off_ln)
193
205
194 ip.options.multi_line_specials = old_mls
206 ip.options.multi_line_specials = old_mls
195
207
196
208
197 # Automagic
209 # Automagic
198 # =========
210 # =========
199
211
200 # Pick one magic fun and one non_magic fun, make sure both exist
212 # Pick one magic fun and one non_magic fun, make sure both exist
201 assert hasattr(ip.IP, "magic_cpaste")
213 assert hasattr(ip.IP, "magic_cpaste")
202 assert not hasattr(ip.IP, "magic_does_not_exist")
214 assert not hasattr(ip.IP, "magic_does_not_exist")
203 ip.options.autocall = 0 # gotta have this off to get handle_normal
215 ip.options.autocall = 0 # gotta have this off to get handle_normal
204 ip.options.automagic = 0
216 ip.options.automagic = 0
205 run_handler_tests([
217 run_handler_tests([
206 # Without automagic, only shows up with explicit escape
218 # Without automagic, only shows up with explicit escape
207 ( 'cpaste', handle_normal),
219 ( 'cpaste', handle_normal),
208 ( '%cpaste', handle_magic),
220 ( '%cpaste', handle_magic),
209 ( '%does_not_exist', handle_magic),
221 ( '%does_not_exist', handle_magic),
210 ])
222 ])
211 ip.options.automagic = 1
223 ip.options.automagic = 1
212 run_handler_tests([
224 run_handler_tests([
213 ( 'cpaste', handle_magic),
225 ( 'cpaste', handle_magic),
214 ( '%cpaste', handle_magic),
226 ( '%cpaste', handle_magic),
215 ( 'does_not_exist', handle_normal),
227 ( 'does_not_exist', handle_normal),
216 ( '%does_not_exist', handle_magic),
228 ( '%does_not_exist', handle_magic),
217 ( 'cd /', handle_magic),
229 ( 'cd /', handle_magic),
218 ( 'cd = 2', handle_normal),
230 ( 'cd = 2', handle_normal),
231 ( 'r', handle_magic),
232 ( 'r thing', handle_magic),
233 ( 'r"str"', handle_normal),
219 ])
234 ])
220
235
221 # If next elt starts with anything that could be an assignment, func call,
236 # 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.
237 # etc, we don't call the magic func, unless explicitly escaped to do so.
223 #magic_killing_tests = []
238 #magic_killing_tests = []
224 #for c in list('!=()<>,'):
239 #for c in list('!=()<>,'):
225 # magic_killing_tests.append(('cpaste %s killed_automagic' % c, handle_normal))
240 # magic_killing_tests.append(('cpaste %s killed_automagic' % c, handle_normal))
226 # magic_killing_tests.append(('%%cpaste %s escaped_magic' % c, handle_magic))
241 # magic_killing_tests.append(('%%cpaste %s escaped_magic' % c, handle_magic))
227 #run_handler_tests(magic_killing_tests)
242 #run_handler_tests(magic_killing_tests)
228
243
229 # magic on indented continuation lines -- on iff multi_line_specials == 1
244 # magic on indented continuation lines -- on iff multi_line_specials == 1
230 ip.options.multi_line_specials = 0
245 ip.options.multi_line_specials = 0
231 ln = ' cpaste multi_line off kills magic'
246 ln = ' cpaste multi_line off kills magic'
232 ignore = ip.IP.prefilter(ln, continue_prompt=True)
247 ignore = ip.IP.prefilter(ln, continue_prompt=True)
233 check_handler(handle_normal, ln)
248 check_handler(handle_normal, ln)
234
249
235 ip.options.multi_line_specials = 1
250 ip.options.multi_line_specials = 1
236 ln = ' cpaste multi_line on enables magic'
251 ln = ' cpaste multi_line on enables magic'
237 ignore = ip.IP.prefilter(ln, continue_prompt=True)
252 ignore = ip.IP.prefilter(ln, continue_prompt=True)
238 check_handler(handle_magic, ln)
253 check_handler(handle_magic, ln)
239
254
240 # user namespace shadows the magic one unless shell escaped
255 # user namespace shadows the magic one unless shell escaped
241 ip.user_ns['cpaste'] = 'user_ns'
256 ip.user_ns['cpaste'] = 'user_ns'
242 run_handler_tests([
257 run_handler_tests([
243 ( 'cpaste', handle_normal),
258 ( 'cpaste', handle_normal),
244 ( '%cpaste', handle_magic)])
259 ( '%cpaste', handle_magic)])
245 del ip.user_ns['cpaste']
260 del ip.user_ns['cpaste']
246
261
247
262
248
263
249 # Check for !=() turning off .ofind
264 # Check for !=() turning off .ofind
250 # =================================
265 # =================================
251 class AttributeMutator(object):
266 class AttributeMutator(object):
252 """A class which will be modified on attribute access, to test ofind"""
267 """A class which will be modified on attribute access, to test ofind"""
253 def __init__(self):
268 def __init__(self):
254 self.called = False
269 self.called = False
255
270
256 def getFoo(self): self.called = True
271 def getFoo(self): self.called = True
257 foo = property(getFoo)
272 foo = property(getFoo)
258
273
259 attr_mutator = AttributeMutator()
274 attr_mutator = AttributeMutator()
260 ip.to_user_ns('attr_mutator')
275 ip.to_user_ns('attr_mutator')
261
276
262 ip.options.autocall = 1
277 ip.options.autocall = 1
263
278
264 run_one_test('attr_mutator.foo should mutate', handle_normal)
279 run_one_test('attr_mutator.foo should mutate', handle_normal)
265 check(attr_mutator.called, 'ofind should be called in absence of assign characters')
280 check(attr_mutator.called, 'ofind should be called in absence of assign characters')
266
281
267 for c in list('!=()<>+*/%^&|'):
282 for c in list('!=()<>+*/%^&|'):
268 attr_mutator.called = False
283 attr_mutator.called = False
269 run_one_test('attr_mutator.foo %s should *not* mutate' % c, handle_normal)
284 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)
285 run_one_test('attr_mutator.foo%s should *not* mutate' % c, handle_normal)
271
286
272 check(not attr_mutator.called,
287 check(not attr_mutator.called,
273 'ofind should not be called near character %s' % c)
288 'ofind should not be called near character %s' % c)
274
289
275
290
276
291
277 # Alias expansion
292 # Alias expansion
278 # ===============
293 # ===============
279
294
280 # With autocall on or off, aliases should be shadowed by user, internal and
295 # With autocall on or off, aliases should be shadowed by user, internal and
281 # __builtin__ namespaces
296 # __builtin__ namespaces
282 #
297 #
283 # XXX Can aliases have '.' in their name? With autocall off, that works,
298 # XXX Can aliases have '.' in their name? With autocall off, that works,
284 # with autocall on, it doesn't. Hmmm.
299 # with autocall on, it doesn't. Hmmm.
285 import __builtin__
300 import __builtin__
286 for ac_state in [0,1]:
301 for ac_state in [0,1]:
287 ip.options.autocall = ac_state
302 ip.options.autocall = ac_state
288 ip.IP.alias_table['alias_cmd'] = 'alias_result'
303 ip.IP.alias_table['alias_cmd'] = 'alias_result'
289 ip.IP.alias_table['alias_head.with_dot'] = 'alias_result'
304 ip.IP.alias_table['alias_head.with_dot'] = 'alias_result'
290 run_handler_tests([
305 run_handler_tests([
291 ("alias_cmd", handle_alias),
306 ("alias_cmd", handle_alias),
292 # XXX See note above
307 # XXX See note above
293 #("alias_head.with_dot unshadowed, autocall=%s" % ac_state, handle_alias),
308 #("alias_head.with_dot unshadowed, autocall=%s" % ac_state, handle_alias),
294 ("alias_cmd.something aliases must match whole expr", handle_normal),
309 ("alias_cmd.something aliases must match whole expr", handle_normal),
295 ("alias_cmd /", handle_alias),
310 ("alias_cmd /", handle_alias),
296 ])
311 ])
297
312
298 for ns in [ip.user_ns, ip.IP.internal_ns, __builtin__.__dict__ ]:
313 for ns in [ip.user_ns, ip.IP.internal_ns, __builtin__.__dict__ ]:
299 ns['alias_cmd'] = 'a user value'
314 ns['alias_cmd'] = 'a user value'
300 ns['alias_head'] = 'a user value'
315 ns['alias_head'] = 'a user value'
301 run_handler_tests([
316 run_handler_tests([
302 ("alias_cmd", handle_normal),
317 ("alias_cmd", handle_normal),
303 ("alias_head.with_dot", handle_normal)])
318 ("alias_head.with_dot", handle_normal)])
304 del ns['alias_cmd']
319 del ns['alias_cmd']
305 del ns['alias_head']
320 del ns['alias_head']
306
321
307 ip.options.autocall = 1
322 ip.options.autocall = 1
308
323
309
324
310
325
311
326
312 # Autocall
327 # Autocall
313 # ========
328 # ========
314
329
315 # For all the tests below, 'len' is callable / 'thing' is not
330 # For all the tests below, 'len' is callable / 'thing' is not
316
331
317 # Objects which are instances of IPyAutocall are *always* autocalled
332 # Objects which are instances of IPyAutocall are *always* autocalled
318 import IPython.ipapi
333 import IPython.ipapi
319 class Autocallable(IPython.ipapi.IPyAutocall):
334 class Autocallable(IPython.ipapi.IPyAutocall):
320 def __call__(self):
335 def __call__(self):
321 return "called"
336 return "called"
322
337
323 autocallable = Autocallable()
338 autocallable = Autocallable()
324 ip.to_user_ns('autocallable')
339 ip.to_user_ns('autocallable')
325
340
326
341
327 # First, with autocalling fully off
342 # First, with autocalling fully off
328 ip.options.autocall = 0
343 ip.options.autocall = 0
329 run_handler_tests( [
344 run_handler_tests( [
330 # With no escapes, no autocalling expansions happen, callable or not,
345 # With no escapes, no autocalling expansions happen, callable or not,
331 # unless the obj extends IPyAutocall
346 # unless the obj extends IPyAutocall
332 ( 'len autocall_0', handle_normal),
347 ( 'len autocall_0', handle_normal),
333 ( 'thing autocall_0', handle_normal),
348 ( 'thing autocall_0', handle_normal),
334 ( 'autocallable', handle_auto),
349 ( 'autocallable', handle_auto),
335
350
336 # With explicit escapes, callable and non-callables both get expanded,
351 # With explicit escapes, callable and non-callables both get expanded,
337 # regardless of the %autocall setting:
352 # regardless of the %autocall setting:
338 ( '/len autocall_0', handle_auto),
353 ( '/len autocall_0', handle_auto),
339 ( ',len autocall_0 b0', handle_auto),
354 ( ',len autocall_0 b0', handle_auto),
340 ( ';len autocall_0 b0', handle_auto),
355 ( ';len autocall_0 b0', handle_auto),
341
356
342 ( '/thing autocall_0', handle_auto),
357 ( '/thing autocall_0', handle_auto),
343 ( ',thing autocall_0 b0', handle_auto),
358 ( ',thing autocall_0 b0', handle_auto),
344 ( ';thing autocall_0 b0', handle_auto),
359 ( ';thing autocall_0 b0', handle_auto),
345
360
346 # Explicit autocall should not trigger if there is leading whitespace
361 # Explicit autocall should not trigger if there is leading whitespace
347 ( ' /len autocall_0', handle_normal),
362 ( ' /len autocall_0', handle_normal),
348 ( ' ;len autocall_0', handle_normal),
363 ( ' ;len autocall_0', handle_normal),
349 ( ' ,len autocall_0', handle_normal),
364 ( ' ,len autocall_0', handle_normal),
350 ( ' / len autocall_0', handle_normal),
365 ( ' / len autocall_0', handle_normal),
351
366
352 # But should work if the whitespace comes after the esc char
367 # But should work if the whitespace comes after the esc char
353 ( '/ len autocall_0', handle_auto),
368 ( '/ len autocall_0', handle_auto),
354 ( '; len autocall_0', handle_auto),
369 ( '; len autocall_0', handle_auto),
355 ( ', len autocall_0', handle_auto),
370 ( ', len autocall_0', handle_auto),
356 ( '/ len autocall_0', handle_auto),
371 ( '/ len autocall_0', handle_auto),
357 ])
372 ])
358
373
359
374
360 # Now, with autocall in default, 'smart' mode
375 # Now, with autocall in default, 'smart' mode
361 ip.options.autocall = 1
376 ip.options.autocall = 1
362 run_handler_tests( [
377 run_handler_tests( [
363 # Autocalls without escapes -- only expand if it's callable
378 # Autocalls without escapes -- only expand if it's callable
364 ( 'len a1', handle_auto),
379 ( 'len a1', handle_auto),
365 ( 'thing a1', handle_normal),
380 ( 'thing a1', handle_normal),
366 ( 'autocallable', handle_auto),
381 ( 'autocallable', handle_auto),
367
382
368 # As above, all explicit escapes generate auto-calls, callable or not
383 # As above, all explicit escapes generate auto-calls, callable or not
369 ( '/len a1', handle_auto),
384 ( '/len a1', handle_auto),
370 ( ',len a1 b1', handle_auto),
385 ( ',len a1 b1', handle_auto),
371 ( ';len a1 b1', handle_auto),
386 ( ';len a1 b1', handle_auto),
372 ( '/thing a1', handle_auto),
387 ( '/thing a1', handle_auto),
373 ( ',thing a1 b1', handle_auto),
388 ( ',thing a1 b1', handle_auto),
374 ( ';thing a1 b1', handle_auto),
389 ( ';thing a1 b1', handle_auto),
375
390
376 # Autocalls only happen on things which look like funcs, even if
391 # Autocalls only happen on things which look like funcs, even if
377 # explicitly requested. Which, in this case means they look like a
392 # explicitly requested. Which, in this case means they look like a
378 # sequence of identifiers and . attribute references. Possibly the
393 # sequence of identifiers and . attribute references. Possibly the
379 # second of these two should trigger handle_auto. But not for now.
394 # second of these two should trigger handle_auto. But not for now.
380 ( '"abc".join range(4)', handle_normal),
395 ( '"abc".join range(4)', handle_normal),
381 ( '/"abc".join range(4)', handle_normal),
396 ( '/"abc".join range(4)', handle_normal),
382 ])
397 ])
383
398
384
399
385 # No tests for autocall = 2, since the extra magic there happens inside the
400 # No tests for autocall = 2, since the extra magic there happens inside the
386 # handle_auto function, which our test doesn't examine.
401 # handle_auto function, which our test doesn't examine.
387
402
388 # Note that we leave autocall in default, 1, 'smart' mode
403 # Note that we leave autocall in default, 1, 'smart' mode
389
404
390
405
391 # Autocall / Binary operators
406 # Autocall / Binary operators
392 # ==========================
407 # ==========================
393
408
394 # Even with autocall on, 'len in thing' won't transform.
409 # Even with autocall on, 'len in thing' won't transform.
395 # But ';len in thing' will
410 # But ';len in thing' will
396
411
397 # Note, the tests below don't check for multi-char ops. It could.
412 # Note, the tests below don't check for multi-char ops. It could.
398
413
399 # XXX % is a binary op and should be in the list, too, but fails
414 # XXX % is a binary op and should be in the list, too, but fails
400 bin_ops = list(r'<>,&^|*/+-') + 'is not in and or'.split()
415 bin_ops = list(r'<>,&^|*/+-') + 'is not in and or'.split()
401 bin_tests = []
416 bin_tests = []
402 for b in bin_ops:
417 for b in bin_ops:
403 bin_tests.append(('len %s binop_autocall' % b, handle_normal))
418 bin_tests.append(('len %s binop_autocall' % b, handle_normal))
404 bin_tests.append((';len %s binop_autocall' % b, handle_auto))
419 bin_tests.append((';len %s binop_autocall' % b, handle_auto))
405 bin_tests.append((',len %s binop_autocall' % b, handle_auto))
420 bin_tests.append((',len %s binop_autocall' % b, handle_auto))
406 bin_tests.append(('/len %s binop_autocall' % b, handle_auto))
421 bin_tests.append(('/len %s binop_autocall' % b, handle_auto))
407
422
408 # Who loves auto-generating tests?
423 # Who loves auto-generating tests?
409 run_handler_tests(bin_tests)
424 run_handler_tests(bin_tests)
410
425
411
426
412 # Possibly add tests for namespace shadowing (really ofind's business?).
427 # Possibly add tests for namespace shadowing (really ofind's business?).
413 #
428 #
414 # user > ipython internal > python builtin > alias > magic
429 # user > ipython internal > python builtin > alias > magic
415
430
416
431
417 # ============
432 # ============
418 # Test Summary
433 # Test Summary
419 # ============
434 # ============
420 num_f = len(failures)
435 num_f = len(failures)
421 if verbose:
436 if verbose:
422 print
437 print
423 print "%s tests run, %s failure%s" % (num_tests,
438 print "%s tests run, %s failure%s" % (num_tests,
424 num_f,
439 num_f,
425 num_f != 1 and "s" or "")
440 num_f != 1 and "s" or "")
426 for f in failures:
441 for f in failures:
427 print f
442 print f
428
443
General Comments 0
You need to be logged in to leave comments. Login now