##// END OF EJS Templates
suggested fix for autocall failure (#14555)...
M Bussonnier -
r28909:18e45295 merge
parent child Browse files
Show More
@@ -1,705 +1,707
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Prefiltering components.
3 Prefiltering components.
4
4
5 Prefilters transform user input before it is exec'd by Python. These
5 Prefilters transform user input before it is exec'd by Python. These
6 transforms are used to implement additional syntax such as !ls and %magic.
6 transforms are used to implement additional syntax such as !ls and %magic.
7 """
7 """
8
8
9 # Copyright (c) IPython Development Team.
9 # Copyright (c) IPython Development Team.
10 # Distributed under the terms of the Modified BSD License.
10 # Distributed under the terms of the Modified BSD License.
11
11
12 from keyword import iskeyword
12 from keyword import iskeyword
13 import re
13 import re
14
14
15 from .autocall import IPyAutocall
15 from .autocall import IPyAutocall
16 from traitlets.config.configurable import Configurable
16 from traitlets.config.configurable import Configurable
17 from .inputtransformer2 import (
17 from .inputtransformer2 import (
18 ESC_MAGIC,
18 ESC_MAGIC,
19 ESC_QUOTE,
19 ESC_QUOTE,
20 ESC_QUOTE2,
20 ESC_QUOTE2,
21 ESC_PAREN,
21 ESC_PAREN,
22 )
22 )
23 from .macro import Macro
23 from .macro import Macro
24 from .splitinput import LineInfo
24 from .splitinput import LineInfo
25
25
26 from traitlets import (
26 from traitlets import (
27 List, Integer, Unicode, Bool, Instance, CRegExp
27 List, Integer, Unicode, Bool, Instance, CRegExp
28 )
28 )
29
29
30 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
31 # Global utilities, errors and constants
31 # Global utilities, errors and constants
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33
33
34
34
35 class PrefilterError(Exception):
35 class PrefilterError(Exception):
36 pass
36 pass
37
37
38
38
39 # RegExp to identify potential function names
39 # RegExp to identify potential function names
40 re_fun_name = re.compile(r'[^\W\d]([\w.]*) *$')
40 re_fun_name = re.compile(r'[^\W\d]([\w.]*) *$')
41
41
42 # RegExp to exclude strings with this start from autocalling. In
42 # RegExp to exclude strings with this start from autocalling. In
43 # particular, all binary operators should be excluded, so that if foo is
43 # particular, all binary operators should be excluded, so that if foo is
44 # callable, foo OP bar doesn't become foo(OP bar), which is invalid. The
44 # callable, foo OP bar doesn't become foo(OP bar), which is invalid. The
45 # characters '!=()' don't need to be checked for, as the checkPythonChars
45 # characters '!=()' don't need to be checked for, as the checkPythonChars
46 # routine explicitly does so, to catch direct calls and rebindings of
46 # routine explicitly does so, to catch direct calls and rebindings of
47 # existing names.
47 # existing names.
48
48
49 # Warning: the '-' HAS TO BE AT THE END of the first group, otherwise
49 # Warning: the '-' HAS TO BE AT THE END of the first group, otherwise
50 # it affects the rest of the group in square brackets.
50 # it affects the rest of the group in square brackets.
51 re_exclude_auto = re.compile(r'^[,&^\|\*/\+-]'
51 re_exclude_auto = re.compile(r'^[,&^\|\*/\+-]'
52 r'|^is |^not |^in |^and |^or ')
52 r'|^is |^not |^in |^and |^or ')
53
53
54 # try to catch also methods for stuff in lists/tuples/dicts: off
54 # try to catch also methods for stuff in lists/tuples/dicts: off
55 # (experimental). For this to work, the line_split regexp would need
55 # (experimental). For this to work, the line_split regexp would need
56 # to be modified so it wouldn't break things at '['. That line is
56 # to be modified so it wouldn't break things at '['. That line is
57 # nasty enough that I shouldn't change it until I can test it _well_.
57 # nasty enough that I shouldn't change it until I can test it _well_.
58 #self.re_fun_name = re.compile (r'[a-zA-Z_]([a-zA-Z0-9_.\[\]]*) ?$')
58 #self.re_fun_name = re.compile (r'[a-zA-Z_]([a-zA-Z0-9_.\[\]]*) ?$')
59
59
60
60
61 # Handler Check Utilities
61 # Handler Check Utilities
62 def is_shadowed(identifier, ip):
62 def is_shadowed(identifier, ip):
63 """Is the given identifier defined in one of the namespaces which shadow
63 """Is the given identifier defined in one of the namespaces which shadow
64 the alias and magic namespaces? Note that an identifier is different
64 the alias and magic namespaces? Note that an identifier is different
65 than ifun, because it can not contain a '.' character."""
65 than ifun, because it can not contain a '.' character."""
66 # This is much safer than calling ofind, which can change state
66 # This is much safer than calling ofind, which can change state
67 return (identifier in ip.user_ns \
67 return (identifier in ip.user_ns \
68 or identifier in ip.user_global_ns \
68 or identifier in ip.user_global_ns \
69 or identifier in ip.ns_table['builtin']\
69 or identifier in ip.ns_table['builtin']\
70 or iskeyword(identifier))
70 or iskeyword(identifier))
71
71
72
72
73 #-----------------------------------------------------------------------------
73 #-----------------------------------------------------------------------------
74 # Main Prefilter manager
74 # Main Prefilter manager
75 #-----------------------------------------------------------------------------
75 #-----------------------------------------------------------------------------
76
76
77
77
78 class PrefilterManager(Configurable):
78 class PrefilterManager(Configurable):
79 """Main prefilter component.
79 """Main prefilter component.
80
80
81 The IPython prefilter is run on all user input before it is run. The
81 The IPython prefilter is run on all user input before it is run. The
82 prefilter consumes lines of input and produces transformed lines of
82 prefilter consumes lines of input and produces transformed lines of
83 input.
83 input.
84
84
85 The implementation consists of two phases:
85 The implementation consists of two phases:
86
86
87 1. Transformers
87 1. Transformers
88 2. Checkers and handlers
88 2. Checkers and handlers
89
89
90 Over time, we plan on deprecating the checkers and handlers and doing
90 Over time, we plan on deprecating the checkers and handlers and doing
91 everything in the transformers.
91 everything in the transformers.
92
92
93 The transformers are instances of :class:`PrefilterTransformer` and have
93 The transformers are instances of :class:`PrefilterTransformer` and have
94 a single method :meth:`transform` that takes a line and returns a
94 a single method :meth:`transform` that takes a line and returns a
95 transformed line. The transformation can be accomplished using any
95 transformed line. The transformation can be accomplished using any
96 tool, but our current ones use regular expressions for speed.
96 tool, but our current ones use regular expressions for speed.
97
97
98 After all the transformers have been run, the line is fed to the checkers,
98 After all the transformers have been run, the line is fed to the checkers,
99 which are instances of :class:`PrefilterChecker`. The line is passed to
99 which are instances of :class:`PrefilterChecker`. The line is passed to
100 the :meth:`check` method, which either returns `None` or a
100 the :meth:`check` method, which either returns `None` or a
101 :class:`PrefilterHandler` instance. If `None` is returned, the other
101 :class:`PrefilterHandler` instance. If `None` is returned, the other
102 checkers are tried. If an :class:`PrefilterHandler` instance is returned,
102 checkers are tried. If an :class:`PrefilterHandler` instance is returned,
103 the line is passed to the :meth:`handle` method of the returned
103 the line is passed to the :meth:`handle` method of the returned
104 handler and no further checkers are tried.
104 handler and no further checkers are tried.
105
105
106 Both transformers and checkers have a `priority` attribute, that determines
106 Both transformers and checkers have a `priority` attribute, that determines
107 the order in which they are called. Smaller priorities are tried first.
107 the order in which they are called. Smaller priorities are tried first.
108
108
109 Both transformers and checkers also have `enabled` attribute, which is
109 Both transformers and checkers also have `enabled` attribute, which is
110 a boolean that determines if the instance is used.
110 a boolean that determines if the instance is used.
111
111
112 Users or developers can change the priority or enabled attribute of
112 Users or developers can change the priority or enabled attribute of
113 transformers or checkers, but they must call the :meth:`sort_checkers`
113 transformers or checkers, but they must call the :meth:`sort_checkers`
114 or :meth:`sort_transformers` method after changing the priority.
114 or :meth:`sort_transformers` method after changing the priority.
115 """
115 """
116
116
117 multi_line_specials = Bool(True).tag(config=True)
117 multi_line_specials = Bool(True).tag(config=True)
118 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', allow_none=True)
118 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', allow_none=True)
119
119
120 def __init__(self, shell=None, **kwargs):
120 def __init__(self, shell=None, **kwargs):
121 super(PrefilterManager, self).__init__(shell=shell, **kwargs)
121 super(PrefilterManager, self).__init__(shell=shell, **kwargs)
122 self.shell = shell
122 self.shell = shell
123 self._transformers = []
123 self._transformers = []
124 self.init_handlers()
124 self.init_handlers()
125 self.init_checkers()
125 self.init_checkers()
126
126
127 #-------------------------------------------------------------------------
127 #-------------------------------------------------------------------------
128 # API for managing transformers
128 # API for managing transformers
129 #-------------------------------------------------------------------------
129 #-------------------------------------------------------------------------
130
130
131 def sort_transformers(self):
131 def sort_transformers(self):
132 """Sort the transformers by priority.
132 """Sort the transformers by priority.
133
133
134 This must be called after the priority of a transformer is changed.
134 This must be called after the priority of a transformer is changed.
135 The :meth:`register_transformer` method calls this automatically.
135 The :meth:`register_transformer` method calls this automatically.
136 """
136 """
137 self._transformers.sort(key=lambda x: x.priority)
137 self._transformers.sort(key=lambda x: x.priority)
138
138
139 @property
139 @property
140 def transformers(self):
140 def transformers(self):
141 """Return a list of checkers, sorted by priority."""
141 """Return a list of checkers, sorted by priority."""
142 return self._transformers
142 return self._transformers
143
143
144 def register_transformer(self, transformer):
144 def register_transformer(self, transformer):
145 """Register a transformer instance."""
145 """Register a transformer instance."""
146 if transformer not in self._transformers:
146 if transformer not in self._transformers:
147 self._transformers.append(transformer)
147 self._transformers.append(transformer)
148 self.sort_transformers()
148 self.sort_transformers()
149
149
150 def unregister_transformer(self, transformer):
150 def unregister_transformer(self, transformer):
151 """Unregister a transformer instance."""
151 """Unregister a transformer instance."""
152 if transformer in self._transformers:
152 if transformer in self._transformers:
153 self._transformers.remove(transformer)
153 self._transformers.remove(transformer)
154
154
155 #-------------------------------------------------------------------------
155 #-------------------------------------------------------------------------
156 # API for managing checkers
156 # API for managing checkers
157 #-------------------------------------------------------------------------
157 #-------------------------------------------------------------------------
158
158
159 def init_checkers(self):
159 def init_checkers(self):
160 """Create the default checkers."""
160 """Create the default checkers."""
161 self._checkers = []
161 self._checkers = []
162 for checker in _default_checkers:
162 for checker in _default_checkers:
163 checker(
163 checker(
164 shell=self.shell, prefilter_manager=self, parent=self
164 shell=self.shell, prefilter_manager=self, parent=self
165 )
165 )
166
166
167 def sort_checkers(self):
167 def sort_checkers(self):
168 """Sort the checkers by priority.
168 """Sort the checkers by priority.
169
169
170 This must be called after the priority of a checker is changed.
170 This must be called after the priority of a checker is changed.
171 The :meth:`register_checker` method calls this automatically.
171 The :meth:`register_checker` method calls this automatically.
172 """
172 """
173 self._checkers.sort(key=lambda x: x.priority)
173 self._checkers.sort(key=lambda x: x.priority)
174
174
175 @property
175 @property
176 def checkers(self):
176 def checkers(self):
177 """Return a list of checkers, sorted by priority."""
177 """Return a list of checkers, sorted by priority."""
178 return self._checkers
178 return self._checkers
179
179
180 def register_checker(self, checker):
180 def register_checker(self, checker):
181 """Register a checker instance."""
181 """Register a checker instance."""
182 if checker not in self._checkers:
182 if checker not in self._checkers:
183 self._checkers.append(checker)
183 self._checkers.append(checker)
184 self.sort_checkers()
184 self.sort_checkers()
185
185
186 def unregister_checker(self, checker):
186 def unregister_checker(self, checker):
187 """Unregister a checker instance."""
187 """Unregister a checker instance."""
188 if checker in self._checkers:
188 if checker in self._checkers:
189 self._checkers.remove(checker)
189 self._checkers.remove(checker)
190
190
191 #-------------------------------------------------------------------------
191 #-------------------------------------------------------------------------
192 # API for managing handlers
192 # API for managing handlers
193 #-------------------------------------------------------------------------
193 #-------------------------------------------------------------------------
194
194
195 def init_handlers(self):
195 def init_handlers(self):
196 """Create the default handlers."""
196 """Create the default handlers."""
197 self._handlers = {}
197 self._handlers = {}
198 self._esc_handlers = {}
198 self._esc_handlers = {}
199 for handler in _default_handlers:
199 for handler in _default_handlers:
200 handler(
200 handler(
201 shell=self.shell, prefilter_manager=self, parent=self
201 shell=self.shell, prefilter_manager=self, parent=self
202 )
202 )
203
203
204 @property
204 @property
205 def handlers(self):
205 def handlers(self):
206 """Return a dict of all the handlers."""
206 """Return a dict of all the handlers."""
207 return self._handlers
207 return self._handlers
208
208
209 def register_handler(self, name, handler, esc_strings):
209 def register_handler(self, name, handler, esc_strings):
210 """Register a handler instance by name with esc_strings."""
210 """Register a handler instance by name with esc_strings."""
211 self._handlers[name] = handler
211 self._handlers[name] = handler
212 for esc_str in esc_strings:
212 for esc_str in esc_strings:
213 self._esc_handlers[esc_str] = handler
213 self._esc_handlers[esc_str] = handler
214
214
215 def unregister_handler(self, name, handler, esc_strings):
215 def unregister_handler(self, name, handler, esc_strings):
216 """Unregister a handler instance by name with esc_strings."""
216 """Unregister a handler instance by name with esc_strings."""
217 try:
217 try:
218 del self._handlers[name]
218 del self._handlers[name]
219 except KeyError:
219 except KeyError:
220 pass
220 pass
221 for esc_str in esc_strings:
221 for esc_str in esc_strings:
222 h = self._esc_handlers.get(esc_str)
222 h = self._esc_handlers.get(esc_str)
223 if h is handler:
223 if h is handler:
224 del self._esc_handlers[esc_str]
224 del self._esc_handlers[esc_str]
225
225
226 def get_handler_by_name(self, name):
226 def get_handler_by_name(self, name):
227 """Get a handler by its name."""
227 """Get a handler by its name."""
228 return self._handlers.get(name)
228 return self._handlers.get(name)
229
229
230 def get_handler_by_esc(self, esc_str):
230 def get_handler_by_esc(self, esc_str):
231 """Get a handler by its escape string."""
231 """Get a handler by its escape string."""
232 return self._esc_handlers.get(esc_str)
232 return self._esc_handlers.get(esc_str)
233
233
234 #-------------------------------------------------------------------------
234 #-------------------------------------------------------------------------
235 # Main prefiltering API
235 # Main prefiltering API
236 #-------------------------------------------------------------------------
236 #-------------------------------------------------------------------------
237
237
238 def prefilter_line_info(self, line_info):
238 def prefilter_line_info(self, line_info):
239 """Prefilter a line that has been converted to a LineInfo object.
239 """Prefilter a line that has been converted to a LineInfo object.
240
240
241 This implements the checker/handler part of the prefilter pipe.
241 This implements the checker/handler part of the prefilter pipe.
242 """
242 """
243 # print("prefilter_line_info: ", line_info)
243 # print("prefilter_line_info: ", line_info)
244 handler = self.find_handler(line_info)
244 handler = self.find_handler(line_info)
245 return handler.handle(line_info)
245 return handler.handle(line_info)
246
246
247 def find_handler(self, line_info):
247 def find_handler(self, line_info):
248 """Find a handler for the line_info by trying checkers."""
248 """Find a handler for the line_info by trying checkers."""
249 for checker in self.checkers:
249 for checker in self.checkers:
250 if checker.enabled:
250 if checker.enabled:
251 handler = checker.check(line_info)
251 handler = checker.check(line_info)
252 if handler:
252 if handler:
253 return handler
253 return handler
254 return self.get_handler_by_name('normal')
254 return self.get_handler_by_name('normal')
255
255
256 def transform_line(self, line, continue_prompt):
256 def transform_line(self, line, continue_prompt):
257 """Calls the enabled transformers in order of increasing priority."""
257 """Calls the enabled transformers in order of increasing priority."""
258 for transformer in self.transformers:
258 for transformer in self.transformers:
259 if transformer.enabled:
259 if transformer.enabled:
260 line = transformer.transform(line, continue_prompt)
260 line = transformer.transform(line, continue_prompt)
261 return line
261 return line
262
262
263 def prefilter_line(self, line, continue_prompt=False):
263 def prefilter_line(self, line, continue_prompt=False):
264 """Prefilter a single input line as text.
264 """Prefilter a single input line as text.
265
265
266 This method prefilters a single line of text by calling the
266 This method prefilters a single line of text by calling the
267 transformers and then the checkers/handlers.
267 transformers and then the checkers/handlers.
268 """
268 """
269
269
270 # print("prefilter_line: ", line, continue_prompt)
270 # print("prefilter_line: ", line, continue_prompt)
271 # All handlers *must* return a value, even if it's blank ('').
271 # All handlers *must* return a value, even if it's blank ('').
272
272
273 # save the line away in case we crash, so the post-mortem handler can
273 # save the line away in case we crash, so the post-mortem handler can
274 # record it
274 # record it
275 self.shell._last_input_line = line
275 self.shell._last_input_line = line
276
276
277 if not line:
277 if not line:
278 # Return immediately on purely empty lines, so that if the user
278 # Return immediately on purely empty lines, so that if the user
279 # previously typed some whitespace that started a continuation
279 # previously typed some whitespace that started a continuation
280 # prompt, he can break out of that loop with just an empty line.
280 # prompt, he can break out of that loop with just an empty line.
281 # This is how the default python prompt works.
281 # This is how the default python prompt works.
282 return ''
282 return ''
283
283
284 # At this point, we invoke our transformers.
284 # At this point, we invoke our transformers.
285 if not continue_prompt or (continue_prompt and self.multi_line_specials):
285 if not continue_prompt or (continue_prompt and self.multi_line_specials):
286 line = self.transform_line(line, continue_prompt)
286 line = self.transform_line(line, continue_prompt)
287
287
288 # Now we compute line_info for the checkers and handlers
288 # Now we compute line_info for the checkers and handlers
289 line_info = LineInfo(line, continue_prompt)
289 line_info = LineInfo(line, continue_prompt)
290
290
291 # the input history needs to track even empty lines
291 # the input history needs to track even empty lines
292 stripped = line.strip()
292 stripped = line.strip()
293
293
294 normal_handler = self.get_handler_by_name('normal')
294 normal_handler = self.get_handler_by_name('normal')
295 if not stripped:
295 if not stripped:
296 return normal_handler.handle(line_info)
296 return normal_handler.handle(line_info)
297
297
298 # special handlers are only allowed for single line statements
298 # special handlers are only allowed for single line statements
299 if continue_prompt and not self.multi_line_specials:
299 if continue_prompt and not self.multi_line_specials:
300 return normal_handler.handle(line_info)
300 return normal_handler.handle(line_info)
301
301
302 prefiltered = self.prefilter_line_info(line_info)
302 prefiltered = self.prefilter_line_info(line_info)
303 # print("prefiltered line: %r" % prefiltered)
303 # print("prefiltered line: %r" % prefiltered)
304 return prefiltered
304 return prefiltered
305
305
306 def prefilter_lines(self, lines, continue_prompt=False):
306 def prefilter_lines(self, lines, continue_prompt=False):
307 """Prefilter multiple input lines of text.
307 """Prefilter multiple input lines of text.
308
308
309 This is the main entry point for prefiltering multiple lines of
309 This is the main entry point for prefiltering multiple lines of
310 input. This simply calls :meth:`prefilter_line` for each line of
310 input. This simply calls :meth:`prefilter_line` for each line of
311 input.
311 input.
312
312
313 This covers cases where there are multiple lines in the user entry,
313 This covers cases where there are multiple lines in the user entry,
314 which is the case when the user goes back to a multiline history
314 which is the case when the user goes back to a multiline history
315 entry and presses enter.
315 entry and presses enter.
316 """
316 """
317 llines = lines.rstrip('\n').split('\n')
317 llines = lines.rstrip('\n').split('\n')
318 # We can get multiple lines in one shot, where multiline input 'blends'
318 # We can get multiple lines in one shot, where multiline input 'blends'
319 # into one line, in cases like recalling from the readline history
319 # into one line, in cases like recalling from the readline history
320 # buffer. We need to make sure that in such cases, we correctly
320 # buffer. We need to make sure that in such cases, we correctly
321 # communicate downstream which line is first and which are continuation
321 # communicate downstream which line is first and which are continuation
322 # ones.
322 # ones.
323 if len(llines) > 1:
323 if len(llines) > 1:
324 out = '\n'.join([self.prefilter_line(line, lnum>0)
324 out = '\n'.join([self.prefilter_line(line, lnum>0)
325 for lnum, line in enumerate(llines) ])
325 for lnum, line in enumerate(llines) ])
326 else:
326 else:
327 out = self.prefilter_line(llines[0], continue_prompt)
327 out = self.prefilter_line(llines[0], continue_prompt)
328
328
329 return out
329 return out
330
330
331 #-----------------------------------------------------------------------------
331 #-----------------------------------------------------------------------------
332 # Prefilter transformers
332 # Prefilter transformers
333 #-----------------------------------------------------------------------------
333 #-----------------------------------------------------------------------------
334
334
335
335
336 class PrefilterTransformer(Configurable):
336 class PrefilterTransformer(Configurable):
337 """Transform a line of user input."""
337 """Transform a line of user input."""
338
338
339 priority = Integer(100).tag(config=True)
339 priority = Integer(100).tag(config=True)
340 # Transformers don't currently use shell or prefilter_manager, but as we
340 # Transformers don't currently use shell or prefilter_manager, but as we
341 # move away from checkers and handlers, they will need them.
341 # move away from checkers and handlers, they will need them.
342 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', allow_none=True)
342 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', allow_none=True)
343 prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager', allow_none=True)
343 prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager', allow_none=True)
344 enabled = Bool(True).tag(config=True)
344 enabled = Bool(True).tag(config=True)
345
345
346 def __init__(self, shell=None, prefilter_manager=None, **kwargs):
346 def __init__(self, shell=None, prefilter_manager=None, **kwargs):
347 super(PrefilterTransformer, self).__init__(
347 super(PrefilterTransformer, self).__init__(
348 shell=shell, prefilter_manager=prefilter_manager, **kwargs
348 shell=shell, prefilter_manager=prefilter_manager, **kwargs
349 )
349 )
350 self.prefilter_manager.register_transformer(self)
350 self.prefilter_manager.register_transformer(self)
351
351
352 def transform(self, line, continue_prompt):
352 def transform(self, line, continue_prompt):
353 """Transform a line, returning the new one."""
353 """Transform a line, returning the new one."""
354 return None
354 return None
355
355
356 def __repr__(self):
356 def __repr__(self):
357 return "<%s(priority=%r, enabled=%r)>" % (
357 return "<%s(priority=%r, enabled=%r)>" % (
358 self.__class__.__name__, self.priority, self.enabled)
358 self.__class__.__name__, self.priority, self.enabled)
359
359
360
360
361 #-----------------------------------------------------------------------------
361 #-----------------------------------------------------------------------------
362 # Prefilter checkers
362 # Prefilter checkers
363 #-----------------------------------------------------------------------------
363 #-----------------------------------------------------------------------------
364
364
365
365
366 class PrefilterChecker(Configurable):
366 class PrefilterChecker(Configurable):
367 """Inspect an input line and return a handler for that line."""
367 """Inspect an input line and return a handler for that line."""
368
368
369 priority = Integer(100).tag(config=True)
369 priority = Integer(100).tag(config=True)
370 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', allow_none=True)
370 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', allow_none=True)
371 prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager', allow_none=True)
371 prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager', allow_none=True)
372 enabled = Bool(True).tag(config=True)
372 enabled = Bool(True).tag(config=True)
373
373
374 def __init__(self, shell=None, prefilter_manager=None, **kwargs):
374 def __init__(self, shell=None, prefilter_manager=None, **kwargs):
375 super(PrefilterChecker, self).__init__(
375 super(PrefilterChecker, self).__init__(
376 shell=shell, prefilter_manager=prefilter_manager, **kwargs
376 shell=shell, prefilter_manager=prefilter_manager, **kwargs
377 )
377 )
378 self.prefilter_manager.register_checker(self)
378 self.prefilter_manager.register_checker(self)
379
379
380 def check(self, line_info):
380 def check(self, line_info):
381 """Inspect line_info and return a handler instance or None."""
381 """Inspect line_info and return a handler instance or None."""
382 return None
382 return None
383
383
384 def __repr__(self):
384 def __repr__(self):
385 return "<%s(priority=%r, enabled=%r)>" % (
385 return "<%s(priority=%r, enabled=%r)>" % (
386 self.__class__.__name__, self.priority, self.enabled)
386 self.__class__.__name__, self.priority, self.enabled)
387
387
388
388
389 class EmacsChecker(PrefilterChecker):
389 class EmacsChecker(PrefilterChecker):
390
390
391 priority = Integer(100).tag(config=True)
391 priority = Integer(100).tag(config=True)
392 enabled = Bool(False).tag(config=True)
392 enabled = Bool(False).tag(config=True)
393
393
394 def check(self, line_info):
394 def check(self, line_info):
395 "Emacs ipython-mode tags certain input lines."
395 "Emacs ipython-mode tags certain input lines."
396 if line_info.line.endswith('# PYTHON-MODE'):
396 if line_info.line.endswith('# PYTHON-MODE'):
397 return self.prefilter_manager.get_handler_by_name('emacs')
397 return self.prefilter_manager.get_handler_by_name('emacs')
398 else:
398 else:
399 return None
399 return None
400
400
401
401
402 class MacroChecker(PrefilterChecker):
402 class MacroChecker(PrefilterChecker):
403
403
404 priority = Integer(250).tag(config=True)
404 priority = Integer(250).tag(config=True)
405
405
406 def check(self, line_info):
406 def check(self, line_info):
407 obj = self.shell.user_ns.get(line_info.ifun)
407 obj = self.shell.user_ns.get(line_info.ifun)
408 if isinstance(obj, Macro):
408 if isinstance(obj, Macro):
409 return self.prefilter_manager.get_handler_by_name('macro')
409 return self.prefilter_manager.get_handler_by_name('macro')
410 else:
410 else:
411 return None
411 return None
412
412
413
413
414 class IPyAutocallChecker(PrefilterChecker):
414 class IPyAutocallChecker(PrefilterChecker):
415
415
416 priority = Integer(300).tag(config=True)
416 priority = Integer(300).tag(config=True)
417
417
418 def check(self, line_info):
418 def check(self, line_info):
419 "Instances of IPyAutocall in user_ns get autocalled immediately"
419 "Instances of IPyAutocall in user_ns get autocalled immediately"
420 obj = self.shell.user_ns.get(line_info.ifun, None)
420 obj = self.shell.user_ns.get(line_info.ifun, None)
421 if isinstance(obj, IPyAutocall):
421 if isinstance(obj, IPyAutocall):
422 obj.set_ip(self.shell)
422 obj.set_ip(self.shell)
423 return self.prefilter_manager.get_handler_by_name('auto')
423 return self.prefilter_manager.get_handler_by_name('auto')
424 else:
424 else:
425 return None
425 return None
426
426
427
427
428 class AssignmentChecker(PrefilterChecker):
428 class AssignmentChecker(PrefilterChecker):
429
429
430 priority = Integer(600).tag(config=True)
430 priority = Integer(600).tag(config=True)
431
431
432 def check(self, line_info):
432 def check(self, line_info):
433 """Check to see if user is assigning to a var for the first time, in
433 """Check to see if user is assigning to a var for the first time, in
434 which case we want to avoid any sort of automagic / autocall games.
434 which case we want to avoid any sort of automagic / autocall games.
435
435
436 This allows users to assign to either alias or magic names true python
436 This allows users to assign to either alias or magic names true python
437 variables (the magic/alias systems always take second seat to true
437 variables (the magic/alias systems always take second seat to true
438 python code). E.g. ls='hi', or ls,that=1,2"""
438 python code). E.g. ls='hi', or ls,that=1,2"""
439 if line_info.the_rest:
439 if line_info.the_rest:
440 if line_info.the_rest[0] in '=,':
440 if line_info.the_rest[0] in '=,':
441 return self.prefilter_manager.get_handler_by_name('normal')
441 return self.prefilter_manager.get_handler_by_name('normal')
442 else:
442 else:
443 return None
443 return None
444
444
445
445
446 class AutoMagicChecker(PrefilterChecker):
446 class AutoMagicChecker(PrefilterChecker):
447
447
448 priority = Integer(700).tag(config=True)
448 priority = Integer(700).tag(config=True)
449
449
450 def check(self, line_info):
450 def check(self, line_info):
451 """If the ifun is magic, and automagic is on, run it. Note: normal,
451 """If the ifun is magic, and automagic is on, run it. Note: normal,
452 non-auto magic would already have been triggered via '%' in
452 non-auto magic would already have been triggered via '%' in
453 check_esc_chars. This just checks for automagic. Also, before
453 check_esc_chars. This just checks for automagic. Also, before
454 triggering the magic handler, make sure that there is nothing in the
454 triggering the magic handler, make sure that there is nothing in the
455 user namespace which could shadow it."""
455 user namespace which could shadow it."""
456 if not self.shell.automagic or not self.shell.find_magic(line_info.ifun):
456 if not self.shell.automagic or not self.shell.find_magic(line_info.ifun):
457 return None
457 return None
458
458
459 # We have a likely magic method. Make sure we should actually call it.
459 # We have a likely magic method. Make sure we should actually call it.
460 if line_info.continue_prompt and not self.prefilter_manager.multi_line_specials:
460 if line_info.continue_prompt and not self.prefilter_manager.multi_line_specials:
461 return None
461 return None
462
462
463 head = line_info.ifun.split('.',1)[0]
463 head = line_info.ifun.split('.',1)[0]
464 if is_shadowed(head, self.shell):
464 if is_shadowed(head, self.shell):
465 return None
465 return None
466
466
467 return self.prefilter_manager.get_handler_by_name('magic')
467 return self.prefilter_manager.get_handler_by_name('magic')
468
468
469
469
470 class PythonOpsChecker(PrefilterChecker):
470 class PythonOpsChecker(PrefilterChecker):
471
471
472 priority = Integer(900).tag(config=True)
472 priority = Integer(900).tag(config=True)
473
473
474 def check(self, line_info):
474 def check(self, line_info):
475 """If the 'rest' of the line begins with a function call or pretty much
475 """If the 'rest' of the line begins with a function call or pretty much
476 any python operator, we should simply execute the line (regardless of
476 any python operator, we should simply execute the line (regardless of
477 whether or not there's a possible autocall expansion). This avoids
477 whether or not there's a possible autocall expansion). This avoids
478 spurious (and very confusing) geattr() accesses."""
478 spurious (and very confusing) geattr() accesses."""
479 if line_info.the_rest and line_info.the_rest[0] in "!=()<>,+*/%^&|":
479 if line_info.the_rest and line_info.the_rest[0] in "!=()<>,+*/%^&|":
480 return self.prefilter_manager.get_handler_by_name("normal")
480 return self.prefilter_manager.get_handler_by_name("normal")
481 else:
481 else:
482 return None
482 return None
483
483
484
484
485 class AutocallChecker(PrefilterChecker):
485 class AutocallChecker(PrefilterChecker):
486
486
487 priority = Integer(1000).tag(config=True)
487 priority = Integer(1000).tag(config=True)
488
488
489 function_name_regexp = CRegExp(re_fun_name,
489 function_name_regexp = CRegExp(re_fun_name,
490 help="RegExp to identify potential function names."
490 help="RegExp to identify potential function names."
491 ).tag(config=True)
491 ).tag(config=True)
492 exclude_regexp = CRegExp(re_exclude_auto,
492 exclude_regexp = CRegExp(re_exclude_auto,
493 help="RegExp to exclude strings with this start from autocalling."
493 help="RegExp to exclude strings with this start from autocalling."
494 ).tag(config=True)
494 ).tag(config=True)
495
495
496 def check(self, line_info):
496 def check(self, line_info):
497 "Check if the initial word/function is callable and autocall is on."
497 "Check if the initial word/function is callable and autocall is on."
498 if not self.shell.autocall:
498 if not self.shell.autocall:
499 return None
499 return None
500
500
501 oinfo = line_info.ofind(self.shell) # This can mutate state via getattr
501 oinfo = line_info.ofind(self.shell) # This can mutate state via getattr
502 if not oinfo.found:
502 if not oinfo.found:
503 return None
503 return None
504
504
505 ignored_funs = ['b', 'f', 'r', 'u', 'br', 'rb', 'fr', 'rf']
505 ignored_funs = ['b', 'f', 'r', 'u', 'br', 'rb', 'fr', 'rf']
506 ifun = line_info.ifun
506 ifun = line_info.ifun
507 line = line_info.line
507 line = line_info.line
508 if ifun.lower() in ignored_funs and (line.startswith(ifun + "'") or line.startswith(ifun + '"')):
508 if ifun.lower() in ignored_funs and (line.startswith(ifun + "'") or line.startswith(ifun + '"')):
509 return None
509 return None
510
510
511 if (
511 if (
512 callable(oinfo.obj)
512 callable(oinfo.obj)
513 and (not self.exclude_regexp.match(line_info.the_rest))
513 and (not self.exclude_regexp.match(line_info.the_rest))
514 and self.function_name_regexp.match(line_info.ifun)
514 and self.function_name_regexp.match(line_info.ifun)
515 and line_info.raw_the_rest.startswith(" ")
515 and (
516 line_info.raw_the_rest.startswith(" ")
516 or not line_info.raw_the_rest.strip()
517 or not line_info.raw_the_rest.strip()
518 )
517 ):
519 ):
518 return self.prefilter_manager.get_handler_by_name("auto")
520 return self.prefilter_manager.get_handler_by_name("auto")
519 else:
521 else:
520 return None
522 return None
521
523
522
524
523 #-----------------------------------------------------------------------------
525 #-----------------------------------------------------------------------------
524 # Prefilter handlers
526 # Prefilter handlers
525 #-----------------------------------------------------------------------------
527 #-----------------------------------------------------------------------------
526
528
527
529
528 class PrefilterHandler(Configurable):
530 class PrefilterHandler(Configurable):
529 handler_name = Unicode("normal")
531 handler_name = Unicode("normal")
530 esc_strings: List = List([])
532 esc_strings: List = List([])
531 shell = Instance(
533 shell = Instance(
532 "IPython.core.interactiveshell.InteractiveShellABC", allow_none=True
534 "IPython.core.interactiveshell.InteractiveShellABC", allow_none=True
533 )
535 )
534 prefilter_manager = Instance(
536 prefilter_manager = Instance(
535 "IPython.core.prefilter.PrefilterManager", allow_none=True
537 "IPython.core.prefilter.PrefilterManager", allow_none=True
536 )
538 )
537
539
538 def __init__(self, shell=None, prefilter_manager=None, **kwargs):
540 def __init__(self, shell=None, prefilter_manager=None, **kwargs):
539 super(PrefilterHandler, self).__init__(
541 super(PrefilterHandler, self).__init__(
540 shell=shell, prefilter_manager=prefilter_manager, **kwargs
542 shell=shell, prefilter_manager=prefilter_manager, **kwargs
541 )
543 )
542 self.prefilter_manager.register_handler(
544 self.prefilter_manager.register_handler(
543 self.handler_name,
545 self.handler_name,
544 self,
546 self,
545 self.esc_strings
547 self.esc_strings
546 )
548 )
547
549
548 def handle(self, line_info):
550 def handle(self, line_info):
549 # print("normal: ", line_info)
551 # print("normal: ", line_info)
550 """Handle normal input lines. Use as a template for handlers."""
552 """Handle normal input lines. Use as a template for handlers."""
551
553
552 # With autoindent on, we need some way to exit the input loop, and I
554 # With autoindent on, we need some way to exit the input loop, and I
553 # don't want to force the user to have to backspace all the way to
555 # don't want to force the user to have to backspace all the way to
554 # clear the line. The rule will be in this case, that either two
556 # clear the line. The rule will be in this case, that either two
555 # lines of pure whitespace in a row, or a line of pure whitespace but
557 # lines of pure whitespace in a row, or a line of pure whitespace but
556 # of a size different to the indent level, will exit the input loop.
558 # of a size different to the indent level, will exit the input loop.
557 line = line_info.line
559 line = line_info.line
558 continue_prompt = line_info.continue_prompt
560 continue_prompt = line_info.continue_prompt
559
561
560 if (continue_prompt and
562 if (continue_prompt and
561 self.shell.autoindent and
563 self.shell.autoindent and
562 line.isspace() and
564 line.isspace() and
563 0 < abs(len(line) - self.shell.indent_current_nsp) <= 2):
565 0 < abs(len(line) - self.shell.indent_current_nsp) <= 2):
564 line = ''
566 line = ''
565
567
566 return line
568 return line
567
569
568 def __str__(self):
570 def __str__(self):
569 return "<%s(name=%s)>" % (self.__class__.__name__, self.handler_name)
571 return "<%s(name=%s)>" % (self.__class__.__name__, self.handler_name)
570
572
571
573
572 class MacroHandler(PrefilterHandler):
574 class MacroHandler(PrefilterHandler):
573 handler_name = Unicode("macro")
575 handler_name = Unicode("macro")
574
576
575 def handle(self, line_info):
577 def handle(self, line_info):
576 obj = self.shell.user_ns.get(line_info.ifun)
578 obj = self.shell.user_ns.get(line_info.ifun)
577 pre_space = line_info.pre_whitespace
579 pre_space = line_info.pre_whitespace
578 line_sep = "\n" + pre_space
580 line_sep = "\n" + pre_space
579 return pre_space + line_sep.join(obj.value.splitlines())
581 return pre_space + line_sep.join(obj.value.splitlines())
580
582
581
583
582 class MagicHandler(PrefilterHandler):
584 class MagicHandler(PrefilterHandler):
583
585
584 handler_name = Unicode('magic')
586 handler_name = Unicode('magic')
585 esc_strings = List([ESC_MAGIC])
587 esc_strings = List([ESC_MAGIC])
586
588
587 def handle(self, line_info):
589 def handle(self, line_info):
588 """Execute magic functions."""
590 """Execute magic functions."""
589 ifun = line_info.ifun
591 ifun = line_info.ifun
590 the_rest = line_info.the_rest
592 the_rest = line_info.the_rest
591 #Prepare arguments for get_ipython().run_line_magic(magic_name, magic_args)
593 #Prepare arguments for get_ipython().run_line_magic(magic_name, magic_args)
592 t_arg_s = ifun + " " + the_rest
594 t_arg_s = ifun + " " + the_rest
593 t_magic_name, _, t_magic_arg_s = t_arg_s.partition(' ')
595 t_magic_name, _, t_magic_arg_s = t_arg_s.partition(' ')
594 t_magic_name = t_magic_name.lstrip(ESC_MAGIC)
596 t_magic_name = t_magic_name.lstrip(ESC_MAGIC)
595 cmd = '%sget_ipython().run_line_magic(%r, %r)' % (line_info.pre_whitespace, t_magic_name, t_magic_arg_s)
597 cmd = '%sget_ipython().run_line_magic(%r, %r)' % (line_info.pre_whitespace, t_magic_name, t_magic_arg_s)
596 return cmd
598 return cmd
597
599
598
600
599 class AutoHandler(PrefilterHandler):
601 class AutoHandler(PrefilterHandler):
600
602
601 handler_name = Unicode('auto')
603 handler_name = Unicode('auto')
602 esc_strings = List([ESC_PAREN, ESC_QUOTE, ESC_QUOTE2])
604 esc_strings = List([ESC_PAREN, ESC_QUOTE, ESC_QUOTE2])
603
605
604 def handle(self, line_info):
606 def handle(self, line_info):
605 """Handle lines which can be auto-executed, quoting if requested."""
607 """Handle lines which can be auto-executed, quoting if requested."""
606 line = line_info.line
608 line = line_info.line
607 ifun = line_info.ifun
609 ifun = line_info.ifun
608 the_rest = line_info.the_rest
610 the_rest = line_info.the_rest
609 esc = line_info.esc
611 esc = line_info.esc
610 continue_prompt = line_info.continue_prompt
612 continue_prompt = line_info.continue_prompt
611 obj = line_info.ofind(self.shell).obj
613 obj = line_info.ofind(self.shell).obj
612
614
613 # This should only be active for single-line input!
615 # This should only be active for single-line input!
614 if continue_prompt:
616 if continue_prompt:
615 return line
617 return line
616
618
617 force_auto = isinstance(obj, IPyAutocall)
619 force_auto = isinstance(obj, IPyAutocall)
618
620
619 # User objects sometimes raise exceptions on attribute access other
621 # User objects sometimes raise exceptions on attribute access other
620 # than AttributeError (we've seen it in the past), so it's safest to be
622 # than AttributeError (we've seen it in the past), so it's safest to be
621 # ultra-conservative here and catch all.
623 # ultra-conservative here and catch all.
622 try:
624 try:
623 auto_rewrite = obj.rewrite
625 auto_rewrite = obj.rewrite
624 except Exception:
626 except Exception:
625 auto_rewrite = True
627 auto_rewrite = True
626
628
627 if esc == ESC_QUOTE:
629 if esc == ESC_QUOTE:
628 # Auto-quote splitting on whitespace
630 # Auto-quote splitting on whitespace
629 newcmd = '%s("%s")' % (ifun,'", "'.join(the_rest.split()) )
631 newcmd = '%s("%s")' % (ifun,'", "'.join(the_rest.split()) )
630 elif esc == ESC_QUOTE2:
632 elif esc == ESC_QUOTE2:
631 # Auto-quote whole string
633 # Auto-quote whole string
632 newcmd = '%s("%s")' % (ifun,the_rest)
634 newcmd = '%s("%s")' % (ifun,the_rest)
633 elif esc == ESC_PAREN:
635 elif esc == ESC_PAREN:
634 newcmd = '%s(%s)' % (ifun,",".join(the_rest.split()))
636 newcmd = '%s(%s)' % (ifun,",".join(the_rest.split()))
635 else:
637 else:
636 # Auto-paren.
638 # Auto-paren.
637 if force_auto:
639 if force_auto:
638 # Don't rewrite if it is already a call.
640 # Don't rewrite if it is already a call.
639 do_rewrite = not the_rest.startswith('(')
641 do_rewrite = not the_rest.startswith('(')
640 else:
642 else:
641 if not the_rest:
643 if not the_rest:
642 # We only apply it to argument-less calls if the autocall
644 # We only apply it to argument-less calls if the autocall
643 # parameter is set to 2.
645 # parameter is set to 2.
644 do_rewrite = (self.shell.autocall >= 2)
646 do_rewrite = (self.shell.autocall >= 2)
645 elif the_rest.startswith('[') and hasattr(obj, '__getitem__'):
647 elif the_rest.startswith('[') and hasattr(obj, '__getitem__'):
646 # Don't autocall in this case: item access for an object
648 # Don't autocall in this case: item access for an object
647 # which is BOTH callable and implements __getitem__.
649 # which is BOTH callable and implements __getitem__.
648 do_rewrite = False
650 do_rewrite = False
649 else:
651 else:
650 do_rewrite = True
652 do_rewrite = True
651
653
652 # Figure out the rewritten command
654 # Figure out the rewritten command
653 if do_rewrite:
655 if do_rewrite:
654 if the_rest.endswith(';'):
656 if the_rest.endswith(';'):
655 newcmd = '%s(%s);' % (ifun.rstrip(),the_rest[:-1])
657 newcmd = '%s(%s);' % (ifun.rstrip(),the_rest[:-1])
656 else:
658 else:
657 newcmd = '%s(%s)' % (ifun.rstrip(), the_rest)
659 newcmd = '%s(%s)' % (ifun.rstrip(), the_rest)
658 else:
660 else:
659 normal_handler = self.prefilter_manager.get_handler_by_name('normal')
661 normal_handler = self.prefilter_manager.get_handler_by_name('normal')
660 return normal_handler.handle(line_info)
662 return normal_handler.handle(line_info)
661
663
662 # Display the rewritten call
664 # Display the rewritten call
663 if auto_rewrite:
665 if auto_rewrite:
664 self.shell.auto_rewrite_input(newcmd)
666 self.shell.auto_rewrite_input(newcmd)
665
667
666 return newcmd
668 return newcmd
667
669
668
670
669 class EmacsHandler(PrefilterHandler):
671 class EmacsHandler(PrefilterHandler):
670
672
671 handler_name = Unicode('emacs')
673 handler_name = Unicode('emacs')
672 esc_strings = List([])
674 esc_strings = List([])
673
675
674 def handle(self, line_info):
676 def handle(self, line_info):
675 """Handle input lines marked by python-mode."""
677 """Handle input lines marked by python-mode."""
676
678
677 # Currently, nothing is done. Later more functionality can be added
679 # Currently, nothing is done. Later more functionality can be added
678 # here if needed.
680 # here if needed.
679
681
680 # The input cache shouldn't be updated
682 # The input cache shouldn't be updated
681 return line_info.line
683 return line_info.line
682
684
683
685
684 #-----------------------------------------------------------------------------
686 #-----------------------------------------------------------------------------
685 # Defaults
687 # Defaults
686 #-----------------------------------------------------------------------------
688 #-----------------------------------------------------------------------------
687
689
688
690
689 _default_checkers = [
691 _default_checkers = [
690 EmacsChecker,
692 EmacsChecker,
691 MacroChecker,
693 MacroChecker,
692 IPyAutocallChecker,
694 IPyAutocallChecker,
693 AssignmentChecker,
695 AssignmentChecker,
694 AutoMagicChecker,
696 AutoMagicChecker,
695 PythonOpsChecker,
697 PythonOpsChecker,
696 AutocallChecker
698 AutocallChecker
697 ]
699 ]
698
700
699 _default_handlers = [
701 _default_handlers = [
700 PrefilterHandler,
702 PrefilterHandler,
701 MacroHandler,
703 MacroHandler,
702 MagicHandler,
704 MagicHandler,
703 AutoHandler,
705 AutoHandler,
704 EmacsHandler
706 EmacsHandler
705 ]
707 ]
@@ -1,139 +1,149
1 """Tests for input manipulation machinery."""
1 """Tests for input manipulation machinery."""
2
2
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Imports
4 # Imports
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6 import pytest
6 import pytest
7
7
8 from IPython.core.prefilter import AutocallChecker
8 from IPython.core.prefilter import AutocallChecker
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Tests
11 # Tests
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 def test_prefilter():
14 def test_prefilter():
15 """Test user input conversions"""
15 """Test user input conversions"""
16
16
17 # pairs of (raw, expected correct) input
17 # pairs of (raw, expected correct) input
18 pairs = [ ('2+2','2+2'),
18 pairs = [ ('2+2','2+2'),
19 ]
19 ]
20
20
21 for raw, correct in pairs:
21 for raw, correct in pairs:
22 assert ip.prefilter(raw) == correct
22 assert ip.prefilter(raw) == correct
23
23
24 def test_prefilter_shadowed():
24 def test_prefilter_shadowed():
25 def dummy_magic(line): pass
25 def dummy_magic(line): pass
26
26
27 prev_automagic_state = ip.automagic
27 prev_automagic_state = ip.automagic
28 ip.automagic = True
28 ip.automagic = True
29 ip.autocall = 0
29 ip.autocall = 0
30
30
31 try:
31 try:
32 # These should not be transformed - they are shadowed by other names
32 # These should not be transformed - they are shadowed by other names
33 for name in ['if', 'zip', 'get_ipython']: # keyword, builtin, global
33 for name in ['if', 'zip', 'get_ipython']: # keyword, builtin, global
34 ip.register_magic_function(dummy_magic, magic_name=name)
34 ip.register_magic_function(dummy_magic, magic_name=name)
35 res = ip.prefilter(name + " foo")
35 res = ip.prefilter(name + " foo")
36 assert res == name + " foo"
36 assert res == name + " foo"
37 del ip.magics_manager.magics["line"][name]
37 del ip.magics_manager.magics["line"][name]
38
38
39 # These should be transformed
39 # These should be transformed
40 for name in ['fi', 'piz', 'nohtypi_teg']:
40 for name in ['fi', 'piz', 'nohtypi_teg']:
41 ip.register_magic_function(dummy_magic, magic_name=name)
41 ip.register_magic_function(dummy_magic, magic_name=name)
42 res = ip.prefilter(name + " foo")
42 res = ip.prefilter(name + " foo")
43 assert res != name + " foo"
43 assert res != name + " foo"
44 del ip.magics_manager.magics["line"][name]
44 del ip.magics_manager.magics["line"][name]
45
45
46 finally:
46 finally:
47 ip.automagic = prev_automagic_state
47 ip.automagic = prev_automagic_state
48
48
49 def test_autocall_binops():
49 def test_autocall_binops():
50 """See https://github.com/ipython/ipython/issues/81"""
50 """See https://github.com/ipython/ipython/issues/81"""
51 ip.run_line_magic("autocall", "2")
51 ip.run_line_magic("autocall", "2")
52 f = lambda x: x
52 f = lambda x: x
53 ip.user_ns['f'] = f
53 ip.user_ns['f'] = f
54 try:
54 try:
55 assert ip.prefilter("f 1") == "f(1)"
55 assert ip.prefilter("f 1") == "f(1)"
56 for t in ["f +1", "f -1"]:
56 for t in ["f +1", "f -1"]:
57 assert ip.prefilter(t) == t
57 assert ip.prefilter(t) == t
58
58
59 # Run tests again with a more permissive exclude_regexp, which will
59 # Run tests again with a more permissive exclude_regexp, which will
60 # allow transformation of binary operations ('f -1' -> 'f(-1)').
60 # allow transformation of binary operations ('f -1' -> 'f(-1)').
61 pm = ip.prefilter_manager
61 pm = ip.prefilter_manager
62 ac = AutocallChecker(shell=pm.shell, prefilter_manager=pm,
62 ac = AutocallChecker(shell=pm.shell, prefilter_manager=pm,
63 config=pm.config)
63 config=pm.config)
64 try:
64 try:
65 ac.priority = 1
65 ac.priority = 1
66 ac.exclude_regexp = r'^[,&^\|\*/]|^is |^not |^in |^and |^or '
66 ac.exclude_regexp = r'^[,&^\|\*/]|^is |^not |^in |^and |^or '
67 pm.sort_checkers()
67 pm.sort_checkers()
68
68
69 assert ip.prefilter("f -1") == "f(-1)"
69 assert ip.prefilter("f -1") == "f(-1)"
70 assert ip.prefilter("f +1") == "f(+1)"
70 assert ip.prefilter("f +1") == "f(+1)"
71 finally:
71 finally:
72 pm.unregister_checker(ac)
72 pm.unregister_checker(ac)
73 finally:
73 finally:
74 ip.run_line_magic("autocall", "0")
74 ip.run_line_magic("autocall", "0")
75 del ip.user_ns["f"]
75 del ip.user_ns["f"]
76
76
77
77
78 def test_issue_114():
78 def test_issue_114():
79 """Check that multiline string literals don't expand as magic
79 """Check that multiline string literals don't expand as magic
80 see http://github.com/ipython/ipython/issues/114"""
80 see http://github.com/ipython/ipython/issues/114"""
81
81
82 template = '"""\n%s\n"""'
82 template = '"""\n%s\n"""'
83 # Store the current value of multi_line_specials and turn it off before
83 # Store the current value of multi_line_specials and turn it off before
84 # running test, since it could be true (case in which the test doesn't make
84 # running test, since it could be true (case in which the test doesn't make
85 # sense, as multiline string literals *will* expand as magic in that case).
85 # sense, as multiline string literals *will* expand as magic in that case).
86 msp = ip.prefilter_manager.multi_line_specials
86 msp = ip.prefilter_manager.multi_line_specials
87 ip.prefilter_manager.multi_line_specials = False
87 ip.prefilter_manager.multi_line_specials = False
88 try:
88 try:
89 for mgk in ip.magics_manager.lsmagic()['line']:
89 for mgk in ip.magics_manager.lsmagic()['line']:
90 raw = template % mgk
90 raw = template % mgk
91 assert ip.prefilter(raw) == raw
91 assert ip.prefilter(raw) == raw
92 finally:
92 finally:
93 ip.prefilter_manager.multi_line_specials = msp
93 ip.prefilter_manager.multi_line_specials = msp
94
94
95
95
96 def test_prefilter_attribute_errors():
96 def test_prefilter_attribute_errors():
97 """Capture exceptions thrown by user objects on attribute access.
97 """Capture exceptions thrown by user objects on attribute access.
98
98
99 See http://github.com/ipython/ipython/issues/988."""
99 See http://github.com/ipython/ipython/issues/988."""
100
100
101 class X(object):
101 class X(object):
102 def __getattr__(self, k):
102 def __getattr__(self, k):
103 raise ValueError('broken object')
103 raise ValueError('broken object')
104 def __call__(self, x):
104 def __call__(self, x):
105 return x
105 return x
106
106
107 # Create a callable broken object
107 # Create a callable broken object
108 ip.user_ns["x"] = X()
108 ip.user_ns["x"] = X()
109 ip.run_line_magic("autocall", "2")
109 ip.run_line_magic("autocall", "2")
110 try:
110 try:
111 # Even if x throws an attribute error when looking at its rewrite
111 # Even if x throws an attribute error when looking at its rewrite
112 # attribute, we should not crash. So the test here is simply making
112 # attribute, we should not crash. So the test here is simply making
113 # the prefilter call and not having an exception.
113 # the prefilter call and not having an exception.
114 ip.prefilter('x 1')
114 ip.prefilter('x 1')
115 finally:
115 finally:
116 del ip.user_ns["x"]
116 del ip.user_ns["x"]
117 ip.run_line_magic("autocall", "0")
117 ip.run_line_magic("autocall", "0")
118
118
119
119
120 def test_autocall_type_ann():
120 def test_autocall_type_ann():
121 ip.run_cell("import collections.abc")
121 ip.run_cell("import collections.abc")
122 ip.run_line_magic("autocall", "1")
122 ip.run_line_magic("autocall", "1")
123 try:
123 try:
124 assert (
124 assert (
125 ip.prefilter("collections.abc.Callable[[int], None]")
125 ip.prefilter("collections.abc.Callable[[int], None]")
126 == "collections.abc.Callable[[int], None]"
126 == "collections.abc.Callable[[int], None]"
127 )
127 )
128 finally:
128 finally:
129 ip.run_line_magic("autocall", "0")
129 ip.run_line_magic("autocall", "0")
130
130
131
131
132 def test_autocall_should_support_unicode():
132 def test_autocall_should_support_unicode():
133 ip.run_line_magic("autocall", "2")
133 ip.run_line_magic("autocall", "2")
134 ip.user_ns["π"] = lambda x: x
134 ip.user_ns["π"] = lambda x: x
135 try:
135 try:
136 assert ip.prefilter("π 3") == "π(3)"
136 assert ip.prefilter("π 3") == "π(3)"
137 finally:
137 finally:
138 ip.run_line_magic("autocall", "0")
138 ip.run_line_magic("autocall", "0")
139 del ip.user_ns["π"]
139 del ip.user_ns["π"]
140
141
142 def test_autocall_regression_gh_14513():
143 ip.run_line_magic("autocall", "2")
144 ip.user_ns["foo"] = dict()
145 try:
146 assert ip.prefilter("foo") == "foo"
147 finally:
148 ip.run_line_magic("autocall", "0")
149 del ip.user_ns["foo"]
General Comments 0
You need to be logged in to leave comments. Login now