##// END OF EJS Templates
suggested fix for autocall failure (#14555)...
M Bussonnier -
r28909:18e45295 merge
parent child Browse files
Show More
@@ -1,705 +1,707
1 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 479 if line_info.the_rest and line_info.the_rest[0] in "!=()<>,+*/%^&|":
480 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 and (
516 line_info.raw_the_rest.startswith(" ")
517 or not line_info.raw_the_rest.strip()
518 )
517 519 ):
518 520 return self.prefilter_manager.get_handler_by_name("auto")
519 521 else:
520 522 return None
521 523
522 524
523 525 #-----------------------------------------------------------------------------
524 526 # Prefilter handlers
525 527 #-----------------------------------------------------------------------------
526 528
527 529
528 530 class PrefilterHandler(Configurable):
529 531 handler_name = Unicode("normal")
530 532 esc_strings: List = List([])
531 533 shell = Instance(
532 534 "IPython.core.interactiveshell.InteractiveShellABC", allow_none=True
533 535 )
534 536 prefilter_manager = Instance(
535 537 "IPython.core.prefilter.PrefilterManager", allow_none=True
536 538 )
537 539
538 540 def __init__(self, shell=None, prefilter_manager=None, **kwargs):
539 541 super(PrefilterHandler, self).__init__(
540 542 shell=shell, prefilter_manager=prefilter_manager, **kwargs
541 543 )
542 544 self.prefilter_manager.register_handler(
543 545 self.handler_name,
544 546 self,
545 547 self.esc_strings
546 548 )
547 549
548 550 def handle(self, line_info):
549 551 # print("normal: ", line_info)
550 552 """Handle normal input lines. Use as a template for handlers."""
551 553
552 554 # With autoindent on, we need some way to exit the input loop, and I
553 555 # don't want to force the user to have to backspace all the way to
554 556 # clear the line. The rule will be in this case, that either two
555 557 # lines of pure whitespace in a row, or a line of pure whitespace but
556 558 # of a size different to the indent level, will exit the input loop.
557 559 line = line_info.line
558 560 continue_prompt = line_info.continue_prompt
559 561
560 562 if (continue_prompt and
561 563 self.shell.autoindent and
562 564 line.isspace() and
563 565 0 < abs(len(line) - self.shell.indent_current_nsp) <= 2):
564 566 line = ''
565 567
566 568 return line
567 569
568 570 def __str__(self):
569 571 return "<%s(name=%s)>" % (self.__class__.__name__, self.handler_name)
570 572
571 573
572 574 class MacroHandler(PrefilterHandler):
573 575 handler_name = Unicode("macro")
574 576
575 577 def handle(self, line_info):
576 578 obj = self.shell.user_ns.get(line_info.ifun)
577 579 pre_space = line_info.pre_whitespace
578 580 line_sep = "\n" + pre_space
579 581 return pre_space + line_sep.join(obj.value.splitlines())
580 582
581 583
582 584 class MagicHandler(PrefilterHandler):
583 585
584 586 handler_name = Unicode('magic')
585 587 esc_strings = List([ESC_MAGIC])
586 588
587 589 def handle(self, line_info):
588 590 """Execute magic functions."""
589 591 ifun = line_info.ifun
590 592 the_rest = line_info.the_rest
591 593 #Prepare arguments for get_ipython().run_line_magic(magic_name, magic_args)
592 594 t_arg_s = ifun + " " + the_rest
593 595 t_magic_name, _, t_magic_arg_s = t_arg_s.partition(' ')
594 596 t_magic_name = t_magic_name.lstrip(ESC_MAGIC)
595 597 cmd = '%sget_ipython().run_line_magic(%r, %r)' % (line_info.pre_whitespace, t_magic_name, t_magic_arg_s)
596 598 return cmd
597 599
598 600
599 601 class AutoHandler(PrefilterHandler):
600 602
601 603 handler_name = Unicode('auto')
602 604 esc_strings = List([ESC_PAREN, ESC_QUOTE, ESC_QUOTE2])
603 605
604 606 def handle(self, line_info):
605 607 """Handle lines which can be auto-executed, quoting if requested."""
606 608 line = line_info.line
607 609 ifun = line_info.ifun
608 610 the_rest = line_info.the_rest
609 611 esc = line_info.esc
610 612 continue_prompt = line_info.continue_prompt
611 613 obj = line_info.ofind(self.shell).obj
612 614
613 615 # This should only be active for single-line input!
614 616 if continue_prompt:
615 617 return line
616 618
617 619 force_auto = isinstance(obj, IPyAutocall)
618 620
619 621 # User objects sometimes raise exceptions on attribute access other
620 622 # than AttributeError (we've seen it in the past), so it's safest to be
621 623 # ultra-conservative here and catch all.
622 624 try:
623 625 auto_rewrite = obj.rewrite
624 626 except Exception:
625 627 auto_rewrite = True
626 628
627 629 if esc == ESC_QUOTE:
628 630 # Auto-quote splitting on whitespace
629 631 newcmd = '%s("%s")' % (ifun,'", "'.join(the_rest.split()) )
630 632 elif esc == ESC_QUOTE2:
631 633 # Auto-quote whole string
632 634 newcmd = '%s("%s")' % (ifun,the_rest)
633 635 elif esc == ESC_PAREN:
634 636 newcmd = '%s(%s)' % (ifun,",".join(the_rest.split()))
635 637 else:
636 638 # Auto-paren.
637 639 if force_auto:
638 640 # Don't rewrite if it is already a call.
639 641 do_rewrite = not the_rest.startswith('(')
640 642 else:
641 643 if not the_rest:
642 644 # We only apply it to argument-less calls if the autocall
643 645 # parameter is set to 2.
644 646 do_rewrite = (self.shell.autocall >= 2)
645 647 elif the_rest.startswith('[') and hasattr(obj, '__getitem__'):
646 648 # Don't autocall in this case: item access for an object
647 649 # which is BOTH callable and implements __getitem__.
648 650 do_rewrite = False
649 651 else:
650 652 do_rewrite = True
651 653
652 654 # Figure out the rewritten command
653 655 if do_rewrite:
654 656 if the_rest.endswith(';'):
655 657 newcmd = '%s(%s);' % (ifun.rstrip(),the_rest[:-1])
656 658 else:
657 659 newcmd = '%s(%s)' % (ifun.rstrip(), the_rest)
658 660 else:
659 661 normal_handler = self.prefilter_manager.get_handler_by_name('normal')
660 662 return normal_handler.handle(line_info)
661 663
662 664 # Display the rewritten call
663 665 if auto_rewrite:
664 666 self.shell.auto_rewrite_input(newcmd)
665 667
666 668 return newcmd
667 669
668 670
669 671 class EmacsHandler(PrefilterHandler):
670 672
671 673 handler_name = Unicode('emacs')
672 674 esc_strings = List([])
673 675
674 676 def handle(self, line_info):
675 677 """Handle input lines marked by python-mode."""
676 678
677 679 # Currently, nothing is done. Later more functionality can be added
678 680 # here if needed.
679 681
680 682 # The input cache shouldn't be updated
681 683 return line_info.line
682 684
683 685
684 686 #-----------------------------------------------------------------------------
685 687 # Defaults
686 688 #-----------------------------------------------------------------------------
687 689
688 690
689 691 _default_checkers = [
690 692 EmacsChecker,
691 693 MacroChecker,
692 694 IPyAutocallChecker,
693 695 AssignmentChecker,
694 696 AutoMagicChecker,
695 697 PythonOpsChecker,
696 698 AutocallChecker
697 699 ]
698 700
699 701 _default_handlers = [
700 702 PrefilterHandler,
701 703 MacroHandler,
702 704 MagicHandler,
703 705 AutoHandler,
704 706 EmacsHandler
705 707 ]
@@ -1,139 +1,149
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 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 74 ip.run_line_magic("autocall", "0")
75 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 108 ip.user_ns["x"] = X()
109 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 116 del ip.user_ns["x"]
117 117 ip.run_line_magic("autocall", "0")
118 118
119 119
120 120 def test_autocall_type_ann():
121 121 ip.run_cell("import collections.abc")
122 122 ip.run_line_magic("autocall", "1")
123 123 try:
124 124 assert (
125 125 ip.prefilter("collections.abc.Callable[[int], None]")
126 126 == "collections.abc.Callable[[int], None]"
127 127 )
128 128 finally:
129 129 ip.run_line_magic("autocall", "0")
130 130
131 131
132 132 def test_autocall_should_support_unicode():
133 133 ip.run_line_magic("autocall", "2")
134 134 ip.user_ns["π"] = lambda x: x
135 135 try:
136 136 assert ip.prefilter("π 3") == "π(3)"
137 137 finally:
138 138 ip.run_line_magic("autocall", "0")
139 139 del ip.user_ns["π"]
140
141
142 def test_autocall_regression_gh_14513():
143 ip.run_line_magic("autocall", "2")
144 ip.user_ns["foo"] = dict()
145 try:
146 assert ip.prefilter("foo") == "foo"
147 finally:
148 ip.run_line_magic("autocall", "0")
149 del ip.user_ns["foo"]
General Comments 0
You need to be logged in to leave comments. Login now