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