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