##// END OF EJS Templates
Fix autocall runs on getitem. (#14486)...
M Bussonnier -
r28820:128bd582 merge
parent child Browse files
Show More
@@ -1,703 +1,705 b''
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(" ")
516 or not line_info.raw_the_rest.strip()
515 ):
517 ):
516 return self.prefilter_manager.get_handler_by_name("auto")
518 return self.prefilter_manager.get_handler_by_name("auto")
517 else:
519 else:
518 return None
520 return None
519
521
520
522
521 #-----------------------------------------------------------------------------
523 #-----------------------------------------------------------------------------
522 # Prefilter handlers
524 # Prefilter handlers
523 #-----------------------------------------------------------------------------
525 #-----------------------------------------------------------------------------
524
526
525
527
526 class PrefilterHandler(Configurable):
528 class PrefilterHandler(Configurable):
527 handler_name = Unicode("normal")
529 handler_name = Unicode("normal")
528 esc_strings: List = List([])
530 esc_strings: List = List([])
529 shell = Instance(
531 shell = Instance(
530 "IPython.core.interactiveshell.InteractiveShellABC", allow_none=True
532 "IPython.core.interactiveshell.InteractiveShellABC", allow_none=True
531 )
533 )
532 prefilter_manager = Instance(
534 prefilter_manager = Instance(
533 "IPython.core.prefilter.PrefilterManager", allow_none=True
535 "IPython.core.prefilter.PrefilterManager", allow_none=True
534 )
536 )
535
537
536 def __init__(self, shell=None, prefilter_manager=None, **kwargs):
538 def __init__(self, shell=None, prefilter_manager=None, **kwargs):
537 super(PrefilterHandler, self).__init__(
539 super(PrefilterHandler, self).__init__(
538 shell=shell, prefilter_manager=prefilter_manager, **kwargs
540 shell=shell, prefilter_manager=prefilter_manager, **kwargs
539 )
541 )
540 self.prefilter_manager.register_handler(
542 self.prefilter_manager.register_handler(
541 self.handler_name,
543 self.handler_name,
542 self,
544 self,
543 self.esc_strings
545 self.esc_strings
544 )
546 )
545
547
546 def handle(self, line_info):
548 def handle(self, line_info):
547 # print("normal: ", line_info)
549 # print("normal: ", line_info)
548 """Handle normal input lines. Use as a template for handlers."""
550 """Handle normal input lines. Use as a template for handlers."""
549
551
550 # With autoindent on, we need some way to exit the input loop, and I
552 # With autoindent on, we need some way to exit the input loop, and I
551 # don't want to force the user to have to backspace all the way to
553 # don't want to force the user to have to backspace all the way to
552 # clear the line. The rule will be in this case, that either two
554 # clear the line. The rule will be in this case, that either two
553 # lines of pure whitespace in a row, or a line of pure whitespace but
555 # lines of pure whitespace in a row, or a line of pure whitespace but
554 # of a size different to the indent level, will exit the input loop.
556 # of a size different to the indent level, will exit the input loop.
555 line = line_info.line
557 line = line_info.line
556 continue_prompt = line_info.continue_prompt
558 continue_prompt = line_info.continue_prompt
557
559
558 if (continue_prompt and
560 if (continue_prompt and
559 self.shell.autoindent and
561 self.shell.autoindent and
560 line.isspace() and
562 line.isspace() and
561 0 < abs(len(line) - self.shell.indent_current_nsp) <= 2):
563 0 < abs(len(line) - self.shell.indent_current_nsp) <= 2):
562 line = ''
564 line = ''
563
565
564 return line
566 return line
565
567
566 def __str__(self):
568 def __str__(self):
567 return "<%s(name=%s)>" % (self.__class__.__name__, self.handler_name)
569 return "<%s(name=%s)>" % (self.__class__.__name__, self.handler_name)
568
570
569
571
570 class MacroHandler(PrefilterHandler):
572 class MacroHandler(PrefilterHandler):
571 handler_name = Unicode("macro")
573 handler_name = Unicode("macro")
572
574
573 def handle(self, line_info):
575 def handle(self, line_info):
574 obj = self.shell.user_ns.get(line_info.ifun)
576 obj = self.shell.user_ns.get(line_info.ifun)
575 pre_space = line_info.pre_whitespace
577 pre_space = line_info.pre_whitespace
576 line_sep = "\n" + pre_space
578 line_sep = "\n" + pre_space
577 return pre_space + line_sep.join(obj.value.splitlines())
579 return pre_space + line_sep.join(obj.value.splitlines())
578
580
579
581
580 class MagicHandler(PrefilterHandler):
582 class MagicHandler(PrefilterHandler):
581
583
582 handler_name = Unicode('magic')
584 handler_name = Unicode('magic')
583 esc_strings = List([ESC_MAGIC])
585 esc_strings = List([ESC_MAGIC])
584
586
585 def handle(self, line_info):
587 def handle(self, line_info):
586 """Execute magic functions."""
588 """Execute magic functions."""
587 ifun = line_info.ifun
589 ifun = line_info.ifun
588 the_rest = line_info.the_rest
590 the_rest = line_info.the_rest
589 #Prepare arguments for get_ipython().run_line_magic(magic_name, magic_args)
591 #Prepare arguments for get_ipython().run_line_magic(magic_name, magic_args)
590 t_arg_s = ifun + " " + the_rest
592 t_arg_s = ifun + " " + the_rest
591 t_magic_name, _, t_magic_arg_s = t_arg_s.partition(' ')
593 t_magic_name, _, t_magic_arg_s = t_arg_s.partition(' ')
592 t_magic_name = t_magic_name.lstrip(ESC_MAGIC)
594 t_magic_name = t_magic_name.lstrip(ESC_MAGIC)
593 cmd = '%sget_ipython().run_line_magic(%r, %r)' % (line_info.pre_whitespace, t_magic_name, t_magic_arg_s)
595 cmd = '%sget_ipython().run_line_magic(%r, %r)' % (line_info.pre_whitespace, t_magic_name, t_magic_arg_s)
594 return cmd
596 return cmd
595
597
596
598
597 class AutoHandler(PrefilterHandler):
599 class AutoHandler(PrefilterHandler):
598
600
599 handler_name = Unicode('auto')
601 handler_name = Unicode('auto')
600 esc_strings = List([ESC_PAREN, ESC_QUOTE, ESC_QUOTE2])
602 esc_strings = List([ESC_PAREN, ESC_QUOTE, ESC_QUOTE2])
601
603
602 def handle(self, line_info):
604 def handle(self, line_info):
603 """Handle lines which can be auto-executed, quoting if requested."""
605 """Handle lines which can be auto-executed, quoting if requested."""
604 line = line_info.line
606 line = line_info.line
605 ifun = line_info.ifun
607 ifun = line_info.ifun
606 the_rest = line_info.the_rest
608 the_rest = line_info.the_rest
607 esc = line_info.esc
609 esc = line_info.esc
608 continue_prompt = line_info.continue_prompt
610 continue_prompt = line_info.continue_prompt
609 obj = line_info.ofind(self.shell).obj
611 obj = line_info.ofind(self.shell).obj
610
612
611 # This should only be active for single-line input!
613 # This should only be active for single-line input!
612 if continue_prompt:
614 if continue_prompt:
613 return line
615 return line
614
616
615 force_auto = isinstance(obj, IPyAutocall)
617 force_auto = isinstance(obj, IPyAutocall)
616
618
617 # User objects sometimes raise exceptions on attribute access other
619 # User objects sometimes raise exceptions on attribute access other
618 # than AttributeError (we've seen it in the past), so it's safest to be
620 # than AttributeError (we've seen it in the past), so it's safest to be
619 # ultra-conservative here and catch all.
621 # ultra-conservative here and catch all.
620 try:
622 try:
621 auto_rewrite = obj.rewrite
623 auto_rewrite = obj.rewrite
622 except Exception:
624 except Exception:
623 auto_rewrite = True
625 auto_rewrite = True
624
626
625 if esc == ESC_QUOTE:
627 if esc == ESC_QUOTE:
626 # Auto-quote splitting on whitespace
628 # Auto-quote splitting on whitespace
627 newcmd = '%s("%s")' % (ifun,'", "'.join(the_rest.split()) )
629 newcmd = '%s("%s")' % (ifun,'", "'.join(the_rest.split()) )
628 elif esc == ESC_QUOTE2:
630 elif esc == ESC_QUOTE2:
629 # Auto-quote whole string
631 # Auto-quote whole string
630 newcmd = '%s("%s")' % (ifun,the_rest)
632 newcmd = '%s("%s")' % (ifun,the_rest)
631 elif esc == ESC_PAREN:
633 elif esc == ESC_PAREN:
632 newcmd = '%s(%s)' % (ifun,",".join(the_rest.split()))
634 newcmd = '%s(%s)' % (ifun,",".join(the_rest.split()))
633 else:
635 else:
634 # Auto-paren.
636 # Auto-paren.
635 if force_auto:
637 if force_auto:
636 # Don't rewrite if it is already a call.
638 # Don't rewrite if it is already a call.
637 do_rewrite = not the_rest.startswith('(')
639 do_rewrite = not the_rest.startswith('(')
638 else:
640 else:
639 if not the_rest:
641 if not the_rest:
640 # We only apply it to argument-less calls if the autocall
642 # We only apply it to argument-less calls if the autocall
641 # parameter is set to 2.
643 # parameter is set to 2.
642 do_rewrite = (self.shell.autocall >= 2)
644 do_rewrite = (self.shell.autocall >= 2)
643 elif the_rest.startswith('[') and hasattr(obj, '__getitem__'):
645 elif the_rest.startswith('[') and hasattr(obj, '__getitem__'):
644 # Don't autocall in this case: item access for an object
646 # Don't autocall in this case: item access for an object
645 # which is BOTH callable and implements __getitem__.
647 # which is BOTH callable and implements __getitem__.
646 do_rewrite = False
648 do_rewrite = False
647 else:
649 else:
648 do_rewrite = True
650 do_rewrite = True
649
651
650 # Figure out the rewritten command
652 # Figure out the rewritten command
651 if do_rewrite:
653 if do_rewrite:
652 if the_rest.endswith(';'):
654 if the_rest.endswith(';'):
653 newcmd = '%s(%s);' % (ifun.rstrip(),the_rest[:-1])
655 newcmd = '%s(%s);' % (ifun.rstrip(),the_rest[:-1])
654 else:
656 else:
655 newcmd = '%s(%s)' % (ifun.rstrip(), the_rest)
657 newcmd = '%s(%s)' % (ifun.rstrip(), the_rest)
656 else:
658 else:
657 normal_handler = self.prefilter_manager.get_handler_by_name('normal')
659 normal_handler = self.prefilter_manager.get_handler_by_name('normal')
658 return normal_handler.handle(line_info)
660 return normal_handler.handle(line_info)
659
661
660 # Display the rewritten call
662 # Display the rewritten call
661 if auto_rewrite:
663 if auto_rewrite:
662 self.shell.auto_rewrite_input(newcmd)
664 self.shell.auto_rewrite_input(newcmd)
663
665
664 return newcmd
666 return newcmd
665
667
666
668
667 class EmacsHandler(PrefilterHandler):
669 class EmacsHandler(PrefilterHandler):
668
670
669 handler_name = Unicode('emacs')
671 handler_name = Unicode('emacs')
670 esc_strings = List([])
672 esc_strings = List([])
671
673
672 def handle(self, line_info):
674 def handle(self, line_info):
673 """Handle input lines marked by python-mode."""
675 """Handle input lines marked by python-mode."""
674
676
675 # Currently, nothing is done. Later more functionality can be added
677 # Currently, nothing is done. Later more functionality can be added
676 # here if needed.
678 # here if needed.
677
679
678 # The input cache shouldn't be updated
680 # The input cache shouldn't be updated
679 return line_info.line
681 return line_info.line
680
682
681
683
682 #-----------------------------------------------------------------------------
684 #-----------------------------------------------------------------------------
683 # Defaults
685 # Defaults
684 #-----------------------------------------------------------------------------
686 #-----------------------------------------------------------------------------
685
687
686
688
687 _default_checkers = [
689 _default_checkers = [
688 EmacsChecker,
690 EmacsChecker,
689 MacroChecker,
691 MacroChecker,
690 IPyAutocallChecker,
692 IPyAutocallChecker,
691 AssignmentChecker,
693 AssignmentChecker,
692 AutoMagicChecker,
694 AutoMagicChecker,
693 PythonOpsChecker,
695 PythonOpsChecker,
694 AutocallChecker
696 AutocallChecker
695 ]
697 ]
696
698
697 _default_handlers = [
699 _default_handlers = [
698 PrefilterHandler,
700 PrefilterHandler,
699 MacroHandler,
701 MacroHandler,
700 MagicHandler,
702 MagicHandler,
701 AutoHandler,
703 AutoHandler,
702 EmacsHandler
704 EmacsHandler
703 ]
705 ]
@@ -1,138 +1,145 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Simple utility for splitting user input. This is used by both inputsplitter and
3 Simple utility for splitting user input. This is used by both inputsplitter and
4 prefilter.
4 prefilter.
5
5
6 Authors:
6 Authors:
7
7
8 * Brian Granger
8 * Brian Granger
9 * Fernando Perez
9 * Fernando Perez
10 """
10 """
11
11
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Copyright (C) 2008-2011 The IPython Development Team
13 # Copyright (C) 2008-2011 The IPython Development Team
14 #
14 #
15 # Distributed under the terms of the BSD License. The full license is in
15 # Distributed under the terms of the BSD License. The full license is in
16 # the file COPYING, distributed as part of this software.
16 # the file COPYING, distributed as part of this software.
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Imports
20 # Imports
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22
22
23 import re
23 import re
24 import sys
24 import sys
25
25
26 from IPython.utils import py3compat
26 from IPython.utils import py3compat
27 from IPython.utils.encoding import get_stream_enc
27 from IPython.utils.encoding import get_stream_enc
28 from IPython.core.oinspect import OInfo
28 from IPython.core.oinspect import OInfo
29
29
30 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
31 # Main function
31 # Main function
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33
33
34 # RegExp for splitting line contents into pre-char//first word-method//rest.
34 # RegExp for splitting line contents into pre-char//first word-method//rest.
35 # For clarity, each group in on one line.
35 # For clarity, each group in on one line.
36
36
37 # WARNING: update the regexp if the escapes in interactiveshell are changed, as
37 # WARNING: update the regexp if the escapes in interactiveshell are changed, as
38 # they are hardwired in.
38 # they are hardwired in.
39
39
40 # Although it's not solely driven by the regex, note that:
40 # Although it's not solely driven by the regex, note that:
41 # ,;/% only trigger if they are the first character on the line
41 # ,;/% only trigger if they are the first character on the line
42 # ! and !! trigger if they are first char(s) *or* follow an indent
42 # ! and !! trigger if they are first char(s) *or* follow an indent
43 # ? triggers as first or last char.
43 # ? triggers as first or last char.
44
44
45 line_split = re.compile(r"""
45 line_split = re.compile(r"""
46 ^(\s*) # any leading space
46 ^(\s*) # any leading space
47 ([,;/%]|!!?|\?\??)? # escape character or characters
47 ([,;/%]|!!?|\?\??)? # escape character or characters
48 \s*(%{0,2}[\w\.\*]*) # function/method, possibly with leading %
48 \s*(%{0,2}[\w\.\*]*) # function/method, possibly with leading %
49 # to correctly treat things like '?%magic'
49 # to correctly treat things like '?%magic'
50 (.*?$|$) # rest of line
50 (.*?$|$) # rest of line
51 """, re.VERBOSE)
51 """, re.VERBOSE)
52
52
53
53
54 def split_user_input(line, pattern=None):
54 def split_user_input(line, pattern=None):
55 """Split user input into initial whitespace, escape character, function part
55 """Split user input into initial whitespace, escape character, function part
56 and the rest.
56 and the rest.
57 """
57 """
58 # We need to ensure that the rest of this routine deals only with unicode
58 # We need to ensure that the rest of this routine deals only with unicode
59 encoding = get_stream_enc(sys.stdin, 'utf-8')
59 encoding = get_stream_enc(sys.stdin, 'utf-8')
60 line = py3compat.cast_unicode(line, encoding)
60 line = py3compat.cast_unicode(line, encoding)
61
61
62 if pattern is None:
62 if pattern is None:
63 pattern = line_split
63 pattern = line_split
64 match = pattern.match(line)
64 match = pattern.match(line)
65 if not match:
65 if not match:
66 # print("match failed for line '%s'" % line)
66 # print("match failed for line '%s'" % line)
67 try:
67 try:
68 ifun, the_rest = line.split(None,1)
68 ifun, the_rest = line.split(None,1)
69 except ValueError:
69 except ValueError:
70 # print("split failed for line '%s'" % line)
70 # print("split failed for line '%s'" % line)
71 ifun, the_rest = line, u''
71 ifun, the_rest = line, u''
72 pre = re.match(r'^(\s*)(.*)',line).groups()[0]
72 pre = re.match(r'^(\s*)(.*)',line).groups()[0]
73 esc = ""
73 esc = ""
74 else:
74 else:
75 pre, esc, ifun, the_rest = match.groups()
75 pre, esc, ifun, the_rest = match.groups()
76
76
77 # print('line:<%s>' % line) # dbg
77 # print('line:<%s>' % line) # dbg
78 # print('pre <%s> ifun <%s> rest <%s>' % (pre,ifun.strip(),the_rest)) # dbg
78 # print('pre <%s> ifun <%s> rest <%s>' % (pre,ifun.strip(),the_rest)) # dbg
79 return pre, esc or '', ifun.strip(), the_rest.lstrip()
79 return pre, esc or "", ifun.strip(), the_rest
80
80
81
81
82 class LineInfo(object):
82 class LineInfo(object):
83 """A single line of input and associated info.
83 """A single line of input and associated info.
84
84
85 Includes the following as properties:
85 Includes the following as properties:
86
86
87 line
87 line
88 The original, raw line
88 The original, raw line
89
89
90 continue_prompt
90 continue_prompt
91 Is this line a continuation in a sequence of multiline input?
91 Is this line a continuation in a sequence of multiline input?
92
92
93 pre
93 pre
94 Any leading whitespace.
94 Any leading whitespace.
95
95
96 esc
96 esc
97 The escape character(s) in pre or the empty string if there isn't one.
97 The escape character(s) in pre or the empty string if there isn't one.
98 Note that '!!' and '??' are possible values for esc. Otherwise it will
98 Note that '!!' and '??' are possible values for esc. Otherwise it will
99 always be a single character.
99 always be a single character.
100
100
101 ifun
101 ifun
102 The 'function part', which is basically the maximal initial sequence
102 The 'function part', which is basically the maximal initial sequence
103 of valid python identifiers and the '.' character. This is what is
103 of valid python identifiers and the '.' character. This is what is
104 checked for alias and magic transformations, used for auto-calling,
104 checked for alias and magic transformations, used for auto-calling,
105 etc. In contrast to Python identifiers, it may start with "%" and contain
105 etc. In contrast to Python identifiers, it may start with "%" and contain
106 "*".
106 "*".
107
107
108 the_rest
108 the_rest
109 Everything else on the line.
109 Everything else on the line.
110
111 raw_the_rest
112 the_rest without whitespace stripped.
110 """
113 """
111 def __init__(self, line, continue_prompt=False):
114 def __init__(self, line, continue_prompt=False):
112 self.line = line
115 self.line = line
113 self.continue_prompt = continue_prompt
116 self.continue_prompt = continue_prompt
114 self.pre, self.esc, self.ifun, self.the_rest = split_user_input(line)
117 self.pre, self.esc, self.ifun, self.raw_the_rest = split_user_input(line)
118 self.the_rest = self.raw_the_rest.lstrip()
115
119
116 self.pre_char = self.pre.strip()
120 self.pre_char = self.pre.strip()
117 if self.pre_char:
121 if self.pre_char:
118 self.pre_whitespace = '' # No whitespace allowed before esc chars
122 self.pre_whitespace = '' # No whitespace allowed before esc chars
119 else:
123 else:
120 self.pre_whitespace = self.pre
124 self.pre_whitespace = self.pre
121
125
122 def ofind(self, ip) -> OInfo:
126 def ofind(self, ip) -> OInfo:
123 """Do a full, attribute-walking lookup of the ifun in the various
127 """Do a full, attribute-walking lookup of the ifun in the various
124 namespaces for the given IPython InteractiveShell instance.
128 namespaces for the given IPython InteractiveShell instance.
125
129
126 Return a dict with keys: {found, obj, ospace, ismagic}
130 Return a dict with keys: {found, obj, ospace, ismagic}
127
131
128 Note: can cause state changes because of calling getattr, but should
132 Note: can cause state changes because of calling getattr, but should
129 only be run if autocall is on and if the line hasn't matched any
133 only be run if autocall is on and if the line hasn't matched any
130 other, less dangerous handlers.
134 other, less dangerous handlers.
131
135
132 Does cache the results of the call, so can be called multiple times
136 Does cache the results of the call, so can be called multiple times
133 without worrying about *further* damaging state.
137 without worrying about *further* damaging state.
134 """
138 """
135 return ip._ofind(self.ifun)
139 return ip._ofind(self.ifun)
136
140
137 def __str__(self):
141 def __str__(self):
138 return "LineInfo [%s|%s|%s|%s]" %(self.pre, self.esc, self.ifun, self.the_rest)
142 return "LineInfo [%s|%s|%s|%s]" %(self.pre, self.esc, self.ifun, self.the_rest)
143
144 def __repr__(self):
145 return "<" + str(self) + ">"
@@ -1,97 +1,75 b''
1 """Tests for input handlers.
1 """Tests for input handlers.
2 """
2 """
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Module imports
4 # Module imports
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6
6
7 # our own packages
7 # our own packages
8 from IPython.core import autocall
8 from IPython.core import autocall
9 from IPython.testing import tools as tt
9 from IPython.testing import tools as tt
10 import pytest
11 from collections.abc import Callable
10
12
11 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
12 # Globals
14 # Globals
13 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
14
16
15 # Get the public instance of IPython
16
17 failures = []
18 num_tests = 0
19
20 #-----------------------------------------------------------------------------
21 # Test functions
17 # Test functions
22 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
23
19
24 class CallableIndexable(object):
20 class CallableIndexable(object):
25 def __getitem__(self, idx): return True
21 def __getitem__(self, idx): return True
26 def __call__(self, *args, **kws): return True
22 def __call__(self, *args, **kws): return True
27
23
28
24
29 class Autocallable(autocall.IPyAutocall):
25 class Autocallable(autocall.IPyAutocall):
30 def __call__(self):
26 def __call__(self):
31 return "called"
27 return "called"
32
28
33
29
34 def run(tests):
30 @pytest.mark.parametrize(
35 """Loop through a list of (pre, post) inputs, where pre is the string
31 "autocall, input, output",
36 handed to ipython, and post is how that string looks after it's been
32 [
37 transformed (i.e. ipython's notion of _i)"""
38 tt.check_pairs(ip.prefilter_manager.prefilter_lines, tests)
39
40
41 def test_handlers():
42 call_idx = CallableIndexable()
43 ip.user_ns['call_idx'] = call_idx
44
45 # For many of the below, we're also checking that leading whitespace
33 # For many of the below, we're also checking that leading whitespace
46 # turns off the esc char, which it should unless there is a continuation
34 # turns off the esc char, which it should unless there is a continuation
47 # line.
35 # line.
48 run(
36 ("1", '"no change"', '"no change"'), # normal
49 [('"no change"', '"no change"'), # normal
37 ("1", "lsmagic", "get_ipython().run_line_magic('lsmagic', '')"), # magic
50 (u"lsmagic", "get_ipython().run_line_magic('lsmagic', '')"), # magic
51 #("a = b # PYTHON-MODE", '_i'), # emacs -- avoids _in cache
52 ])
53
54 # Objects which are instances of IPyAutocall are *always* autocalled
55 autocallable = Autocallable()
56 ip.user_ns['autocallable'] = autocallable
57
58 # auto
59 ip.run_line_magic("autocall", "0")
60 # Only explicit escapes or instances of IPyAutocallable should get
38 # Only explicit escapes or instances of IPyAutocallable should get
61 # expanded
39 # expanded
62 run(
40 ("0", 'len "abc"', 'len "abc"'),
63 [
41 ("0", "autocallable", "autocallable()"),
64 ('len "abc"', 'len "abc"'),
65 ("autocallable", "autocallable()"),
66 # Don't add extra brackets (gh-1117)
42 # Don't add extra brackets (gh-1117)
67 ("autocallable()", "autocallable()"),
43 ("0", "autocallable()", "autocallable()"),
68 ]
44 ("1", 'len "abc"', 'len("abc")'),
69 )
45 ("1", 'len "abc";', 'len("abc");'), # ; is special -- moves out of parens
70 ip.run_line_magic("autocall", "1")
71 run(
72 [
73 ('len "abc"', 'len("abc")'),
74 ('len "abc";', 'len("abc");'), # ; is special -- moves out of parens
75 # Autocall is turned off if first arg is [] and the object
46 # Autocall is turned off if first arg is [] and the object
76 # is both callable and indexable. Like so:
47 # is both callable and indexable. Like so:
77 ("len [1,2]", "len([1,2])"), # len doesn't support __getitem__...
48 ("1", "len [1,2]", "len([1,2])"), # len doesn't support __getitem__...
78 ("call_idx [1]", "call_idx [1]"), # call_idx *does*..
49 ("1", "call_idx [1]", "call_idx [1]"), # call_idx *does*..
79 ("call_idx 1", "call_idx(1)"),
50 ("1", "call_idx 1", "call_idx(1)"),
80 ("len", "len"), # only at 2 does it auto-call on single args
51 ("1", "len", "len"), # only at 2 does it auto-call on single args
81 ]
52 ("2", 'len "abc"', 'len("abc")'),
82 )
53 ("2", 'len "abc";', 'len("abc");'),
83 ip.run_line_magic("autocall", "2")
54 ("2", "len [1,2]", "len([1,2])"),
84 run(
55 ("2", "call_idx [1]", "call_idx [1]"),
85 [
56 ("2", "call_idx 1", "call_idx(1)"),
86 ('len "abc"', 'len("abc")'),
87 ('len "abc";', 'len("abc");'),
88 ("len [1,2]", "len([1,2])"),
89 ("call_idx [1]", "call_idx [1]"),
90 ("call_idx 1", "call_idx(1)"),
91 # This is what's different:
57 # T his is what's different:
92 ("len", "len()"), # only at 2 does it auto-call on single args
58 ("2", "len", "len()"), # only at 2 does it auto-call on single args
93 ]
59 ("0", "Callable[[int], None]", "Callable[[int], None]"),
60 ("1", "Callable[[int], None]", "Callable[[int], None]"),
61 ("1", "Callable[[int], None]", "Callable[[int], None]"),
62 ],
94 )
63 )
95 ip.run_line_magic("autocall", "1")
64 def test_handlers_I(autocall, input, output):
65 autocallable = Autocallable()
66 ip.user_ns["autocallable"] = autocallable
67
68 call_idx = CallableIndexable()
69 ip.user_ns["call_idx"] = call_idx
96
70
97 assert failures == []
71 ip.user_ns["Callable"] = Callable
72
73 ip.run_line_magic("autocall", autocall)
74 assert ip.prefilter_manager.prefilter_lines(input) == output
75 ip.run_line_magic("autocall", "1")
@@ -1,127 +1,139 b''
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.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.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.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.magic('autocall 0')
117 ip.run_line_magic("autocall", "0")
118
119
120 def test_autocall_type_ann():
121 ip.run_cell("import collections.abc")
122 ip.run_line_magic("autocall", "1")
123 try:
124 assert (
125 ip.prefilter("collections.abc.Callable[[int], None]")
126 == "collections.abc.Callable[[int], None]"
127 )
128 finally:
129 ip.run_line_magic("autocall", "0")
118
130
119
131
120 def test_autocall_should_support_unicode():
132 def test_autocall_should_support_unicode():
121 ip.magic('autocall 2')
133 ip.run_line_magic("autocall", "2")
122 ip.user_ns['π'] = lambda x: x
134 ip.user_ns["π"] = lambda x: x
123 try:
135 try:
124 assert ip.prefilter("π 3") == "π(3)"
136 assert ip.prefilter("π 3") == "π(3)"
125 finally:
137 finally:
126 ip.magic('autocall 0')
138 ip.run_line_magic("autocall", "0")
127 del ip.user_ns['π']
139 del ip.user_ns["π"]
@@ -1,39 +1,41 b''
1 # coding: utf-8
1 # coding: utf-8
2
2
3 from IPython.core.splitinput import split_user_input, LineInfo
3 from IPython.core.splitinput import split_user_input, LineInfo
4 from IPython.testing import tools as tt
4
5 import pytest
5
6
6 tests = [
7 tests = [
7 ("x=1", ("", "", "x", "=1")),
8 ("x=1", ("", "", "x", "=1")),
8 ("?", ("", "?", "", "")),
9 ("?", ("", "?", "", "")),
9 ("??", ("", "??", "", "")),
10 ("??", ("", "??", "", "")),
10 (" ?", (" ", "?", "", "")),
11 (" ?", (" ", "?", "", "")),
11 (" ??", (" ", "??", "", "")),
12 (" ??", (" ", "??", "", "")),
12 ("??x", ("", "??", "x", "")),
13 ("??x", ("", "??", "x", "")),
13 ("?x=1", ("", "?", "x", "=1")),
14 ("?x=1", ("", "?", "x", "=1")),
14 ("!ls", ("", "!", "ls", "")),
15 ("!ls", ("", "!", "ls", "")),
15 (" !ls", (" ", "!", "ls", "")),
16 (" !ls", (" ", "!", "ls", "")),
16 ("!!ls", ("", "!!", "ls", "")),
17 ("!!ls", ("", "!!", "ls", "")),
17 (" !!ls", (" ", "!!", "ls", "")),
18 (" !!ls", (" ", "!!", "ls", "")),
18 (",ls", ("", ",", "ls", "")),
19 (",ls", ("", ",", "ls", "")),
19 (";ls", ("", ";", "ls", "")),
20 (";ls", ("", ";", "ls", "")),
20 (" ;ls", (" ", ";", "ls", "")),
21 (" ;ls", (" ", ";", "ls", "")),
21 ("f.g(x)", ("", "", "f.g", "(x)")),
22 ("f.g(x)", ("", "", "f.g", "(x)")),
22 ("f.g (x)", ("", "", "f.g", "(x)")),
23 ("f.g (x)", ("", "", "f.g", " (x)")),
23 ("?%hist1", ("", "?", "%hist1", "")),
24 ("?%hist1", ("", "?", "%hist1", "")),
24 ("?%%hist2", ("", "?", "%%hist2", "")),
25 ("?%%hist2", ("", "?", "%%hist2", "")),
25 ("??%hist3", ("", "??", "%hist3", "")),
26 ("??%hist3", ("", "??", "%hist3", "")),
26 ("??%%hist4", ("", "??", "%%hist4", "")),
27 ("??%%hist4", ("", "??", "%%hist4", "")),
27 ("?x*", ("", "?", "x*", "")),
28 ("?x*", ("", "?", "x*", "")),
29 ("Pérez Fernando", ("", "", "Pérez", " Fernando")),
28 ]
30 ]
29 tests.append(("Pérez Fernando", ("", "", "Pérez", "Fernando")))
30
31
31
32
32 def test_split_user_input():
33 @pytest.mark.parametrize("input, output", tests)
33 return tt.check_pairs(split_user_input, tests)
34 def test_split_user_input(input, output):
35 assert split_user_input(input) == output
34
36
35
37
36 def test_LineInfo():
38 def test_LineInfo():
37 """Simple test for LineInfo construction and str()"""
39 """Simple test for LineInfo construction and str()"""
38 linfo = LineInfo(" %cd /home")
40 linfo = LineInfo(" %cd /home")
39 assert str(linfo) == "LineInfo [ |%|cd|/home]"
41 assert str(linfo) == "LineInfo [ |%|cd|/home]"
General Comments 0
You need to be logged in to leave comments. Login now