Show More
@@ -0,0 +1,298 b'' | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | """ | |||
|
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 | |||
|
5 | pieces and triggering the appropriate handlers in iplib to do the actual | |||
|
6 | transforming work. | |||
|
7 | """ | |||
|
8 | __docformat__ = "restructuredtext en" | |||
|
9 | ||||
|
10 | import re | |||
|
11 | import IPython.ipapi | |||
|
12 | ||||
|
13 | class LineInfo(object): | |||
|
14 | """A single line of input and associated info. | |||
|
15 | ||||
|
16 | Includes the following as properties: | |||
|
17 | ||||
|
18 | line | |||
|
19 | The original, raw line | |||
|
20 | ||||
|
21 | continue_prompt | |||
|
22 | Is this line a continuation in a sequence of multiline input? | |||
|
23 | ||||
|
24 | pre | |||
|
25 | The initial esc character or whitespace. | |||
|
26 | ||||
|
27 | preChar | |||
|
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 | |||
|
30 | always be a single character. | |||
|
31 | ||||
|
32 | preWhitespace | |||
|
33 | The leading whitespace from pre if it exists. If there is a preChar, | |||
|
34 | this is just ''. | |||
|
35 | ||||
|
36 | iFun | |||
|
37 | The 'function part', which is basically the maximal initial sequence | |||
|
38 | of valid python identifiers and the '.' character. This is what is | |||
|
39 | checked for alias and magic transformations, used for auto-calling, | |||
|
40 | etc. | |||
|
41 | ||||
|
42 | theRest | |||
|
43 | Everything else on the line. | |||
|
44 | """ | |||
|
45 | def __init__(self, line, continue_prompt): | |||
|
46 | self.line = line | |||
|
47 | self.continue_prompt = continue_prompt | |||
|
48 | self.pre, self.iFun, self.theRest = splitUserInput(line) | |||
|
49 | ||||
|
50 | self.preChar = self.pre.strip() | |||
|
51 | if self.preChar: | |||
|
52 | self.preWhitespace = '' # No whitespace allowd before esc chars | |||
|
53 | else: | |||
|
54 | self.preWhitespace = self.pre | |||
|
55 | ||||
|
56 | self._oinfo = None | |||
|
57 | ||||
|
58 | def ofind(self, ip): | |||
|
59 | """Do a full, attribute-walking lookup of the iFun in the various | |||
|
60 | namespaces for the given IPython InteractiveShell instance. | |||
|
61 | ||||
|
62 | Return a dict with keys: found,obj,ospace,ismagic | |||
|
63 | ||||
|
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 | |||
|
66 | other, less dangerous handlers. | |||
|
67 | ||||
|
68 | Does cache the results of the call, so can be called multiple times | |||
|
69 | without worrying about *further* damaging state. | |||
|
70 | """ | |||
|
71 | if not self._oinfo: | |||
|
72 | self._oinfo = ip._ofind(self.iFun) | |||
|
73 | return self._oinfo | |||
|
74 | ||||
|
75 | ||||
|
76 | def splitUserInput(line, pattern=None): | |||
|
77 | """Split user input into pre-char/whitespace, function part and rest. | |||
|
78 | ||||
|
79 | Mostly internal to this module, but also used by iplib.expand_aliases, | |||
|
80 | which passes in a shell pattern. | |||
|
81 | """ | |||
|
82 | # It seems to me that the shell splitting should be a separate method. | |||
|
83 | ||||
|
84 | if not pattern: | |||
|
85 | pattern = line_split | |||
|
86 | match = pattern.match(line) | |||
|
87 | if not match: | |||
|
88 | #print "match failed for line '%s'" % line | |||
|
89 | try: | |||
|
90 | iFun,theRest = line.split(None,1) | |||
|
91 | except ValueError: | |||
|
92 | #print "split failed for line '%s'" % line | |||
|
93 | iFun,theRest = line,'' | |||
|
94 | pre = re.match('^(\s*)(.*)',line).groups()[0] | |||
|
95 | else: | |||
|
96 | pre,iFun,theRest = match.groups() | |||
|
97 | ||||
|
98 | # iFun has to be a valid python identifier, so it better be only pure | |||
|
99 | # ascii, no unicode: | |||
|
100 | try: | |||
|
101 | iFun = iFun.encode('ascii') | |||
|
102 | except UnicodeEncodeError: | |||
|
103 | theRest = iFun + u' ' + theRest | |||
|
104 | iFun = u'' | |||
|
105 | ||||
|
106 | #print 'line:<%s>' % line # dbg | |||
|
107 | #print 'pre <%s> iFun <%s> rest <%s>' % (pre,iFun.strip(),theRest) # dbg | |||
|
108 | return pre,iFun.strip(),theRest | |||
|
109 | ||||
|
110 | ||||
|
111 | # RegExp for splitting line contents into pre-char//first word-method//rest. | |||
|
112 | # For clarity, each group in on one line. | |||
|
113 | ||||
|
114 | # WARNING: update the regexp if the escapes in iplib are changed, as they | |||
|
115 | # are hardwired in. | |||
|
116 | ||||
|
117 | # Although it's not solely driven by the regex, note that: | |||
|
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 | |||
|
120 | # ? triggers as first or last char. | |||
|
121 | ||||
|
122 | # The three parts of the regex are: | |||
|
123 | # 1) pre: pre_char *or* initial whitespace | |||
|
124 | # 2) iFun: first word/method (mix of \w and '.') | |||
|
125 | # 3) theRest: rest of line | |||
|
126 | line_split = re.compile(r'^([,;/%?]|!!?|\s*)' | |||
|
127 | r'\s*([\w\.]+)\s*' | |||
|
128 | r'(.*)$') | |||
|
129 | ||||
|
130 | shell_line_split = re.compile(r'^(\s*)(\S*\s*)(.*$)') | |||
|
131 | ||||
|
132 | def prefilter(line_info, ip): | |||
|
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 | |||
|
135 | value, even if it's a blank ('').""" | |||
|
136 | # Note: the order of these checks does matter. | |||
|
137 | for check in [ checkEmacs, | |||
|
138 | checkIPyAutocall, | |||
|
139 | checkMultiLineShell, | |||
|
140 | checkEscChars, | |||
|
141 | checkPythonChars, | |||
|
142 | checkAutomagic, | |||
|
143 | checkAlias, | |||
|
144 | checkAutocall, | |||
|
145 | ]: | |||
|
146 | handler = check(line_info, ip) | |||
|
147 | if handler: | |||
|
148 | return handler(line_info) | |||
|
149 | ||||
|
150 | return ip.handle_normal(line_info) | |||
|
151 | ||||
|
152 | # Handler checks | |||
|
153 | # | |||
|
154 | # All have the same interface: they take a LineInfo object and a ref to the | |||
|
155 | # iplib.InteractiveShell object. They check the line to see if a particular | |||
|
156 | # handler should be called, and return either a handler or None. The | |||
|
157 | # handlers which they return are *bound* methods of the InteractiveShell | |||
|
158 | # object. | |||
|
159 | # | |||
|
160 | # In general, these checks should only take responsibility for their 'own' | |||
|
161 | # handler. If it doesn't get triggered, they should just return None and | |||
|
162 | # let the rest of the check sequence run. | |||
|
163 | def checkEmacs(l_info,ip): | |||
|
164 | "Emacs ipython-mode tags certain input lines." | |||
|
165 | if l_info.line.endswith('# PYTHON-MODE'): | |||
|
166 | return ip.handle_emacs | |||
|
167 | else: | |||
|
168 | return None | |||
|
169 | ||||
|
170 | def checkIPyAutocall(l_info,ip): | |||
|
171 | "Instances of IPyAutocall in user_ns get autocalled immediately" | |||
|
172 | obj = ip.user_ns.get(l_info.iFun, None) | |||
|
173 | if isinstance(obj, IPython.ipapi.IPyAutocall): | |||
|
174 | obj.set_ip(ip.api) | |||
|
175 | return ip.handle_auto | |||
|
176 | else: | |||
|
177 | return None | |||
|
178 | ||||
|
179 | ||||
|
180 | def checkMultiLineShell(l_info,ip): | |||
|
181 | "Allow ! and !! in multi-line statements if multi_line_specials is on" | |||
|
182 | # Note that this one of the only places we check the first character of | |||
|
183 | # iFun and *not* the preChar. Also note that the below test matches | |||
|
184 | # both ! and !!. | |||
|
185 | if l_info.continue_prompt \ | |||
|
186 | and ip.rc.multi_line_specials \ | |||
|
187 | and l_info.iFun.startswith(ip.ESC_SHELL): | |||
|
188 | return ip.handle_shell_escape | |||
|
189 | else: | |||
|
190 | return None | |||
|
191 | ||||
|
192 | def checkEscChars(l_info,ip): | |||
|
193 | """Check for escape character and return either a handler to handle it, | |||
|
194 | or None if there is no escape char.""" | |||
|
195 | if l_info.line[-1] == ip.ESC_HELP \ | |||
|
196 | and l_info.preChar != ip.ESC_SHELL \ | |||
|
197 | and l_info.preChar != ip.ESC_SH_CAP: | |||
|
198 | # the ? can be at the end, but *not* for either kind of shell escape, | |||
|
199 | # because a ? can be a vaild final char in a shell cmd | |||
|
200 | return ip.handle_help | |||
|
201 | elif l_info.preChar in ip.esc_handlers: | |||
|
202 | return ip.esc_handlers[l_info.preChar] | |||
|
203 | else: | |||
|
204 | return None | |||
|
205 | ||||
|
206 | def checkPythonChars(l_info,ip): | |||
|
207 | """If the 'rest' of the line begins with an (in)equality, assginment, | |||
|
208 | function call or tuple comma, we should simply execute the line | |||
|
209 | (regardless of whether or not there's a possible alias, automagic or | |||
|
210 | autocall expansion). This both avoids spurious geattr() accesses on | |||
|
211 | objects upon assignment, and also allows users to assign to either alias | |||
|
212 | or magic names true python variables (the magic/alias systems always | |||
|
213 | take second seat to true python code). E.g. ls='hi', or ls,that=1,2""" | |||
|
214 | if l_info.theRest and l_info.theRest[0] in '!=()<>,+*/%^&|': | |||
|
215 | return ip.handle_normal | |||
|
216 | else: | |||
|
217 | return None | |||
|
218 | ||||
|
219 | def checkAutomagic(l_info,ip): | |||
|
220 | """If the iFun is magic, and automagic is on, run it. Note: normal, | |||
|
221 | non-auto magic would already have been triggered via '%' in | |||
|
222 | check_esc_chars. This just checks for automagic.""" | |||
|
223 | if not ip.rc.automagic or not hasattr(ip,'magic_'+l_info.iFun): | |||
|
224 | return None | |||
|
225 | ||||
|
226 | # We have a likely magic method. Make sure we should actually call it. | |||
|
227 | if l_info.continue_prompt and not ip.rc.multi_line_specials: | |||
|
228 | return None | |||
|
229 | ||||
|
230 | head = l_info.iFun.split('.',1)[0] | |||
|
231 | if isShadowed(head,ip): | |||
|
232 | return None | |||
|
233 | ||||
|
234 | return ip.handle_magic | |||
|
235 | ||||
|
236 | ||||
|
237 | def checkAlias(l_info,ip): | |||
|
238 | "Check if the initital identifier on the line is an alias." | |||
|
239 | # Note: aliases can not contain '.' | |||
|
240 | head = l_info.iFun.split('.',1)[0] | |||
|
241 | ||||
|
242 | if l_info.iFun not in ip.alias_table \ | |||
|
243 | or head not in ip.alias_table \ | |||
|
244 | or isShadowed(head,ip): | |||
|
245 | return None | |||
|
246 | ||||
|
247 | return ip.handle_alias | |||
|
248 | ||||
|
249 | ||||
|
250 | def checkAutocall(l_info,ip): | |||
|
251 | "Check if the initial word/function is callable and autocall is on." | |||
|
252 | if not ip.rc.autocall: | |||
|
253 | return None | |||
|
254 | ||||
|
255 | oinfo = l_info.ofind(ip) # This can mutate state via getattr | |||
|
256 | if not oinfo['found']: | |||
|
257 | return None | |||
|
258 | ||||
|
259 | if callable(oinfo['obj']) \ | |||
|
260 | and (not re_exclude_auto.match(l_info.theRest)) \ | |||
|
261 | and re_fun_name.match(l_info.iFun): | |||
|
262 | #print 'going auto' # dbg | |||
|
263 | return ip.handle_auto | |||
|
264 | else: | |||
|
265 | #print 'was callable?', callable(l_info.oinfo['obj']) # dbg | |||
|
266 | return None | |||
|
267 | ||||
|
268 | # RegExp to identify potential function names | |||
|
269 | re_fun_name = re.compile(r'[a-zA-Z_]([a-zA-Z0-9_.]*) *$') | |||
|
270 | ||||
|
271 | # RegExp to exclude strings with this start from autocalling. In | |||
|
272 | # particular, all binary operators should be excluded, so that if foo is | |||
|
273 | # callable, foo OP bar doesn't become foo(OP bar), which is invalid. The | |||
|
274 | # characters '!=()' don't need to be checked for, as the checkPythonChars | |||
|
275 | # routine explicitely does so, to catch direct calls and rebindings of | |||
|
276 | # existing names. | |||
|
277 | ||||
|
278 | # Warning: the '-' HAS TO BE AT THE END of the first group, otherwise | |||
|
279 | # it affects the rest of the group in square brackets. | |||
|
280 | re_exclude_auto = re.compile(r'^[,&^\|\*/\+-]' | |||
|
281 | r'|^is |^not |^in |^and |^or ') | |||
|
282 | ||||
|
283 | # try to catch also methods for stuff in lists/tuples/dicts: off | |||
|
284 | # (experimental). For this to work, the line_split regexp would need | |||
|
285 | # to be modified so it wouldn't break things at '['. That line is | |||
|
286 | # nasty enough that I shouldn't change it until I can test it _well_. | |||
|
287 | #self.re_fun_name = re.compile (r'[a-zA-Z_]([a-zA-Z0-9_.\[\]]*) ?$') | |||
|
288 | ||||
|
289 | # Handler Check Utilities | |||
|
290 | def isShadowed(identifier,ip): | |||
|
291 | """Is the given identifier defined in one of the namespaces which shadow | |||
|
292 | the alias and magic namespaces? Note that an identifier is different | |||
|
293 | than iFun, because it can not contain a '.' character.""" | |||
|
294 | # This is much safer than calling ofind, which can change state | |||
|
295 | return (identifier in ip.user_ns \ | |||
|
296 | or identifier in ip.internal_ns \ | |||
|
297 | or identifier in ip.ns_table['builtin']) | |||
|
298 |
@@ -0,0 +1,200 b'' | |||||
|
1 | """Test the various handlers which do the actual rewriting of the line.""" | |||
|
2 | ||||
|
3 | from StringIO import StringIO | |||
|
4 | import sys | |||
|
5 | ||||
|
6 | failures = [] | |||
|
7 | num_tests = 0 | |||
|
8 | ||||
|
9 | def run(tests): | |||
|
10 | """Loop through a list of (pre, post) inputs, where pre is the string | |||
|
11 | handed to ipython, and post is how that string looks after it's been | |||
|
12 | transformed (i.e. ipython's notion of _i)""" | |||
|
13 | for pre, post in tests: | |||
|
14 | global num_tests | |||
|
15 | num_tests += 1 | |||
|
16 | ip.runlines(pre) | |||
|
17 | ip.runlines('_i') # Not sure why I need this... | |||
|
18 | actual = ip.user_ns['_i'] | |||
|
19 | if actual != None: actual = actual.rstrip('\n') | |||
|
20 | if actual != post: | |||
|
21 | failures.append('Expected %r to become %r, found %r' % ( | |||
|
22 | pre, post, actual)) | |||
|
23 | ||||
|
24 | ||||
|
25 | # Shutdown stdout/stderr so that ipython isn't noisy during tests. Have to | |||
|
26 | # do this *before* importing IPython below. | |||
|
27 | # | |||
|
28 | # NOTE: this means that, if you stick print statements into code as part of | |||
|
29 | # debugging, you won't see the results (unless you comment out some of the | |||
|
30 | # below). I keep on doing this, so apparently it's easy. Or I am an idiot. | |||
|
31 | old_stdout = sys.stdout | |||
|
32 | old_stderr = sys.stderr | |||
|
33 | ||||
|
34 | sys.stdout = StringIO() | |||
|
35 | sys.stderr = StringIO() | |||
|
36 | ||||
|
37 | import IPython | |||
|
38 | import IPython.ipapi | |||
|
39 | ||||
|
40 | IPython.Shell.start() | |||
|
41 | ip = IPython.ipapi.get() | |||
|
42 | ||||
|
43 | class CallableIndexable(object): | |||
|
44 | def __getitem__(self, idx): return True | |||
|
45 | def __call__(self, *args, **kws): return True | |||
|
46 | ||||
|
47 | ||||
|
48 | try: | |||
|
49 | # alias expansion | |||
|
50 | ||||
|
51 | # We're using 'true' as our syscall of choice because it doesn't | |||
|
52 | # write anything to stdout. | |||
|
53 | ||||
|
54 | # Turn off actual execution of aliases, because it's noisy | |||
|
55 | old_system_cmd = ip.IP.system | |||
|
56 | ip.IP.system = lambda cmd: None | |||
|
57 | ||||
|
58 | ||||
|
59 | ip.IP.alias_table['an_alias'] = (0, 'true') | |||
|
60 | # These are useful for checking a particular recursive alias issue | |||
|
61 | ip.IP.alias_table['top'] = (0, 'd:/cygwin/top') | |||
|
62 | ip.IP.alias_table['d'] = (0, 'true') | |||
|
63 | run([("an_alias", '_ip.system("true ")'), # alias | |||
|
64 | # Below: recursive aliases should expand whitespace-surrounded | |||
|
65 | # chars, *not* initial chars which happen to be aliases: | |||
|
66 | ("top", '_ip.system("d:/cygwin/top ")'), | |||
|
67 | ]) | |||
|
68 | ip.IP.system = old_system_cmd | |||
|
69 | ||||
|
70 | ||||
|
71 | call_idx = CallableIndexable() | |||
|
72 | ip.to_user_ns('call_idx') | |||
|
73 | ||||
|
74 | # For many of the below, we're also checking that leading whitespace | |||
|
75 | # turns off the esc char, which it should unless there is a continuation | |||
|
76 | # line. | |||
|
77 | run([('"no change"', '"no change"'), # normal | |||
|
78 | ("!true", '_ip.system("true")'), # shell_escapes | |||
|
79 | ("!! true", '_ip.magic("sx true")'), # shell_escapes + magic | |||
|
80 | ("!!true", '_ip.magic("sx true")'), # shell_escapes + magic | |||
|
81 | ("%lsmagic", '_ip.magic("lsmagic ")'), # magic | |||
|
82 | ("lsmagic", '_ip.magic("lsmagic ")'), # magic | |||
|
83 | ("a = b # PYTHON-MODE", '_i'), # emacs -- avoids _in cache | |||
|
84 | ||||
|
85 | # post-esc-char whitespace goes inside | |||
|
86 | ("! true", '_ip.system(" true")'), | |||
|
87 | ||||
|
88 | # Leading whitespace generally turns off escape characters | |||
|
89 | (" ! true", ' ! true'), | |||
|
90 | (" !true", ' !true'), | |||
|
91 | ||||
|
92 | # handle_help | |||
|
93 | ||||
|
94 | # These are weak tests -- just looking at what the help handlers | |||
|
95 | # logs, which is not how it really does its work. But it still | |||
|
96 | # lets us check the key paths through the handler. | |||
|
97 | ||||
|
98 | ("x=1 # what?", "x=1 # what?"), # no help if valid python | |||
|
99 | ("len?", "#?len"), # this is what help logs when it runs | |||
|
100 | ("len??", "#?len?"), | |||
|
101 | ("?len", "#?len"), | |||
|
102 | ]) | |||
|
103 | ||||
|
104 | # multi_line_specials | |||
|
105 | ip.options.multi_line_specials = 0 | |||
|
106 | # W/ multi_line_specials off, leading ws kills esc chars/autoexpansion | |||
|
107 | run([ | |||
|
108 | ('if 1:\n !true', 'if 1:\n !true'), | |||
|
109 | ('if 1:\n lsmagic', 'if 1:\n lsmagic'), | |||
|
110 | ('if 1:\n an_alias', 'if 1:\n an_alias'), | |||
|
111 | ]) | |||
|
112 | ||||
|
113 | ip.options.multi_line_specials = 1 | |||
|
114 | # initial indents must be preserved. | |||
|
115 | run([ | |||
|
116 | ('if 1:\n !true', 'if 1:\n _ip.system("true")'), | |||
|
117 | ('if 1:\n lsmagic', 'if 1:\n _ip.magic("lsmagic ")'), | |||
|
118 | ('if 1:\n an_alias', 'if 1:\n _ip.system("true ")'), | |||
|
119 | # Weird one | |||
|
120 | ('if 1:\n !!true', 'if 1:\n _ip.magic("sx true")'), | |||
|
121 | ||||
|
122 | ||||
|
123 | # Even with m_l_s on, all esc_chars except ! are off | |||
|
124 | ('if 1:\n %lsmagic', 'if 1:\n %lsmagic'), | |||
|
125 | ('if 1:\n /fun 1 2', 'if 1:\n /fun 1 2'), | |||
|
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'), | |||
|
128 | ('if 1:\n ?fun 1 2', 'if 1:\n ?fun 1 2'), | |||
|
129 | # What about !! | |||
|
130 | ]) | |||
|
131 | ||||
|
132 | ||||
|
133 | # Objects which are instances of IPyAutocall are *always* autocalled | |||
|
134 | import IPython.ipapi | |||
|
135 | class Autocallable(IPython.ipapi.IPyAutocall): | |||
|
136 | def __call__(self): | |||
|
137 | return "called" | |||
|
138 | ||||
|
139 | autocallable = Autocallable() | |||
|
140 | ip.to_user_ns('autocallable') | |||
|
141 | ||||
|
142 | # auto | |||
|
143 | ip.options.autocall = 0 | |||
|
144 | # Only explicit escapes or instances of IPyAutocallable should get | |||
|
145 | # expanded | |||
|
146 | run([ | |||
|
147 | ('len "abc"', 'len "abc"'), | |||
|
148 | ('autocallable', 'autocallable()'), | |||
|
149 | (",list 1 2 3", 'list("1", "2", "3")'), | |||
|
150 | (";list 1 2 3", 'list("1 2 3")'), | |||
|
151 | ("/len range(1,4)", 'len(range(1,4))'), | |||
|
152 | ]) | |||
|
153 | ip.options.autocall = 1 | |||
|
154 | run([ | |||
|
155 | (",list 1 2 3", 'list("1", "2", "3")'), | |||
|
156 | (";list 1 2 3", 'list("1 2 3")'), | |||
|
157 | ("/len range(1,4)", 'len(range(1,4))'), | |||
|
158 | ('len "abc"', 'len("abc")'), | |||
|
159 | ('len "abc";', 'len("abc");'), # ; is special -- moves out of parens | |||
|
160 | # Autocall is turned off if first arg is [] and the object | |||
|
161 | # is both callable and indexable. Like so: | |||
|
162 | ('len [1,2]', 'len([1,2])'), # len doesn't support __getitem__... | |||
|
163 | ('call_idx [1]', 'call_idx [1]'), # call_idx *does*.. | |||
|
164 | ('call_idx 1', 'call_idx(1)'), | |||
|
165 | ('len', 'len '), # only at 2 does it auto-call on single args | |||
|
166 | ]) | |||
|
167 | ||||
|
168 | ip.options.autocall = 2 | |||
|
169 | run([ | |||
|
170 | (",list 1 2 3", 'list("1", "2", "3")'), | |||
|
171 | (";list 1 2 3", 'list("1 2 3")'), | |||
|
172 | ("/len range(1,4)", 'len(range(1,4))'), | |||
|
173 | ('len "abc"', 'len("abc")'), | |||
|
174 | ('len "abc";', 'len("abc");'), | |||
|
175 | ('len [1,2]', 'len([1,2])'), | |||
|
176 | ('call_idx [1]', 'call_idx [1]'), | |||
|
177 | ('call_idx 1', 'call_idx(1)'), | |||
|
178 | # This is what's different: | |||
|
179 | ('len', 'len()'), # only at 2 does it auto-call on single args | |||
|
180 | ]) | |||
|
181 | ip.options.autocall = 1 | |||
|
182 | ||||
|
183 | # Ignoring handle_emacs, 'cause it doesn't do anything. | |||
|
184 | finally: | |||
|
185 | sys.stdout = old_stdout | |||
|
186 | sys.stderr = old_stderr | |||
|
187 | ||||
|
188 | ||||
|
189 | ||||
|
190 | ||||
|
191 | num_f = len(failures) | |||
|
192 | #if verbose: | |||
|
193 | ||||
|
194 | ||||
|
195 | ||||
|
196 | print "%s tests run, %s failure%s" % (num_tests, | |||
|
197 | num_f, | |||
|
198 | num_f != 1 and "s" or "") | |||
|
199 | for f in failures: | |||
|
200 | print f |
@@ -6,7 +6,7 b' Requires Python 2.3 or newer.' | |||||
6 |
|
6 | |||
7 | This file contains all the classes and helper functions specific to IPython. |
|
7 | This file contains all the classes and helper functions specific to IPython. | |
8 |
|
8 | |||
9 |
$Id: iplib.py 235 |
|
9 | $Id: iplib.py 2354 2007-05-16 13:06:12Z dan.milstein $ | |
10 | """ |
|
10 | """ | |
11 |
|
11 | |||
12 | #***************************************************************************** |
|
12 | #***************************************************************************** | |
@@ -75,6 +75,8 b' from IPython.genutils import *' | |||||
75 | from IPython.strdispatch import StrDispatch |
|
75 | from IPython.strdispatch import StrDispatch | |
76 | import IPython.ipapi |
|
76 | import IPython.ipapi | |
77 |
|
77 | |||
|
78 | import IPython.prefilter as prefilter | |||
|
79 | ||||
78 | # Globals |
|
80 | # Globals | |
79 |
|
81 | |||
80 | # store the builtin raw_input globally, and use this always, in case user code |
|
82 | # store the builtin raw_input globally, and use this always, in case user code | |
@@ -385,6 +387,7 b' class InteractiveShell(object,Magic):' | |||||
385 |
|
387 | |||
386 | # escapes for automatic behavior on the command line |
|
388 | # escapes for automatic behavior on the command line | |
387 | self.ESC_SHELL = '!' |
|
389 | self.ESC_SHELL = '!' | |
|
390 | self.ESC_SH_CAP = '!!' | |||
388 | self.ESC_HELP = '?' |
|
391 | self.ESC_HELP = '?' | |
389 | self.ESC_MAGIC = '%' |
|
392 | self.ESC_MAGIC = '%' | |
390 | self.ESC_QUOTE = ',' |
|
393 | self.ESC_QUOTE = ',' | |
@@ -398,6 +401,7 b' class InteractiveShell(object,Magic):' | |||||
398 | self.ESC_MAGIC : self.handle_magic, |
|
401 | self.ESC_MAGIC : self.handle_magic, | |
399 | self.ESC_HELP : self.handle_help, |
|
402 | self.ESC_HELP : self.handle_help, | |
400 | self.ESC_SHELL : self.handle_shell_escape, |
|
403 | self.ESC_SHELL : self.handle_shell_escape, | |
|
404 | self.ESC_SH_CAP : self.handle_shell_escape, | |||
401 | } |
|
405 | } | |
402 |
|
406 | |||
403 | # class initializations |
|
407 | # class initializations | |
@@ -484,57 +488,6 b' class InteractiveShell(object,Magic):' | |||||
484 | header=self.rc.system_header, |
|
488 | header=self.rc.system_header, | |
485 | verbose=self.rc.system_verbose) |
|
489 | verbose=self.rc.system_verbose) | |
486 |
|
490 | |||
487 | # RegExp for splitting line contents into pre-char//first |
|
|||
488 | # word-method//rest. For clarity, each group in on one line. |
|
|||
489 |
|
||||
490 | # WARNING: update the regexp if the above escapes are changed, as they |
|
|||
491 | # are hardwired in. |
|
|||
492 |
|
||||
493 | # Don't get carried away with trying to make the autocalling catch too |
|
|||
494 | # much: it's better to be conservative rather than to trigger hidden |
|
|||
495 | # evals() somewhere and end up causing side effects. |
|
|||
496 | self.line_split = re.compile(r'^(\s*[,;/]?\s*)' |
|
|||
497 | r'([\?\w\.]+\w*\s*)' |
|
|||
498 | r'(\(?.*$)') |
|
|||
499 |
|
||||
500 | self.shell_line_split = re.compile(r'^(\s*)' |
|
|||
501 | r'(\S*\s*)' |
|
|||
502 | r'(\(?.*$)') |
|
|||
503 |
|
||||
504 | # A simpler regexp used as a fallback if the above doesn't work. This |
|
|||
505 | # one is more conservative in how it partitions the input. This code |
|
|||
506 | # can probably be cleaned up to do everything with just one regexp, but |
|
|||
507 | # I'm afraid of breaking something; do it once the unit tests are in |
|
|||
508 | # place. |
|
|||
509 | self.line_split_fallback = re.compile(r'^(\s*)' |
|
|||
510 | r'([%\!\?\w\.]*)' |
|
|||
511 | r'(.*)') |
|
|||
512 |
|
||||
513 | # Original re, keep around for a while in case changes break something |
|
|||
514 | #self.line_split = re.compile(r'(^[\s*!\?%,/]?)' |
|
|||
515 | # r'(\s*[\?\w\.]+\w*\s*)' |
|
|||
516 | # r'(\(?.*$)') |
|
|||
517 |
|
||||
518 | # RegExp to identify potential function names |
|
|||
519 | self.re_fun_name = re.compile(r'[a-zA-Z_]([a-zA-Z0-9_.]*) *$') |
|
|||
520 |
|
||||
521 | # RegExp to exclude strings with this start from autocalling. In |
|
|||
522 | # particular, all binary operators should be excluded, so that if foo |
|
|||
523 | # is callable, foo OP bar doesn't become foo(OP bar), which is |
|
|||
524 | # invalid. The characters '!=()' don't need to be checked for, as the |
|
|||
525 | # _prefilter routine explicitely does so, to catch direct calls and |
|
|||
526 | # rebindings of existing names. |
|
|||
527 |
|
||||
528 | # Warning: the '-' HAS TO BE AT THE END of the first group, otherwise |
|
|||
529 | # it affects the rest of the group in square brackets. |
|
|||
530 | self.re_exclude_auto = re.compile(r'^[<>,&^\|\*/\+-]' |
|
|||
531 | '|^is |^not |^in |^and |^or ') |
|
|||
532 |
|
||||
533 | # try to catch also methods for stuff in lists/tuples/dicts: off |
|
|||
534 | # (experimental). For this to work, the line_split regexp would need |
|
|||
535 | # to be modified so it wouldn't break things at '['. That line is |
|
|||
536 | # nasty enough that I shouldn't change it until I can test it _well_. |
|
|||
537 | #self.re_fun_name = re.compile (r'[a-zA-Z_]([a-zA-Z0-9_.\[\]]*) ?$') |
|
|||
538 |
|
491 | |||
539 | # keep track of where we started running (mainly for crash post-mortem) |
|
492 | # keep track of where we started running (mainly for crash post-mortem) | |
540 | self.starting_dir = os.getcwd() |
|
493 | self.starting_dir = os.getcwd() | |
@@ -1731,8 +1684,8 b' want to merge them back into the new files.""" % locals()' | |||||
1731 |
|
1684 | |||
1732 | done = Set() |
|
1685 | done = Set() | |
1733 | while 1: |
|
1686 | while 1: | |
1734 |
pre,fn,rest = |
|
1687 | pre,fn,rest = prefilter.splitUserInput(line, | |
1735 | # print "!",fn,"!",rest # dbg |
|
1688 | prefilter.shell_line_split) | |
1736 | if fn in self.alias_table: |
|
1689 | if fn in self.alias_table: | |
1737 | if fn in done: |
|
1690 | if fn in done: | |
1738 | warn("Cyclic alias definition, repeated '%s'" % fn) |
|
1691 | warn("Cyclic alias definition, repeated '%s'" % fn) | |
@@ -2056,53 +2009,6 b' want to merge them back into the new files.""" % locals()' | |||||
2056 | else: |
|
2009 | else: | |
2057 | return lineout |
|
2010 | return lineout | |
2058 |
|
2011 | |||
2059 | def split_user_input(self,line, pattern = None): |
|
|||
2060 | """Split user input into pre-char, function part and rest.""" |
|
|||
2061 |
|
||||
2062 | if pattern is None: |
|
|||
2063 | pattern = self.line_split |
|
|||
2064 |
|
||||
2065 | lsplit = pattern.match(line) |
|
|||
2066 | if lsplit is None: # no regexp match returns None |
|
|||
2067 | #print "match failed for line '%s'" % line # dbg |
|
|||
2068 | try: |
|
|||
2069 | iFun,theRest = line.split(None,1) |
|
|||
2070 | except ValueError: |
|
|||
2071 | #print "split failed for line '%s'" % line # dbg |
|
|||
2072 | iFun,theRest = line,'' |
|
|||
2073 | pre = re.match('^(\s*)(.*)',line).groups()[0] |
|
|||
2074 | else: |
|
|||
2075 | pre,iFun,theRest = lsplit.groups() |
|
|||
2076 |
|
||||
2077 | # iFun has to be a valid python identifier, so it better be only pure |
|
|||
2078 | #ascii, no unicode: |
|
|||
2079 | try: |
|
|||
2080 | iFun = iFun.encode('ascii') |
|
|||
2081 | except UnicodeEncodeError: |
|
|||
2082 | theRest = iFun+u' '+theRest |
|
|||
2083 | iFun = u'' |
|
|||
2084 |
|
||||
2085 | #print 'line:<%s>' % line # dbg |
|
|||
2086 | #print 'pre <%s> iFun <%s> rest <%s>' % (pre,iFun.strip(),theRest) # dbg |
|
|||
2087 | return pre,iFun.strip(),theRest |
|
|||
2088 |
|
||||
2089 | # THIS VERSION IS BROKEN!!! It was intended to prevent spurious attribute |
|
|||
2090 | # accesses with a more stringent check of inputs, but it introduced other |
|
|||
2091 | # bugs. Disable it for now until I can properly fix it. |
|
|||
2092 | def split_user_inputBROKEN(self,line): |
|
|||
2093 | """Split user input into pre-char, function part and rest.""" |
|
|||
2094 |
|
||||
2095 | lsplit = self.line_split.match(line) |
|
|||
2096 | if lsplit is None: # no regexp match returns None |
|
|||
2097 | lsplit = self.line_split_fallback.match(line) |
|
|||
2098 |
|
||||
2099 | #pre,iFun,theRest = lsplit.groups() # dbg |
|
|||
2100 | #print 'line:<%s>' % line # dbg |
|
|||
2101 | #print 'pre <%s> iFun <%s> rest <%s>' % (pre,iFun.strip(),theRest) # dbg |
|
|||
2102 | #return pre,iFun.strip(),theRest # dbg |
|
|||
2103 |
|
||||
2104 | return lsplit.groups() |
|
|||
2105 |
|
||||
2106 | def _prefilter(self, line, continue_prompt): |
|
2012 | def _prefilter(self, line, continue_prompt): | |
2107 | """Calls different preprocessors, depending on the form of line.""" |
|
2013 | """Calls different preprocessors, depending on the form of line.""" | |
2108 |
|
2014 | |||
@@ -2112,15 +2018,6 b' want to merge them back into the new files.""" % locals()' | |||||
2112 | # needed, update the cache AND log it (so that the input cache array |
|
2018 | # needed, update the cache AND log it (so that the input cache array | |
2113 | # stays synced). |
|
2019 | # stays synced). | |
2114 |
|
2020 | |||
2115 | # This function is _very_ delicate, and since it's also the one which |
|
|||
2116 | # determines IPython's response to user input, it must be as efficient |
|
|||
2117 | # as possible. For this reason it has _many_ returns in it, trying |
|
|||
2118 | # always to exit as quickly as it can figure out what it needs to do. |
|
|||
2119 |
|
||||
2120 | # This function is the main responsible for maintaining IPython's |
|
|||
2121 | # behavior respectful of Python's semantics. So be _very_ careful if |
|
|||
2122 | # making changes to anything here. |
|
|||
2123 |
|
||||
2124 | #..................................................................... |
|
2021 | #..................................................................... | |
2125 | # Code begins |
|
2022 | # Code begins | |
2126 |
|
2023 | |||
@@ -2131,6 +2028,8 b' want to merge them back into the new files.""" % locals()' | |||||
2131 | self._last_input_line = line |
|
2028 | self._last_input_line = line | |
2132 |
|
2029 | |||
2133 | #print '***line: <%s>' % line # dbg |
|
2030 | #print '***line: <%s>' % line # dbg | |
|
2031 | ||||
|
2032 | line_info = prefilter.LineInfo(line, continue_prompt) | |||
2134 |
|
2033 | |||
2135 | # the input history needs to track even empty lines |
|
2034 | # the input history needs to track even empty lines | |
2136 | stripped = line.strip() |
|
2035 | stripped = line.strip() | |
@@ -2138,139 +2037,25 b' want to merge them back into the new files.""" % locals()' | |||||
2138 | if not stripped: |
|
2037 | if not stripped: | |
2139 | if not continue_prompt: |
|
2038 | if not continue_prompt: | |
2140 | self.outputcache.prompt_count -= 1 |
|
2039 | self.outputcache.prompt_count -= 1 | |
2141 |
return self.handle_normal(line |
|
2040 | return self.handle_normal(line_info) | |
2142 | #return self.handle_normal('',continue_prompt) |
|
|||
2143 |
|
2041 | |||
2144 | # print '***cont',continue_prompt # dbg |
|
2042 | # print '***cont',continue_prompt # dbg | |
2145 | # special handlers are only allowed for single line statements |
|
2043 | # special handlers are only allowed for single line statements | |
2146 | if continue_prompt and not self.rc.multi_line_specials: |
|
2044 | if continue_prompt and not self.rc.multi_line_specials: | |
2147 |
return self.handle_normal(line |
|
2045 | return self.handle_normal(line_info) | |
2148 |
|
||||
2149 |
|
2046 | |||
2150 | # For the rest, we need the structure of the input |
|
|||
2151 | pre,iFun,theRest = self.split_user_input(line) |
|
|||
2152 |
|
2047 | |||
2153 | # See whether any pre-existing handler can take care of it |
|
2048 | # See whether any pre-existing handler can take care of it | |
2154 |
|
||||
2155 | rewritten = self.hooks.input_prefilter(stripped) |
|
2049 | rewritten = self.hooks.input_prefilter(stripped) | |
2156 | if rewritten != stripped: # ok, some prefilter did something |
|
2050 | if rewritten != stripped: # ok, some prefilter did something | |
2157 | rewritten = pre + rewritten # add indentation |
|
2051 | rewritten = line_info.pre + rewritten # add indentation | |
2158 |
return self.handle_normal(rewritten |
|
2052 | return self.handle_normal(prefilter.LineInfo(rewritten, | |
|
2053 | continue_prompt)) | |||
2159 |
|
2054 | |||
2160 | #print 'pre <%s> iFun <%s> rest <%s>' % (pre,iFun,theRest) # dbg |
|
2055 | #print 'pre <%s> iFun <%s> rest <%s>' % (pre,iFun,theRest) # dbg | |
2161 |
|
2056 | |||
2162 | # Next, check if we can automatically execute this thing |
|
2057 | return prefilter.prefilter(line_info, self) | |
2163 |
|
||||
2164 | # Allow ! in multi-line statements if multi_line_specials is on: |
|
|||
2165 | if continue_prompt and self.rc.multi_line_specials and \ |
|
|||
2166 | iFun.startswith(self.ESC_SHELL): |
|
|||
2167 | return self.handle_shell_escape(line,continue_prompt, |
|
|||
2168 | pre=pre,iFun=iFun, |
|
|||
2169 | theRest=theRest) |
|
|||
2170 |
|
||||
2171 | # First check for explicit escapes in the last/first character |
|
|||
2172 | handler = None |
|
|||
2173 | if line[-1] == self.ESC_HELP and line[0] != self.ESC_SHELL: |
|
|||
2174 | handler = self.esc_handlers.get(line[-1]) # the ? can be at the end |
|
|||
2175 | if handler is None: |
|
|||
2176 | # look at the first character of iFun, NOT of line, so we skip |
|
|||
2177 | # leading whitespace in multiline input |
|
|||
2178 | handler = self.esc_handlers.get(iFun[0:1]) |
|
|||
2179 | if handler is not None: |
|
|||
2180 | return handler(line,continue_prompt,pre,iFun,theRest) |
|
|||
2181 | # Emacs ipython-mode tags certain input lines |
|
|||
2182 | if line.endswith('# PYTHON-MODE'): |
|
|||
2183 | return self.handle_emacs(line,continue_prompt) |
|
|||
2184 |
|
||||
2185 | # instances of IPyAutocall in user_ns get autocalled immediately |
|
|||
2186 | obj = self.user_ns.get(iFun,None) |
|
|||
2187 | if isinstance(obj, IPython.ipapi.IPyAutocall): |
|
|||
2188 | obj.set_ip(self.api) |
|
|||
2189 | return self.handle_auto(line,continue_prompt, |
|
|||
2190 | pre,iFun,theRest,obj) |
|
|||
2191 |
|
||||
2192 | # Let's try to find if the input line is a magic fn |
|
|||
2193 | oinfo = None |
|
|||
2194 | if hasattr(self,'magic_'+iFun): |
|
|||
2195 | # WARNING: _ofind uses getattr(), so it can consume generators and |
|
|||
2196 | # cause other side effects. |
|
|||
2197 | oinfo = self._ofind(iFun) # FIXME - _ofind is part of Magic |
|
|||
2198 | if oinfo['ismagic']: |
|
|||
2199 | # Be careful not to call magics when a variable assignment is |
|
|||
2200 | # being made (ls='hi', for example) |
|
|||
2201 | if self.rc.automagic and \ |
|
|||
2202 | (len(theRest)==0 or theRest[0] not in '!=()<>,') and \ |
|
|||
2203 | (self.rc.multi_line_specials or not continue_prompt): |
|
|||
2204 | return self.handle_magic(line,continue_prompt, |
|
|||
2205 | pre,iFun,theRest) |
|
|||
2206 | else: |
|
|||
2207 | return self.handle_normal(line,continue_prompt) |
|
|||
2208 |
|
||||
2209 | # If the rest of the line begins with an (in)equality, assginment or |
|
|||
2210 | # function call, we should not call _ofind but simply execute it. |
|
|||
2211 | # This avoids spurious geattr() accesses on objects upon assignment. |
|
|||
2212 | # |
|
|||
2213 | # It also allows users to assign to either alias or magic names true |
|
|||
2214 | # python variables (the magic/alias systems always take second seat to |
|
|||
2215 | # true python code). |
|
|||
2216 | if theRest and theRest[0] in '!=()': |
|
|||
2217 | return self.handle_normal(line,continue_prompt) |
|
|||
2218 |
|
||||
2219 | if oinfo is None: |
|
|||
2220 | # let's try to ensure that _oinfo is ONLY called when autocall is |
|
|||
2221 | # on. Since it has inevitable potential side effects, at least |
|
|||
2222 | # having autocall off should be a guarantee to the user that no |
|
|||
2223 | # weird things will happen. |
|
|||
2224 |
|
||||
2225 | if self.rc.autocall: |
|
|||
2226 | oinfo = self._ofind(iFun) # FIXME - _ofind is part of Magic |
|
|||
2227 | else: |
|
|||
2228 | # in this case, all that's left is either an alias or |
|
|||
2229 | # processing the line normally. |
|
|||
2230 | if iFun in self.alias_table: |
|
|||
2231 | # if autocall is off, by not running _ofind we won't know |
|
|||
2232 | # whether the given name may also exist in one of the |
|
|||
2233 | # user's namespace. At this point, it's best to do a |
|
|||
2234 | # quick check just to be sure that we don't let aliases |
|
|||
2235 | # shadow variables. |
|
|||
2236 | head = iFun.split('.',1)[0] |
|
|||
2237 | if head in self.user_ns or head in self.internal_ns \ |
|
|||
2238 | or head in __builtin__.__dict__: |
|
|||
2239 | return self.handle_normal(line,continue_prompt) |
|
|||
2240 | else: |
|
|||
2241 | return self.handle_alias(line,continue_prompt, |
|
|||
2242 | pre,iFun,theRest) |
|
|||
2243 |
|
||||
2244 | else: |
|
|||
2245 | return self.handle_normal(line,continue_prompt) |
|
|||
2246 |
|
||||
2247 | if not oinfo['found']: |
|
|||
2248 | return self.handle_normal(line,continue_prompt) |
|
|||
2249 | else: |
|
|||
2250 | #print 'pre<%s> iFun <%s> rest <%s>' % (pre,iFun,theRest) # dbg |
|
|||
2251 | if oinfo['isalias']: |
|
|||
2252 | return self.handle_alias(line,continue_prompt, |
|
|||
2253 | pre,iFun,theRest) |
|
|||
2254 |
|
||||
2255 | if (self.rc.autocall |
|
|||
2256 | and |
|
|||
2257 | ( |
|
|||
2258 | #only consider exclusion re if not "," or ";" autoquoting |
|
|||
2259 | (pre == self.ESC_QUOTE or pre == self.ESC_QUOTE2 |
|
|||
2260 | or pre == self.ESC_PAREN) or |
|
|||
2261 | (not self.re_exclude_auto.match(theRest))) |
|
|||
2262 | and |
|
|||
2263 | self.re_fun_name.match(iFun) and |
|
|||
2264 | callable(oinfo['obj'])) : |
|
|||
2265 | #print 'going auto' # dbg |
|
|||
2266 | return self.handle_auto(line,continue_prompt, |
|
|||
2267 | pre,iFun,theRest,oinfo['obj']) |
|
|||
2268 | else: |
|
|||
2269 | #print 'was callable?', callable(oinfo['obj']) # dbg |
|
|||
2270 | return self.handle_normal(line,continue_prompt) |
|
|||
2271 |
|
2058 | |||
2272 | # If we get here, we have a normal Python line. Log and return. |
|
|||
2273 | return self.handle_normal(line,continue_prompt) |
|
|||
2274 |
|
2059 | |||
2275 | def _prefilter_dumb(self, line, continue_prompt): |
|
2060 | def _prefilter_dumb(self, line, continue_prompt): | |
2276 | """simple prefilter function, for debugging""" |
|
2061 | """simple prefilter function, for debugging""" | |
@@ -2293,8 +2078,7 b' want to merge them back into the new files.""" % locals()' | |||||
2293 | # Set the default prefilter() function (this can be user-overridden) |
|
2078 | # Set the default prefilter() function (this can be user-overridden) | |
2294 | prefilter = multiline_prefilter |
|
2079 | prefilter = multiline_prefilter | |
2295 |
|
2080 | |||
2296 |
def handle_normal(self,line |
|
2081 | def handle_normal(self,line_info): | |
2297 | pre=None,iFun=None,theRest=None): |
|
|||
2298 | """Handle normal input lines. Use as a template for handlers.""" |
|
2082 | """Handle normal input lines. Use as a template for handlers.""" | |
2299 |
|
2083 | |||
2300 | # With autoindent on, we need some way to exit the input loop, and I |
|
2084 | # With autoindent on, we need some way to exit the input loop, and I | |
@@ -2302,6 +2086,8 b' want to merge them back into the new files.""" % locals()' | |||||
2302 | # clear the line. The rule will be in this case, that either two |
|
2086 | # clear the line. The rule will be in this case, that either two | |
2303 | # lines of pure whitespace in a row, or a line of pure whitespace but |
|
2087 | # lines of pure whitespace in a row, or a line of pure whitespace but | |
2304 | # of a size different to the indent level, will exit the input loop. |
|
2088 | # of a size different to the indent level, will exit the input loop. | |
|
2089 | line = line_info.line | |||
|
2090 | continue_prompt = line_info.continue_prompt | |||
2305 |
|
2091 | |||
2306 | if (continue_prompt and self.autoindent and line.isspace() and |
|
2092 | if (continue_prompt and self.autoindent and line.isspace() and | |
2307 | (0 < abs(len(line) - self.indent_current_nsp) <= 2 or |
|
2093 | (0 < abs(len(line) - self.indent_current_nsp) <= 2 or | |
@@ -2311,55 +2097,62 b' want to merge them back into the new files.""" % locals()' | |||||
2311 | self.log(line,line,continue_prompt) |
|
2097 | self.log(line,line,continue_prompt) | |
2312 | return line |
|
2098 | return line | |
2313 |
|
2099 | |||
2314 |
def handle_alias(self,line |
|
2100 | def handle_alias(self,line_info): | |
2315 | pre=None,iFun=None,theRest=None): |
|
2101 | """Handle alias input lines. """ | |
2316 | """Handle alias input lines. """ |
|
2102 | transformed = self.expand_aliases(line_info.iFun,line_info.theRest) | |
2317 |
|
2103 | |||
2318 | # pre is needed, because it carries the leading whitespace. Otherwise |
|
2104 | # pre is needed, because it carries the leading whitespace. Otherwise | |
2319 | # aliases won't work in indented sections. |
|
2105 | # aliases won't work in indented sections. | |
2320 | transformed = self.expand_aliases(iFun, theRest) |
|
2106 | line_out = '%s_ip.system(%s)' % (line_info.preWhitespace, | |
2321 | line_out = '%s_ip.system(%s)' % (pre, make_quoted_expr( transformed )) |
|
2107 | make_quoted_expr( transformed )) | |
2322 | self.log(line,line_out,continue_prompt) |
|
2108 | ||
|
2109 | self.log(line_info.line,line_out,line_info.continue_prompt) | |||
2323 | #print 'line out:',line_out # dbg |
|
2110 | #print 'line out:',line_out # dbg | |
2324 | return line_out |
|
2111 | return line_out | |
2325 |
|
2112 | |||
2326 |
def handle_shell_escape(self, line |
|
2113 | def handle_shell_escape(self, line_info): | |
2327 | pre=None,iFun=None,theRest=None): |
|
|||
2328 | """Execute the line in a shell, empty return value""" |
|
2114 | """Execute the line in a shell, empty return value""" | |
2329 |
|
||||
2330 | #print 'line in :', `line` # dbg |
|
2115 | #print 'line in :', `line` # dbg | |
2331 | # Example of a special handler. Others follow a similar pattern. |
|
2116 | line = line_info.line | |
2332 | if line.lstrip().startswith('!!'): |
|
2117 | if line.lstrip().startswith('!!'): | |
2333 |
# rewrite |
|
2118 | # rewrite LineInfo's line, iFun and theRest to properly hold the | |
2334 |
# the actual command to be executed, so |
|
2119 | # call to %sx and the actual command to be executed, so | |
2335 | # correctly |
|
2120 | # handle_magic can work correctly. Note that this works even if | |
2336 | theRest = '%s %s' % (iFun[2:],theRest) |
|
2121 | # the line is indented, so it handles multi_line_specials | |
2337 | iFun = 'sx' |
|
2122 | # properly. | |
2338 | return self.handle_magic('%ssx %s' % (self.ESC_MAGIC, |
|
2123 | new_rest = line.lstrip()[2:] | |
2339 | line.lstrip()[2:]), |
|
2124 | line_info.line = '%ssx %s' % (self.ESC_MAGIC,new_rest) | |
2340 | continue_prompt,pre,iFun,theRest) |
|
2125 | line_info.iFun = 'sx' | |
|
2126 | line_info.theRest = new_rest | |||
|
2127 | return self.handle_magic(line_info) | |||
2341 | else: |
|
2128 | else: | |
2342 | cmd=line.lstrip().lstrip('!') |
|
2129 | cmd = line.lstrip().lstrip('!') | |
2343 |
line_out = '%s_ip.system(%s)' % (pre, |
|
2130 | line_out = '%s_ip.system(%s)' % (line_info.preWhitespace, | |
|
2131 | make_quoted_expr(cmd)) | |||
2344 | # update cache/log and return |
|
2132 | # update cache/log and return | |
2345 | self.log(line,line_out,continue_prompt) |
|
2133 | self.log(line,line_out,line_info.continue_prompt) | |
2346 | return line_out |
|
2134 | return line_out | |
2347 |
|
2135 | |||
2348 |
def handle_magic(self, line |
|
2136 | def handle_magic(self, line_info): | |
2349 | pre=None,iFun=None,theRest=None): |
|
|||
2350 | """Execute magic functions.""" |
|
2137 | """Execute magic functions.""" | |
2351 |
|
2138 | iFun = line_info.iFun | ||
2352 |
|
2139 | theRest = line_info.theRest | ||
2353 | cmd = '%s_ip.magic(%s)' % (pre,make_quoted_expr(iFun + " " + theRest)) |
|
2140 | cmd = '%s_ip.magic(%s)' % (line_info.preWhitespace, | |
2354 | self.log(line,cmd,continue_prompt) |
|
2141 | make_quoted_expr(iFun + " " + theRest)) | |
|
2142 | self.log(line_info.line,cmd,line_info.continue_prompt) | |||
2355 | #print 'in handle_magic, cmd=<%s>' % cmd # dbg |
|
2143 | #print 'in handle_magic, cmd=<%s>' % cmd # dbg | |
2356 | return cmd |
|
2144 | return cmd | |
2357 |
|
2145 | |||
2358 |
def handle_auto(self, line |
|
2146 | def handle_auto(self, line_info): | |
2359 | pre=None,iFun=None,theRest=None,obj=None): |
|
|||
2360 | """Hande lines which can be auto-executed, quoting if requested.""" |
|
2147 | """Hande lines which can be auto-executed, quoting if requested.""" | |
2361 |
|
2148 | |||
2362 | #print 'pre <%s> iFun <%s> rest <%s>' % (pre,iFun,theRest) # dbg |
|
2149 | #print 'pre <%s> iFun <%s> rest <%s>' % (pre,iFun,theRest) # dbg | |
|
2150 | line = line_info.line | |||
|
2151 | iFun = line_info.iFun | |||
|
2152 | theRest = line_info.theRest | |||
|
2153 | pre = line_info.pre | |||
|
2154 | continue_prompt = line_info.continue_prompt | |||
|
2155 | obj = line_info.ofind(self)['obj'] | |||
2363 |
|
2156 | |||
2364 | # This should only be active for single-line input! |
|
2157 | # This should only be active for single-line input! | |
2365 | if continue_prompt: |
|
2158 | if continue_prompt: | |
@@ -2408,14 +2201,14 b' want to merge them back into the new files.""" % locals()' | |||||
2408 | self.log(line,newcmd,continue_prompt) |
|
2201 | self.log(line,newcmd,continue_prompt) | |
2409 | return newcmd |
|
2202 | return newcmd | |
2410 |
|
2203 | |||
2411 |
def handle_help(self, line |
|
2204 | def handle_help(self, line_info): | |
2412 | pre=None,iFun=None,theRest=None): |
|
|||
2413 | """Try to get some help for the object. |
|
2205 | """Try to get some help for the object. | |
2414 |
|
2206 | |||
2415 | obj? or ?obj -> basic information. |
|
2207 | obj? or ?obj -> basic information. | |
2416 | obj?? or ??obj -> more details. |
|
2208 | obj?? or ??obj -> more details. | |
2417 | """ |
|
2209 | """ | |
2418 |
|
2210 | |||
|
2211 | line = line_info.line | |||
2419 | # We need to make sure that we don't process lines which would be |
|
2212 | # We need to make sure that we don't process lines which would be | |
2420 | # otherwise valid python, such as "x=1 # what?" |
|
2213 | # otherwise valid python, such as "x=1 # what?" | |
2421 | try: |
|
2214 | try: | |
@@ -2426,7 +2219,7 b' want to merge them back into the new files.""" % locals()' | |||||
2426 | line = line[1:] |
|
2219 | line = line[1:] | |
2427 | elif line[-1]==self.ESC_HELP: |
|
2220 | elif line[-1]==self.ESC_HELP: | |
2428 | line = line[:-1] |
|
2221 | line = line[:-1] | |
2429 | self.log(line,'#?'+line,continue_prompt) |
|
2222 | self.log(line,'#?'+line,line_info.continue_prompt) | |
2430 | if line: |
|
2223 | if line: | |
2431 | #print 'line:<%r>' % line # dbg |
|
2224 | #print 'line:<%r>' % line # dbg | |
2432 | self.magic_pinfo(line) |
|
2225 | self.magic_pinfo(line) | |
@@ -2435,10 +2228,10 b' want to merge them back into the new files.""" % locals()' | |||||
2435 | return '' # Empty string is needed here! |
|
2228 | return '' # Empty string is needed here! | |
2436 | except: |
|
2229 | except: | |
2437 | # Pass any other exceptions through to the normal handler |
|
2230 | # Pass any other exceptions through to the normal handler | |
2438 |
return self.handle_normal(line |
|
2231 | return self.handle_normal(line_info) | |
2439 | else: |
|
2232 | else: | |
2440 | # If the code compiles ok, we should handle it normally |
|
2233 | # If the code compiles ok, we should handle it normally | |
2441 |
return self.handle_normal(line |
|
2234 | return self.handle_normal(line_info) | |
2442 |
|
2235 | |||
2443 | def getapi(self): |
|
2236 | def getapi(self): | |
2444 | """ Get an IPApi object for this shell instance |
|
2237 | """ Get an IPApi object for this shell instance | |
@@ -2451,17 +2244,16 b' want to merge them back into the new files.""" % locals()' | |||||
2451 |
|
2244 | |||
2452 | """ |
|
2245 | """ | |
2453 | return self.api |
|
2246 | return self.api | |
2454 |
|
2247 | |||
2455 |
def handle_emacs(self, |
|
2248 | def handle_emacs(self, line_info): | |
2456 | pre=None,iFun=None,theRest=None): |
|
|||
2457 | """Handle input lines marked by python-mode.""" |
|
2249 | """Handle input lines marked by python-mode.""" | |
2458 |
|
2250 | |||
2459 | # Currently, nothing is done. Later more functionality can be added |
|
2251 | # Currently, nothing is done. Later more functionality can be added | |
2460 | # here if needed. |
|
2252 | # here if needed. | |
2461 |
|
2253 | |||
2462 | # The input cache shouldn't be updated |
|
2254 | # The input cache shouldn't be updated | |
2463 |
|
2255 | return line_info.line | ||
2464 | return line |
|
2256 | ||
2465 |
|
2257 | |||
2466 | def mktempfile(self,data=None): |
|
2258 | def mktempfile(self,data=None): | |
2467 | """Make a new tempfile and return its filename. |
|
2259 | """Make a new tempfile and return its filename. |
@@ -73,6 +73,7 b' def reset_esc_handlers():' | |||||
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 | } |
|
77 | } | |
77 | reset_esc_handlers() |
|
78 | reset_esc_handlers() | |
78 |
|
79 | |||
@@ -145,7 +146,14 b' esc_handler_tests = [' | |||||
145 | ( '?thing', handle_help, ), |
|
146 | ( '?thing', handle_help, ), | |
146 | ( 'thing?', handle_help ), # '?' can trail... |
|
147 | ( 'thing?', handle_help ), # '?' can trail... | |
147 | ( 'thing!', handle_normal), # but only '?' can trail |
|
148 | ( 'thing!', handle_normal), # but only '?' can trail | |
148 |
( ' ?thing', handle_ |
|
149 | ( ' ?thing', handle_normal), # leading whitespace turns off esc chars | |
|
150 | ( '!ls', handle_shell_escape), | |||
|
151 | ( '! true', handle_shell_escape), | |||
|
152 | ( '!! true', handle_shell_escape), | |||
|
153 | ( '%magic', handle_magic), | |||
|
154 | # XXX Possibly, add test for /,; once those are unhooked from %autocall | |||
|
155 | ( 'emacs_mode # PYTHON-MODE', handle_emacs ), | |||
|
156 | ( ' ', handle_normal), | |||
149 | # Trailing qmark combos. Odd special cases abound |
|
157 | # Trailing qmark combos. Odd special cases abound | |
150 | ( '!thing?', handle_shell_escape), # trailing '?' loses to shell esc |
|
158 | ( '!thing?', handle_shell_escape), # trailing '?' loses to shell esc | |
151 | ( '!thing ?', handle_shell_escape), |
|
159 | ( '!thing ?', handle_shell_escape), | |
@@ -154,11 +162,6 b' esc_handler_tests = [' | |||||
154 | ( '/cmd?', handle_help), |
|
162 | ( '/cmd?', handle_help), | |
155 | ( ';cmd?', handle_help), |
|
163 | ( ';cmd?', handle_help), | |
156 | ( ',cmd?', handle_help), |
|
164 | ( ',cmd?', handle_help), | |
157 | ( '!ls', handle_shell_escape ), |
|
|||
158 | ( '%magic', handle_magic), |
|
|||
159 | # Possibly, add test for /,; once those are unhooked from %autocall |
|
|||
160 | ( 'emacs_mode # PYTHON-MODE', handle_emacs ), |
|
|||
161 | ( ' ', handle_normal), |
|
|||
162 | ] |
|
165 | ] | |
163 | run_handler_tests(esc_handler_tests) |
|
166 | run_handler_tests(esc_handler_tests) | |
164 |
|
167 | |||
@@ -167,19 +170,26 b' run_handler_tests(esc_handler_tests)' | |||||
167 | # Shell Escapes in Multi-line statements |
|
170 | # Shell Escapes in Multi-line statements | |
168 | # ====================================== |
|
171 | # ====================================== | |
169 | # |
|
172 | # | |
170 |
# We can't test this via runlines, since the hacked |
|
173 | # We can't test this via runlines, since the hacked-over-for-testing | |
171 |
# return None, so continue_prompt never becomes true. Instead |
|
174 | # handlers all return None, so continue_prompt never becomes true. Instead | |
172 | # into prefilter directly and pass in continue_prompt. |
|
175 | # we drop into prefilter directly and pass in continue_prompt. | |
173 |
|
176 | |||
174 | old_mls = ip.options.multi_line_specials |
|
177 | old_mls = ip.options.multi_line_specials | |
175 |
ln |
|
178 | for ln in [ ' !ls $f multi_line_specials %s', | |
176 | ignore = ip.IP.prefilter(ln, continue_prompt=True) |
|
179 | ' !!ls $f multi_line_specials %s', # !! escapes work on mls | |
177 | check_handler(handle_shell_escape, ln) |
|
180 | # Trailing ? doesn't trigger help: | |
178 |
|
181 | ' !ls $f multi_line_specials %s ?', | ||
179 | ip.options.multi_line_specials = 0 |
|
182 | ' !!ls $f multi_line_specials %s ?', | |
180 | ln = '!ls $f multi_line_specials off' |
|
183 | ]: | |
181 | ignore = ip.IP.prefilter(ln, continue_prompt=True) |
|
184 | ip.options.multi_line_specials = 1 | |
182 | check_handler(handle_normal, ln) |
|
185 | on_ln = ln % 'on' | |
|
186 | ignore = ip.IP.prefilter(on_ln, continue_prompt=True) | |||
|
187 | check_handler(handle_shell_escape, on_ln) | |||
|
188 | ||||
|
189 | ip.options.multi_line_specials = 0 | |||
|
190 | off_ln = ln % 'off' | |||
|
191 | ignore = ip.IP.prefilter(off_ln, continue_prompt=True) | |||
|
192 | check_handler(handle_normal, off_ln) | |||
183 |
|
193 | |||
184 | ip.options.multi_line_specials = old_mls |
|
194 | ip.options.multi_line_specials = old_mls | |
185 |
|
195 | |||
@@ -190,6 +200,7 b' ip.options.multi_line_specials = old_mls' | |||||
190 | # Pick one magic fun and one non_magic fun, make sure both exist |
|
200 | # Pick one magic fun and one non_magic fun, make sure both exist | |
191 | assert hasattr(ip.IP, "magic_cpaste") |
|
201 | assert hasattr(ip.IP, "magic_cpaste") | |
192 | assert not hasattr(ip.IP, "magic_does_not_exist") |
|
202 | assert not hasattr(ip.IP, "magic_does_not_exist") | |
|
203 | ip.options.autocall = 0 # gotta have this off to get handle_normal | |||
193 | ip.options.automagic = 0 |
|
204 | ip.options.automagic = 0 | |
194 | run_handler_tests([ |
|
205 | run_handler_tests([ | |
195 | # Without automagic, only shows up with explicit escape |
|
206 | # Without automagic, only shows up with explicit escape | |
@@ -214,12 +225,12 b' run_handler_tests(magic_killing_tests)' | |||||
214 |
|
225 | |||
215 | # magic on indented continuation lines -- on iff multi_line_specials == 1 |
|
226 | # magic on indented continuation lines -- on iff multi_line_specials == 1 | |
216 | ip.options.multi_line_specials = 0 |
|
227 | ip.options.multi_line_specials = 0 | |
217 | ln = 'cpaste multi_line off kills magic' |
|
228 | ln = ' cpaste multi_line off kills magic' | |
218 | ignore = ip.IP.prefilter(ln, continue_prompt=True) |
|
229 | ignore = ip.IP.prefilter(ln, continue_prompt=True) | |
219 | check_handler(handle_normal, ln) |
|
230 | check_handler(handle_normal, ln) | |
220 |
|
231 | |||
221 | ip.options.multi_line_specials = 1 |
|
232 | ip.options.multi_line_specials = 1 | |
222 | ln = 'cpaste multi_line on enables magic' |
|
233 | ln = ' cpaste multi_line on enables magic' | |
223 | ignore = ip.IP.prefilter(ln, continue_prompt=True) |
|
234 | ignore = ip.IP.prefilter(ln, continue_prompt=True) | |
224 | check_handler(handle_magic, ln) |
|
235 | check_handler(handle_magic, ln) | |
225 |
|
236 | |||
@@ -231,6 +242,7 b' run_handler_tests([' | |||||
231 | del ip.user_ns['cpaste'] |
|
242 | del ip.user_ns['cpaste'] | |
232 |
|
243 | |||
233 |
|
244 | |||
|
245 | ||||
234 | # Check for !=() turning off .ofind |
|
246 | # Check for !=() turning off .ofind | |
235 | # ================================= |
|
247 | # ================================= | |
236 | class AttributeMutator(object): |
|
248 | class AttributeMutator(object): | |
@@ -249,7 +261,7 b' ip.options.autocall = 1' | |||||
249 | run_one_test('attr_mutator.foo should mutate', handle_normal) |
|
261 | run_one_test('attr_mutator.foo should mutate', handle_normal) | |
250 | check(attr_mutator.called, 'ofind should be called in absence of assign characters') |
|
262 | check(attr_mutator.called, 'ofind should be called in absence of assign characters') | |
251 |
|
263 | |||
252 | for c in list('!=()'): # XXX What about <> -- they *are* important above |
|
264 | for c in list('!=()<>+*/%^&|'): | |
253 | attr_mutator.called = False |
|
265 | attr_mutator.called = False | |
254 | run_one_test('attr_mutator.foo %s should *not* mutate' % c, handle_normal) |
|
266 | run_one_test('attr_mutator.foo %s should *not* mutate' % c, handle_normal) | |
255 | run_one_test('attr_mutator.foo%s should *not* mutate' % c, handle_normal) |
|
267 | run_one_test('attr_mutator.foo%s should *not* mutate' % c, handle_normal) | |
@@ -296,51 +308,73 b' ip.options.autocall = 1' | |||||
296 | # Autocall |
|
308 | # Autocall | |
297 | # ======== |
|
309 | # ======== | |
298 |
|
310 | |||
299 | # First, with autocalling fully off |
|
311 | # For all the tests below, 'len' is callable / 'thing' is not | |
300 | ip.options.autocall = 0 |
|
|||
301 | run_handler_tests( [ |
|
|||
302 | # Since len is callable, these *should* get auto-called |
|
|||
303 |
|
||||
304 | # XXX Except, at the moment, they're *not*, because the code is wrong |
|
|||
305 | # XXX So I'm commenting 'em out to keep the tests quiet |
|
|||
306 |
|
312 | |||
307 | #( '/len autocall_0', handle_auto), |
|
313 | # Objects which are instances of IPyAutocall are *always* autocalled | |
308 | #( ',len autocall_0 b0', handle_auto), |
|
314 | import IPython.ipapi | |
309 | #( ';len autocall_0 b0', handle_auto), |
|
315 | class Autocallable(IPython.ipapi.IPyAutocall): | |
|
316 | def __call__(self): | |||
|
317 | return "called" | |||
310 |
|
318 | |||
311 | # But these, since fun is not a callable, should *not* get auto-called |
|
319 | autocallable = Autocallable() | |
312 | ( '/fun autocall_0', handle_normal), |
|
320 | ip.to_user_ns('autocallable') | |
313 | ( ',fun autocall_0 b0', handle_normal), |
|
321 | ||
314 | ( ';fun autocall_0 b0', handle_normal), |
|
|||
315 |
|
322 | |||
316 | # With no escapes, no autocalling should happen, callable or not |
|
323 | # First, with autocalling fully off | |
|
324 | ip.options.autocall = 0 | |||
|
325 | run_handler_tests( [ | |||
|
326 | # With no escapes, no autocalling expansions happen, callable or not, | |||
|
327 | # unless the obj extends IPyAutocall | |||
317 | ( 'len autocall_0', handle_normal), |
|
328 | ( 'len autocall_0', handle_normal), | |
318 |
( ' |
|
329 | ( 'thing autocall_0', handle_normal), | |
|
330 | ( 'autocallable', handle_auto), | |||
|
331 | ||||
|
332 | # With explicit escapes, callable and non-callables both get expanded, | |||
|
333 | # regardless of the %autocall setting: | |||
|
334 | ( '/len autocall_0', handle_auto), | |||
|
335 | ( ',len autocall_0 b0', handle_auto), | |||
|
336 | ( ';len autocall_0 b0', handle_auto), | |||
|
337 | ||||
|
338 | ( '/thing autocall_0', handle_auto), | |||
|
339 | ( ',thing autocall_0 b0', handle_auto), | |||
|
340 | ( ';thing autocall_0 b0', handle_auto), | |||
|
341 | ||||
|
342 | # Explicit autocall should not trigger if there is leading whitespace | |||
|
343 | ( ' /len autocall_0', handle_normal), | |||
|
344 | ( ' ;len autocall_0', handle_normal), | |||
|
345 | ( ' ,len autocall_0', handle_normal), | |||
|
346 | ( ' / len autocall_0', handle_normal), | |||
|
347 | ||||
|
348 | # But should work if the whitespace comes after the esc char | |||
|
349 | ( '/ len autocall_0', handle_auto), | |||
|
350 | ( '; len autocall_0', handle_auto), | |||
|
351 | ( ', len autocall_0', handle_auto), | |||
|
352 | ( '/ len autocall_0', handle_auto), | |||
319 | ]) |
|
353 | ]) | |
320 |
|
354 | |||
321 |
|
355 | |||
322 | # Now, with autocall in default, 'smart' mode |
|
356 | # Now, with autocall in default, 'smart' mode | |
323 | ip.options.autocall = 1 |
|
357 | ip.options.autocall = 1 | |
324 | run_handler_tests( [ |
|
358 | run_handler_tests( [ | |
325 | # Since len is callable, these *do* get auto-called |
|
359 | # Autocalls without escapes -- only expand if it's callable | |
326 |
( ' |
|
360 | ( 'len a1', handle_auto), | |
327 |
( ' |
|
361 | ( 'thing a1', handle_normal), | |
328 |
( ' |
|
362 | ( 'autocallable', handle_auto), | |
329 | # But these, since fun is not a callable, should *not* get auto-called |
|
363 | ||
330 | ( '/fun a1', handle_normal), |
|
364 | # As above, all explicit escapes generate auto-calls, callable or not | |
331 |
( ' |
|
365 | ( '/len a1', handle_auto), | |
332 |
( ' |
|
366 | ( ',len a1 b1', handle_auto), | |
333 | # Autocalls without escapes |
|
367 | ( ';len a1 b1', handle_auto), | |
334 |
( ' |
|
368 | ( '/thing a1', handle_auto), | |
335 | ( 'fun a1', handle_normal), # Not callable -> no add |
|
369 | ( ',thing a1 b1', handle_auto), | |
|
370 | ( ';thing a1 b1', handle_auto), | |||
|
371 | ||||
336 | # Autocalls only happen on things which look like funcs, even if |
|
372 | # Autocalls only happen on things which look like funcs, even if | |
337 | # explicitly requested. Which, in this case means they look like a |
|
373 | # explicitly requested. Which, in this case means they look like a | |
338 |
# sequence of identifiers and . attribute references. |
|
374 | # sequence of identifiers and . attribute references. Possibly the | |
339 | # test should pass, but it's not at the moment (meaning, IPython is |
|
375 | # second of these two should trigger handle_auto. But not for now. | |
340 | # attempting to run an autocall). Though it does blow up in ipython |
|
|||
341 | # later (because of how lines are split, I think). |
|
|||
342 | ( '"abc".join range(4)', handle_normal), |
|
376 | ( '"abc".join range(4)', handle_normal), | |
343 |
|
|
377 | ( '/"abc".join range(4)', handle_normal), | |
344 | ]) |
|
378 | ]) | |
345 |
|
379 | |||
346 |
|
380 |
General Comments 0
You need to be logged in to leave comments.
Login now