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