##// END OF EJS Templates
Simplify InputSplitter by stripping out input_mode distinction
Thomas Kluyver -
Show More
@@ -1,721 +1,661 b''
1 1 """Analysis of text input into executable blocks.
2 2
3 3 The main class in this module, :class:`InputSplitter`, is designed to break
4 4 input from either interactive, line-by-line environments or block-based ones,
5 5 into standalone blocks that can be executed by Python as 'single' statements
6 6 (thus triggering sys.displayhook).
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
11 11 For more details, see the class docstring below.
12 12
13 13 Syntax Transformations
14 14 ----------------------
15 15
16 16 One of the main jobs of the code in this file is to apply all syntax
17 17 transformations that make up 'the IPython language', i.e. magics, shell
18 18 escapes, etc. All transformations should be implemented as *fully stateless*
19 19 entities, that simply take one line as their input and return a line.
20 20 Internally for implementation purposes they may be a normal function or a
21 21 callable object, but the only input they receive will be a single line and they
22 22 should only return a line, without holding any data-dependent state between
23 23 calls.
24 24
25 25 As an example, the EscapedTransformer is a class so we can more clearly group
26 26 together the functionality of dispatching to individual functions based on the
27 27 starting escape character, but the only method for public use is its call
28 28 method.
29 29
30 30
31 31 ToDo
32 32 ----
33 33
34 34 - Should we make push() actually raise an exception once push_accepts_more()
35 35 returns False?
36 36
37 37 - Naming cleanups. The tr_* names aren't the most elegant, though now they are
38 38 at least just attributes of a class so not really very exposed.
39 39
40 40 - Think about the best way to support dynamic things: automagic, autocall,
41 41 macros, etc.
42 42
43 43 - Think of a better heuristic for the application of the transforms in
44 44 IPythonInputSplitter.push() than looking at the buffer ending in ':'. Idea:
45 45 track indentation change events (indent, dedent, nothing) and apply them only
46 46 if the indentation went up, but not otherwise.
47 47
48 48 - Think of the cleanest way for supporting user-specified transformations (the
49 49 user prefilters we had before).
50 50
51 51 Authors
52 52 -------
53 53
54 54 * Fernando Perez
55 55 * Brian Granger
56 56 """
57 57 #-----------------------------------------------------------------------------
58 58 # Copyright (C) 2010 The IPython Development Team
59 59 #
60 60 # Distributed under the terms of the BSD License. The full license is in
61 61 # the file COPYING, distributed as part of this software.
62 62 #-----------------------------------------------------------------------------
63 63
64 64 #-----------------------------------------------------------------------------
65 65 # Imports
66 66 #-----------------------------------------------------------------------------
67 67 # stdlib
68 68 import ast
69 69 import codeop
70 70 import re
71 71 import sys
72 72
73 73 # IPython modules
74 74 from IPython.core.splitinput import split_user_input, LineInfo
75 75 from IPython.utils.py3compat import cast_unicode
76 76 from IPython.core.inputtransformer import (leading_indent,
77 77 classic_prompt,
78 78 ipy_prompt,
79 79 cellmagic,
80 80 assemble_logical_lines,
81 81 help_end,
82 82 escaped_commands,
83 83 assign_from_magic,
84 84 assign_from_system,
85 85 assemble_python_lines,
86 86 )
87 87
88 88 # Temporary!
89 89 from IPython.core.inputtransformer import (ESC_SHELL, ESC_SH_CAP, ESC_HELP,
90 90 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,
91 91 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN, ESC_SEQUENCES)
92 92
93 93 #-----------------------------------------------------------------------------
94 94 # Utilities
95 95 #-----------------------------------------------------------------------------
96 96
97 97 # FIXME: These are general-purpose utilities that later can be moved to the
98 98 # general ward. Kept here for now because we're being very strict about test
99 99 # coverage with this code, and this lets us ensure that we keep 100% coverage
100 100 # while developing.
101 101
102 102 # compiled regexps for autoindent management
103 103 dedent_re = re.compile('|'.join([
104 104 r'^\s+raise(\s.*)?$', # raise statement (+ space + other stuff, maybe)
105 105 r'^\s+raise\([^\)]*\).*$', # wacky raise with immediate open paren
106 106 r'^\s+return(\s.*)?$', # normal return (+ space + other stuff, maybe)
107 107 r'^\s+return\([^\)]*\).*$', # wacky return with immediate open paren
108 108 r'^\s+pass\s*$', # pass (optionally followed by trailing spaces)
109 109 r'^\s+break\s*$', # break (optionally followed by trailing spaces)
110 110 r'^\s+continue\s*$', # continue (optionally followed by trailing spaces)
111 111 ]))
112 112 ini_spaces_re = re.compile(r'^([ \t\r\f\v]+)')
113 113
114 114 # regexp to match pure comment lines so we don't accidentally insert 'if 1:'
115 115 # before pure comments
116 116 comment_line_re = re.compile('^\s*\#')
117 117
118 118
119 119 def num_ini_spaces(s):
120 120 """Return the number of initial spaces in a string.
121 121
122 122 Note that tabs are counted as a single space. For now, we do *not* support
123 123 mixing of tabs and spaces in the user's input.
124 124
125 125 Parameters
126 126 ----------
127 127 s : string
128 128
129 129 Returns
130 130 -------
131 131 n : int
132 132 """
133 133
134 134 ini_spaces = ini_spaces_re.match(s)
135 135 if ini_spaces:
136 136 return ini_spaces.end()
137 137 else:
138 138 return 0
139 139
140 140 def last_blank(src):
141 141 """Determine if the input source ends in a blank.
142 142
143 143 A blank is either a newline or a line consisting of whitespace.
144 144
145 145 Parameters
146 146 ----------
147 147 src : string
148 148 A single or multiline string.
149 149 """
150 150 if not src: return False
151 151 ll = src.splitlines()[-1]
152 152 return (ll == '') or ll.isspace()
153 153
154 154
155 155 last_two_blanks_re = re.compile(r'\n\s*\n\s*$', re.MULTILINE)
156 156 last_two_blanks_re2 = re.compile(r'.+\n\s*\n\s+$', re.MULTILINE)
157 157
158 158 def last_two_blanks(src):
159 159 """Determine if the input source ends in two blanks.
160 160
161 161 A blank is either a newline or a line consisting of whitespace.
162 162
163 163 Parameters
164 164 ----------
165 165 src : string
166 166 A single or multiline string.
167 167 """
168 168 if not src: return False
169 169 # The logic here is tricky: I couldn't get a regexp to work and pass all
170 170 # the tests, so I took a different approach: split the source by lines,
171 171 # grab the last two and prepend '###\n' as a stand-in for whatever was in
172 172 # the body before the last two lines. Then, with that structure, it's
173 173 # possible to analyze with two regexps. Not the most elegant solution, but
174 174 # it works. If anyone tries to change this logic, make sure to validate
175 175 # the whole test suite first!
176 176 new_src = '\n'.join(['###\n'] + src.splitlines()[-2:])
177 177 return (bool(last_two_blanks_re.match(new_src)) or
178 178 bool(last_two_blanks_re2.match(new_src)) )
179 179
180 180
181 181 def remove_comments(src):
182 182 """Remove all comments from input source.
183 183
184 184 Note: comments are NOT recognized inside of strings!
185 185
186 186 Parameters
187 187 ----------
188 188 src : string
189 189 A single or multiline input string.
190 190
191 191 Returns
192 192 -------
193 193 String with all Python comments removed.
194 194 """
195 195
196 196 return re.sub('#.*', '', src)
197 197
198 198
199 199 def get_input_encoding():
200 200 """Return the default standard input encoding.
201 201
202 202 If sys.stdin has no encoding, 'ascii' is returned."""
203 203 # There are strange environments for which sys.stdin.encoding is None. We
204 204 # ensure that a valid encoding is returned.
205 205 encoding = getattr(sys.stdin, 'encoding', None)
206 206 if encoding is None:
207 207 encoding = 'ascii'
208 208 return encoding
209 209
210 210 #-----------------------------------------------------------------------------
211 211 # Classes and functions for normal Python syntax handling
212 212 #-----------------------------------------------------------------------------
213 213
214 214 class InputSplitter(object):
215 215 """An object that can accumulate lines of Python source before execution.
216 216
217 217 This object is designed to be fed python source line-by-line, using
218 218 :meth:`push`. It will return on each push whether the currently pushed
219 219 code could be executed already. In addition, it provides a method called
220 220 :meth:`push_accepts_more` that can be used to query whether more input
221 221 can be pushed into a single interactive block.
222 222
223 223 This is a simple example of how an interactive terminal-based client can use
224 224 this tool::
225 225
226 226 isp = InputSplitter()
227 227 while isp.push_accepts_more():
228 228 indent = ' '*isp.indent_spaces
229 229 prompt = '>>> ' + indent
230 230 line = indent + raw_input(prompt)
231 231 isp.push(line)
232 232 print 'Input source was:\n', isp.source_reset(),
233 233 """
234 234 # Number of spaces of indentation computed from input that has been pushed
235 235 # so far. This is the attributes callers should query to get the current
236 236 # indentation level, in order to provide auto-indent facilities.
237 237 indent_spaces = 0
238 238 # String, indicating the default input encoding. It is computed by default
239 239 # at initialization time via get_input_encoding(), but it can be reset by a
240 240 # client with specific knowledge of the encoding.
241 241 encoding = ''
242 242 # String where the current full source input is stored, properly encoded.
243 243 # Reading this attribute is the normal way of querying the currently pushed
244 244 # source code, that has been properly encoded.
245 245 source = ''
246 246 # Code object corresponding to the current source. It is automatically
247 247 # synced to the source, so it can be queried at any time to obtain the code
248 248 # object; it will be None if the source doesn't compile to valid Python.
249 249 code = None
250 # Input mode
251 input_mode = 'line'
252 250
253 251 # Private attributes
254 252
255 253 # List with lines of input accumulated so far
256 254 _buffer = None
257 255 # Command compiler
258 256 _compile = None
259 257 # Mark when input has changed indentation all the way back to flush-left
260 258 _full_dedent = False
261 259 # Boolean indicating whether the current block is complete
262 260 _is_complete = None
263 261
264 def __init__(self, input_mode=None):
262 def __init__(self):
265 263 """Create a new InputSplitter instance.
266
267 Parameters
268 ----------
269 input_mode : str
270
271 One of ['line', 'cell']; default is 'line'.
272
273 The input_mode parameter controls how new inputs are used when fed via
274 the :meth:`push` method:
275
276 - 'line': meant for line-oriented clients, inputs are appended one at a
277 time to the internal buffer and the whole buffer is compiled.
278
279 - 'cell': meant for clients that can edit multi-line 'cells' of text at
280 a time. A cell can contain one or more blocks that can be compile in
281 'single' mode by Python. In this mode, each new input new input
282 completely replaces all prior inputs. Cell mode is thus equivalent
283 to prepending a full reset() to every push() call.
284 264 """
285 265 self._buffer = []
286 266 self._compile = codeop.CommandCompiler()
287 267 self.encoding = get_input_encoding()
288 self.input_mode = InputSplitter.input_mode if input_mode is None \
289 else input_mode
290 268
291 269 def reset(self):
292 270 """Reset the input buffer and associated state."""
293 271 self.indent_spaces = 0
294 272 self._buffer[:] = []
295 273 self.source = ''
296 274 self.code = None
297 275 self._is_complete = False
298 276 self._full_dedent = False
299 277
300 278 def source_reset(self):
301 279 """Return the input source and perform a full reset.
302 280 """
303 281 out = self.source
304 282 self.reset()
305 283 return out
306 284
307 285 def push(self, lines):
308 286 """Push one or more lines of input.
309 287
310 288 This stores the given lines and returns a status code indicating
311 289 whether the code forms a complete Python block or not.
312 290
313 291 Any exceptions generated in compilation are swallowed, but if an
314 292 exception was produced, the method returns True.
315 293
316 294 Parameters
317 295 ----------
318 296 lines : string
319 297 One or more lines of Python input.
320 298
321 299 Returns
322 300 -------
323 301 is_complete : boolean
324 302 True if the current input source (the result of the current input
325 303 plus prior inputs) forms a complete Python execution block. Note that
326 304 this value is also stored as a private attribute (``_is_complete``), so it
327 305 can be queried at any time.
328 306 """
329 if self.input_mode == 'cell':
330 self.reset()
331
332 307 self._store(lines)
333 308 source = self.source
334 309
335 310 # Before calling _compile(), reset the code object to None so that if an
336 311 # exception is raised in compilation, we don't mislead by having
337 312 # inconsistent code/source attributes.
338 313 self.code, self._is_complete = None, None
339 314
340 315 # Honor termination lines properly
341 316 if source.endswith('\\\n'):
342 317 return False
343 318
344 319 self._update_indent(lines)
345 320 try:
346 321 self.code = self._compile(source, symbol="exec")
347 322 # Invalid syntax can produce any of a number of different errors from
348 323 # inside the compiler, so we have to catch them all. Syntax errors
349 324 # immediately produce a 'ready' block, so the invalid Python can be
350 325 # sent to the kernel for evaluation with possible ipython
351 326 # special-syntax conversion.
352 327 except (SyntaxError, OverflowError, ValueError, TypeError,
353 328 MemoryError):
354 329 self._is_complete = True
355 330 else:
356 331 # Compilation didn't produce any exceptions (though it may not have
357 332 # given a complete code object)
358 333 self._is_complete = self.code is not None
359 334
360 335 return self._is_complete
361 336
362 337 def push_accepts_more(self):
363 338 """Return whether a block of interactive input can accept more input.
364 339
365 340 This method is meant to be used by line-oriented frontends, who need to
366 341 guess whether a block is complete or not based solely on prior and
367 342 current input lines. The InputSplitter considers it has a complete
368 343 interactive block and will not accept more input only when either a
369 344 SyntaxError is raised, or *all* of the following are true:
370 345
371 346 1. The input compiles to a complete statement.
372 347
373 348 2. The indentation level is flush-left (because if we are indented,
374 349 like inside a function definition or for loop, we need to keep
375 350 reading new input).
376 351
377 352 3. There is one extra line consisting only of whitespace.
378 353
379 354 Because of condition #3, this method should be used only by
380 355 *line-oriented* frontends, since it means that intermediate blank lines
381 356 are not allowed in function definitions (or any other indented block).
382 357
383 358 If the current input produces a syntax error, this method immediately
384 359 returns False but does *not* raise the syntax error exception, as
385 360 typically clients will want to send invalid syntax to an execution
386 361 backend which might convert the invalid syntax into valid Python via
387 362 one of the dynamic IPython mechanisms.
388 363 """
389 364
390 365 # With incomplete input, unconditionally accept more
366 # A syntax error also sets _is_complete to True - see push()
391 367 if not self._is_complete:
368 #print("Not complete") # debug
392 369 return True
393 370
394 # If we already have complete input and we're flush left, the answer
395 # depends. In line mode, if there hasn't been any indentation,
396 # that's it. If we've come back from some indentation, we need
397 # the blank final line to finish.
398 # In cell mode, we need to check how many blocks the input so far
399 # compiles into, because if there's already more than one full
400 # independent block of input, then the client has entered full
401 # 'cell' mode and is feeding lines that each is complete. In this
402 # case we should then keep accepting. The Qt terminal-like console
403 # does precisely this, to provide the convenience of terminal-like
404 # input of single expressions, but allowing the user (with a
405 # separate keystroke) to switch to 'cell' mode and type multiple
406 # expressions in one shot.
407 if self.indent_spaces==0:
408 if self.input_mode=='line':
409 if not self._full_dedent:
371 # The user can make any (complete) input execute by leaving a blank line
372 last_line = self.source.splitlines()[-1]
373 if (not last_line) or last_line.isspace():
374 #print("Blank line") # debug
410 375 return False
411 else:
376
377 # If there's just a single AST node, and we're flush left, as is the
378 # case after a simple statement such as 'a=1', we want to execute it
379 # straight away.
380 if self.indent_spaces==0:
412 381 try:
413 382 code_ast = ast.parse(u''.join(self._buffer))
414 383 except Exception:
384 #print("Can't parse AST") # debug
415 385 return False
416 386 else:
417 if len(code_ast.body) == 1:
387 if len(code_ast.body) == 1 and \
388 not hasattr(code_ast.body[0], 'body'):
389 #print("Simple statement") # debug
418 390 return False
419 391
420 # When input is complete, then termination is marked by an extra blank
421 # line at the end.
422 last_line = self.source.splitlines()[-1]
423 return bool(last_line and not last_line.isspace())
392 # General fallback - accept more code
393 return True
424 394
425 395 #------------------------------------------------------------------------
426 396 # Private interface
427 397 #------------------------------------------------------------------------
428 398
429 399 def _find_indent(self, line):
430 400 """Compute the new indentation level for a single line.
431 401
432 402 Parameters
433 403 ----------
434 404 line : str
435 405 A single new line of non-whitespace, non-comment Python input.
436 406
437 407 Returns
438 408 -------
439 409 indent_spaces : int
440 410 New value for the indent level (it may be equal to self.indent_spaces
441 411 if indentation doesn't change.
442 412
443 413 full_dedent : boolean
444 414 Whether the new line causes a full flush-left dedent.
445 415 """
446 416 indent_spaces = self.indent_spaces
447 417 full_dedent = self._full_dedent
448 418
449 419 inisp = num_ini_spaces(line)
450 420 if inisp < indent_spaces:
451 421 indent_spaces = inisp
452 422 if indent_spaces <= 0:
453 423 #print 'Full dedent in text',self.source # dbg
454 424 full_dedent = True
455 425
456 426 if line.rstrip()[-1] == ':':
457 427 indent_spaces += 4
458 428 elif dedent_re.match(line):
459 429 indent_spaces -= 4
460 430 if indent_spaces <= 0:
461 431 full_dedent = True
462 432
463 433 # Safety
464 434 if indent_spaces < 0:
465 435 indent_spaces = 0
466 436 #print 'safety' # dbg
467 437
468 438 return indent_spaces, full_dedent
469 439
470 440 def _update_indent(self, lines):
471 441 for line in remove_comments(lines).splitlines():
472 442 if line and not line.isspace():
473 443 self.indent_spaces, self._full_dedent = self._find_indent(line)
474 444
475 445 def _store(self, lines, buffer=None, store='source'):
476 446 """Store one or more lines of input.
477 447
478 448 If input lines are not newline-terminated, a newline is automatically
479 449 appended."""
480 450
481 451 if buffer is None:
482 452 buffer = self._buffer
483 453
484 454 if lines.endswith('\n'):
485 455 buffer.append(lines)
486 456 else:
487 457 buffer.append(lines+'\n')
488 458 setattr(self, store, self._set_source(buffer))
489 459
490 460 def _set_source(self, buffer):
491 461 return u''.join(buffer)
492 462
493 463
494 464 class IPythonInputSplitter(InputSplitter):
495 465 """An input splitter that recognizes all of IPython's special syntax."""
496 466
497 467 # String with raw, untransformed input.
498 468 source_raw = ''
499 469
500 470 # Flag to track when a transformer has stored input that it hasn't given
501 471 # back yet.
502 472 transformer_accumulating = False
503 473
504 474 # Flag to track when assemble_python_lines has stored input that it hasn't
505 475 # given back yet.
506 476 within_python_line = False
507 477
508 478 # Private attributes
509 479
510 480 # List with lines of raw input accumulated so far.
511 481 _buffer_raw = None
512 482
513 def __init__(self, input_mode=None, physical_line_transforms=None,
483 def __init__(self, physical_line_transforms=None,
514 484 logical_line_transforms=None, python_line_transforms=None):
515 super(IPythonInputSplitter, self).__init__(input_mode)
485 super(IPythonInputSplitter, self).__init__()
516 486 self._buffer_raw = []
517 487 self._validate = True
518 488
519 489 if physical_line_transforms is not None:
520 490 self.physical_line_transforms = physical_line_transforms
521 491 else:
522 492 self.physical_line_transforms = [leading_indent(),
523 493 classic_prompt(),
524 494 ipy_prompt(),
525 495 ]
526 496
527 497 self.assemble_logical_lines = assemble_logical_lines()
528 498 if logical_line_transforms is not None:
529 499 self.logical_line_transforms = logical_line_transforms
530 500 else:
531 501 self.logical_line_transforms = [cellmagic(),
532 502 help_end(),
533 503 escaped_commands(),
534 504 assign_from_magic(),
535 505 assign_from_system(),
536 506 ]
537 507
538 508 self.assemble_python_lines = assemble_python_lines()
539 509 if python_line_transforms is not None:
540 510 self.python_line_transforms = python_line_transforms
541 511 else:
542 512 # We don't use any of these at present
543 513 self.python_line_transforms = []
544 514
545 515 @property
546 516 def transforms(self):
547 517 "Quick access to all transformers."
548 518 return self.physical_line_transforms + \
549 519 [self.assemble_logical_lines] + self.logical_line_transforms + \
550 520 [self.assemble_python_lines] + self.python_line_transforms
551 521
552 522 @property
553 523 def transforms_in_use(self):
554 524 """Transformers, excluding logical line transformers if we're in a
555 525 Python line."""
556 526 t = self.physical_line_transforms[:]
557 527 if not self.within_python_line:
558 528 t += [self.assemble_logical_lines] + self.logical_line_transforms
559 529 return t + [self.assemble_python_lines] + self.python_line_transforms
560 530
561 531 def reset(self):
562 532 """Reset the input buffer and associated state."""
563 533 super(IPythonInputSplitter, self).reset()
564 534 self._buffer_raw[:] = []
565 535 self.source_raw = ''
566 536 self.transformer_accumulating = False
567 537 self.within_python_line = False
568 538 for t in self.transforms:
569 539 t.reset()
570 540
571 541 def flush_transformers(self):
572 542 def _flush(transform, out):
573 543 if out is not None:
574 544 tmp = transform.push(out)
575 545 return tmp or transform.reset() or None
576 546 else:
577 547 return transform.reset() or None
578 548
579 549 out = None
580 550 for t in self.transforms_in_use:
581 551 out = _flush(t, out)
582 552
583 553 if out is not None:
584 554 self._store(out)
585 555
586 556 def source_raw_reset(self):
587 557 """Return input and raw source and perform a full reset.
588 558 """
589 559 self.flush_transformers()
590 560 out = self.source
591 561 out_r = self.source_raw
592 562 self.reset()
593 563 return out, out_r
594 564
595 565 def source_reset(self):
596 566 self.flush_transformers()
597 567 return super(IPythonInputSplitter, self).source_reset()
598 568
599 569 def push_accepts_more(self):
600 570 if self.transformer_accumulating:
601 571 return True
602 572 else:
603 573 return super(IPythonInputSplitter, self).push_accepts_more()
604 574
605 575 def transform_cell(self, cell):
606 576 """Process and translate a cell of input.
607 577 """
608 578 self.reset()
609 579 self.push(cell)
610 580 return self.source_reset()
611 581
612 582 def push(self, lines):
613 583 """Push one or more lines of IPython input.
614 584
615 585 This stores the given lines and returns a status code indicating
616 586 whether the code forms a complete Python block or not, after processing
617 587 all input lines for special IPython syntax.
618 588
619 589 Any exceptions generated in compilation are swallowed, but if an
620 590 exception was produced, the method returns True.
621 591
622 592 Parameters
623 593 ----------
624 594 lines : string
625 595 One or more lines of Python input.
626 596
627 597 Returns
628 598 -------
629 599 is_complete : boolean
630 600 True if the current input source (the result of the current input
631 601 plus prior inputs) forms a complete Python execution block. Note that
632 602 this value is also stored as a private attribute (_is_complete), so it
633 603 can be queried at any time.
634 604 """
635 605
636 606 # We must ensure all input is pure unicode
637 607 lines = cast_unicode(lines, self.encoding)
638 608
639 609 # ''.splitlines() --> [], but we need to push the empty line to transformers
640 610 lines_list = lines.splitlines()
641 611 if not lines_list:
642 612 lines_list = ['']
643 613
644 # Transform logic
645 #
646 # We only apply the line transformers to the input if we have either no
647 # input yet, or complete input, or if the last line of the buffer ends
648 # with ':' (opening an indented block). This prevents the accidental
649 # transformation of escapes inside multiline expressions like
650 # triple-quoted strings or parenthesized expressions.
651 #
652 # The last heuristic, while ugly, ensures that the first line of an
653 # indented block is correctly transformed.
654 #
655 # FIXME: try to find a cleaner approach for this last bit.
656
657 # If we were in 'block' mode, since we're going to pump the parent
658 # class by hand line by line, we need to temporarily switch out to
659 # 'line' mode, do a single manual reset and then feed the lines one
660 # by one. Note that this only matters if the input has more than one
661 # line.
662 changed_input_mode = False
663
664 if self.input_mode == 'cell':
665 self.reset()
666 changed_input_mode = True
667 saved_input_mode = 'cell'
668 self.input_mode = 'line'
669
670 614 # Store raw source before applying any transformations to it. Note
671 615 # that this must be done *after* the reset() call that would otherwise
672 616 # flush the buffer.
673 617 self._store(lines, self._buffer_raw, 'source_raw')
674 618
675 try:
676 619 for line in lines_list:
677 620 out = self.push_line(line)
678 finally:
679 if changed_input_mode:
680 self.input_mode = saved_input_mode
681 621
682 622 return out
683 623
684 624 def push_line(self, line):
685 625 buf = self._buffer
686 626
687 627 def _accumulating(dbg):
688 628 #print(dbg)
689 629 self.transformer_accumulating = True
690 630 return False
691 631
692 632 for transformer in self.physical_line_transforms:
693 633 line = transformer.push(line)
694 634 if line is None:
695 635 return _accumulating(transformer)
696 636
697 637 if not self.within_python_line:
698 638 line = self.assemble_logical_lines.push(line)
699 639 if line is None:
700 640 return _accumulating('acc logical line')
701 641
702 642 for transformer in self.logical_line_transforms:
703 643 line = transformer.push(line)
704 644 if line is None:
705 645 return _accumulating(transformer)
706 646
707 647 line = self.assemble_python_lines.push(line)
708 648 if line is None:
709 649 self.within_python_line = True
710 650 return _accumulating('acc python line')
711 651 else:
712 652 self.within_python_line = False
713 653
714 654 for transformer in self.python_line_transforms:
715 655 line = transformer.push(line)
716 656 if line is None:
717 657 return _accumulating(transformer)
718 658
719 659 #print("transformers clear") #debug
720 660 self.transformer_accumulating = False
721 661 return super(IPythonInputSplitter, self).push(line)
@@ -1,626 +1,576 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Tests for the inputsplitter module.
3 3
4 4 Authors
5 5 -------
6 6 * Fernando Perez
7 7 * Robert Kern
8 8 """
9 9 #-----------------------------------------------------------------------------
10 10 # Copyright (C) 2010-2011 The IPython Development Team
11 11 #
12 12 # Distributed under the terms of the BSD License. The full license is in
13 13 # the file COPYING, distributed as part of this software.
14 14 #-----------------------------------------------------------------------------
15 15
16 16 #-----------------------------------------------------------------------------
17 17 # Imports
18 18 #-----------------------------------------------------------------------------
19 19 # stdlib
20 20 import unittest
21 21 import sys
22 22
23 23 # Third party
24 24 import nose.tools as nt
25 25
26 26 # Our own
27 27 from IPython.core import inputsplitter as isp
28 28 from IPython.core.tests.test_inputtransformer import syntax, syntax_ml
29 29 from IPython.testing import tools as tt
30 30 from IPython.utils import py3compat
31 31
32 32 #-----------------------------------------------------------------------------
33 33 # Semi-complete examples (also used as tests)
34 34 #-----------------------------------------------------------------------------
35 35
36 36 # Note: at the bottom, there's a slightly more complete version of this that
37 37 # can be useful during development of code here.
38 38
39 39 def mini_interactive_loop(input_func):
40 40 """Minimal example of the logic of an interactive interpreter loop.
41 41
42 42 This serves as an example, and it is used by the test system with a fake
43 43 raw_input that simulates interactive input."""
44 44
45 45 from IPython.core.inputsplitter import InputSplitter
46 46
47 47 isp = InputSplitter()
48 48 # In practice, this input loop would be wrapped in an outside loop to read
49 49 # input indefinitely, until some exit/quit command was issued. Here we
50 50 # only illustrate the basic inner loop.
51 51 while isp.push_accepts_more():
52 52 indent = ' '*isp.indent_spaces
53 53 prompt = '>>> ' + indent
54 54 line = indent + input_func(prompt)
55 55 isp.push(line)
56 56
57 57 # Here we just return input so we can use it in a test suite, but a real
58 58 # interpreter would instead send it for execution somewhere.
59 59 src = isp.source_reset()
60 60 #print 'Input source was:\n', src # dbg
61 61 return src
62 62
63 63 #-----------------------------------------------------------------------------
64 64 # Test utilities, just for local use
65 65 #-----------------------------------------------------------------------------
66 66
67 67 def assemble(block):
68 68 """Assemble a block into multi-line sub-blocks."""
69 69 return ['\n'.join(sub_block)+'\n' for sub_block in block]
70 70
71 71
72 72 def pseudo_input(lines):
73 73 """Return a function that acts like raw_input but feeds the input list."""
74 74 ilines = iter(lines)
75 75 def raw_in(prompt):
76 76 try:
77 77 return next(ilines)
78 78 except StopIteration:
79 79 return ''
80 80 return raw_in
81 81
82 82 #-----------------------------------------------------------------------------
83 83 # Tests
84 84 #-----------------------------------------------------------------------------
85 85 def test_spaces():
86 86 tests = [('', 0),
87 87 (' ', 1),
88 88 ('\n', 0),
89 89 (' \n', 1),
90 90 ('x', 0),
91 91 (' x', 1),
92 92 (' x',2),
93 93 (' x',4),
94 94 # Note: tabs are counted as a single whitespace!
95 95 ('\tx', 1),
96 96 ('\t x', 2),
97 97 ]
98 98 tt.check_pairs(isp.num_ini_spaces, tests)
99 99
100 100
101 101 def test_remove_comments():
102 102 tests = [('text', 'text'),
103 103 ('text # comment', 'text '),
104 104 ('text # comment\n', 'text \n'),
105 105 ('text # comment \n', 'text \n'),
106 106 ('line # c \nline\n','line \nline\n'),
107 107 ('line # c \nline#c2 \nline\nline #c\n\n',
108 108 'line \nline\nline\nline \n\n'),
109 109 ]
110 110 tt.check_pairs(isp.remove_comments, tests)
111 111
112 112
113 113 def test_get_input_encoding():
114 114 encoding = isp.get_input_encoding()
115 115 nt.assert_true(isinstance(encoding, basestring))
116 116 # simple-minded check that at least encoding a simple string works with the
117 117 # encoding we got.
118 118 nt.assert_equal(u'test'.encode(encoding), b'test')
119 119
120 120
121 121 class NoInputEncodingTestCase(unittest.TestCase):
122 122 def setUp(self):
123 123 self.old_stdin = sys.stdin
124 124 class X: pass
125 125 fake_stdin = X()
126 126 sys.stdin = fake_stdin
127 127
128 128 def test(self):
129 129 # Verify that if sys.stdin has no 'encoding' attribute we do the right
130 130 # thing
131 131 enc = isp.get_input_encoding()
132 132 self.assertEqual(enc, 'ascii')
133 133
134 134 def tearDown(self):
135 135 sys.stdin = self.old_stdin
136 136
137 137
138 138 class InputSplitterTestCase(unittest.TestCase):
139 139 def setUp(self):
140 140 self.isp = isp.InputSplitter()
141 141
142 142 def test_reset(self):
143 143 isp = self.isp
144 144 isp.push('x=1')
145 145 isp.reset()
146 146 self.assertEqual(isp._buffer, [])
147 147 self.assertEqual(isp.indent_spaces, 0)
148 148 self.assertEqual(isp.source, '')
149 149 self.assertEqual(isp.code, None)
150 150 self.assertEqual(isp._is_complete, False)
151 151
152 152 def test_source(self):
153 153 self.isp._store('1')
154 154 self.isp._store('2')
155 155 self.assertEqual(self.isp.source, '1\n2\n')
156 156 self.assertTrue(len(self.isp._buffer)>0)
157 157 self.assertEqual(self.isp.source_reset(), '1\n2\n')
158 158 self.assertEqual(self.isp._buffer, [])
159 159 self.assertEqual(self.isp.source, '')
160 160
161 161 def test_indent(self):
162 162 isp = self.isp # shorthand
163 163 isp.push('x=1')
164 164 self.assertEqual(isp.indent_spaces, 0)
165 165 isp.push('if 1:\n x=1')
166 166 self.assertEqual(isp.indent_spaces, 4)
167 167 isp.push('y=2\n')
168 168 self.assertEqual(isp.indent_spaces, 0)
169 169
170 170 def test_indent2(self):
171 # In cell mode, inputs must be fed in whole blocks, so skip this test
172 if self.isp.input_mode == 'cell': return
173
174 171 isp = self.isp
175 172 isp.push('if 1:')
176 173 self.assertEqual(isp.indent_spaces, 4)
177 174 isp.push(' x=1')
178 175 self.assertEqual(isp.indent_spaces, 4)
179 176 # Blank lines shouldn't change the indent level
180 177 isp.push(' '*2)
181 178 self.assertEqual(isp.indent_spaces, 4)
182 179
183 180 def test_indent3(self):
184 # In cell mode, inputs must be fed in whole blocks, so skip this test
185 if self.isp.input_mode == 'cell': return
186
187 181 isp = self.isp
188 182 # When a multiline statement contains parens or multiline strings, we
189 183 # shouldn't get confused.
190 184 isp.push("if 1:")
191 185 isp.push(" x = (1+\n 2)")
192 186 self.assertEqual(isp.indent_spaces, 4)
193 187
194 188 def test_indent4(self):
195 # In cell mode, inputs must be fed in whole blocks, so skip this test
196 if self.isp.input_mode == 'cell': return
197
198 189 isp = self.isp
199 190 # whitespace after ':' should not screw up indent level
200 191 isp.push('if 1: \n x=1')
201 192 self.assertEqual(isp.indent_spaces, 4)
202 193 isp.push('y=2\n')
203 194 self.assertEqual(isp.indent_spaces, 0)
204 195 isp.push('if 1:\t\n x=1')
205 196 self.assertEqual(isp.indent_spaces, 4)
206 197 isp.push('y=2\n')
207 198 self.assertEqual(isp.indent_spaces, 0)
208 199
209 200 def test_dedent_pass(self):
210 201 isp = self.isp # shorthand
211 202 # should NOT cause dedent
212 203 isp.push('if 1:\n passes = 5')
213 204 self.assertEqual(isp.indent_spaces, 4)
214 205 isp.push('if 1:\n pass')
215 206 self.assertEqual(isp.indent_spaces, 0)
216 207 isp.push('if 1:\n pass ')
217 208 self.assertEqual(isp.indent_spaces, 0)
218 209
219 210 def test_dedent_break(self):
220 211 isp = self.isp # shorthand
221 212 # should NOT cause dedent
222 213 isp.push('while 1:\n breaks = 5')
223 214 self.assertEqual(isp.indent_spaces, 4)
224 215 isp.push('while 1:\n break')
225 216 self.assertEqual(isp.indent_spaces, 0)
226 217 isp.push('while 1:\n break ')
227 218 self.assertEqual(isp.indent_spaces, 0)
228 219
229 220 def test_dedent_continue(self):
230 221 isp = self.isp # shorthand
231 222 # should NOT cause dedent
232 223 isp.push('while 1:\n continues = 5')
233 224 self.assertEqual(isp.indent_spaces, 4)
234 225 isp.push('while 1:\n continue')
235 226 self.assertEqual(isp.indent_spaces, 0)
236 227 isp.push('while 1:\n continue ')
237 228 self.assertEqual(isp.indent_spaces, 0)
238 229
239 230 def test_dedent_raise(self):
240 231 isp = self.isp # shorthand
241 232 # should NOT cause dedent
242 233 isp.push('if 1:\n raised = 4')
243 234 self.assertEqual(isp.indent_spaces, 4)
244 235 isp.push('if 1:\n raise TypeError()')
245 236 self.assertEqual(isp.indent_spaces, 0)
246 237 isp.push('if 1:\n raise')
247 238 self.assertEqual(isp.indent_spaces, 0)
248 239 isp.push('if 1:\n raise ')
249 240 self.assertEqual(isp.indent_spaces, 0)
250 241
251 242 def test_dedent_return(self):
252 243 isp = self.isp # shorthand
253 244 # should NOT cause dedent
254 245 isp.push('if 1:\n returning = 4')
255 246 self.assertEqual(isp.indent_spaces, 4)
256 247 isp.push('if 1:\n return 5 + 493')
257 248 self.assertEqual(isp.indent_spaces, 0)
258 249 isp.push('if 1:\n return')
259 250 self.assertEqual(isp.indent_spaces, 0)
260 251 isp.push('if 1:\n return ')
261 252 self.assertEqual(isp.indent_spaces, 0)
262 253 isp.push('if 1:\n return(0)')
263 254 self.assertEqual(isp.indent_spaces, 0)
264 255
265 256 def test_push(self):
266 257 isp = self.isp
267 258 self.assertTrue(isp.push('x=1'))
268 259
269 260 def test_push2(self):
270 261 isp = self.isp
271 262 self.assertFalse(isp.push('if 1:'))
272 263 for line in [' x=1', '# a comment', ' y=2']:
273 264 print(line)
274 265 self.assertTrue(isp.push(line))
275 266
276 267 def test_push3(self):
277 268 isp = self.isp
278 269 isp.push('if True:')
279 270 isp.push(' a = 1')
280 271 self.assertFalse(isp.push('b = [1,'))
281 272
282 def test_replace_mode(self):
283 isp = self.isp
284 isp.input_mode = 'cell'
285 isp.push('x=1')
286 self.assertEqual(isp.source, 'x=1\n')
287 isp.push('x=2')
288 self.assertEqual(isp.source, 'x=2\n')
289
290 273 def test_push_accepts_more(self):
291 274 isp = self.isp
292 275 isp.push('x=1')
293 276 self.assertFalse(isp.push_accepts_more())
294 277
295 278 def test_push_accepts_more2(self):
296 # In cell mode, inputs must be fed in whole blocks, so skip this test
297 if self.isp.input_mode == 'cell': return
298
299 279 isp = self.isp
300 280 isp.push('if 1:')
301 281 self.assertTrue(isp.push_accepts_more())
302 282 isp.push(' x=1')
303 283 self.assertTrue(isp.push_accepts_more())
304 284 isp.push('')
305 285 self.assertFalse(isp.push_accepts_more())
306 286
307 287 def test_push_accepts_more3(self):
308 288 isp = self.isp
309 289 isp.push("x = (2+\n3)")
310 290 self.assertFalse(isp.push_accepts_more())
311 291
312 292 def test_push_accepts_more4(self):
313 # In cell mode, inputs must be fed in whole blocks, so skip this test
314 if self.isp.input_mode == 'cell': return
315
316 293 isp = self.isp
317 294 # When a multiline statement contains parens or multiline strings, we
318 295 # shouldn't get confused.
319 296 # FIXME: we should be able to better handle de-dents in statements like
320 297 # multiline strings and multiline expressions (continued with \ or
321 298 # parens). Right now we aren't handling the indentation tracking quite
322 299 # correctly with this, though in practice it may not be too much of a
323 300 # problem. We'll need to see.
324 301 isp.push("if 1:")
325 302 isp.push(" x = (2+")
326 303 isp.push(" 3)")
327 304 self.assertTrue(isp.push_accepts_more())
328 305 isp.push(" y = 3")
329 306 self.assertTrue(isp.push_accepts_more())
330 307 isp.push('')
331 308 self.assertFalse(isp.push_accepts_more())
332 309
333 310 def test_push_accepts_more5(self):
334 # In cell mode, inputs must be fed in whole blocks, so skip this test
335 if self.isp.input_mode == 'cell': return
336
337 311 isp = self.isp
338 312 isp.push('try:')
339 313 isp.push(' a = 5')
340 314 isp.push('except:')
341 315 isp.push(' raise')
316 # We want to be able to add an else: block at this point, so it should
317 # wait for a blank line.
342 318 self.assertTrue(isp.push_accepts_more())
343 319
344 320 def test_continuation(self):
345 321 isp = self.isp
346 322 isp.push("import os, \\")
347 323 self.assertTrue(isp.push_accepts_more())
348 324 isp.push("sys")
349 325 self.assertFalse(isp.push_accepts_more())
350 326
351 327 def test_syntax_error(self):
352 328 isp = self.isp
353 329 # Syntax errors immediately produce a 'ready' block, so the invalid
354 330 # Python can be sent to the kernel for evaluation with possible ipython
355 331 # special-syntax conversion.
356 332 isp.push('run foo')
357 333 self.assertFalse(isp.push_accepts_more())
358 334
359 335 def test_unicode(self):
360 336 self.isp.push(u"PΓ©rez")
361 337 self.isp.push(u'\xc3\xa9')
362 338 self.isp.push(u"u'\xc3\xa9'")
363 339
364 340 def test_line_continuation(self):
365 341 """ Test issue #2108."""
366 342 isp = self.isp
367 343 # A blank line after a line continuation should not accept more
368 344 isp.push("1 \\\n\n")
369 345 self.assertFalse(isp.push_accepts_more())
370 346 # Whitespace after a \ is a SyntaxError. The only way to test that
371 347 # here is to test that push doesn't accept more (as with
372 348 # test_syntax_error() above).
373 349 isp.push(r"1 \ ")
374 350 self.assertFalse(isp.push_accepts_more())
375 351 # Even if the line is continuable (c.f. the regular Python
376 352 # interpreter)
377 353 isp.push(r"(1 \ ")
378 354 self.assertFalse(isp.push_accepts_more())
379 355
380 356 class InteractiveLoopTestCase(unittest.TestCase):
381 357 """Tests for an interactive loop like a python shell.
382 358 """
383 359 def check_ns(self, lines, ns):
384 360 """Validate that the given input lines produce the resulting namespace.
385 361
386 362 Note: the input lines are given exactly as they would be typed in an
387 363 auto-indenting environment, as mini_interactive_loop above already does
388 364 auto-indenting and prepends spaces to the input.
389 365 """
390 366 src = mini_interactive_loop(pseudo_input(lines))
391 367 test_ns = {}
392 368 exec src in test_ns
393 369 # We can't check that the provided ns is identical to the test_ns,
394 370 # because Python fills test_ns with extra keys (copyright, etc). But
395 371 # we can check that the given dict is *contained* in test_ns
396 372 for k,v in ns.iteritems():
397 373 self.assertEqual(test_ns[k], v)
398 374
399 375 def test_simple(self):
400 376 self.check_ns(['x=1'], dict(x=1))
401 377
402 378 def test_simple2(self):
403 379 self.check_ns(['if 1:', 'x=2'], dict(x=2))
404 380
405 381 def test_xy(self):
406 382 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
407 383
408 384 def test_abc(self):
409 385 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
410 386
411 387 def test_multi(self):
412 388 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
413 389
414 390
415 391 def test_LineInfo():
416 392 """Simple test for LineInfo construction and str()"""
417 393 linfo = isp.LineInfo(' %cd /home')
418 394 nt.assert_equal(str(linfo), 'LineInfo [ |%|cd|/home]')
419 395
420 396
421 397
422 398
423 399 class IPythonInputTestCase(InputSplitterTestCase):
424 400 """By just creating a new class whose .isp is a different instance, we
425 401 re-run the same test battery on the new input splitter.
426 402
427 403 In addition, this runs the tests over the syntax and syntax_ml dicts that
428 404 were tested by individual functions, as part of the OO interface.
429 405
430 406 It also makes some checks on the raw buffer storage.
431 407 """
432 408
433 409 def setUp(self):
434 self.isp = isp.IPythonInputSplitter(input_mode='line')
410 self.isp = isp.IPythonInputSplitter()
435 411
436 412 def test_syntax(self):
437 413 """Call all single-line syntax tests from the main object"""
438 414 isp = self.isp
439 415 for example in syntax.itervalues():
440 416 for raw, out_t in example:
441 417 if raw.startswith(' '):
442 418 continue
443 419
444 420 isp.push(raw+'\n')
445 421 out, out_raw = isp.source_raw_reset()
446 422 self.assertEqual(out.rstrip(), out_t,
447 423 tt.pair_fail_msg.format("inputsplitter",raw, out_t, out))
448 424 self.assertEqual(out_raw.rstrip(), raw.rstrip())
449 425
450 426 def test_syntax_multiline(self):
451 427 isp = self.isp
452 428 for example in syntax_ml.itervalues():
453 429 for line_pairs in example:
454 430 out_t_parts = []
455 431 raw_parts = []
456 432 for lraw, out_t_part in line_pairs:
457 433 if out_t_part is not None:
458 434 out_t_parts.append(out_t_part)
459 435
460 436 if lraw is not None:
461 437 isp.push(lraw)
462 438 raw_parts.append(lraw)
463 439
464 440 out, out_raw = isp.source_raw_reset()
465 441 out_t = '\n'.join(out_t_parts).rstrip()
466 442 raw = '\n'.join(raw_parts).rstrip()
467 443 self.assertEqual(out.rstrip(), out_t)
468 444 self.assertEqual(out_raw.rstrip(), raw)
469 445
470
471 class BlockIPythonInputTestCase(IPythonInputTestCase):
472
473 # Deactivate tests that don't make sense for the block mode
474 test_push3 = test_split = lambda s: None
475
476 def setUp(self):
477 self.isp = isp.IPythonInputSplitter(input_mode='cell')
478
479 def test_syntax_multiline(self):
480 isp = self.isp
481 for example in syntax_ml.itervalues():
482 raw_parts = []
483 out_t_parts = []
484 for line_pairs in example:
485 raw_parts, out_t_parts = zip(*line_pairs)
486
487 raw = '\n'.join(r for r in raw_parts if r is not None)
488 out_t = '\n'.join(o for o in out_t_parts if o is not None)
489
490 isp.push(raw)
491 out, out_raw = isp.source_raw_reset()
492 # Match ignoring trailing whitespace
493 self.assertEqual(out.rstrip(), out_t.rstrip())
494 self.assertEqual(out_raw.rstrip(), raw.rstrip())
495
496 446 def test_syntax_multiline_cell(self):
497 447 isp = self.isp
498 448 for example in syntax_ml.itervalues():
499 449
500 450 out_t_parts = []
501 451 for line_pairs in example:
502 452 raw = '\n'.join(r for r, _ in line_pairs if r is not None)
503 453 out_t = '\n'.join(t for _,t in line_pairs if t is not None)
504 454 out = isp.transform_cell(raw)
505 455 # Match ignoring trailing whitespace
506 456 self.assertEqual(out.rstrip(), out_t.rstrip())
507 457
508 458 #-----------------------------------------------------------------------------
509 459 # Main - use as a script, mostly for developer experiments
510 460 #-----------------------------------------------------------------------------
511 461
512 462 if __name__ == '__main__':
513 463 # A simple demo for interactive experimentation. This code will not get
514 464 # picked up by any test suite.
515 465 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
516 466
517 467 # configure here the syntax to use, prompt and whether to autoindent
518 468 #isp, start_prompt = InputSplitter(), '>>> '
519 469 isp, start_prompt = IPythonInputSplitter(), 'In> '
520 470
521 471 autoindent = True
522 472 #autoindent = False
523 473
524 474 try:
525 475 while True:
526 476 prompt = start_prompt
527 477 while isp.push_accepts_more():
528 478 indent = ' '*isp.indent_spaces
529 479 if autoindent:
530 480 line = indent + raw_input(prompt+indent)
531 481 else:
532 482 line = raw_input(prompt)
533 483 isp.push(line)
534 484 prompt = '... '
535 485
536 486 # Here we just return input so we can use it in a test suite, but a
537 487 # real interpreter would instead send it for execution somewhere.
538 488 #src = isp.source; raise EOFError # dbg
539 489 src, raw = isp.source_raw_reset()
540 490 print 'Input source was:\n', src
541 491 print 'Raw source was:\n', raw
542 492 except EOFError:
543 493 print 'Bye'
544 494
545 495 # Tests for cell magics support
546 496
547 497 def test_last_blank():
548 498 nt.assert_false(isp.last_blank(''))
549 499 nt.assert_false(isp.last_blank('abc'))
550 500 nt.assert_false(isp.last_blank('abc\n'))
551 501 nt.assert_false(isp.last_blank('abc\na'))
552 502
553 503 nt.assert_true(isp.last_blank('\n'))
554 504 nt.assert_true(isp.last_blank('\n '))
555 505 nt.assert_true(isp.last_blank('abc\n '))
556 506 nt.assert_true(isp.last_blank('abc\n\n'))
557 507 nt.assert_true(isp.last_blank('abc\nd\n\n'))
558 508 nt.assert_true(isp.last_blank('abc\nd\ne\n\n'))
559 509 nt.assert_true(isp.last_blank('abc \n \n \n\n'))
560 510
561 511
562 512 def test_last_two_blanks():
563 513 nt.assert_false(isp.last_two_blanks(''))
564 514 nt.assert_false(isp.last_two_blanks('abc'))
565 515 nt.assert_false(isp.last_two_blanks('abc\n'))
566 516 nt.assert_false(isp.last_two_blanks('abc\n\na'))
567 517 nt.assert_false(isp.last_two_blanks('abc\n \n'))
568 518 nt.assert_false(isp.last_two_blanks('abc\n\n'))
569 519
570 520 nt.assert_true(isp.last_two_blanks('\n\n'))
571 521 nt.assert_true(isp.last_two_blanks('\n\n '))
572 522 nt.assert_true(isp.last_two_blanks('\n \n'))
573 523 nt.assert_true(isp.last_two_blanks('abc\n\n '))
574 524 nt.assert_true(isp.last_two_blanks('abc\n\n\n'))
575 525 nt.assert_true(isp.last_two_blanks('abc\n\n \n'))
576 526 nt.assert_true(isp.last_two_blanks('abc\n\n \n '))
577 527 nt.assert_true(isp.last_two_blanks('abc\n\n \n \n'))
578 528 nt.assert_true(isp.last_two_blanks('abc\nd\n\n\n'))
579 529 nt.assert_true(isp.last_two_blanks('abc\nd\ne\nf\n\n\n'))
580 530
581 531
582 532 class CellMagicsCommon(object):
583 533
584 534 def test_whole_cell(self):
585 535 src = "%%cellm line\nbody\n"
586 536 sp = self.sp
587 537 sp.push(src)
588 538 out = sp.source_reset()
589 539 ref = u"get_ipython().run_cell_magic({u}'cellm', {u}'line', {u}'body')\n"
590 540 nt.assert_equal(out, py3compat.u_format(ref))
591 541
592 542 def tearDown(self):
593 543 self.sp.reset()
594 544
595 545
596 546 class CellModeCellMagics(CellMagicsCommon, unittest.TestCase):
597 547 sp = isp.IPythonInputSplitter(input_mode='cell')
598 548
599 549 def test_incremental(self):
600 550 sp = self.sp
601 551 src = '%%cellm line2\n'
602 552 sp.push(src)
603 553 nt.assert_true(sp.push_accepts_more()) #1
604 554 src += '\n'
605 555 sp.push(src)
606 556 # Note: if we ever change the logic to allow full blank lines (see
607 557 # _handle_cell_magic), then the following test should change to true
608 558 nt.assert_false(sp.push_accepts_more()) #2
609 559 # By now, even with full blanks allowed, a second blank should signal
610 560 # the end. For now this test is only a redundancy safety, but don't
611 561 # delete it in case we change our mind and the previous one goes to
612 562 # true.
613 563 src += '\n'
614 564 sp.push(src)
615 565 nt.assert_false(sp.push_accepts_more()) #3
616 566
617 567
618 568 class LineModeCellMagics(CellMagicsCommon, unittest.TestCase):
619 569 sp = isp.IPythonInputSplitter(input_mode='line')
620 570
621 571 def test_incremental(self):
622 572 sp = self.sp
623 573 sp.push('%%cellm line2\n')
624 574 nt.assert_true(sp.push_accepts_more()) #1
625 575 sp.push('\n')
626 576 nt.assert_false(sp.push_accepts_more()) #2
@@ -1,770 +1,771 b''
1 1 from __future__ import print_function
2 2
3 3 # Standard library imports
4 4 from collections import namedtuple
5 5 import sys
6 6 import time
7 7 import uuid
8 8
9 9 # System library imports
10 10 from pygments.lexers import PythonLexer
11 11 from IPython.external import qt
12 12 from IPython.external.qt import QtCore, QtGui
13 13
14 14 # Local imports
15 15 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
16 16 from IPython.core.inputtransformer import classic_prompt
17 17 from IPython.core.oinspect import call_tip
18 18 from IPython.frontend.qt.base_frontend_mixin import BaseFrontendMixin
19 19 from IPython.utils.traitlets import Bool, Instance, Unicode
20 20 from bracket_matcher import BracketMatcher
21 21 from call_tip_widget import CallTipWidget
22 22 from completion_lexer import CompletionLexer
23 23 from history_console_widget import HistoryConsoleWidget
24 24 from pygments_highlighter import PygmentsHighlighter
25 25
26 26
27 27 class FrontendHighlighter(PygmentsHighlighter):
28 28 """ A PygmentsHighlighter that understands and ignores prompts.
29 29 """
30 30
31 31 def __init__(self, frontend):
32 32 super(FrontendHighlighter, self).__init__(frontend._control.document())
33 33 self._current_offset = 0
34 34 self._frontend = frontend
35 35 self.highlighting_on = False
36 36
37 37 def highlightBlock(self, string):
38 38 """ Highlight a block of text. Reimplemented to highlight selectively.
39 39 """
40 40 if not self.highlighting_on:
41 41 return
42 42
43 43 # The input to this function is a unicode string that may contain
44 44 # paragraph break characters, non-breaking spaces, etc. Here we acquire
45 45 # the string as plain text so we can compare it.
46 46 current_block = self.currentBlock()
47 47 string = self._frontend._get_block_plain_text(current_block)
48 48
49 49 # Decide whether to check for the regular or continuation prompt.
50 50 if current_block.contains(self._frontend._prompt_pos):
51 51 prompt = self._frontend._prompt
52 52 else:
53 53 prompt = self._frontend._continuation_prompt
54 54
55 55 # Only highlight if we can identify a prompt, but make sure not to
56 56 # highlight the prompt.
57 57 if string.startswith(prompt):
58 58 self._current_offset = len(prompt)
59 59 string = string[len(prompt):]
60 60 super(FrontendHighlighter, self).highlightBlock(string)
61 61
62 62 def rehighlightBlock(self, block):
63 63 """ Reimplemented to temporarily enable highlighting if disabled.
64 64 """
65 65 old = self.highlighting_on
66 66 self.highlighting_on = True
67 67 super(FrontendHighlighter, self).rehighlightBlock(block)
68 68 self.highlighting_on = old
69 69
70 70 def setFormat(self, start, count, format):
71 71 """ Reimplemented to highlight selectively.
72 72 """
73 73 start += self._current_offset
74 74 super(FrontendHighlighter, self).setFormat(start, count, format)
75 75
76 76
77 77 class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):
78 78 """ A Qt frontend for a generic Python kernel.
79 79 """
80 80
81 81 # The text to show when the kernel is (re)started.
82 82 banner = Unicode()
83 83
84 84 # An option and corresponding signal for overriding the default kernel
85 85 # interrupt behavior.
86 86 custom_interrupt = Bool(False)
87 87 custom_interrupt_requested = QtCore.Signal()
88 88
89 89 # An option and corresponding signals for overriding the default kernel
90 90 # restart behavior.
91 91 custom_restart = Bool(False)
92 92 custom_restart_kernel_died = QtCore.Signal(float)
93 93 custom_restart_requested = QtCore.Signal()
94 94
95 95 # Whether to automatically show calltips on open-parentheses.
96 96 enable_calltips = Bool(True, config=True,
97 97 help="Whether to draw information calltips on open-parentheses.")
98 98
99 99 clear_on_kernel_restart = Bool(True, config=True,
100 100 help="Whether to clear the console when the kernel is restarted")
101 101
102 102 confirm_restart = Bool(True, config=True,
103 103 help="Whether to ask for user confirmation when restarting kernel")
104 104
105 105 # Emitted when a user visible 'execute_request' has been submitted to the
106 106 # kernel from the FrontendWidget. Contains the code to be executed.
107 107 executing = QtCore.Signal(object)
108 108
109 109 # Emitted when a user-visible 'execute_reply' has been received from the
110 110 # kernel and processed by the FrontendWidget. Contains the response message.
111 111 executed = QtCore.Signal(object)
112 112
113 113 # Emitted when an exit request has been received from the kernel.
114 114 exit_requested = QtCore.Signal(object)
115 115
116 116 # Protected class variables.
117 117 _prompt_transformer = IPythonInputSplitter(physical_line_transforms=[classic_prompt()],
118 118 logical_line_transforms=[],
119 119 python_line_transforms=[],
120 120 )
121 121 _CallTipRequest = namedtuple('_CallTipRequest', ['id', 'pos'])
122 122 _CompletionRequest = namedtuple('_CompletionRequest', ['id', 'pos'])
123 123 _ExecutionRequest = namedtuple('_ExecutionRequest', ['id', 'kind'])
124 124 _input_splitter_class = InputSplitter
125 125 _local_kernel = False
126 126 _highlighter = Instance(FrontendHighlighter)
127 127
128 128 #---------------------------------------------------------------------------
129 129 # 'object' interface
130 130 #---------------------------------------------------------------------------
131 131
132 132 def __init__(self, *args, **kw):
133 133 super(FrontendWidget, self).__init__(*args, **kw)
134 134 # FIXME: remove this when PySide min version is updated past 1.0.7
135 135 # forcefully disable calltips if PySide is < 1.0.7, because they crash
136 136 if qt.QT_API == qt.QT_API_PYSIDE:
137 137 import PySide
138 138 if PySide.__version_info__ < (1,0,7):
139 139 self.log.warn("PySide %s < 1.0.7 detected, disabling calltips" % PySide.__version__)
140 140 self.enable_calltips = False
141 141
142 142 # FrontendWidget protected variables.
143 143 self._bracket_matcher = BracketMatcher(self._control)
144 144 self._call_tip_widget = CallTipWidget(self._control)
145 145 self._completion_lexer = CompletionLexer(PythonLexer())
146 146 self._copy_raw_action = QtGui.QAction('Copy (Raw Text)', None)
147 147 self._hidden = False
148 148 self._highlighter = FrontendHighlighter(self)
149 self._input_splitter = self._input_splitter_class(input_mode='cell')
149 self._input_splitter = self._input_splitter_class()
150 150 self._kernel_manager = None
151 151 self._request_info = {}
152 152 self._request_info['execute'] = {};
153 153 self._callback_dict = {}
154 154
155 155 # Configure the ConsoleWidget.
156 156 self.tab_width = 4
157 157 self._set_continuation_prompt('... ')
158 158
159 159 # Configure the CallTipWidget.
160 160 self._call_tip_widget.setFont(self.font)
161 161 self.font_changed.connect(self._call_tip_widget.setFont)
162 162
163 163 # Configure actions.
164 164 action = self._copy_raw_action
165 165 key = QtCore.Qt.CTRL | QtCore.Qt.SHIFT | QtCore.Qt.Key_C
166 166 action.setEnabled(False)
167 167 action.setShortcut(QtGui.QKeySequence(key))
168 168 action.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
169 169 action.triggered.connect(self.copy_raw)
170 170 self.copy_available.connect(action.setEnabled)
171 171 self.addAction(action)
172 172
173 173 # Connect signal handlers.
174 174 document = self._control.document()
175 175 document.contentsChange.connect(self._document_contents_change)
176 176
177 177 # Set flag for whether we are connected via localhost.
178 178 self._local_kernel = kw.get('local_kernel',
179 179 FrontendWidget._local_kernel)
180 180
181 181 #---------------------------------------------------------------------------
182 182 # 'ConsoleWidget' public interface
183 183 #---------------------------------------------------------------------------
184 184
185 185 def copy(self):
186 186 """ Copy the currently selected text to the clipboard, removing prompts.
187 187 """
188 188 if self._page_control is not None and self._page_control.hasFocus():
189 189 self._page_control.copy()
190 190 elif self._control.hasFocus():
191 191 text = self._control.textCursor().selection().toPlainText()
192 192 if text:
193 193 text = self._prompt_transformer.transform_cell(text)
194 194 QtGui.QApplication.clipboard().setText(text)
195 195 else:
196 196 self.log.debug("frontend widget : unknown copy target")
197 197
198 198 #---------------------------------------------------------------------------
199 199 # 'ConsoleWidget' abstract interface
200 200 #---------------------------------------------------------------------------
201 201
202 202 def _is_complete(self, source, interactive):
203 203 """ Returns whether 'source' can be completely processed and a new
204 204 prompt created. When triggered by an Enter/Return key press,
205 205 'interactive' is True; otherwise, it is False.
206 206 """
207 self._input_splitter.reset()
207 208 complete = self._input_splitter.push(source)
208 209 if interactive:
209 210 complete = not self._input_splitter.push_accepts_more()
210 211 return complete
211 212
212 213 def _execute(self, source, hidden):
213 214 """ Execute 'source'. If 'hidden', do not show any output.
214 215
215 216 See parent class :meth:`execute` docstring for full details.
216 217 """
217 218 msg_id = self.kernel_manager.shell_channel.execute(source, hidden)
218 219 self._request_info['execute'][msg_id] = self._ExecutionRequest(msg_id, 'user')
219 220 self._hidden = hidden
220 221 if not hidden:
221 222 self.executing.emit(source)
222 223
223 224 def _prompt_started_hook(self):
224 225 """ Called immediately after a new prompt is displayed.
225 226 """
226 227 if not self._reading:
227 228 self._highlighter.highlighting_on = True
228 229
229 230 def _prompt_finished_hook(self):
230 231 """ Called immediately after a prompt is finished, i.e. when some input
231 232 will be processed and a new prompt displayed.
232 233 """
233 234 # Flush all state from the input splitter so the next round of
234 235 # reading input starts with a clean buffer.
235 236 self._input_splitter.reset()
236 237
237 238 if not self._reading:
238 239 self._highlighter.highlighting_on = False
239 240
240 241 def _tab_pressed(self):
241 242 """ Called when the tab key is pressed. Returns whether to continue
242 243 processing the event.
243 244 """
244 245 # Perform tab completion if:
245 246 # 1) The cursor is in the input buffer.
246 247 # 2) There is a non-whitespace character before the cursor.
247 248 text = self._get_input_buffer_cursor_line()
248 249 if text is None:
249 250 return False
250 251 complete = bool(text[:self._get_input_buffer_cursor_column()].strip())
251 252 if complete:
252 253 self._complete()
253 254 return not complete
254 255
255 256 #---------------------------------------------------------------------------
256 257 # 'ConsoleWidget' protected interface
257 258 #---------------------------------------------------------------------------
258 259
259 260 def _context_menu_make(self, pos):
260 261 """ Reimplemented to add an action for raw copy.
261 262 """
262 263 menu = super(FrontendWidget, self)._context_menu_make(pos)
263 264 for before_action in menu.actions():
264 265 if before_action.shortcut().matches(QtGui.QKeySequence.Paste) == \
265 266 QtGui.QKeySequence.ExactMatch:
266 267 menu.insertAction(before_action, self._copy_raw_action)
267 268 break
268 269 return menu
269 270
270 271 def request_interrupt_kernel(self):
271 272 if self._executing:
272 273 self.interrupt_kernel()
273 274
274 275 def request_restart_kernel(self):
275 276 message = 'Are you sure you want to restart the kernel?'
276 277 self.restart_kernel(message, now=False)
277 278
278 279 def _event_filter_console_keypress(self, event):
279 280 """ Reimplemented for execution interruption and smart backspace.
280 281 """
281 282 key = event.key()
282 283 if self._control_key_down(event.modifiers(), include_command=False):
283 284
284 285 if key == QtCore.Qt.Key_C and self._executing:
285 286 self.request_interrupt_kernel()
286 287 return True
287 288
288 289 elif key == QtCore.Qt.Key_Period:
289 290 self.request_restart_kernel()
290 291 return True
291 292
292 293 elif not event.modifiers() & QtCore.Qt.AltModifier:
293 294
294 295 # Smart backspace: remove four characters in one backspace if:
295 296 # 1) everything left of the cursor is whitespace
296 297 # 2) the four characters immediately left of the cursor are spaces
297 298 if key == QtCore.Qt.Key_Backspace:
298 299 col = self._get_input_buffer_cursor_column()
299 300 cursor = self._control.textCursor()
300 301 if col > 3 and not cursor.hasSelection():
301 302 text = self._get_input_buffer_cursor_line()[:col]
302 303 if text.endswith(' ') and not text.strip():
303 304 cursor.movePosition(QtGui.QTextCursor.Left,
304 305 QtGui.QTextCursor.KeepAnchor, 4)
305 306 cursor.removeSelectedText()
306 307 return True
307 308
308 309 return super(FrontendWidget, self)._event_filter_console_keypress(event)
309 310
310 311 def _insert_continuation_prompt(self, cursor):
311 312 """ Reimplemented for auto-indentation.
312 313 """
313 314 super(FrontendWidget, self)._insert_continuation_prompt(cursor)
314 315 cursor.insertText(' ' * self._input_splitter.indent_spaces)
315 316
316 317 #---------------------------------------------------------------------------
317 318 # 'BaseFrontendMixin' abstract interface
318 319 #---------------------------------------------------------------------------
319 320
320 321 def _handle_complete_reply(self, rep):
321 322 """ Handle replies for tab completion.
322 323 """
323 324 self.log.debug("complete: %s", rep.get('content', ''))
324 325 cursor = self._get_cursor()
325 326 info = self._request_info.get('complete')
326 327 if info and info.id == rep['parent_header']['msg_id'] and \
327 328 info.pos == cursor.position():
328 329 text = '.'.join(self._get_context())
329 330 cursor.movePosition(QtGui.QTextCursor.Left, n=len(text))
330 331 self._complete_with_items(cursor, rep['content']['matches'])
331 332
332 333 def _silent_exec_callback(self, expr, callback):
333 334 """Silently execute `expr` in the kernel and call `callback` with reply
334 335
335 336 the `expr` is evaluated silently in the kernel (without) output in
336 337 the frontend. Call `callback` with the
337 338 `repr <http://docs.python.org/library/functions.html#repr> `_ as first argument
338 339
339 340 Parameters
340 341 ----------
341 342 expr : string
342 343 valid string to be executed by the kernel.
343 344 callback : function
344 345 function accepting one argument, as a string. The string will be
345 346 the `repr` of the result of evaluating `expr`
346 347
347 348 The `callback` is called with the `repr()` of the result of `expr` as
348 349 first argument. To get the object, do `eval()` on the passed value.
349 350
350 351 See Also
351 352 --------
352 353 _handle_exec_callback : private method, deal with calling callback with reply
353 354
354 355 """
355 356
356 357 # generate uuid, which would be used as an indication of whether or
357 358 # not the unique request originated from here (can use msg id ?)
358 359 local_uuid = str(uuid.uuid1())
359 360 msg_id = self.kernel_manager.shell_channel.execute('',
360 361 silent=True, user_expressions={ local_uuid:expr })
361 362 self._callback_dict[local_uuid] = callback
362 363 self._request_info['execute'][msg_id] = self._ExecutionRequest(msg_id, 'silent_exec_callback')
363 364
364 365 def _handle_exec_callback(self, msg):
365 366 """Execute `callback` corresponding to `msg` reply, after ``_silent_exec_callback``
366 367
367 368 Parameters
368 369 ----------
369 370 msg : raw message send by the kernel containing an `user_expressions`
370 371 and having a 'silent_exec_callback' kind.
371 372
372 373 Notes
373 374 -----
374 375 This function will look for a `callback` associated with the
375 376 corresponding message id. Association has been made by
376 377 `_silent_exec_callback`. `callback` is then called with the `repr()`
377 378 of the value of corresponding `user_expressions` as argument.
378 379 `callback` is then removed from the known list so that any message
379 380 coming again with the same id won't trigger it.
380 381
381 382 """
382 383
383 384 user_exp = msg['content'].get('user_expressions')
384 385 if not user_exp:
385 386 return
386 387 for expression in user_exp:
387 388 if expression in self._callback_dict:
388 389 self._callback_dict.pop(expression)(user_exp[expression])
389 390
390 391 def _handle_execute_reply(self, msg):
391 392 """ Handles replies for code execution.
392 393 """
393 394 self.log.debug("execute: %s", msg.get('content', ''))
394 395 msg_id = msg['parent_header']['msg_id']
395 396 info = self._request_info['execute'].get(msg_id)
396 397 # unset reading flag, because if execute finished, raw_input can't
397 398 # still be pending.
398 399 self._reading = False
399 400 if info and info.kind == 'user' and not self._hidden:
400 401 # Make sure that all output from the SUB channel has been processed
401 402 # before writing a new prompt.
402 403 self.kernel_manager.iopub_channel.flush()
403 404
404 405 # Reset the ANSI style information to prevent bad text in stdout
405 406 # from messing up our colors. We're not a true terminal so we're
406 407 # allowed to do this.
407 408 if self.ansi_codes:
408 409 self._ansi_processor.reset_sgr()
409 410
410 411 content = msg['content']
411 412 status = content['status']
412 413 if status == 'ok':
413 414 self._process_execute_ok(msg)
414 415 elif status == 'error':
415 416 self._process_execute_error(msg)
416 417 elif status == 'aborted':
417 418 self._process_execute_abort(msg)
418 419
419 420 self._show_interpreter_prompt_for_reply(msg)
420 421 self.executed.emit(msg)
421 422 self._request_info['execute'].pop(msg_id)
422 423 elif info and info.kind == 'silent_exec_callback' and not self._hidden:
423 424 self._handle_exec_callback(msg)
424 425 self._request_info['execute'].pop(msg_id)
425 426 else:
426 427 super(FrontendWidget, self)._handle_execute_reply(msg)
427 428
428 429 def _handle_input_request(self, msg):
429 430 """ Handle requests for raw_input.
430 431 """
431 432 self.log.debug("input: %s", msg.get('content', ''))
432 433 if self._hidden:
433 434 raise RuntimeError('Request for raw input during hidden execution.')
434 435
435 436 # Make sure that all output from the SUB channel has been processed
436 437 # before entering readline mode.
437 438 self.kernel_manager.iopub_channel.flush()
438 439
439 440 def callback(line):
440 441 self.kernel_manager.stdin_channel.input(line)
441 442 if self._reading:
442 443 self.log.debug("Got second input request, assuming first was interrupted.")
443 444 self._reading = False
444 445 self._readline(msg['content']['prompt'], callback=callback)
445 446
446 447 def _handle_kernel_died(self, since_last_heartbeat):
447 448 """ Handle the kernel's death by asking if the user wants to restart.
448 449 """
449 450 self.log.debug("kernel died: %s", since_last_heartbeat)
450 451 if self.custom_restart:
451 452 self.custom_restart_kernel_died.emit(since_last_heartbeat)
452 453 else:
453 454 message = 'The kernel heartbeat has been inactive for %.2f ' \
454 455 'seconds. Do you want to restart the kernel? You may ' \
455 456 'first want to check the network connection.' % \
456 457 since_last_heartbeat
457 458 self.restart_kernel(message, now=True)
458 459
459 460 def _handle_object_info_reply(self, rep):
460 461 """ Handle replies for call tips.
461 462 """
462 463 self.log.debug("oinfo: %s", rep.get('content', ''))
463 464 cursor = self._get_cursor()
464 465 info = self._request_info.get('call_tip')
465 466 if info and info.id == rep['parent_header']['msg_id'] and \
466 467 info.pos == cursor.position():
467 468 # Get the information for a call tip. For now we format the call
468 469 # line as string, later we can pass False to format_call and
469 470 # syntax-highlight it ourselves for nicer formatting in the
470 471 # calltip.
471 472 content = rep['content']
472 473 # if this is from pykernel, 'docstring' will be the only key
473 474 if content.get('ismagic', False):
474 475 # Don't generate a call-tip for magics. Ideally, we should
475 476 # generate a tooltip, but not on ( like we do for actual
476 477 # callables.
477 478 call_info, doc = None, None
478 479 else:
479 480 call_info, doc = call_tip(content, format_call=True)
480 481 if call_info or doc:
481 482 self._call_tip_widget.show_call_info(call_info, doc)
482 483
483 484 def _handle_pyout(self, msg):
484 485 """ Handle display hook output.
485 486 """
486 487 self.log.debug("pyout: %s", msg.get('content', ''))
487 488 if not self._hidden and self._is_from_this_session(msg):
488 489 text = msg['content']['data']
489 490 self._append_plain_text(text + '\n', before_prompt=True)
490 491
491 492 def _handle_stream(self, msg):
492 493 """ Handle stdout, stderr, and stdin.
493 494 """
494 495 self.log.debug("stream: %s", msg.get('content', ''))
495 496 if not self._hidden and self._is_from_this_session(msg):
496 497 # Most consoles treat tabs as being 8 space characters. Convert tabs
497 498 # to spaces so that output looks as expected regardless of this
498 499 # widget's tab width.
499 500 text = msg['content']['data'].expandtabs(8)
500 501
501 502 self._append_plain_text(text, before_prompt=True)
502 503 self._control.moveCursor(QtGui.QTextCursor.End)
503 504
504 505 def _handle_shutdown_reply(self, msg):
505 506 """ Handle shutdown signal, only if from other console.
506 507 """
507 508 self.log.debug("shutdown: %s", msg.get('content', ''))
508 509 if not self._hidden and not self._is_from_this_session(msg):
509 510 if self._local_kernel:
510 511 if not msg['content']['restart']:
511 512 self.exit_requested.emit(self)
512 513 else:
513 514 # we just got notified of a restart!
514 515 time.sleep(0.25) # wait 1/4 sec to reset
515 516 # lest the request for a new prompt
516 517 # goes to the old kernel
517 518 self.reset()
518 519 else: # remote kernel, prompt on Kernel shutdown/reset
519 520 title = self.window().windowTitle()
520 521 if not msg['content']['restart']:
521 522 reply = QtGui.QMessageBox.question(self, title,
522 523 "Kernel has been shutdown permanently. "
523 524 "Close the Console?",
524 525 QtGui.QMessageBox.Yes,QtGui.QMessageBox.No)
525 526 if reply == QtGui.QMessageBox.Yes:
526 527 self.exit_requested.emit(self)
527 528 else:
528 529 # XXX: remove message box in favor of using the
529 530 # clear_on_kernel_restart setting?
530 531 reply = QtGui.QMessageBox.question(self, title,
531 532 "Kernel has been reset. Clear the Console?",
532 533 QtGui.QMessageBox.Yes,QtGui.QMessageBox.No)
533 534 if reply == QtGui.QMessageBox.Yes:
534 535 time.sleep(0.25) # wait 1/4 sec to reset
535 536 # lest the request for a new prompt
536 537 # goes to the old kernel
537 538 self.reset()
538 539
539 540 def _started_channels(self):
540 541 """ Called when the KernelManager channels have started listening or
541 542 when the frontend is assigned an already listening KernelManager.
542 543 """
543 544 self.reset(clear=True)
544 545
545 546 #---------------------------------------------------------------------------
546 547 # 'FrontendWidget' public interface
547 548 #---------------------------------------------------------------------------
548 549
549 550 def copy_raw(self):
550 551 """ Copy the currently selected text to the clipboard without attempting
551 552 to remove prompts or otherwise alter the text.
552 553 """
553 554 self._control.copy()
554 555
555 556 def execute_file(self, path, hidden=False):
556 557 """ Attempts to execute file with 'path'. If 'hidden', no output is
557 558 shown.
558 559 """
559 560 self.execute('execfile(%r)' % path, hidden=hidden)
560 561
561 562 def interrupt_kernel(self):
562 563 """ Attempts to interrupt the running kernel.
563 564
564 565 Also unsets _reading flag, to avoid runtime errors
565 566 if raw_input is called again.
566 567 """
567 568 if self.custom_interrupt:
568 569 self._reading = False
569 570 self.custom_interrupt_requested.emit()
570 571 elif self.kernel_manager.has_kernel:
571 572 self._reading = False
572 573 self.kernel_manager.interrupt_kernel()
573 574 else:
574 575 self._append_plain_text('Kernel process is either remote or '
575 576 'unspecified. Cannot interrupt.\n')
576 577
577 578 def reset(self, clear=False):
578 579 """ Resets the widget to its initial state if ``clear`` parameter or
579 580 ``clear_on_kernel_restart`` configuration setting is True, otherwise
580 581 prints a visual indication of the fact that the kernel restarted, but
581 582 does not clear the traces from previous usage of the kernel before it
582 583 was restarted. With ``clear=True``, it is similar to ``%clear``, but
583 584 also re-writes the banner and aborts execution if necessary.
584 585 """
585 586 if self._executing:
586 587 self._executing = False
587 588 self._request_info['execute'] = {}
588 589 self._reading = False
589 590 self._highlighter.highlighting_on = False
590 591
591 592 if self.clear_on_kernel_restart or clear:
592 593 self._control.clear()
593 594 self._append_plain_text(self.banner)
594 595 else:
595 596 self._append_plain_text("# restarting kernel...")
596 597 self._append_html("<hr><br>")
597 598 # XXX: Reprinting the full banner may be too much, but once #1680 is
598 599 # addressed, that will mitigate it.
599 600 #self._append_plain_text(self.banner)
600 601 # update output marker for stdout/stderr, so that startup
601 602 # messages appear after banner:
602 603 self._append_before_prompt_pos = self._get_cursor().position()
603 604 self._show_interpreter_prompt()
604 605
605 606 def restart_kernel(self, message, now=False):
606 607 """ Attempts to restart the running kernel.
607 608 """
608 609 # FIXME: now should be configurable via a checkbox in the dialog. Right
609 610 # now at least the heartbeat path sets it to True and the manual restart
610 611 # to False. But those should just be the pre-selected states of a
611 612 # checkbox that the user could override if so desired. But I don't know
612 613 # enough Qt to go implementing the checkbox now.
613 614
614 615 if self.custom_restart:
615 616 self.custom_restart_requested.emit()
616 617
617 618 elif self.kernel_manager.has_kernel:
618 619 # Pause the heart beat channel to prevent further warnings.
619 620 self.kernel_manager.hb_channel.pause()
620 621
621 622 # Prompt the user to restart the kernel. Un-pause the heartbeat if
622 623 # they decline. (If they accept, the heartbeat will be un-paused
623 624 # automatically when the kernel is restarted.)
624 625 if self.confirm_restart:
625 626 buttons = QtGui.QMessageBox.Yes | QtGui.QMessageBox.No
626 627 result = QtGui.QMessageBox.question(self, 'Restart kernel?',
627 628 message, buttons)
628 629 do_restart = result == QtGui.QMessageBox.Yes
629 630 else:
630 631 # confirm_restart is False, so we don't need to ask user
631 632 # anything, just do the restart
632 633 do_restart = True
633 634 if do_restart:
634 635 try:
635 636 self.kernel_manager.restart_kernel(now=now)
636 637 except RuntimeError:
637 638 self._append_plain_text('Kernel started externally. '
638 639 'Cannot restart.\n',
639 640 before_prompt=True
640 641 )
641 642 else:
642 643 self.reset()
643 644 else:
644 645 self.kernel_manager.hb_channel.unpause()
645 646
646 647 else:
647 648 self._append_plain_text('Kernel process is either remote or '
648 649 'unspecified. Cannot restart.\n',
649 650 before_prompt=True
650 651 )
651 652
652 653 #---------------------------------------------------------------------------
653 654 # 'FrontendWidget' protected interface
654 655 #---------------------------------------------------------------------------
655 656
656 657 def _call_tip(self):
657 658 """ Shows a call tip, if appropriate, at the current cursor location.
658 659 """
659 660 # Decide if it makes sense to show a call tip
660 661 if not self.enable_calltips:
661 662 return False
662 663 cursor = self._get_cursor()
663 664 cursor.movePosition(QtGui.QTextCursor.Left)
664 665 if cursor.document().characterAt(cursor.position()) != '(':
665 666 return False
666 667 context = self._get_context(cursor)
667 668 if not context:
668 669 return False
669 670
670 671 # Send the metadata request to the kernel
671 672 name = '.'.join(context)
672 673 msg_id = self.kernel_manager.shell_channel.object_info(name)
673 674 pos = self._get_cursor().position()
674 675 self._request_info['call_tip'] = self._CallTipRequest(msg_id, pos)
675 676 return True
676 677
677 678 def _complete(self):
678 679 """ Performs completion at the current cursor location.
679 680 """
680 681 context = self._get_context()
681 682 if context:
682 683 # Send the completion request to the kernel
683 684 msg_id = self.kernel_manager.shell_channel.complete(
684 685 '.'.join(context), # text
685 686 self._get_input_buffer_cursor_line(), # line
686 687 self._get_input_buffer_cursor_column(), # cursor_pos
687 688 self.input_buffer) # block
688 689 pos = self._get_cursor().position()
689 690 info = self._CompletionRequest(msg_id, pos)
690 691 self._request_info['complete'] = info
691 692
692 693 def _get_context(self, cursor=None):
693 694 """ Gets the context for the specified cursor (or the current cursor
694 695 if none is specified).
695 696 """
696 697 if cursor is None:
697 698 cursor = self._get_cursor()
698 699 cursor.movePosition(QtGui.QTextCursor.StartOfBlock,
699 700 QtGui.QTextCursor.KeepAnchor)
700 701 text = cursor.selection().toPlainText()
701 702 return self._completion_lexer.get_context(text)
702 703
703 704 def _process_execute_abort(self, msg):
704 705 """ Process a reply for an aborted execution request.
705 706 """
706 707 self._append_plain_text("ERROR: execution aborted\n")
707 708
708 709 def _process_execute_error(self, msg):
709 710 """ Process a reply for an execution request that resulted in an error.
710 711 """
711 712 content = msg['content']
712 713 # If a SystemExit is passed along, this means exit() was called - also
713 714 # all the ipython %exit magic syntax of '-k' to be used to keep
714 715 # the kernel running
715 716 if content['ename']=='SystemExit':
716 717 keepkernel = content['evalue']=='-k' or content['evalue']=='True'
717 718 self._keep_kernel_on_exit = keepkernel
718 719 self.exit_requested.emit(self)
719 720 else:
720 721 traceback = ''.join(content['traceback'])
721 722 self._append_plain_text(traceback)
722 723
723 724 def _process_execute_ok(self, msg):
724 725 """ Process a reply for a successful execution request.
725 726 """
726 727 payload = msg['content']['payload']
727 728 for item in payload:
728 729 if not self._process_execute_payload(item):
729 730 warning = 'Warning: received unknown payload of type %s'
730 731 print(warning % repr(item['source']))
731 732
732 733 def _process_execute_payload(self, item):
733 734 """ Process a single payload item from the list of payload items in an
734 735 execution reply. Returns whether the payload was handled.
735 736 """
736 737 # The basic FrontendWidget doesn't handle payloads, as they are a
737 738 # mechanism for going beyond the standard Python interpreter model.
738 739 return False
739 740
740 741 def _show_interpreter_prompt(self):
741 742 """ Shows a prompt for the interpreter.
742 743 """
743 744 self._show_prompt('>>> ')
744 745
745 746 def _show_interpreter_prompt_for_reply(self, msg):
746 747 """ Shows a prompt for the interpreter given an 'execute_reply' message.
747 748 """
748 749 self._show_interpreter_prompt()
749 750
750 751 #------ Signal handlers ----------------------------------------------------
751 752
752 753 def _document_contents_change(self, position, removed, added):
753 754 """ Called whenever the document's content changes. Display a call tip
754 755 if appropriate.
755 756 """
756 757 # Calculate where the cursor should be *after* the change:
757 758 position += added
758 759
759 760 document = self._control.document()
760 761 if position == self._get_cursor().position():
761 762 self._call_tip()
762 763
763 764 #------ Trait default initializers -----------------------------------------
764 765
765 766 def _banner_default(self):
766 767 """ Returns the standard Python banner.
767 768 """
768 769 banner = 'Python %s on %s\nType "help", "copyright", "credits" or ' \
769 770 '"license" for more information.'
770 771 return banner % (sys.version, sys.platform)
@@ -1,752 +1,752 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Subclass of InteractiveShell for terminal based frontends."""
3 3
4 4 #-----------------------------------------------------------------------------
5 5 # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de>
6 6 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
7 7 # Copyright (C) 2008-2011 The IPython Development Team
8 8 #
9 9 # Distributed under the terms of the BSD License. The full license is in
10 10 # the file COPYING, distributed as part of this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from __future__ import print_function
17 17
18 18 import bdb
19 19 import os
20 20 import re
21 21 import sys
22 22 import textwrap
23 23
24 24 # We need to use nested to support python 2.6, once we move to >=2.7, we can
25 25 # use the with keyword's new builtin support for nested managers
26 26 try:
27 27 from contextlib import nested
28 28 except:
29 29 from IPython.utils.nested_context import nested
30 30
31 31 from IPython.core.error import TryNext, UsageError
32 32 from IPython.core.usage import interactive_usage, default_banner
33 33 from IPython.core.inputsplitter import IPythonInputSplitter
34 34 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
35 35 from IPython.core.magic import Magics, magics_class, line_magic
36 36 from IPython.testing.skipdoctest import skip_doctest
37 37 from IPython.utils.encoding import get_stream_enc
38 38 from IPython.utils import py3compat
39 39 from IPython.utils.terminal import toggle_set_term_title, set_term_title
40 40 from IPython.utils.process import abbrev_cwd
41 41 from IPython.utils.warn import warn, error
42 42 from IPython.utils.text import num_ini_spaces, SList, strip_email_quotes
43 43 from IPython.utils.traitlets import Integer, CBool, Unicode
44 44
45 45 #-----------------------------------------------------------------------------
46 46 # Utilities
47 47 #-----------------------------------------------------------------------------
48 48
49 49 def get_default_editor():
50 50 try:
51 51 ed = os.environ['EDITOR']
52 52 except KeyError:
53 53 if os.name == 'posix':
54 54 ed = 'vi' # the only one guaranteed to be there!
55 55 else:
56 56 ed = 'notepad' # same in Windows!
57 57 return ed
58 58
59 59
60 60 def get_pasted_lines(sentinel, l_input=py3compat.input):
61 61 """ Yield pasted lines until the user enters the given sentinel value.
62 62 """
63 63 print("Pasting code; enter '%s' alone on the line to stop or use Ctrl-D." \
64 64 % sentinel)
65 65 while True:
66 66 try:
67 67 l = l_input(':')
68 68 if l == sentinel:
69 69 return
70 70 else:
71 71 yield l
72 72 except EOFError:
73 73 print('<EOF>')
74 74 return
75 75
76 76
77 77 #------------------------------------------------------------------------
78 78 # Terminal-specific magics
79 79 #------------------------------------------------------------------------
80 80
81 81 @magics_class
82 82 class TerminalMagics(Magics):
83 83 def __init__(self, shell):
84 84 super(TerminalMagics, self).__init__(shell)
85 self.input_splitter = IPythonInputSplitter(input_mode='line')
85 self.input_splitter = IPythonInputSplitter()
86 86
87 87 def cleanup_input(self, block):
88 88 """Apply all possible IPython cleanups to an input block.
89 89
90 90 This means:
91 91
92 92 - remove any global leading whitespace (dedent)
93 93 - remove any email quotes ('>') if they are present in *all* lines
94 94 - apply all static inputsplitter transforms and break into sub-blocks
95 95 - apply prefilter() to each sub-block that is a single line.
96 96
97 97 Parameters
98 98 ----------
99 99 block : str
100 100 A possibly multiline input string of code.
101 101
102 102 Returns
103 103 -------
104 104 transformed block : str
105 105 The input, with all transformations above applied.
106 106 """
107 107 # We have to effectively implement client-side the loop that is done by
108 108 # the terminal frontend, and furthermore do it on a block that can
109 109 # possibly contain multiple statments pasted in one go.
110 110
111 111 # First, run the input through the block splitting code. We should
112 112 # eventually make this a self-contained method in the inputsplitter.
113 113 isp = self.input_splitter
114 114 isp.reset()
115 115 b = textwrap.dedent(block)
116 116
117 117 # Remove email quotes first. These must be consistently applied to
118 118 # *all* lines to be removed
119 119 b = strip_email_quotes(b)
120 120
121 121 # Split the input into independent sub-blocks so we can later do
122 122 # prefiltering (which must be done *only* to single-line inputs)
123 123 blocks = []
124 124 last_block = []
125 125 for line in b.splitlines():
126 126 isp.push(line)
127 127 last_block.append(line)
128 128 if not isp.push_accepts_more():
129 129 blocks.append(isp.source_reset())
130 130 last_block = []
131 131 if last_block:
132 132 blocks.append('\n'.join(last_block))
133 133
134 134 # Now, apply prefiltering to any one-line block to match the behavior
135 135 # of the interactive terminal
136 136 final_blocks = []
137 137 for block in blocks:
138 138 lines = block.splitlines()
139 139 if len(lines) == 1:
140 140 final_blocks.append(self.shell.prefilter(lines[0]))
141 141 else:
142 142 final_blocks.append(block)
143 143
144 144 # We now have the final version of the input code as a list of blocks,
145 145 # with all inputsplitter transformations applied and single-line blocks
146 146 # run through prefilter. For further processing, turn into a single
147 147 # string as the rest of our apis use string inputs.
148 148 return '\n'.join(final_blocks)
149 149
150 150 def store_or_execute(self, block, name):
151 151 """ Execute a block, or store it in a variable, per the user's request.
152 152 """
153 153
154 154 b = self.cleanup_input(block)
155 155 if name:
156 156 # If storing it for further editing
157 157 self.shell.user_ns[name] = SList(b.splitlines())
158 158 print("Block assigned to '%s'" % name)
159 159 else:
160 160 self.shell.user_ns['pasted_block'] = b
161 161 self.shell.using_paste_magics = True
162 162 try:
163 163 self.shell.run_cell(b)
164 164 finally:
165 165 self.shell.using_paste_magics = False
166 166
167 167 def rerun_pasted(self, name='pasted_block'):
168 168 """ Rerun a previously pasted command.
169 169 """
170 170 b = self.shell.user_ns.get(name)
171 171
172 172 # Sanity checks
173 173 if b is None:
174 174 raise UsageError('No previous pasted block available')
175 175 if not isinstance(b, basestring):
176 176 raise UsageError(
177 177 "Variable 'pasted_block' is not a string, can't execute")
178 178
179 179 print("Re-executing '%s...' (%d chars)"% (b.split('\n',1)[0], len(b)))
180 180 self.shell.run_cell(b)
181 181
182 182 @line_magic
183 183 def autoindent(self, parameter_s = ''):
184 184 """Toggle autoindent on/off (if available)."""
185 185
186 186 self.shell.set_autoindent()
187 187 print("Automatic indentation is:",['OFF','ON'][self.shell.autoindent])
188 188
189 189 @skip_doctest
190 190 @line_magic
191 191 def cpaste(self, parameter_s=''):
192 192 """Paste & execute a pre-formatted code block from clipboard.
193 193
194 194 You must terminate the block with '--' (two minus-signs) or Ctrl-D
195 195 alone on the line. You can also provide your own sentinel with '%paste
196 196 -s %%' ('%%' is the new sentinel for this operation)
197 197
198 198 The block is dedented prior to execution to enable execution of method
199 199 definitions. '>' and '+' characters at the beginning of a line are
200 200 ignored, to allow pasting directly from e-mails, diff files and
201 201 doctests (the '...' continuation prompt is also stripped). The
202 202 executed block is also assigned to variable named 'pasted_block' for
203 203 later editing with '%edit pasted_block'.
204 204
205 205 You can also pass a variable name as an argument, e.g. '%cpaste foo'.
206 206 This assigns the pasted block to variable 'foo' as string, without
207 207 dedenting or executing it (preceding >>> and + is still stripped)
208 208
209 209 '%cpaste -r' re-executes the block previously entered by cpaste.
210 210
211 211 Do not be alarmed by garbled output on Windows (it's a readline bug).
212 212 Just press enter and type -- (and press enter again) and the block
213 213 will be what was just pasted.
214 214
215 215 IPython statements (magics, shell escapes) are not supported (yet).
216 216
217 217 See also
218 218 --------
219 219 paste: automatically pull code from clipboard.
220 220
221 221 Examples
222 222 --------
223 223 ::
224 224
225 225 In [8]: %cpaste
226 226 Pasting code; enter '--' alone on the line to stop.
227 227 :>>> a = ["world!", "Hello"]
228 228 :>>> print " ".join(sorted(a))
229 229 :--
230 230 Hello world!
231 231 """
232 232 opts, name = self.parse_options(parameter_s, 'rs:', mode='string')
233 233 if 'r' in opts:
234 234 self.rerun_pasted()
235 235 return
236 236
237 237 sentinel = opts.get('s', '--')
238 238 block = '\n'.join(get_pasted_lines(sentinel))
239 239 self.store_or_execute(block, name)
240 240
241 241 @line_magic
242 242 def paste(self, parameter_s=''):
243 243 """Paste & execute a pre-formatted code block from clipboard.
244 244
245 245 The text is pulled directly from the clipboard without user
246 246 intervention and printed back on the screen before execution (unless
247 247 the -q flag is given to force quiet mode).
248 248
249 249 The block is dedented prior to execution to enable execution of method
250 250 definitions. '>' and '+' characters at the beginning of a line are
251 251 ignored, to allow pasting directly from e-mails, diff files and
252 252 doctests (the '...' continuation prompt is also stripped). The
253 253 executed block is also assigned to variable named 'pasted_block' for
254 254 later editing with '%edit pasted_block'.
255 255
256 256 You can also pass a variable name as an argument, e.g. '%paste foo'.
257 257 This assigns the pasted block to variable 'foo' as string, without
258 258 executing it (preceding >>> and + is still stripped).
259 259
260 260 Options
261 261 -------
262 262
263 263 -r: re-executes the block previously entered by cpaste.
264 264
265 265 -q: quiet mode: do not echo the pasted text back to the terminal.
266 266
267 267 IPython statements (magics, shell escapes) are not supported (yet).
268 268
269 269 See also
270 270 --------
271 271 cpaste: manually paste code into terminal until you mark its end.
272 272 """
273 273 opts, name = self.parse_options(parameter_s, 'rq', mode='string')
274 274 if 'r' in opts:
275 275 self.rerun_pasted()
276 276 return
277 277 try:
278 278 block = self.shell.hooks.clipboard_get()
279 279 except TryNext as clipboard_exc:
280 280 message = getattr(clipboard_exc, 'args')
281 281 if message:
282 282 error(message[0])
283 283 else:
284 284 error('Could not get text from the clipboard.')
285 285 return
286 286
287 287 # By default, echo back to terminal unless quiet mode is requested
288 288 if 'q' not in opts:
289 289 write = self.shell.write
290 290 write(self.shell.pycolorize(block))
291 291 if not block.endswith('\n'):
292 292 write('\n')
293 293 write("## -- End pasted text --\n")
294 294
295 295 self.store_or_execute(block, name)
296 296
297 297 # Class-level: add a '%cls' magic only on Windows
298 298 if sys.platform == 'win32':
299 299 @line_magic
300 300 def cls(self, s):
301 301 """Clear screen.
302 302 """
303 303 os.system("cls")
304 304
305 305 #-----------------------------------------------------------------------------
306 306 # Main class
307 307 #-----------------------------------------------------------------------------
308 308
309 309 class TerminalInteractiveShell(InteractiveShell):
310 310
311 311 autoedit_syntax = CBool(False, config=True,
312 312 help="auto editing of files with syntax errors.")
313 313 banner = Unicode('')
314 314 banner1 = Unicode(default_banner, config=True,
315 315 help="""The part of the banner to be printed before the profile"""
316 316 )
317 317 banner2 = Unicode('', config=True,
318 318 help="""The part of the banner to be printed after the profile"""
319 319 )
320 320 confirm_exit = CBool(True, config=True,
321 321 help="""
322 322 Set to confirm when you try to exit IPython with an EOF (Control-D
323 323 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
324 324 you can force a direct exit without any confirmation.""",
325 325 )
326 326 # This display_banner only controls whether or not self.show_banner()
327 327 # is called when mainloop/interact are called. The default is False
328 328 # because for the terminal based application, the banner behavior
329 329 # is controlled by Global.display_banner, which IPythonApp looks at
330 330 # to determine if *it* should call show_banner() by hand or not.
331 331 display_banner = CBool(False) # This isn't configurable!
332 332 embedded = CBool(False)
333 333 embedded_active = CBool(False)
334 334 editor = Unicode(get_default_editor(), config=True,
335 335 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
336 336 )
337 337 pager = Unicode('less', config=True,
338 338 help="The shell program to be used for paging.")
339 339
340 340 screen_length = Integer(0, config=True,
341 341 help=
342 342 """Number of lines of your screen, used to control printing of very
343 343 long strings. Strings longer than this number of lines will be sent
344 344 through a pager instead of directly printed. The default value for
345 345 this is 0, which means IPython will auto-detect your screen size every
346 346 time it needs to print certain potentially long strings (this doesn't
347 347 change the behavior of the 'print' keyword, it's only triggered
348 348 internally). If for some reason this isn't working well (it needs
349 349 curses support), specify it yourself. Otherwise don't change the
350 350 default.""",
351 351 )
352 352 term_title = CBool(False, config=True,
353 353 help="Enable auto setting the terminal title."
354 354 )
355 355
356 356 # This `using_paste_magics` is used to detect whether the code is being
357 357 # executed via paste magics functions
358 358 using_paste_magics = CBool(False)
359 359
360 360 # In the terminal, GUI control is done via PyOS_InputHook
361 361 @staticmethod
362 362 def enable_gui(gui=None, app=None):
363 363 """Switch amongst GUI input hooks by name.
364 364 """
365 365 # Deferred import
366 366 from IPython.lib.inputhook import enable_gui as real_enable_gui
367 367 return real_enable_gui(gui, app)
368 368
369 369 def __init__(self, config=None, ipython_dir=None, profile_dir=None,
370 370 user_ns=None, user_module=None, custom_exceptions=((),None),
371 371 usage=None, banner1=None, banner2=None, display_banner=None):
372 372
373 373 super(TerminalInteractiveShell, self).__init__(
374 374 config=config, ipython_dir=ipython_dir, profile_dir=profile_dir, user_ns=user_ns,
375 375 user_module=user_module, custom_exceptions=custom_exceptions
376 376 )
377 377 # use os.system instead of utils.process.system by default,
378 378 # because piped system doesn't make sense in the Terminal:
379 379 self.system = self.system_raw
380 380
381 381 self.init_term_title()
382 382 self.init_usage(usage)
383 383 self.init_banner(banner1, banner2, display_banner)
384 384
385 385 #-------------------------------------------------------------------------
386 386 # Overrides of init stages
387 387 #-------------------------------------------------------------------------
388 388
389 389 def init_display_formatter(self):
390 390 super(TerminalInteractiveShell, self).init_display_formatter()
391 391 # terminal only supports plaintext
392 392 self.display_formatter.active_types = ['text/plain']
393 393
394 394 #-------------------------------------------------------------------------
395 395 # Things related to the terminal
396 396 #-------------------------------------------------------------------------
397 397
398 398 @property
399 399 def usable_screen_length(self):
400 400 if self.screen_length == 0:
401 401 return 0
402 402 else:
403 403 num_lines_bot = self.separate_in.count('\n')+1
404 404 return self.screen_length - num_lines_bot
405 405
406 406 def init_term_title(self):
407 407 # Enable or disable the terminal title.
408 408 if self.term_title:
409 409 toggle_set_term_title(True)
410 410 set_term_title('IPython: ' + abbrev_cwd())
411 411 else:
412 412 toggle_set_term_title(False)
413 413
414 414 #-------------------------------------------------------------------------
415 415 # Things related to aliases
416 416 #-------------------------------------------------------------------------
417 417
418 418 def init_alias(self):
419 419 # The parent class defines aliases that can be safely used with any
420 420 # frontend.
421 421 super(TerminalInteractiveShell, self).init_alias()
422 422
423 423 # Now define aliases that only make sense on the terminal, because they
424 424 # need direct access to the console in a way that we can't emulate in
425 425 # GUI or web frontend
426 426 if os.name == 'posix':
427 427 aliases = [('clear', 'clear'), ('more', 'more'), ('less', 'less'),
428 428 ('man', 'man')]
429 429 elif os.name == 'nt':
430 430 aliases = [('cls', 'cls')]
431 431
432 432
433 433 for name, cmd in aliases:
434 434 self.alias_manager.define_alias(name, cmd)
435 435
436 436 #-------------------------------------------------------------------------
437 437 # Things related to the banner and usage
438 438 #-------------------------------------------------------------------------
439 439
440 440 def _banner1_changed(self):
441 441 self.compute_banner()
442 442
443 443 def _banner2_changed(self):
444 444 self.compute_banner()
445 445
446 446 def _term_title_changed(self, name, new_value):
447 447 self.init_term_title()
448 448
449 449 def init_banner(self, banner1, banner2, display_banner):
450 450 if banner1 is not None:
451 451 self.banner1 = banner1
452 452 if banner2 is not None:
453 453 self.banner2 = banner2
454 454 if display_banner is not None:
455 455 self.display_banner = display_banner
456 456 self.compute_banner()
457 457
458 458 def show_banner(self, banner=None):
459 459 if banner is None:
460 460 banner = self.banner
461 461 self.write(banner)
462 462
463 463 def compute_banner(self):
464 464 self.banner = self.banner1
465 465 if self.profile and self.profile != 'default':
466 466 self.banner += '\nIPython profile: %s\n' % self.profile
467 467 if self.banner2:
468 468 self.banner += '\n' + self.banner2
469 469
470 470 def init_usage(self, usage=None):
471 471 if usage is None:
472 472 self.usage = interactive_usage
473 473 else:
474 474 self.usage = usage
475 475
476 476 #-------------------------------------------------------------------------
477 477 # Mainloop and code execution logic
478 478 #-------------------------------------------------------------------------
479 479
480 480 def mainloop(self, display_banner=None):
481 481 """Start the mainloop.
482 482
483 483 If an optional banner argument is given, it will override the
484 484 internally created default banner.
485 485 """
486 486
487 487 with nested(self.builtin_trap, self.display_trap):
488 488
489 489 while 1:
490 490 try:
491 491 self.interact(display_banner=display_banner)
492 492 #self.interact_with_readline()
493 493 # XXX for testing of a readline-decoupled repl loop, call
494 494 # interact_with_readline above
495 495 break
496 496 except KeyboardInterrupt:
497 497 # this should not be necessary, but KeyboardInterrupt
498 498 # handling seems rather unpredictable...
499 499 self.write("\nKeyboardInterrupt in interact()\n")
500 500
501 501 def _replace_rlhist_multiline(self, source_raw, hlen_before_cell):
502 502 """Store multiple lines as a single entry in history"""
503 503
504 504 # do nothing without readline or disabled multiline
505 505 if not self.has_readline or not self.multiline_history:
506 506 return hlen_before_cell
507 507
508 508 # windows rl has no remove_history_item
509 509 if not hasattr(self.readline, "remove_history_item"):
510 510 return hlen_before_cell
511 511
512 512 # skip empty cells
513 513 if not source_raw.rstrip():
514 514 return hlen_before_cell
515 515
516 516 # nothing changed do nothing, e.g. when rl removes consecutive dups
517 517 hlen = self.readline.get_current_history_length()
518 518 if hlen == hlen_before_cell:
519 519 return hlen_before_cell
520 520
521 521 for i in range(hlen - hlen_before_cell):
522 522 self.readline.remove_history_item(hlen - i - 1)
523 523 stdin_encoding = get_stream_enc(sys.stdin, 'utf-8')
524 524 self.readline.add_history(py3compat.unicode_to_str(source_raw.rstrip(),
525 525 stdin_encoding))
526 526 return self.readline.get_current_history_length()
527 527
528 528 def interact(self, display_banner=None):
529 529 """Closely emulate the interactive Python console."""
530 530
531 531 # batch run -> do not interact
532 532 if self.exit_now:
533 533 return
534 534
535 535 if display_banner is None:
536 536 display_banner = self.display_banner
537 537
538 538 if isinstance(display_banner, basestring):
539 539 self.show_banner(display_banner)
540 540 elif display_banner:
541 541 self.show_banner()
542 542
543 543 more = False
544 544
545 545 if self.has_readline:
546 546 self.readline_startup_hook(self.pre_readline)
547 547 hlen_b4_cell = self.readline.get_current_history_length()
548 548 else:
549 549 hlen_b4_cell = 0
550 550 # exit_now is set by a call to %Exit or %Quit, through the
551 551 # ask_exit callback.
552 552
553 553 while not self.exit_now:
554 554 self.hooks.pre_prompt_hook()
555 555 if more:
556 556 try:
557 557 prompt = self.prompt_manager.render('in2')
558 558 except:
559 559 self.showtraceback()
560 560 if self.autoindent:
561 561 self.rl_do_indent = True
562 562
563 563 else:
564 564 try:
565 565 prompt = self.separate_in + self.prompt_manager.render('in')
566 566 except:
567 567 self.showtraceback()
568 568 try:
569 569 line = self.raw_input(prompt)
570 570 if self.exit_now:
571 571 # quick exit on sys.std[in|out] close
572 572 break
573 573 if self.autoindent:
574 574 self.rl_do_indent = False
575 575
576 576 except KeyboardInterrupt:
577 577 #double-guard against keyboardinterrupts during kbdint handling
578 578 try:
579 579 self.write('\nKeyboardInterrupt\n')
580 580 source_raw = self.input_splitter.source_raw_reset()[1]
581 581 hlen_b4_cell = \
582 582 self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
583 583 more = False
584 584 except KeyboardInterrupt:
585 585 pass
586 586 except EOFError:
587 587 if self.autoindent:
588 588 self.rl_do_indent = False
589 589 if self.has_readline:
590 590 self.readline_startup_hook(None)
591 591 self.write('\n')
592 592 self.exit()
593 593 except bdb.BdbQuit:
594 594 warn('The Python debugger has exited with a BdbQuit exception.\n'
595 595 'Because of how pdb handles the stack, it is impossible\n'
596 596 'for IPython to properly format this particular exception.\n'
597 597 'IPython will resume normal operation.')
598 598 except:
599 599 # exceptions here are VERY RARE, but they can be triggered
600 600 # asynchronously by signal handlers, for example.
601 601 self.showtraceback()
602 602 else:
603 603 self.input_splitter.push(line)
604 604 more = self.input_splitter.push_accepts_more()
605 605 if (self.SyntaxTB.last_syntax_error and
606 606 self.autoedit_syntax):
607 607 self.edit_syntax_error()
608 608 if not more:
609 609 source_raw = self.input_splitter.source_raw_reset()[1]
610 610 self.run_cell(source_raw, store_history=True)
611 611 hlen_b4_cell = \
612 612 self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
613 613
614 614 # Turn off the exit flag, so the mainloop can be restarted if desired
615 615 self.exit_now = False
616 616
617 617 def raw_input(self, prompt=''):
618 618 """Write a prompt and read a line.
619 619
620 620 The returned line does not include the trailing newline.
621 621 When the user enters the EOF key sequence, EOFError is raised.
622 622
623 623 Optional inputs:
624 624
625 625 - prompt(''): a string to be printed to prompt the user.
626 626
627 627 - continue_prompt(False): whether this line is the first one or a
628 628 continuation in a sequence of inputs.
629 629 """
630 630 # Code run by the user may have modified the readline completer state.
631 631 # We must ensure that our completer is back in place.
632 632
633 633 if self.has_readline:
634 634 self.set_readline_completer()
635 635
636 636 # raw_input expects str, but we pass it unicode sometimes
637 637 prompt = py3compat.cast_bytes_py2(prompt)
638 638
639 639 try:
640 640 line = py3compat.str_to_unicode(self.raw_input_original(prompt))
641 641 except ValueError:
642 642 warn("\n********\nYou or a %run:ed script called sys.stdin.close()"
643 643 " or sys.stdout.close()!\nExiting IPython!\n")
644 644 self.ask_exit()
645 645 return ""
646 646
647 647 # Try to be reasonably smart about not re-indenting pasted input more
648 648 # than necessary. We do this by trimming out the auto-indent initial
649 649 # spaces, if the user's actual input started itself with whitespace.
650 650 if self.autoindent:
651 651 if num_ini_spaces(line) > self.indent_current_nsp:
652 652 line = line[self.indent_current_nsp:]
653 653 self.indent_current_nsp = 0
654 654
655 655 return line
656 656
657 657 #-------------------------------------------------------------------------
658 658 # Methods to support auto-editing of SyntaxErrors.
659 659 #-------------------------------------------------------------------------
660 660
661 661 def edit_syntax_error(self):
662 662 """The bottom half of the syntax error handler called in the main loop.
663 663
664 664 Loop until syntax error is fixed or user cancels.
665 665 """
666 666
667 667 while self.SyntaxTB.last_syntax_error:
668 668 # copy and clear last_syntax_error
669 669 err = self.SyntaxTB.clear_err_state()
670 670 if not self._should_recompile(err):
671 671 return
672 672 try:
673 673 # may set last_syntax_error again if a SyntaxError is raised
674 674 self.safe_execfile(err.filename,self.user_ns)
675 675 except:
676 676 self.showtraceback()
677 677 else:
678 678 try:
679 679 f = open(err.filename)
680 680 try:
681 681 # This should be inside a display_trap block and I
682 682 # think it is.
683 683 sys.displayhook(f.read())
684 684 finally:
685 685 f.close()
686 686 except:
687 687 self.showtraceback()
688 688
689 689 def _should_recompile(self,e):
690 690 """Utility routine for edit_syntax_error"""
691 691
692 692 if e.filename in ('<ipython console>','<input>','<string>',
693 693 '<console>','<BackgroundJob compilation>',
694 694 None):
695 695
696 696 return False
697 697 try:
698 698 if (self.autoedit_syntax and
699 699 not self.ask_yes_no('Return to editor to correct syntax error? '
700 700 '[Y/n] ','y')):
701 701 return False
702 702 except EOFError:
703 703 return False
704 704
705 705 def int0(x):
706 706 try:
707 707 return int(x)
708 708 except TypeError:
709 709 return 0
710 710 # always pass integer line and offset values to editor hook
711 711 try:
712 712 self.hooks.fix_error_editor(e.filename,
713 713 int0(e.lineno),int0(e.offset),e.msg)
714 714 except TryNext:
715 715 warn('Could not open editor')
716 716 return False
717 717 return True
718 718
719 719 #-------------------------------------------------------------------------
720 720 # Things related to exiting
721 721 #-------------------------------------------------------------------------
722 722
723 723 def ask_exit(self):
724 724 """ Ask the shell to exit. Can be overiden and used as a callback. """
725 725 self.exit_now = True
726 726
727 727 def exit(self):
728 728 """Handle interactive exit.
729 729
730 730 This method calls the ask_exit callback."""
731 731 if self.confirm_exit:
732 732 if self.ask_yes_no('Do you really want to exit ([y]/n)?','y'):
733 733 self.ask_exit()
734 734 else:
735 735 self.ask_exit()
736 736
737 737 #-------------------------------------------------------------------------
738 738 # Things related to magics
739 739 #-------------------------------------------------------------------------
740 740
741 741 def init_magics(self):
742 742 super(TerminalInteractiveShell, self).init_magics()
743 743 self.register_magics(TerminalMagics)
744 744
745 745 def showindentationerror(self):
746 746 super(TerminalInteractiveShell, self).showindentationerror()
747 747 if not self.using_paste_magics:
748 748 print("If you want to paste code into IPython, try the "
749 749 "%paste and %cpaste magic functions.")
750 750
751 751
752 752 InteractiveShellABC.register(TerminalInteractiveShell)
General Comments 0
You need to be logged in to leave comments. Login now