##// END OF EJS Templates
handle from ptshell side
Chris -
Show More
@@ -1,685 +1,681 b''
1 1 """Input handling and transformation machinery.
2 2
3 3 The first class in this module, :class:`InputSplitter`, is designed to tell when
4 4 input from a line-oriented frontend is complete and should be executed, and when
5 5 the user should be prompted for another line of code instead. The name 'input
6 6 splitter' is largely for historical reasons.
7 7
8 8 A companion, :class:`IPythonInputSplitter`, provides the same functionality but
9 9 with full support for the extended IPython syntax (magics, system calls, etc).
10 10 The code to actually do these transformations is in :mod:`IPython.core.inputtransformer`.
11 11 :class:`IPythonInputSplitter` feeds the raw code to the transformers in order
12 12 and stores the results.
13 13
14 14 For more details, see the class docstrings below.
15 15 """
16 16
17 17 # Copyright (c) IPython Development Team.
18 18 # Distributed under the terms of the Modified BSD License.
19 19 import ast
20 20 import codeop
21 21 import re
22 22 import sys
23 23 import warnings
24 24
25 25 from IPython.utils.py3compat import cast_unicode
26 26 from IPython.core.inputtransformer import (leading_indent,
27 27 classic_prompt,
28 28 ipy_prompt,
29 29 strip_encoding_cookie,
30 30 cellmagic,
31 31 assemble_logical_lines,
32 32 help_end,
33 33 escaped_commands,
34 34 assign_from_magic,
35 35 assign_from_system,
36 36 assemble_python_lines,
37 37 )
38 38
39 39 # These are available in this module for backwards compatibility.
40 40 from IPython.core.inputtransformer import (ESC_SHELL, ESC_SH_CAP, ESC_HELP,
41 41 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,
42 42 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN, ESC_SEQUENCES)
43 43
44 44 #-----------------------------------------------------------------------------
45 45 # Utilities
46 46 #-----------------------------------------------------------------------------
47 47
48 48 # FIXME: These are general-purpose utilities that later can be moved to the
49 49 # general ward. Kept here for now because we're being very strict about test
50 50 # coverage with this code, and this lets us ensure that we keep 100% coverage
51 51 # while developing.
52 52
53 53 # compiled regexps for autoindent management
54 54 dedent_re = re.compile('|'.join([
55 55 r'^\s+raise(\s.*)?$', # raise statement (+ space + other stuff, maybe)
56 56 r'^\s+raise\([^\)]*\).*$', # wacky raise with immediate open paren
57 57 r'^\s+return(\s.*)?$', # normal return (+ space + other stuff, maybe)
58 58 r'^\s+return\([^\)]*\).*$', # wacky return with immediate open paren
59 59 r'^\s+pass\s*$', # pass (optionally followed by trailing spaces)
60 60 r'^\s+break\s*$', # break (optionally followed by trailing spaces)
61 61 r'^\s+continue\s*$', # continue (optionally followed by trailing spaces)
62 62 ]))
63 63 ini_spaces_re = re.compile(r'^([ \t\r\f\v]+)')
64 64
65 65 # regexp to match pure comment lines so we don't accidentally insert 'if 1:'
66 66 # before pure comments
67 67 comment_line_re = re.compile('^\s*\#')
68 68
69 69
70 70 def num_ini_spaces(s):
71 71 """Return the number of initial spaces in a string.
72 72
73 73 Note that tabs are counted as a single space. For now, we do *not* support
74 74 mixing of tabs and spaces in the user's input.
75 75
76 76 Parameters
77 77 ----------
78 78 s : string
79 79
80 80 Returns
81 81 -------
82 82 n : int
83 83 """
84 84
85 85 ini_spaces = ini_spaces_re.match(s)
86 86 if ini_spaces:
87 87 return ini_spaces.end()
88 88 else:
89 89 return 0
90 90
91 91 def last_blank(src):
92 92 """Determine if the input source ends in a blank.
93 93
94 94 A blank is either a newline or a line consisting of whitespace.
95 95
96 96 Parameters
97 97 ----------
98 98 src : string
99 99 A single or multiline string.
100 100 """
101 101 if not src: return False
102 102 ll = src.splitlines()[-1]
103 103 return (ll == '') or ll.isspace()
104 104
105 105
106 106 last_two_blanks_re = re.compile(r'\n\s*\n\s*$', re.MULTILINE)
107 107 last_two_blanks_re2 = re.compile(r'.+\n\s*\n\s+$', re.MULTILINE)
108 108
109 109 def last_two_blanks(src):
110 110 """Determine if the input source ends in two blanks.
111 111
112 112 A blank is either a newline or a line consisting of whitespace.
113 113
114 114 Parameters
115 115 ----------
116 116 src : string
117 117 A single or multiline string.
118 118 """
119 119 if not src: return False
120 120 # The logic here is tricky: I couldn't get a regexp to work and pass all
121 121 # the tests, so I took a different approach: split the source by lines,
122 122 # grab the last two and prepend '###\n' as a stand-in for whatever was in
123 123 # the body before the last two lines. Then, with that structure, it's
124 124 # possible to analyze with two regexps. Not the most elegant solution, but
125 125 # it works. If anyone tries to change this logic, make sure to validate
126 126 # the whole test suite first!
127 127 new_src = '\n'.join(['###\n'] + src.splitlines()[-2:])
128 128 return (bool(last_two_blanks_re.match(new_src)) or
129 129 bool(last_two_blanks_re2.match(new_src)) )
130 130
131 131
132 132 def remove_comments(src):
133 133 """Remove all comments from input source.
134 134
135 135 Note: comments are NOT recognized inside of strings!
136 136
137 137 Parameters
138 138 ----------
139 139 src : string
140 140 A single or multiline input string.
141 141
142 142 Returns
143 143 -------
144 144 String with all Python comments removed.
145 145 """
146 146
147 147 return re.sub('#.*', '', src)
148 148
149 149
150 150 def get_input_encoding():
151 151 """Return the default standard input encoding.
152 152
153 153 If sys.stdin has no encoding, 'ascii' is returned."""
154 154 # There are strange environments for which sys.stdin.encoding is None. We
155 155 # ensure that a valid encoding is returned.
156 156 encoding = getattr(sys.stdin, 'encoding', None)
157 157 if encoding is None:
158 158 encoding = 'ascii'
159 159 return encoding
160 160
161 161 #-----------------------------------------------------------------------------
162 162 # Classes and functions for normal Python syntax handling
163 163 #-----------------------------------------------------------------------------
164 164
165 165 class InputSplitter(object):
166 166 r"""An object that can accumulate lines of Python source before execution.
167 167
168 168 This object is designed to be fed python source line-by-line, using
169 169 :meth:`push`. It will return on each push whether the currently pushed
170 170 code could be executed already. In addition, it provides a method called
171 171 :meth:`push_accepts_more` that can be used to query whether more input
172 172 can be pushed into a single interactive block.
173 173
174 174 This is a simple example of how an interactive terminal-based client can use
175 175 this tool::
176 176
177 177 isp = InputSplitter()
178 178 while isp.push_accepts_more():
179 179 indent = ' '*isp.indent_spaces
180 180 prompt = '>>> ' + indent
181 181 line = indent + raw_input(prompt)
182 182 isp.push(line)
183 183 print 'Input source was:\n', isp.source_reset(),
184 184 """
185 185 # Number of spaces of indentation computed from input that has been pushed
186 186 # so far. This is the attributes callers should query to get the current
187 187 # indentation level, in order to provide auto-indent facilities.
188 188 indent_spaces = 0
189 189 # String, indicating the default input encoding. It is computed by default
190 190 # at initialization time via get_input_encoding(), but it can be reset by a
191 191 # client with specific knowledge of the encoding.
192 192 encoding = ''
193 193 # String where the current full source input is stored, properly encoded.
194 194 # Reading this attribute is the normal way of querying the currently pushed
195 195 # source code, that has been properly encoded.
196 196 source = ''
197 197 # Code object corresponding to the current source. It is automatically
198 198 # synced to the source, so it can be queried at any time to obtain the code
199 199 # object; it will be None if the source doesn't compile to valid Python.
200 200 code = None
201 201
202 202 # Private attributes
203 203
204 204 # List with lines of input accumulated so far
205 205 _buffer = None
206 206 # Command compiler
207 207 _compile = None
208 208 # Mark when input has changed indentation all the way back to flush-left
209 209 _full_dedent = False
210 210 # Boolean indicating whether the current block is complete
211 211 _is_complete = None
212 212 # Boolean indicating whether the current block has an unrecoverable syntax error
213 213 _is_invalid = False
214 214
215 215 def __init__(self):
216 216 """Create a new InputSplitter instance.
217 217 """
218 218 self._buffer = []
219 219 self._compile = codeop.CommandCompiler()
220 220 self.encoding = get_input_encoding()
221 221
222 222 def reset(self):
223 223 """Reset the input buffer and associated state."""
224 224 self.indent_spaces = 0
225 225 self._buffer[:] = []
226 226 self.source = ''
227 227 self.code = None
228 228 self._is_complete = False
229 229 self._is_invalid = False
230 230 self._full_dedent = False
231 231
232 232 def source_reset(self):
233 233 """Return the input source and perform a full reset.
234 234 """
235 235 out = self.source
236 236 self.reset()
237 237 return out
238 238
239 239 def check_complete(self, source):
240 240 """Return whether a block of code is ready to execute, or should be continued
241 241
242 242 This is a non-stateful API, and will reset the state of this InputSplitter.
243 243
244 244 Parameters
245 245 ----------
246 246 source : string
247 247 Python input code, which can be multiline.
248 248
249 249 Returns
250 250 -------
251 251 status : str
252 252 One of 'complete', 'incomplete', or 'invalid' if source is not a
253 253 prefix of valid code.
254 254 indent_spaces : int or None
255 255 The number of spaces by which to indent the next line of code. If
256 256 status is not 'incomplete', this is None.
257 257 """
258 258 self.reset()
259 259 try:
260 260 self.push(source)
261 261 except SyntaxError:
262 262 # Transformers in IPythonInputSplitter can raise SyntaxError,
263 263 # which push() will not catch.
264 264 return 'invalid', None
265 265 else:
266 266 if self._is_invalid:
267 267 return 'invalid', None
268 268 elif self.push_accepts_more():
269 269 return 'incomplete', self.indent_spaces
270 270 else:
271 271 return 'complete', None
272 272 finally:
273 273 self.reset()
274 274
275 275 def push(self, lines):
276 276 """Push one or more lines of input.
277 277
278 278 This stores the given lines and returns a status code indicating
279 279 whether the code forms a complete Python block or not.
280 280
281 281 Any exceptions generated in compilation are swallowed, but if an
282 282 exception was produced, the method returns True.
283 283
284 284 Parameters
285 285 ----------
286 286 lines : string
287 287 One or more lines of Python input.
288 288
289 289 Returns
290 290 -------
291 291 is_complete : boolean
292 292 True if the current input source (the result of the current input
293 293 plus prior inputs) forms a complete Python execution block. Note that
294 294 this value is also stored as a private attribute (``_is_complete``), so it
295 295 can be queried at any time.
296 296 """
297 297 self._store(lines)
298 298 source = self.source
299 299
300 300 # Before calling _compile(), reset the code object to None so that if an
301 301 # exception is raised in compilation, we don't mislead by having
302 302 # inconsistent code/source attributes.
303 303 self.code, self._is_complete = None, None
304 304 self._is_invalid = False
305 305
306 306 # Honor termination lines properly
307 307 if source.endswith('\\\n'):
308 308 return False
309 309
310 310 self._update_indent(lines)
311 311 try:
312 312 with warnings.catch_warnings():
313 313 warnings.simplefilter('error', SyntaxWarning)
314 314 self.code = self._compile(source, symbol="exec")
315 315 # Invalid syntax can produce any of a number of different errors from
316 316 # inside the compiler, so we have to catch them all. Syntax errors
317 317 # immediately produce a 'ready' block, so the invalid Python can be
318 318 # sent to the kernel for evaluation with possible ipython
319 319 # special-syntax conversion.
320 320 except (SyntaxError, OverflowError, ValueError, TypeError,
321 321 MemoryError, SyntaxWarning):
322 322 self._is_complete = True
323 323 self._is_invalid = True
324 324 else:
325 325 # Compilation didn't produce any exceptions (though it may not have
326 326 # given a complete code object)
327 327 self._is_complete = self.code is not None
328 328
329 329 return self._is_complete
330 330
331 331 def push_accepts_more(self):
332 332 """Return whether a block of interactive input can accept more input.
333 333
334 334 This method is meant to be used by line-oriented frontends, who need to
335 335 guess whether a block is complete or not based solely on prior and
336 336 current input lines. The InputSplitter considers it has a complete
337 337 interactive block and will not accept more input when either:
338 338
339 339 * A SyntaxError is raised
340 340
341 341 * The code is complete and consists of a single line or a single
342 342 non-compound statement
343 343
344 344 * The code is complete and has a blank line at the end
345 345
346 346 If the current input produces a syntax error, this method immediately
347 347 returns False but does *not* raise the syntax error exception, as
348 348 typically clients will want to send invalid syntax to an execution
349 349 backend which might convert the invalid syntax into valid Python via
350 350 one of the dynamic IPython mechanisms.
351 351 """
352 352
353 353 # With incomplete input, unconditionally accept more
354 354 # A syntax error also sets _is_complete to True - see push()
355 355 if not self._is_complete:
356 356 #print("Not complete") # debug
357 357 return True
358 358
359 359 # The user can make any (complete) input execute by leaving a blank line
360 360 last_line = self.source.splitlines()[-1]
361 361 if (not last_line) or last_line.isspace():
362 362 #print("Blank line") # debug
363 363 return False
364 364
365 365 # If there's just a single line or AST node, and we're flush left, as is
366 366 # the case after a simple statement such as 'a=1', we want to execute it
367 367 # straight away.
368 368 if self.indent_spaces==0:
369 369 if len(self.source.splitlines()) <= 1:
370 370 return False
371 371
372 372 try:
373 373 code_ast = ast.parse(u''.join(self._buffer))
374 374 except Exception:
375 375 #print("Can't parse AST") # debug
376 376 return False
377 377 else:
378 378 if len(code_ast.body) == 1 and \
379 379 not hasattr(code_ast.body[0], 'body'):
380 380 #print("Simple statement") # debug
381 381 return False
382 382
383 383 # General fallback - accept more code
384 384 return True
385 385
386 386 #------------------------------------------------------------------------
387 387 # Private interface
388 388 #------------------------------------------------------------------------
389 389
390 390 def _find_indent(self, line):
391 391 """Compute the new indentation level for a single line.
392 392
393 393 Parameters
394 394 ----------
395 395 line : str
396 396 A single new line of non-whitespace, non-comment Python input.
397 397
398 398 Returns
399 399 -------
400 400 indent_spaces : int
401 401 New value for the indent level (it may be equal to self.indent_spaces
402 402 if indentation doesn't change.
403 403
404 404 full_dedent : boolean
405 405 Whether the new line causes a full flush-left dedent.
406 406 """
407 407 indent_spaces = self.indent_spaces
408 408 full_dedent = self._full_dedent
409 409
410 410 inisp = num_ini_spaces(line)
411 411 if inisp < indent_spaces:
412 412 indent_spaces = inisp
413 413 if indent_spaces <= 0:
414 414 #print 'Full dedent in text',self.source # dbg
415 415 full_dedent = True
416 416
417 417 if line.rstrip()[-1] == ':':
418 418 indent_spaces += 4
419 419 elif dedent_re.match(line):
420 420 indent_spaces -= 4
421 421 if indent_spaces <= 0:
422 422 full_dedent = True
423 423
424 424 # Safety
425 425 if indent_spaces < 0:
426 426 indent_spaces = 0
427 427 #print 'safety' # dbg
428 428
429 429 return indent_spaces, full_dedent
430 430
431 431 def _update_indent(self, lines):
432 432 for line in remove_comments(lines).splitlines():
433 433 if line and not line.isspace():
434 434 self.indent_spaces, self._full_dedent = self._find_indent(line)
435 435
436 436 def _store(self, lines, buffer=None, store='source'):
437 437 """Store one or more lines of input.
438 438
439 439 If input lines are not newline-terminated, a newline is automatically
440 440 appended."""
441 441
442 442 if buffer is None:
443 443 buffer = self._buffer
444 444
445 445 if lines.endswith('\n'):
446 446 buffer.append(lines)
447 447 else:
448 448 buffer.append(lines+'\n')
449 449 setattr(self, store, self._set_source(buffer))
450 450
451 451 def _set_source(self, buffer):
452 452 return u''.join(buffer)
453 453
454 454
455 455 class IPythonInputSplitter(InputSplitter):
456 456 """An input splitter that recognizes all of IPython's special syntax."""
457 457
458 458 # String with raw, untransformed input.
459 459 source_raw = ''
460 460
461 461 # Flag to track when a transformer has stored input that it hasn't given
462 462 # back yet.
463 463 transformer_accumulating = False
464 464
465 465 # Flag to track when assemble_python_lines has stored input that it hasn't
466 466 # given back yet.
467 467 within_python_line = False
468 468
469 469 # Private attributes
470 470
471 471 # List with lines of raw input accumulated so far.
472 472 _buffer_raw = None
473 473
474 474 def __init__(self, line_input_checker=True, physical_line_transforms=None,
475 475 logical_line_transforms=None, python_line_transforms=None):
476 476 super(IPythonInputSplitter, self).__init__()
477 477 self._buffer_raw = []
478 478 self._validate = True
479 479
480 480 if physical_line_transforms is not None:
481 481 self.physical_line_transforms = physical_line_transforms
482 482 else:
483 483 self.physical_line_transforms = [
484 484 leading_indent(),
485 485 classic_prompt(),
486 486 ipy_prompt(),
487 487 cellmagic(end_on_blank_line=line_input_checker),
488 488 strip_encoding_cookie(),
489 489 ]
490 490
491 491 self.assemble_logical_lines = assemble_logical_lines()
492 492 if logical_line_transforms is not None:
493 493 self.logical_line_transforms = logical_line_transforms
494 494 else:
495 495 self.logical_line_transforms = [
496 496 help_end(),
497 497 escaped_commands(),
498 498 assign_from_magic(),
499 499 assign_from_system(),
500 500 ]
501 501
502 502 self.assemble_python_lines = assemble_python_lines()
503 503 if python_line_transforms is not None:
504 504 self.python_line_transforms = python_line_transforms
505 505 else:
506 506 # We don't use any of these at present
507 507 self.python_line_transforms = []
508 508
509 509 @property
510 510 def transforms(self):
511 511 "Quick access to all transformers."
512 512 return self.physical_line_transforms + \
513 513 [self.assemble_logical_lines] + self.logical_line_transforms + \
514 514 [self.assemble_python_lines] + self.python_line_transforms
515 515
516 516 @property
517 517 def transforms_in_use(self):
518 518 """Transformers, excluding logical line transformers if we're in a
519 519 Python line."""
520 520 t = self.physical_line_transforms[:]
521 521 if not self.within_python_line:
522 522 t += [self.assemble_logical_lines] + self.logical_line_transforms
523 523 return t + [self.assemble_python_lines] + self.python_line_transforms
524 524
525 525 def reset(self):
526 526 """Reset the input buffer and associated state."""
527 527 super(IPythonInputSplitter, self).reset()
528 528 self._buffer_raw[:] = []
529 529 self.source_raw = ''
530 530 self.transformer_accumulating = False
531 531 self.within_python_line = False
532 532
533 533 for t in self.transforms:
534 534 try:
535 535 t.reset()
536 536 except SyntaxError:
537 537 # Nothing that calls reset() expects to handle transformer
538 538 # errors
539 539 pass
540 540
541 541 def flush_transformers(self):
542 542 def _flush(transform, outs):
543 543 """yield transformed lines
544 544
545 545 always strings, never None
546 546
547 547 transform: the current transform
548 548 outs: an iterable of previously transformed inputs.
549 549 Each may be multiline, which will be passed
550 550 one line at a time to transform.
551 551 """
552 552 for out in outs:
553 553 for line in out.splitlines():
554 554 # push one line at a time
555 555 tmp = transform.push(line)
556 556 if tmp is not None:
557 557 yield tmp
558 558
559 559 # reset the transform
560 560 tmp = transform.reset()
561 561 if tmp is not None:
562 562 yield tmp
563 563
564 564 out = []
565 565 for t in self.transforms_in_use:
566 566 out = _flush(t, out)
567 567
568 568 out = list(out)
569 569 if out:
570 570 self._store('\n'.join(out))
571 571
572 572 def raw_reset(self):
573 573 """Return raw input only and perform a full reset.
574 574 """
575 575 out = self.source_raw
576 576 self.reset()
577 577 return out
578 578
579 579 def source_reset(self):
580 580 try:
581 581 self.flush_transformers()
582 582 return self.source
583 583 finally:
584 584 self.reset()
585 585
586 586 def push_accepts_more(self):
587 587 if self.transformer_accumulating:
588 588 return True
589 589 else:
590 590 return super(IPythonInputSplitter, self).push_accepts_more()
591 591
592 592 def transform_cell(self, cell):
593 593 """Process and translate a cell of input.
594 594 """
595 595 self.reset()
596 596 try:
597 597 self.push(cell)
598 598 self.flush_transformers()
599 599 return self.source
600 600 finally:
601 601 self.reset()
602 602
603 603 def push(self, lines):
604 604 """Push one or more lines of IPython input.
605 605
606 606 This stores the given lines and returns a status code indicating
607 607 whether the code forms a complete Python block or not, after processing
608 608 all input lines for special IPython syntax.
609 609
610 610 Any exceptions generated in compilation are swallowed, but if an
611 611 exception was produced, the method returns True.
612 612
613 613 Parameters
614 614 ----------
615 615 lines : string
616 616 One or more lines of Python input.
617 617
618 618 Returns
619 619 -------
620 620 is_complete : boolean
621 621 True if the current input source (the result of the current input
622 622 plus prior inputs) forms a complete Python execution block. Note that
623 623 this value is also stored as a private attribute (_is_complete), so it
624 624 can be queried at any time.
625 625 """
626 626
627 627 # We must ensure all input is pure unicode
628 628 lines = cast_unicode(lines, self.encoding)
629 629 # ''.splitlines() --> [], but we need to push the empty line to transformers
630 630 lines_list = lines.splitlines()
631 631 if not lines_list:
632 632 lines_list = ['']
633 633
634 # interpet trailing newline as a blank line
635 if lines.endswith('\n'):
636 lines_list += ['']
637
638 634 # Store raw source before applying any transformations to it. Note
639 635 # that this must be done *after* the reset() call that would otherwise
640 636 # flush the buffer.
641 637 self._store(lines, self._buffer_raw, 'source_raw')
642 638
643 639 for line in lines_list:
644 640 out = self.push_line(line)
645 641
646 642 return out
647 643
648 644 def push_line(self, line):
649 645 buf = self._buffer
650 646
651 647 def _accumulating(dbg):
652 648 #print(dbg)
653 649 self.transformer_accumulating = True
654 650 return False
655 651
656 652 for transformer in self.physical_line_transforms:
657 653 line = transformer.push(line)
658 654 if line is None:
659 655 return _accumulating(transformer)
660 656
661 657 if not self.within_python_line:
662 658 line = self.assemble_logical_lines.push(line)
663 659 if line is None:
664 660 return _accumulating('acc logical line')
665 661
666 662 for transformer in self.logical_line_transforms:
667 663 line = transformer.push(line)
668 664 if line is None:
669 665 return _accumulating(transformer)
670 666
671 667 line = self.assemble_python_lines.push(line)
672 668 if line is None:
673 669 self.within_python_line = True
674 670 return _accumulating('acc python line')
675 671 else:
676 672 self.within_python_line = False
677 673
678 674 for transformer in self.python_line_transforms:
679 675 line = transformer.push(line)
680 676 if line is None:
681 677 return _accumulating(transformer)
682 678
683 679 #print("transformers clear") #debug
684 680 self.transformer_accumulating = False
685 681 return super(IPythonInputSplitter, self).push(line)
@@ -1,540 +1,540 b''
1 1 """IPython terminal interface using prompt_toolkit in place of readline"""
2 2 from __future__ import print_function
3 3
4 4 import os
5 5 import sys
6 6 import signal
7 7 from warnings import warn
8 8
9 9 from IPython.core.error import TryNext
10 10 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
11 11 from IPython.utils.py3compat import PY3, cast_unicode_py2, input
12 12 from IPython.utils.terminal import toggle_set_term_title, set_term_title
13 13 from IPython.utils.process import abbrev_cwd
14 14 from traitlets import Bool, Unicode, Dict, Integer, observe, Instance, Type, default
15 15
16 16 from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER, EditingMode
17 17 from prompt_toolkit.filters import HasFocus, HasSelection, Condition, ViInsertMode, EmacsInsertMode, IsDone, HasCompletions
18 18 from prompt_toolkit.history import InMemoryHistory
19 19 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout
20 20 from prompt_toolkit.interface import CommandLineInterface
21 21 from prompt_toolkit.key_binding.manager import KeyBindingManager
22 22 from prompt_toolkit.keys import Keys
23 23 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
24 24 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
25 25
26 26 from pygments.styles import get_style_by_name, get_all_styles
27 27 from pygments.token import Token
28 28
29 29 from .debugger import TerminalPdb, Pdb
30 30 from .magics import TerminalMagics
31 31 from .pt_inputhooks import get_inputhook_func
32 32 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
33 33 from .ptutils import IPythonPTCompleter, IPythonPTLexer
34 34
35 35
36 36 def get_default_editor():
37 37 try:
38 38 ed = os.environ['EDITOR']
39 39 if not PY3:
40 40 ed = ed.decode()
41 41 return ed
42 42 except KeyError:
43 43 pass
44 44 except UnicodeError:
45 45 warn("$EDITOR environment variable is not pure ASCII. Using platform "
46 46 "default editor.")
47 47
48 48 if os.name == 'posix':
49 49 return 'vi' # the only one guaranteed to be there!
50 50 else:
51 51 return 'notepad' # same in Windows!
52 52
53 53
54 54 if sys.stdin and sys.stdout and sys.stderr:
55 55 _is_tty = (sys.stdin.isatty()) and (sys.stdout.isatty()) and (sys.stderr.isatty())
56 56 else:
57 57 _is_tty = False
58 58
59 59
60 60 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
61 61
62 62 class TerminalInteractiveShell(InteractiveShell):
63 63 colors_force = True
64 64
65 65 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
66 66 'to reserve for the completion menu'
67 67 ).tag(config=True)
68 68
69 69 def _space_for_menu_changed(self, old, new):
70 70 self._update_layout()
71 71
72 72 pt_cli = None
73 73 debugger_history = None
74 74
75 75 simple_prompt = Bool(_use_simple_prompt,
76 76 help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors.
77 77
78 78 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
79 79 IPython own testing machinery, and emacs inferior-shell integration through elpy.
80 80
81 81 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
82 82 environment variable is set, or the current terminal is not a tty.
83 83
84 84 """
85 85 ).tag(config=True)
86 86
87 87 @property
88 88 def debugger_cls(self):
89 89 return Pdb if self.simple_prompt else TerminalPdb
90 90
91 91 autoedit_syntax = Bool(False,
92 92 help="auto editing of files with syntax errors.",
93 93 ).tag(config=True)
94 94
95 95
96 96 confirm_exit = Bool(True,
97 97 help="""
98 98 Set to confirm when you try to exit IPython with an EOF (Control-D
99 99 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
100 100 you can force a direct exit without any confirmation.""",
101 101 ).tag(config=True)
102 102
103 103 editing_mode = Unicode('emacs',
104 104 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
105 105 ).tag(config=True)
106 106
107 107 mouse_support = Bool(False,
108 108 help="Enable mouse support in the prompt"
109 109 ).tag(config=True)
110 110
111 111 highlighting_style = Unicode('default',
112 112 help="The name of a Pygments style to use for syntax highlighting: \n %s" % ', '.join(get_all_styles())
113 113 ).tag(config=True)
114 114
115 115
116 116 @observe('highlighting_style')
117 117 def _highlighting_style_changed(self, change):
118 118 self._style = self._make_style_from_name(self.highlighting_style)
119 119
120 120 highlighting_style_overrides = Dict(
121 121 help="Override highlighting format for specific tokens"
122 122 ).tag(config=True)
123 123
124 124 editor = Unicode(get_default_editor(),
125 125 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
126 126 ).tag(config=True)
127 127
128 128 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
129 129
130 130 prompts = Instance(Prompts)
131 131
132 132 @default('prompts')
133 133 def _prompts_default(self):
134 134 return self.prompts_class(self)
135 135
136 136 @observe('prompts')
137 137 def _(self, change):
138 138 self._update_layout()
139 139
140 140 @default('displayhook_class')
141 141 def _displayhook_class_default(self):
142 142 return RichPromptDisplayHook
143 143
144 144 term_title = Bool(True,
145 145 help="Automatically set the terminal title"
146 146 ).tag(config=True)
147 147
148 148 display_completions_in_columns = Bool(False,
149 149 help="Display a multi column completion menu.",
150 150 ).tag(config=True)
151 151
152 152 highlight_matching_brackets = Bool(True,
153 153 help="Highlight matching brackets .",
154 154 ).tag(config=True)
155 155
156 156 @observe('term_title')
157 157 def init_term_title(self, change=None):
158 158 # Enable or disable the terminal title.
159 159 if self.term_title:
160 160 toggle_set_term_title(True)
161 161 set_term_title('IPython: ' + abbrev_cwd())
162 162 else:
163 163 toggle_set_term_title(False)
164 164
165 165 def init_prompt_toolkit_cli(self):
166 166 self._app = None
167 167 if self.simple_prompt:
168 168 # Fall back to plain non-interactive output for tests.
169 169 # This is very limited, and only accepts a single line.
170 170 def prompt():
171 171 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
172 172 self.prompt_for_code = prompt
173 173 return
174 174
175 175 kbmanager = KeyBindingManager.for_prompt()
176 176 insert_mode = ViInsertMode() | EmacsInsertMode()
177 177 # Ctrl+J == Enter, seemingly
178 178 @kbmanager.registry.add_binding(Keys.ControlJ,
179 179 filter=(HasFocus(DEFAULT_BUFFER)
180 180 & ~HasSelection()
181 181 & insert_mode
182 182 ))
183 183 def _(event):
184 184 b = event.current_buffer
185 185 d = b.document
186 186
187 187 if b.complete_state:
188 188 cc = b.complete_state.current_completion
189 189 if cc:
190 190 b.apply_completion(cc)
191 191 return
192 192
193 193 if not (d.on_last_line or d.cursor_position_row >= d.line_count
194 194 - d.empty_line_count_at_the_end()):
195 195 b.newline()
196 196 return
197 197
198 status, indent = self.input_splitter.check_complete(d.text)
198 status, indent = self.input_splitter.check_complete(d.text + '\n')
199 199
200 200 if (status != 'incomplete') and b.accept_action.is_returnable:
201 201 b.accept_action.validate_and_handle(event.cli, b)
202 202 else:
203 203 b.insert_text('\n' + (' ' * (indent or 0)))
204 204
205 205 @kbmanager.registry.add_binding(Keys.ControlP, filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER)))
206 206 def _previous_history_or_previous_completion(event):
207 207 """
208 208 Control-P in vi edit mode on readline is history next, unlike default prompt toolkit.
209 209
210 210 If completer is open this still select previous completion.
211 211 """
212 212 event.current_buffer.auto_up()
213 213
214 214 @kbmanager.registry.add_binding(Keys.ControlN, filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER)))
215 215 def _next_history_or_next_completion(event):
216 216 """
217 217 Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit.
218 218
219 219 If completer is open this still select next completion.
220 220 """
221 221 event.current_buffer.auto_down()
222 222
223 223 @kbmanager.registry.add_binding(Keys.ControlG, filter=(
224 224 HasFocus(DEFAULT_BUFFER) & HasCompletions()
225 225 ))
226 226 def _dismiss_completion(event):
227 227 b = event.current_buffer
228 228 if b.complete_state:
229 229 b.cancel_completion()
230 230
231 231 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER))
232 232 def _reset_buffer(event):
233 233 b = event.current_buffer
234 234 if b.complete_state:
235 235 b.cancel_completion()
236 236 else:
237 237 b.reset()
238 238
239 239 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER))
240 240 def _reset_search_buffer(event):
241 241 if event.current_buffer.document.text:
242 242 event.current_buffer.reset()
243 243 else:
244 244 event.cli.push_focus(DEFAULT_BUFFER)
245 245
246 246 supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP'))
247 247
248 248 @kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend)
249 249 def _suspend_to_bg(event):
250 250 event.cli.suspend_to_background()
251 251
252 252 @Condition
253 253 def cursor_in_leading_ws(cli):
254 254 before = cli.application.buffer.document.current_line_before_cursor
255 255 return (not before) or before.isspace()
256 256
257 257 # Ctrl+I == Tab
258 258 @kbmanager.registry.add_binding(Keys.ControlI,
259 259 filter=(HasFocus(DEFAULT_BUFFER)
260 260 & ~HasSelection()
261 261 & insert_mode
262 262 & cursor_in_leading_ws
263 263 ))
264 264 def _indent_buffer(event):
265 265 event.current_buffer.insert_text(' ' * 4)
266 266
267 267 # Pre-populate history from IPython's history database
268 268 history = InMemoryHistory()
269 269 last_cell = u""
270 270 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
271 271 include_latest=True):
272 272 # Ignore blank lines and consecutive duplicates
273 273 cell = cell.rstrip()
274 274 if cell and (cell != last_cell):
275 275 history.append(cell)
276 276
277 277 self._style = self._make_style_from_name(self.highlighting_style)
278 278 style = DynamicStyle(lambda: self._style)
279 279
280 280 editing_mode = getattr(EditingMode, self.editing_mode.upper())
281 281
282 282 self._app = create_prompt_application(
283 283 editing_mode=editing_mode,
284 284 key_bindings_registry=kbmanager.registry,
285 285 history=history,
286 286 completer=IPythonPTCompleter(self.Completer),
287 287 enable_history_search=True,
288 288 style=style,
289 289 mouse_support=self.mouse_support,
290 290 **self._layout_options()
291 291 )
292 292 self._eventloop = create_eventloop(self.inputhook)
293 293 self.pt_cli = CommandLineInterface(self._app, eventloop=self._eventloop)
294 294
295 295 def _make_style_from_name(self, name):
296 296 """
297 297 Small wrapper that make an IPython compatible style from a style name
298 298
299 299 We need that to add style for prompt ... etc.
300 300 """
301 301 style_cls = get_style_by_name(name)
302 302 style_overrides = {
303 303 Token.Prompt: '#009900',
304 304 Token.PromptNum: '#00ff00 bold',
305 305 Token.OutPrompt: '#990000',
306 306 Token.OutPromptNum: '#ff0000 bold',
307 307 }
308 308 if name == 'default':
309 309 style_cls = get_style_by_name('default')
310 310 # The default theme needs to be visible on both a dark background
311 311 # and a light background, because we can't tell what the terminal
312 312 # looks like. These tweaks to the default theme help with that.
313 313 style_overrides.update({
314 314 Token.Number: '#007700',
315 315 Token.Operator: 'noinherit',
316 316 Token.String: '#BB6622',
317 317 Token.Name.Function: '#2080D0',
318 318 Token.Name.Class: 'bold #2080D0',
319 319 Token.Name.Namespace: 'bold #2080D0',
320 320 })
321 321 style_overrides.update(self.highlighting_style_overrides)
322 322 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
323 323 style_dict=style_overrides)
324 324
325 325 return style
326 326
327 327 def _layout_options(self):
328 328 """
329 329 Return the current layout option for the current Terminal InteractiveShell
330 330 """
331 331 return {
332 332 'lexer':IPythonPTLexer(),
333 333 'reserve_space_for_menu':self.space_for_menu,
334 334 'get_prompt_tokens':self.prompts.in_prompt_tokens,
335 335 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
336 336 'multiline':True,
337 337 'display_completions_in_columns': self.display_completions_in_columns,
338 338
339 339 # Highlight matching brackets, but only when this setting is
340 340 # enabled, and only when the DEFAULT_BUFFER has the focus.
341 341 'extra_input_processors': [ConditionalProcessor(
342 342 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
343 343 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
344 344 Condition(lambda cli: self.highlight_matching_brackets))],
345 345 }
346 346
347 347 def _update_layout(self):
348 348 """
349 349 Ask for a re computation of the application layout, if for example ,
350 350 some configuration options have changed.
351 351 """
352 352 if getattr(self, '._app', None):
353 353 self._app.layout = create_prompt_layout(**self._layout_options())
354 354
355 355 def prompt_for_code(self):
356 356 document = self.pt_cli.run(
357 357 pre_run=self.pre_prompt, reset_current_buffer=True)
358 358 return document.text
359 359
360 360 def init_io(self):
361 361 if sys.platform not in {'win32', 'cli'}:
362 362 return
363 363
364 364 import colorama
365 365 colorama.init()
366 366
367 367 # For some reason we make these wrappers around stdout/stderr.
368 368 # For now, we need to reset them so all output gets coloured.
369 369 # https://github.com/ipython/ipython/issues/8669
370 370 from IPython.utils import io
371 371 io.stdout = io.IOStream(sys.stdout)
372 372 io.stderr = io.IOStream(sys.stderr)
373 373
374 374 def init_magics(self):
375 375 super(TerminalInteractiveShell, self).init_magics()
376 376 self.register_magics(TerminalMagics)
377 377
378 378 def init_alias(self):
379 379 # The parent class defines aliases that can be safely used with any
380 380 # frontend.
381 381 super(TerminalInteractiveShell, self).init_alias()
382 382
383 383 # Now define aliases that only make sense on the terminal, because they
384 384 # need direct access to the console in a way that we can't emulate in
385 385 # GUI or web frontend
386 386 if os.name == 'posix':
387 387 for cmd in ['clear', 'more', 'less', 'man']:
388 388 self.alias_manager.soft_define_alias(cmd, cmd)
389 389
390 390
391 391 def __init__(self, *args, **kwargs):
392 392 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
393 393 self.init_prompt_toolkit_cli()
394 394 self.init_term_title()
395 395 self.keep_running = True
396 396
397 397 self.debugger_history = InMemoryHistory()
398 398
399 399 def ask_exit(self):
400 400 self.keep_running = False
401 401
402 402 rl_next_input = None
403 403
404 404 def pre_prompt(self):
405 405 if self.rl_next_input:
406 406 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
407 407 self.rl_next_input = None
408 408
409 409 def interact(self):
410 410 while self.keep_running:
411 411 print(self.separate_in, end='')
412 412
413 413 try:
414 414 code = self.prompt_for_code()
415 415 except EOFError:
416 416 if (not self.confirm_exit) \
417 417 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
418 418 self.ask_exit()
419 419
420 420 else:
421 421 if code:
422 422 self.run_cell(code, store_history=True)
423 423 if self.autoedit_syntax and self.SyntaxTB.last_syntax_error:
424 424 self.edit_syntax_error()
425 425
426 426 def mainloop(self):
427 427 # An extra layer of protection in case someone mashing Ctrl-C breaks
428 428 # out of our internal code.
429 429 while True:
430 430 try:
431 431 self.interact()
432 432 break
433 433 except KeyboardInterrupt:
434 434 print("\nKeyboardInterrupt escaped interact()\n")
435 435
436 436 if hasattr(self, '_eventloop'):
437 437 self._eventloop.close()
438 438
439 439 _inputhook = None
440 440 def inputhook(self, context):
441 441 if self._inputhook is not None:
442 442 self._inputhook(context)
443 443
444 444 def enable_gui(self, gui=None):
445 445 if gui:
446 446 self._inputhook = get_inputhook_func(gui)
447 447 else:
448 448 self._inputhook = None
449 449
450 450 # Methods to support auto-editing of SyntaxErrors:
451 451
452 452 def edit_syntax_error(self):
453 453 """The bottom half of the syntax error handler called in the main loop.
454 454
455 455 Loop until syntax error is fixed or user cancels.
456 456 """
457 457
458 458 while self.SyntaxTB.last_syntax_error:
459 459 # copy and clear last_syntax_error
460 460 err = self.SyntaxTB.clear_err_state()
461 461 if not self._should_recompile(err):
462 462 return
463 463 try:
464 464 # may set last_syntax_error again if a SyntaxError is raised
465 465 self.safe_execfile(err.filename, self.user_ns)
466 466 except:
467 467 self.showtraceback()
468 468 else:
469 469 try:
470 470 with open(err.filename) as f:
471 471 # This should be inside a display_trap block and I
472 472 # think it is.
473 473 sys.displayhook(f.read())
474 474 except:
475 475 self.showtraceback()
476 476
477 477 def _should_recompile(self, e):
478 478 """Utility routine for edit_syntax_error"""
479 479
480 480 if e.filename in ('<ipython console>', '<input>', '<string>',
481 481 '<console>', '<BackgroundJob compilation>',
482 482 None):
483 483 return False
484 484 try:
485 485 if (self.autoedit_syntax and
486 486 not self.ask_yes_no(
487 487 'Return to editor to correct syntax error? '
488 488 '[Y/n] ', 'y')):
489 489 return False
490 490 except EOFError:
491 491 return False
492 492
493 493 def int0(x):
494 494 try:
495 495 return int(x)
496 496 except TypeError:
497 497 return 0
498 498
499 499 # always pass integer line and offset values to editor hook
500 500 try:
501 501 self.hooks.fix_error_editor(e.filename,
502 502 int0(e.lineno), int0(e.offset),
503 503 e.msg)
504 504 except TryNext:
505 505 warn('Could not open editor')
506 506 return False
507 507 return True
508 508
509 509 # Run !system commands directly, not through pipes, so terminal programs
510 510 # work correctly.
511 511 system = InteractiveShell.system_raw
512 512
513 513 def auto_rewrite_input(self, cmd):
514 514 """Overridden from the parent class to use fancy rewriting prompt"""
515 515 if not self.show_rewritten_input:
516 516 return
517 517
518 518 tokens = self.prompts.rewrite_prompt_tokens()
519 519 if self.pt_cli:
520 520 self.pt_cli.print_tokens(tokens)
521 521 print(cmd)
522 522 else:
523 523 prompt = ''.join(s for t, s in tokens)
524 524 print(prompt, cmd, sep='')
525 525
526 526 _prompts_before = None
527 527 def switch_doctest_mode(self, mode):
528 528 """Switch prompts to classic for %doctest_mode"""
529 529 if mode:
530 530 self._prompts_before = self.prompts
531 531 self.prompts = ClassicPrompts(self)
532 532 elif self._prompts_before:
533 533 self.prompts = self._prompts_before
534 534 self._prompts_before = None
535 535
536 536
537 537 InteractiveShellABC.register(TerminalInteractiveShell)
538 538
539 539 if __name__ == '__main__':
540 540 TerminalInteractiveShell.instance().interact()
General Comments 0
You need to be logged in to leave comments. Login now