##// END OF EJS Templates
Expose IPython machinery for testing code completeness
Thomas Kluyver -
Show More
@@ -1,638 +1,654 b''
1 1 """Input handling and transformation machinery.
2 2
3 3 The first class in this module, :class:`InputSplitter`, is designed to tell when
4 4 input from a line-oriented frontend is complete and should be executed, and when
5 5 the user should be prompted for another line of code instead. The name 'input
6 6 splitter' is largely for historical reasons.
7 7
8 8 A companion, :class:`IPythonInputSplitter`, provides the same functionality but
9 9 with full support for the extended IPython syntax (magics, system calls, etc).
10 10 The code to actually do these transformations is in :mod:`IPython.core.inputtransformer`.
11 11 :class:`IPythonInputSplitter` feeds the raw code to the transformers in order
12 12 and stores the results.
13 13
14 14 For more details, see the class docstrings below.
15 15
16 16 Authors
17 17 -------
18 18
19 19 * Fernando Perez
20 20 * Brian Granger
21 21 * Thomas Kluyver
22 22 """
23 23 #-----------------------------------------------------------------------------
24 24 # Copyright (C) 2010 The IPython Development Team
25 25 #
26 26 # Distributed under the terms of the BSD License. The full license is in
27 27 # the file COPYING, distributed as part of this software.
28 28 #-----------------------------------------------------------------------------
29 29
30 30 #-----------------------------------------------------------------------------
31 31 # Imports
32 32 #-----------------------------------------------------------------------------
33 33 # stdlib
34 34 import ast
35 35 import codeop
36 36 import re
37 37 import sys
38 38
39 39 # IPython modules
40 40 from IPython.utils.py3compat import cast_unicode
41 41 from IPython.core.inputtransformer import (leading_indent,
42 42 classic_prompt,
43 43 ipy_prompt,
44 44 strip_encoding_cookie,
45 45 cellmagic,
46 46 assemble_logical_lines,
47 47 help_end,
48 48 escaped_commands,
49 49 assign_from_magic,
50 50 assign_from_system,
51 51 assemble_python_lines,
52 52 )
53 53
54 54 # These are available in this module for backwards compatibility.
55 55 from IPython.core.inputtransformer import (ESC_SHELL, ESC_SH_CAP, ESC_HELP,
56 56 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,
57 57 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN, ESC_SEQUENCES)
58 58
59 59 #-----------------------------------------------------------------------------
60 60 # Utilities
61 61 #-----------------------------------------------------------------------------
62 62
63 63 # FIXME: These are general-purpose utilities that later can be moved to the
64 64 # general ward. Kept here for now because we're being very strict about test
65 65 # coverage with this code, and this lets us ensure that we keep 100% coverage
66 66 # while developing.
67 67
68 68 # compiled regexps for autoindent management
69 69 dedent_re = re.compile('|'.join([
70 70 r'^\s+raise(\s.*)?$', # raise statement (+ space + other stuff, maybe)
71 71 r'^\s+raise\([^\)]*\).*$', # wacky raise with immediate open paren
72 72 r'^\s+return(\s.*)?$', # normal return (+ space + other stuff, maybe)
73 73 r'^\s+return\([^\)]*\).*$', # wacky return with immediate open paren
74 74 r'^\s+pass\s*$', # pass (optionally followed by trailing spaces)
75 75 r'^\s+break\s*$', # break (optionally followed by trailing spaces)
76 76 r'^\s+continue\s*$', # continue (optionally followed by trailing spaces)
77 77 ]))
78 78 ini_spaces_re = re.compile(r'^([ \t\r\f\v]+)')
79 79
80 80 # regexp to match pure comment lines so we don't accidentally insert 'if 1:'
81 81 # before pure comments
82 82 comment_line_re = re.compile('^\s*\#')
83 83
84 84
85 85 def num_ini_spaces(s):
86 86 """Return the number of initial spaces in a string.
87 87
88 88 Note that tabs are counted as a single space. For now, we do *not* support
89 89 mixing of tabs and spaces in the user's input.
90 90
91 91 Parameters
92 92 ----------
93 93 s : string
94 94
95 95 Returns
96 96 -------
97 97 n : int
98 98 """
99 99
100 100 ini_spaces = ini_spaces_re.match(s)
101 101 if ini_spaces:
102 102 return ini_spaces.end()
103 103 else:
104 104 return 0
105 105
106 106 def last_blank(src):
107 107 """Determine if the input source ends in a blank.
108 108
109 109 A blank is either a newline or a line consisting of whitespace.
110 110
111 111 Parameters
112 112 ----------
113 113 src : string
114 114 A single or multiline string.
115 115 """
116 116 if not src: return False
117 117 ll = src.splitlines()[-1]
118 118 return (ll == '') or ll.isspace()
119 119
120 120
121 121 last_two_blanks_re = re.compile(r'\n\s*\n\s*$', re.MULTILINE)
122 122 last_two_blanks_re2 = re.compile(r'.+\n\s*\n\s+$', re.MULTILINE)
123 123
124 124 def last_two_blanks(src):
125 125 """Determine if the input source ends in two blanks.
126 126
127 127 A blank is either a newline or a line consisting of whitespace.
128 128
129 129 Parameters
130 130 ----------
131 131 src : string
132 132 A single or multiline string.
133 133 """
134 134 if not src: return False
135 135 # The logic here is tricky: I couldn't get a regexp to work and pass all
136 136 # the tests, so I took a different approach: split the source by lines,
137 137 # grab the last two and prepend '###\n' as a stand-in for whatever was in
138 138 # the body before the last two lines. Then, with that structure, it's
139 139 # possible to analyze with two regexps. Not the most elegant solution, but
140 140 # it works. If anyone tries to change this logic, make sure to validate
141 141 # the whole test suite first!
142 142 new_src = '\n'.join(['###\n'] + src.splitlines()[-2:])
143 143 return (bool(last_two_blanks_re.match(new_src)) or
144 144 bool(last_two_blanks_re2.match(new_src)) )
145 145
146 146
147 147 def remove_comments(src):
148 148 """Remove all comments from input source.
149 149
150 150 Note: comments are NOT recognized inside of strings!
151 151
152 152 Parameters
153 153 ----------
154 154 src : string
155 155 A single or multiline input string.
156 156
157 157 Returns
158 158 -------
159 159 String with all Python comments removed.
160 160 """
161 161
162 162 return re.sub('#.*', '', src)
163 163
164 164
165 165 def get_input_encoding():
166 166 """Return the default standard input encoding.
167 167
168 168 If sys.stdin has no encoding, 'ascii' is returned."""
169 169 # There are strange environments for which sys.stdin.encoding is None. We
170 170 # ensure that a valid encoding is returned.
171 171 encoding = getattr(sys.stdin, 'encoding', None)
172 172 if encoding is None:
173 173 encoding = 'ascii'
174 174 return encoding
175 175
176 176 #-----------------------------------------------------------------------------
177 177 # Classes and functions for normal Python syntax handling
178 178 #-----------------------------------------------------------------------------
179 179
180 180 class InputSplitter(object):
181 181 r"""An object that can accumulate lines of Python source before execution.
182 182
183 183 This object is designed to be fed python source line-by-line, using
184 184 :meth:`push`. It will return on each push whether the currently pushed
185 185 code could be executed already. In addition, it provides a method called
186 186 :meth:`push_accepts_more` that can be used to query whether more input
187 187 can be pushed into a single interactive block.
188 188
189 189 This is a simple example of how an interactive terminal-based client can use
190 190 this tool::
191 191
192 192 isp = InputSplitter()
193 193 while isp.push_accepts_more():
194 194 indent = ' '*isp.indent_spaces
195 195 prompt = '>>> ' + indent
196 196 line = indent + raw_input(prompt)
197 197 isp.push(line)
198 198 print 'Input source was:\n', isp.source_reset(),
199 199 """
200 200 # Number of spaces of indentation computed from input that has been pushed
201 201 # so far. This is the attributes callers should query to get the current
202 202 # indentation level, in order to provide auto-indent facilities.
203 203 indent_spaces = 0
204 204 # String, indicating the default input encoding. It is computed by default
205 205 # at initialization time via get_input_encoding(), but it can be reset by a
206 206 # client with specific knowledge of the encoding.
207 207 encoding = ''
208 208 # String where the current full source input is stored, properly encoded.
209 209 # Reading this attribute is the normal way of querying the currently pushed
210 210 # source code, that has been properly encoded.
211 211 source = ''
212 212 # Code object corresponding to the current source. It is automatically
213 213 # synced to the source, so it can be queried at any time to obtain the code
214 214 # object; it will be None if the source doesn't compile to valid Python.
215 215 code = None
216 216
217 217 # Private attributes
218 218
219 219 # List with lines of input accumulated so far
220 220 _buffer = None
221 221 # Command compiler
222 222 _compile = None
223 223 # Mark when input has changed indentation all the way back to flush-left
224 224 _full_dedent = False
225 225 # Boolean indicating whether the current block is complete
226 226 _is_complete = None
227 227
228 228 def __init__(self):
229 229 """Create a new InputSplitter instance.
230 230 """
231 231 self._buffer = []
232 232 self._compile = codeop.CommandCompiler()
233 233 self.encoding = get_input_encoding()
234 234
235 235 def reset(self):
236 236 """Reset the input buffer and associated state."""
237 237 self.indent_spaces = 0
238 238 self._buffer[:] = []
239 239 self.source = ''
240 240 self.code = None
241 241 self._is_complete = False
242 242 self._full_dedent = False
243 243
244 244 def source_reset(self):
245 245 """Return the input source and perform a full reset.
246 246 """
247 247 out = self.source
248 248 self.reset()
249 249 return out
250 250
251 def is_complete(self, source):
252 """Return whether a block of code is ready to execute, or should be continued
253
254 This is a non-stateful API, and will reset the state of this InputSplitter.
255 """
256 self.reset()
257 try:
258 self.push(source)
259 return not self.push_accepts_more()
260 except SyntaxError:
261 # Transformers in IPythonInputSplitter can raise SyntaxError,
262 # which push() will not catch.
263 return True
264 finally:
265 self.reset()
266
251 267 def push(self, lines):
252 268 """Push one or more lines of input.
253 269
254 270 This stores the given lines and returns a status code indicating
255 271 whether the code forms a complete Python block or not.
256 272
257 273 Any exceptions generated in compilation are swallowed, but if an
258 274 exception was produced, the method returns True.
259 275
260 276 Parameters
261 277 ----------
262 278 lines : string
263 279 One or more lines of Python input.
264 280
265 281 Returns
266 282 -------
267 283 is_complete : boolean
268 284 True if the current input source (the result of the current input
269 285 plus prior inputs) forms a complete Python execution block. Note that
270 286 this value is also stored as a private attribute (``_is_complete``), so it
271 287 can be queried at any time.
272 288 """
273 289 self._store(lines)
274 290 source = self.source
275 291
276 292 # Before calling _compile(), reset the code object to None so that if an
277 293 # exception is raised in compilation, we don't mislead by having
278 294 # inconsistent code/source attributes.
279 295 self.code, self._is_complete = None, None
280 296
281 297 # Honor termination lines properly
282 298 if source.endswith('\\\n'):
283 299 return False
284 300
285 301 self._update_indent(lines)
286 302 try:
287 303 self.code = self._compile(source, symbol="exec")
288 304 # Invalid syntax can produce any of a number of different errors from
289 305 # inside the compiler, so we have to catch them all. Syntax errors
290 306 # immediately produce a 'ready' block, so the invalid Python can be
291 307 # sent to the kernel for evaluation with possible ipython
292 308 # special-syntax conversion.
293 309 except (SyntaxError, OverflowError, ValueError, TypeError,
294 310 MemoryError):
295 311 self._is_complete = True
296 312 else:
297 313 # Compilation didn't produce any exceptions (though it may not have
298 314 # given a complete code object)
299 315 self._is_complete = self.code is not None
300 316
301 317 return self._is_complete
302 318
303 319 def push_accepts_more(self):
304 320 """Return whether a block of interactive input can accept more input.
305 321
306 322 This method is meant to be used by line-oriented frontends, who need to
307 323 guess whether a block is complete or not based solely on prior and
308 324 current input lines. The InputSplitter considers it has a complete
309 325 interactive block and will not accept more input when either:
310 326
311 327 * A SyntaxError is raised
312 328
313 329 * The code is complete and consists of a single line or a single
314 330 non-compound statement
315 331
316 332 * The code is complete and has a blank line at the end
317 333
318 334 If the current input produces a syntax error, this method immediately
319 335 returns False but does *not* raise the syntax error exception, as
320 336 typically clients will want to send invalid syntax to an execution
321 337 backend which might convert the invalid syntax into valid Python via
322 338 one of the dynamic IPython mechanisms.
323 339 """
324 340
325 341 # With incomplete input, unconditionally accept more
326 342 # A syntax error also sets _is_complete to True - see push()
327 343 if not self._is_complete:
328 344 #print("Not complete") # debug
329 345 return True
330 346
331 347 # The user can make any (complete) input execute by leaving a blank line
332 348 last_line = self.source.splitlines()[-1]
333 349 if (not last_line) or last_line.isspace():
334 350 #print("Blank line") # debug
335 351 return False
336 352
337 353 # If there's just a single line or AST node, and we're flush left, as is
338 354 # the case after a simple statement such as 'a=1', we want to execute it
339 355 # straight away.
340 356 if self.indent_spaces==0:
341 357 if len(self.source.splitlines()) <= 1:
342 358 return False
343 359
344 360 try:
345 361 code_ast = ast.parse(u''.join(self._buffer))
346 362 except Exception:
347 363 #print("Can't parse AST") # debug
348 364 return False
349 365 else:
350 366 if len(code_ast.body) == 1 and \
351 367 not hasattr(code_ast.body[0], 'body'):
352 368 #print("Simple statement") # debug
353 369 return False
354 370
355 371 # General fallback - accept more code
356 372 return True
357 373
358 374 #------------------------------------------------------------------------
359 375 # Private interface
360 376 #------------------------------------------------------------------------
361 377
362 378 def _find_indent(self, line):
363 379 """Compute the new indentation level for a single line.
364 380
365 381 Parameters
366 382 ----------
367 383 line : str
368 384 A single new line of non-whitespace, non-comment Python input.
369 385
370 386 Returns
371 387 -------
372 388 indent_spaces : int
373 389 New value for the indent level (it may be equal to self.indent_spaces
374 390 if indentation doesn't change.
375 391
376 392 full_dedent : boolean
377 393 Whether the new line causes a full flush-left dedent.
378 394 """
379 395 indent_spaces = self.indent_spaces
380 396 full_dedent = self._full_dedent
381 397
382 398 inisp = num_ini_spaces(line)
383 399 if inisp < indent_spaces:
384 400 indent_spaces = inisp
385 401 if indent_spaces <= 0:
386 402 #print 'Full dedent in text',self.source # dbg
387 403 full_dedent = True
388 404
389 405 if line.rstrip()[-1] == ':':
390 406 indent_spaces += 4
391 407 elif dedent_re.match(line):
392 408 indent_spaces -= 4
393 409 if indent_spaces <= 0:
394 410 full_dedent = True
395 411
396 412 # Safety
397 413 if indent_spaces < 0:
398 414 indent_spaces = 0
399 415 #print 'safety' # dbg
400 416
401 417 return indent_spaces, full_dedent
402 418
403 419 def _update_indent(self, lines):
404 420 for line in remove_comments(lines).splitlines():
405 421 if line and not line.isspace():
406 422 self.indent_spaces, self._full_dedent = self._find_indent(line)
407 423
408 424 def _store(self, lines, buffer=None, store='source'):
409 425 """Store one or more lines of input.
410 426
411 427 If input lines are not newline-terminated, a newline is automatically
412 428 appended."""
413 429
414 430 if buffer is None:
415 431 buffer = self._buffer
416 432
417 433 if lines.endswith('\n'):
418 434 buffer.append(lines)
419 435 else:
420 436 buffer.append(lines+'\n')
421 437 setattr(self, store, self._set_source(buffer))
422 438
423 439 def _set_source(self, buffer):
424 440 return u''.join(buffer)
425 441
426 442
427 443 class IPythonInputSplitter(InputSplitter):
428 444 """An input splitter that recognizes all of IPython's special syntax."""
429 445
430 446 # String with raw, untransformed input.
431 447 source_raw = ''
432 448
433 449 # Flag to track when a transformer has stored input that it hasn't given
434 450 # back yet.
435 451 transformer_accumulating = False
436 452
437 453 # Flag to track when assemble_python_lines has stored input that it hasn't
438 454 # given back yet.
439 455 within_python_line = False
440 456
441 457 # Private attributes
442 458
443 459 # List with lines of raw input accumulated so far.
444 460 _buffer_raw = None
445 461
446 462 def __init__(self, line_input_checker=True, physical_line_transforms=None,
447 463 logical_line_transforms=None, python_line_transforms=None):
448 464 super(IPythonInputSplitter, self).__init__()
449 465 self._buffer_raw = []
450 466 self._validate = True
451 467
452 468 if physical_line_transforms is not None:
453 469 self.physical_line_transforms = physical_line_transforms
454 470 else:
455 471 self.physical_line_transforms = [
456 472 leading_indent(),
457 473 classic_prompt(),
458 474 ipy_prompt(),
459 475 strip_encoding_cookie(),
460 476 cellmagic(end_on_blank_line=line_input_checker),
461 477 ]
462 478
463 479 self.assemble_logical_lines = assemble_logical_lines()
464 480 if logical_line_transforms is not None:
465 481 self.logical_line_transforms = logical_line_transforms
466 482 else:
467 483 self.logical_line_transforms = [
468 484 help_end(),
469 485 escaped_commands(),
470 486 assign_from_magic(),
471 487 assign_from_system(),
472 488 ]
473 489
474 490 self.assemble_python_lines = assemble_python_lines()
475 491 if python_line_transforms is not None:
476 492 self.python_line_transforms = python_line_transforms
477 493 else:
478 494 # We don't use any of these at present
479 495 self.python_line_transforms = []
480 496
481 497 @property
482 498 def transforms(self):
483 499 "Quick access to all transformers."
484 500 return self.physical_line_transforms + \
485 501 [self.assemble_logical_lines] + self.logical_line_transforms + \
486 502 [self.assemble_python_lines] + self.python_line_transforms
487 503
488 504 @property
489 505 def transforms_in_use(self):
490 506 """Transformers, excluding logical line transformers if we're in a
491 507 Python line."""
492 508 t = self.physical_line_transforms[:]
493 509 if not self.within_python_line:
494 510 t += [self.assemble_logical_lines] + self.logical_line_transforms
495 511 return t + [self.assemble_python_lines] + self.python_line_transforms
496 512
497 513 def reset(self):
498 514 """Reset the input buffer and associated state."""
499 515 super(IPythonInputSplitter, self).reset()
500 516 self._buffer_raw[:] = []
501 517 self.source_raw = ''
502 518 self.transformer_accumulating = False
503 519 self.within_python_line = False
504 520
505 521 for t in self.transforms:
506 522 try:
507 523 t.reset()
508 524 except SyntaxError:
509 525 # Nothing that calls reset() expects to handle transformer
510 526 # errors
511 527 pass
512 528
513 529 def flush_transformers(self):
514 530 def _flush(transform, out):
515 531 if out is not None:
516 532 tmp = transform.push(out)
517 533 return tmp or transform.reset() or None
518 534 else:
519 535 return transform.reset() or None
520 536
521 537 out = None
522 538 for t in self.transforms_in_use:
523 539 out = _flush(t, out)
524 540
525 541 if out is not None:
526 542 self._store(out)
527 543
528 544 def raw_reset(self):
529 545 """Return raw input only and perform a full reset.
530 546 """
531 547 out = self.source_raw
532 548 self.reset()
533 549 return out
534 550
535 551 def source_reset(self):
536 552 try:
537 553 self.flush_transformers()
538 554 return self.source
539 555 finally:
540 556 self.reset()
541 557
542 558 def push_accepts_more(self):
543 559 if self.transformer_accumulating:
544 560 return True
545 561 else:
546 562 return super(IPythonInputSplitter, self).push_accepts_more()
547 563
548 564 def transform_cell(self, cell):
549 565 """Process and translate a cell of input.
550 566 """
551 567 self.reset()
552 568 try:
553 569 self.push(cell)
554 570 self.flush_transformers()
555 571 return self.source
556 572 finally:
557 573 self.reset()
558 574
559 575 def push(self, lines):
560 576 """Push one or more lines of IPython input.
561 577
562 578 This stores the given lines and returns a status code indicating
563 579 whether the code forms a complete Python block or not, after processing
564 580 all input lines for special IPython syntax.
565 581
566 582 Any exceptions generated in compilation are swallowed, but if an
567 583 exception was produced, the method returns True.
568 584
569 585 Parameters
570 586 ----------
571 587 lines : string
572 588 One or more lines of Python input.
573 589
574 590 Returns
575 591 -------
576 592 is_complete : boolean
577 593 True if the current input source (the result of the current input
578 594 plus prior inputs) forms a complete Python execution block. Note that
579 595 this value is also stored as a private attribute (_is_complete), so it
580 596 can be queried at any time.
581 597 """
582 598
583 599 # We must ensure all input is pure unicode
584 600 lines = cast_unicode(lines, self.encoding)
585 601
586 602 # ''.splitlines() --> [], but we need to push the empty line to transformers
587 603 lines_list = lines.splitlines()
588 604 if not lines_list:
589 605 lines_list = ['']
590 606
591 607 # Store raw source before applying any transformations to it. Note
592 608 # that this must be done *after* the reset() call that would otherwise
593 609 # flush the buffer.
594 610 self._store(lines, self._buffer_raw, 'source_raw')
595 611
596 612 for line in lines_list:
597 613 out = self.push_line(line)
598 614
599 615 return out
600 616
601 617 def push_line(self, line):
602 618 buf = self._buffer
603 619
604 620 def _accumulating(dbg):
605 621 #print(dbg)
606 622 self.transformer_accumulating = True
607 623 return False
608 624
609 625 for transformer in self.physical_line_transforms:
610 626 line = transformer.push(line)
611 627 if line is None:
612 628 return _accumulating(transformer)
613 629
614 630 if not self.within_python_line:
615 631 line = self.assemble_logical_lines.push(line)
616 632 if line is None:
617 633 return _accumulating('acc logical line')
618 634
619 635 for transformer in self.logical_line_transforms:
620 636 line = transformer.push(line)
621 637 if line is None:
622 638 return _accumulating(transformer)
623 639
624 640 line = self.assemble_python_lines.push(line)
625 641 if line is None:
626 642 self.within_python_line = True
627 643 return _accumulating('acc python line')
628 644 else:
629 645 self.within_python_line = False
630 646
631 647 for transformer in self.python_line_transforms:
632 648 line = transformer.push(line)
633 649 if line is None:
634 650 return _accumulating(transformer)
635 651
636 652 #print("transformers clear") #debug
637 653 self.transformer_accumulating = False
638 654 return super(IPythonInputSplitter, self).push(line)
@@ -1,585 +1,592 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 from __future__ import print_function
10 10 #-----------------------------------------------------------------------------
11 11 # Copyright (C) 2010-2011 The IPython Development Team
12 12 #
13 13 # Distributed under the terms of the BSD License. The full license is in
14 14 # the file COPYING, distributed as part of this software.
15 15 #-----------------------------------------------------------------------------
16 16
17 17 #-----------------------------------------------------------------------------
18 18 # Imports
19 19 #-----------------------------------------------------------------------------
20 20 # stdlib
21 21 import unittest
22 22 import sys
23 23
24 24 # Third party
25 25 import nose.tools as nt
26 26
27 27 # Our own
28 28 from IPython.core import inputsplitter as isp
29 29 from IPython.core.tests.test_inputtransformer import syntax, syntax_ml
30 30 from IPython.testing import tools as tt
31 31 from IPython.utils import py3compat
32 32 from IPython.utils.py3compat import string_types, input
33 33
34 34 #-----------------------------------------------------------------------------
35 35 # Semi-complete examples (also used as tests)
36 36 #-----------------------------------------------------------------------------
37 37
38 38 # Note: at the bottom, there's a slightly more complete version of this that
39 39 # can be useful during development of code here.
40 40
41 41 def mini_interactive_loop(input_func):
42 42 """Minimal example of the logic of an interactive interpreter loop.
43 43
44 44 This serves as an example, and it is used by the test system with a fake
45 45 raw_input that simulates interactive input."""
46 46
47 47 from IPython.core.inputsplitter import InputSplitter
48 48
49 49 isp = InputSplitter()
50 50 # In practice, this input loop would be wrapped in an outside loop to read
51 51 # input indefinitely, until some exit/quit command was issued. Here we
52 52 # only illustrate the basic inner loop.
53 53 while isp.push_accepts_more():
54 54 indent = ' '*isp.indent_spaces
55 55 prompt = '>>> ' + indent
56 56 line = indent + input_func(prompt)
57 57 isp.push(line)
58 58
59 59 # Here we just return input so we can use it in a test suite, but a real
60 60 # interpreter would instead send it for execution somewhere.
61 61 src = isp.source_reset()
62 62 #print 'Input source was:\n', src # dbg
63 63 return src
64 64
65 65 #-----------------------------------------------------------------------------
66 66 # Test utilities, just for local use
67 67 #-----------------------------------------------------------------------------
68 68
69 69 def assemble(block):
70 70 """Assemble a block into multi-line sub-blocks."""
71 71 return ['\n'.join(sub_block)+'\n' for sub_block in block]
72 72
73 73
74 74 def pseudo_input(lines):
75 75 """Return a function that acts like raw_input but feeds the input list."""
76 76 ilines = iter(lines)
77 77 def raw_in(prompt):
78 78 try:
79 79 return next(ilines)
80 80 except StopIteration:
81 81 return ''
82 82 return raw_in
83 83
84 84 #-----------------------------------------------------------------------------
85 85 # Tests
86 86 #-----------------------------------------------------------------------------
87 87 def test_spaces():
88 88 tests = [('', 0),
89 89 (' ', 1),
90 90 ('\n', 0),
91 91 (' \n', 1),
92 92 ('x', 0),
93 93 (' x', 1),
94 94 (' x',2),
95 95 (' x',4),
96 96 # Note: tabs are counted as a single whitespace!
97 97 ('\tx', 1),
98 98 ('\t x', 2),
99 99 ]
100 100 tt.check_pairs(isp.num_ini_spaces, tests)
101 101
102 102
103 103 def test_remove_comments():
104 104 tests = [('text', 'text'),
105 105 ('text # comment', 'text '),
106 106 ('text # comment\n', 'text \n'),
107 107 ('text # comment \n', 'text \n'),
108 108 ('line # c \nline\n','line \nline\n'),
109 109 ('line # c \nline#c2 \nline\nline #c\n\n',
110 110 'line \nline\nline\nline \n\n'),
111 111 ]
112 112 tt.check_pairs(isp.remove_comments, tests)
113 113
114 114
115 115 def test_get_input_encoding():
116 116 encoding = isp.get_input_encoding()
117 117 nt.assert_true(isinstance(encoding, string_types))
118 118 # simple-minded check that at least encoding a simple string works with the
119 119 # encoding we got.
120 120 nt.assert_equal(u'test'.encode(encoding), b'test')
121 121
122 122
123 123 class NoInputEncodingTestCase(unittest.TestCase):
124 124 def setUp(self):
125 125 self.old_stdin = sys.stdin
126 126 class X: pass
127 127 fake_stdin = X()
128 128 sys.stdin = fake_stdin
129 129
130 130 def test(self):
131 131 # Verify that if sys.stdin has no 'encoding' attribute we do the right
132 132 # thing
133 133 enc = isp.get_input_encoding()
134 134 self.assertEqual(enc, 'ascii')
135 135
136 136 def tearDown(self):
137 137 sys.stdin = self.old_stdin
138 138
139 139
140 140 class InputSplitterTestCase(unittest.TestCase):
141 141 def setUp(self):
142 142 self.isp = isp.InputSplitter()
143 143
144 144 def test_reset(self):
145 145 isp = self.isp
146 146 isp.push('x=1')
147 147 isp.reset()
148 148 self.assertEqual(isp._buffer, [])
149 149 self.assertEqual(isp.indent_spaces, 0)
150 150 self.assertEqual(isp.source, '')
151 151 self.assertEqual(isp.code, None)
152 152 self.assertEqual(isp._is_complete, False)
153 153
154 154 def test_source(self):
155 155 self.isp._store('1')
156 156 self.isp._store('2')
157 157 self.assertEqual(self.isp.source, '1\n2\n')
158 158 self.assertTrue(len(self.isp._buffer)>0)
159 159 self.assertEqual(self.isp.source_reset(), '1\n2\n')
160 160 self.assertEqual(self.isp._buffer, [])
161 161 self.assertEqual(self.isp.source, '')
162 162
163 163 def test_indent(self):
164 164 isp = self.isp # shorthand
165 165 isp.push('x=1')
166 166 self.assertEqual(isp.indent_spaces, 0)
167 167 isp.push('if 1:\n x=1')
168 168 self.assertEqual(isp.indent_spaces, 4)
169 169 isp.push('y=2\n')
170 170 self.assertEqual(isp.indent_spaces, 0)
171 171
172 172 def test_indent2(self):
173 173 isp = self.isp
174 174 isp.push('if 1:')
175 175 self.assertEqual(isp.indent_spaces, 4)
176 176 isp.push(' x=1')
177 177 self.assertEqual(isp.indent_spaces, 4)
178 178 # Blank lines shouldn't change the indent level
179 179 isp.push(' '*2)
180 180 self.assertEqual(isp.indent_spaces, 4)
181 181
182 182 def test_indent3(self):
183 183 isp = self.isp
184 184 # When a multiline statement contains parens or multiline strings, we
185 185 # shouldn't get confused.
186 186 isp.push("if 1:")
187 187 isp.push(" x = (1+\n 2)")
188 188 self.assertEqual(isp.indent_spaces, 4)
189 189
190 190 def test_indent4(self):
191 191 isp = self.isp
192 192 # whitespace after ':' should not screw up indent level
193 193 isp.push('if 1: \n x=1')
194 194 self.assertEqual(isp.indent_spaces, 4)
195 195 isp.push('y=2\n')
196 196 self.assertEqual(isp.indent_spaces, 0)
197 197 isp.push('if 1:\t\n x=1')
198 198 self.assertEqual(isp.indent_spaces, 4)
199 199 isp.push('y=2\n')
200 200 self.assertEqual(isp.indent_spaces, 0)
201 201
202 202 def test_dedent_pass(self):
203 203 isp = self.isp # shorthand
204 204 # should NOT cause dedent
205 205 isp.push('if 1:\n passes = 5')
206 206 self.assertEqual(isp.indent_spaces, 4)
207 207 isp.push('if 1:\n pass')
208 208 self.assertEqual(isp.indent_spaces, 0)
209 209 isp.push('if 1:\n pass ')
210 210 self.assertEqual(isp.indent_spaces, 0)
211 211
212 212 def test_dedent_break(self):
213 213 isp = self.isp # shorthand
214 214 # should NOT cause dedent
215 215 isp.push('while 1:\n breaks = 5')
216 216 self.assertEqual(isp.indent_spaces, 4)
217 217 isp.push('while 1:\n break')
218 218 self.assertEqual(isp.indent_spaces, 0)
219 219 isp.push('while 1:\n break ')
220 220 self.assertEqual(isp.indent_spaces, 0)
221 221
222 222 def test_dedent_continue(self):
223 223 isp = self.isp # shorthand
224 224 # should NOT cause dedent
225 225 isp.push('while 1:\n continues = 5')
226 226 self.assertEqual(isp.indent_spaces, 4)
227 227 isp.push('while 1:\n continue')
228 228 self.assertEqual(isp.indent_spaces, 0)
229 229 isp.push('while 1:\n continue ')
230 230 self.assertEqual(isp.indent_spaces, 0)
231 231
232 232 def test_dedent_raise(self):
233 233 isp = self.isp # shorthand
234 234 # should NOT cause dedent
235 235 isp.push('if 1:\n raised = 4')
236 236 self.assertEqual(isp.indent_spaces, 4)
237 237 isp.push('if 1:\n raise TypeError()')
238 238 self.assertEqual(isp.indent_spaces, 0)
239 239 isp.push('if 1:\n raise')
240 240 self.assertEqual(isp.indent_spaces, 0)
241 241 isp.push('if 1:\n raise ')
242 242 self.assertEqual(isp.indent_spaces, 0)
243 243
244 244 def test_dedent_return(self):
245 245 isp = self.isp # shorthand
246 246 # should NOT cause dedent
247 247 isp.push('if 1:\n returning = 4')
248 248 self.assertEqual(isp.indent_spaces, 4)
249 249 isp.push('if 1:\n return 5 + 493')
250 250 self.assertEqual(isp.indent_spaces, 0)
251 251 isp.push('if 1:\n return')
252 252 self.assertEqual(isp.indent_spaces, 0)
253 253 isp.push('if 1:\n return ')
254 254 self.assertEqual(isp.indent_spaces, 0)
255 255 isp.push('if 1:\n return(0)')
256 256 self.assertEqual(isp.indent_spaces, 0)
257 257
258 258 def test_push(self):
259 259 isp = self.isp
260 260 self.assertTrue(isp.push('x=1'))
261 261
262 262 def test_push2(self):
263 263 isp = self.isp
264 264 self.assertFalse(isp.push('if 1:'))
265 265 for line in [' x=1', '# a comment', ' y=2']:
266 266 print(line)
267 267 self.assertTrue(isp.push(line))
268 268
269 269 def test_push3(self):
270 270 isp = self.isp
271 271 isp.push('if True:')
272 272 isp.push(' a = 1')
273 273 self.assertFalse(isp.push('b = [1,'))
274 274
275 275 def test_push_accepts_more(self):
276 276 isp = self.isp
277 277 isp.push('x=1')
278 278 self.assertFalse(isp.push_accepts_more())
279 279
280 280 def test_push_accepts_more2(self):
281 281 isp = self.isp
282 282 isp.push('if 1:')
283 283 self.assertTrue(isp.push_accepts_more())
284 284 isp.push(' x=1')
285 285 self.assertTrue(isp.push_accepts_more())
286 286 isp.push('')
287 287 self.assertFalse(isp.push_accepts_more())
288 288
289 289 def test_push_accepts_more3(self):
290 290 isp = self.isp
291 291 isp.push("x = (2+\n3)")
292 292 self.assertFalse(isp.push_accepts_more())
293 293
294 294 def test_push_accepts_more4(self):
295 295 isp = self.isp
296 296 # When a multiline statement contains parens or multiline strings, we
297 297 # shouldn't get confused.
298 298 # FIXME: we should be able to better handle de-dents in statements like
299 299 # multiline strings and multiline expressions (continued with \ or
300 300 # parens). Right now we aren't handling the indentation tracking quite
301 301 # correctly with this, though in practice it may not be too much of a
302 302 # problem. We'll need to see.
303 303 isp.push("if 1:")
304 304 isp.push(" x = (2+")
305 305 isp.push(" 3)")
306 306 self.assertTrue(isp.push_accepts_more())
307 307 isp.push(" y = 3")
308 308 self.assertTrue(isp.push_accepts_more())
309 309 isp.push('')
310 310 self.assertFalse(isp.push_accepts_more())
311 311
312 312 def test_push_accepts_more5(self):
313 313 isp = self.isp
314 314 isp.push('try:')
315 315 isp.push(' a = 5')
316 316 isp.push('except:')
317 317 isp.push(' raise')
318 318 # We want to be able to add an else: block at this point, so it should
319 319 # wait for a blank line.
320 320 self.assertTrue(isp.push_accepts_more())
321 321
322 322 def test_continuation(self):
323 323 isp = self.isp
324 324 isp.push("import os, \\")
325 325 self.assertTrue(isp.push_accepts_more())
326 326 isp.push("sys")
327 327 self.assertFalse(isp.push_accepts_more())
328 328
329 329 def test_syntax_error(self):
330 330 isp = self.isp
331 331 # Syntax errors immediately produce a 'ready' block, so the invalid
332 332 # Python can be sent to the kernel for evaluation with possible ipython
333 333 # special-syntax conversion.
334 334 isp.push('run foo')
335 335 self.assertFalse(isp.push_accepts_more())
336 336
337 337 def test_unicode(self):
338 338 self.isp.push(u"PΓ©rez")
339 339 self.isp.push(u'\xc3\xa9')
340 340 self.isp.push(u"u'\xc3\xa9'")
341 341
342 342 def test_line_continuation(self):
343 343 """ Test issue #2108."""
344 344 isp = self.isp
345 345 # A blank line after a line continuation should not accept more
346 346 isp.push("1 \\\n\n")
347 347 self.assertFalse(isp.push_accepts_more())
348 348 # Whitespace after a \ is a SyntaxError. The only way to test that
349 349 # here is to test that push doesn't accept more (as with
350 350 # test_syntax_error() above).
351 351 isp.push(r"1 \ ")
352 352 self.assertFalse(isp.push_accepts_more())
353 353 # Even if the line is continuable (c.f. the regular Python
354 354 # interpreter)
355 355 isp.push(r"(1 \ ")
356 356 self.assertFalse(isp.push_accepts_more())
357 357
358 def test_is_complete(self):
359 isp = self.isp
360 assert isp.is_complete("a = 1")
361 assert not isp.is_complete("for a in range(5):")
362 assert isp.is_complete("raise = 2") # SyntaxError should mean complete
363 assert not isp.is_complete("a = [1,\n2,")
364
358 365 class InteractiveLoopTestCase(unittest.TestCase):
359 366 """Tests for an interactive loop like a python shell.
360 367 """
361 368 def check_ns(self, lines, ns):
362 369 """Validate that the given input lines produce the resulting namespace.
363 370
364 371 Note: the input lines are given exactly as they would be typed in an
365 372 auto-indenting environment, as mini_interactive_loop above already does
366 373 auto-indenting and prepends spaces to the input.
367 374 """
368 375 src = mini_interactive_loop(pseudo_input(lines))
369 376 test_ns = {}
370 377 exec(src, test_ns)
371 378 # We can't check that the provided ns is identical to the test_ns,
372 379 # because Python fills test_ns with extra keys (copyright, etc). But
373 380 # we can check that the given dict is *contained* in test_ns
374 381 for k,v in ns.items():
375 382 self.assertEqual(test_ns[k], v)
376 383
377 384 def test_simple(self):
378 385 self.check_ns(['x=1'], dict(x=1))
379 386
380 387 def test_simple2(self):
381 388 self.check_ns(['if 1:', 'x=2'], dict(x=2))
382 389
383 390 def test_xy(self):
384 391 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
385 392
386 393 def test_abc(self):
387 394 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
388 395
389 396 def test_multi(self):
390 397 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
391 398
392 399
393 400 class IPythonInputTestCase(InputSplitterTestCase):
394 401 """By just creating a new class whose .isp is a different instance, we
395 402 re-run the same test battery on the new input splitter.
396 403
397 404 In addition, this runs the tests over the syntax and syntax_ml dicts that
398 405 were tested by individual functions, as part of the OO interface.
399 406
400 407 It also makes some checks on the raw buffer storage.
401 408 """
402 409
403 410 def setUp(self):
404 411 self.isp = isp.IPythonInputSplitter()
405 412
406 413 def test_syntax(self):
407 414 """Call all single-line syntax tests from the main object"""
408 415 isp = self.isp
409 416 for example in syntax.values():
410 417 for raw, out_t in example:
411 418 if raw.startswith(' '):
412 419 continue
413 420
414 421 isp.push(raw+'\n')
415 422 out_raw = isp.source_raw
416 423 out = isp.source_reset()
417 424 self.assertEqual(out.rstrip(), out_t,
418 425 tt.pair_fail_msg.format("inputsplitter",raw, out_t, out))
419 426 self.assertEqual(out_raw.rstrip(), raw.rstrip())
420 427
421 428 def test_syntax_multiline(self):
422 429 isp = self.isp
423 430 for example in syntax_ml.values():
424 431 for line_pairs in example:
425 432 out_t_parts = []
426 433 raw_parts = []
427 434 for lraw, out_t_part in line_pairs:
428 435 if out_t_part is not None:
429 436 out_t_parts.append(out_t_part)
430 437
431 438 if lraw is not None:
432 439 isp.push(lraw)
433 440 raw_parts.append(lraw)
434 441
435 442 out_raw = isp.source_raw
436 443 out = isp.source_reset()
437 444 out_t = '\n'.join(out_t_parts).rstrip()
438 445 raw = '\n'.join(raw_parts).rstrip()
439 446 self.assertEqual(out.rstrip(), out_t)
440 447 self.assertEqual(out_raw.rstrip(), raw)
441 448
442 449 def test_syntax_multiline_cell(self):
443 450 isp = self.isp
444 451 for example in syntax_ml.values():
445 452
446 453 out_t_parts = []
447 454 for line_pairs in example:
448 455 raw = '\n'.join(r for r, _ in line_pairs if r is not None)
449 456 out_t = '\n'.join(t for _,t in line_pairs if t is not None)
450 457 out = isp.transform_cell(raw)
451 458 # Match ignoring trailing whitespace
452 459 self.assertEqual(out.rstrip(), out_t.rstrip())
453 460
454 461 def test_cellmagic_preempt(self):
455 462 isp = self.isp
456 463 for raw, name, line, cell in [
457 464 ("%%cellm a\nIn[1]:", u'cellm', u'a', u'In[1]:'),
458 465 ("%%cellm \nline\n>>> hi", u'cellm', u'', u'line\n>>> hi'),
459 466 (">>> %%cellm \nline\n>>> hi", u'cellm', u'', u'line\nhi'),
460 467 ("%%cellm \n>>> hi", u'cellm', u'', u'hi'),
461 468 ("%%cellm \nline1\nline2", u'cellm', u'', u'line1\nline2'),
462 469 ("%%cellm \nline1\\\\\nline2", u'cellm', u'', u'line1\\\\\nline2'),
463 470 ]:
464 471 expected = "get_ipython().run_cell_magic(%r, %r, %r)" % (
465 472 name, line, cell
466 473 )
467 474 out = isp.transform_cell(raw)
468 475 self.assertEqual(out.rstrip(), expected.rstrip())
469 476
470 477
471 478
472 479 #-----------------------------------------------------------------------------
473 480 # Main - use as a script, mostly for developer experiments
474 481 #-----------------------------------------------------------------------------
475 482
476 483 if __name__ == '__main__':
477 484 # A simple demo for interactive experimentation. This code will not get
478 485 # picked up by any test suite.
479 486 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
480 487
481 488 # configure here the syntax to use, prompt and whether to autoindent
482 489 #isp, start_prompt = InputSplitter(), '>>> '
483 490 isp, start_prompt = IPythonInputSplitter(), 'In> '
484 491
485 492 autoindent = True
486 493 #autoindent = False
487 494
488 495 try:
489 496 while True:
490 497 prompt = start_prompt
491 498 while isp.push_accepts_more():
492 499 indent = ' '*isp.indent_spaces
493 500 if autoindent:
494 501 line = indent + input(prompt+indent)
495 502 else:
496 503 line = input(prompt)
497 504 isp.push(line)
498 505 prompt = '... '
499 506
500 507 # Here we just return input so we can use it in a test suite, but a
501 508 # real interpreter would instead send it for execution somewhere.
502 509 #src = isp.source; raise EOFError # dbg
503 510 raw = isp.source_raw
504 511 src = isp.source_reset()
505 512 print('Input source was:\n', src)
506 513 print('Raw source was:\n', raw)
507 514 except EOFError:
508 515 print('Bye')
509 516
510 517 # Tests for cell magics support
511 518
512 519 def test_last_blank():
513 520 nt.assert_false(isp.last_blank(''))
514 521 nt.assert_false(isp.last_blank('abc'))
515 522 nt.assert_false(isp.last_blank('abc\n'))
516 523 nt.assert_false(isp.last_blank('abc\na'))
517 524
518 525 nt.assert_true(isp.last_blank('\n'))
519 526 nt.assert_true(isp.last_blank('\n '))
520 527 nt.assert_true(isp.last_blank('abc\n '))
521 528 nt.assert_true(isp.last_blank('abc\n\n'))
522 529 nt.assert_true(isp.last_blank('abc\nd\n\n'))
523 530 nt.assert_true(isp.last_blank('abc\nd\ne\n\n'))
524 531 nt.assert_true(isp.last_blank('abc \n \n \n\n'))
525 532
526 533
527 534 def test_last_two_blanks():
528 535 nt.assert_false(isp.last_two_blanks(''))
529 536 nt.assert_false(isp.last_two_blanks('abc'))
530 537 nt.assert_false(isp.last_two_blanks('abc\n'))
531 538 nt.assert_false(isp.last_two_blanks('abc\n\na'))
532 539 nt.assert_false(isp.last_two_blanks('abc\n \n'))
533 540 nt.assert_false(isp.last_two_blanks('abc\n\n'))
534 541
535 542 nt.assert_true(isp.last_two_blanks('\n\n'))
536 543 nt.assert_true(isp.last_two_blanks('\n\n '))
537 544 nt.assert_true(isp.last_two_blanks('\n \n'))
538 545 nt.assert_true(isp.last_two_blanks('abc\n\n '))
539 546 nt.assert_true(isp.last_two_blanks('abc\n\n\n'))
540 547 nt.assert_true(isp.last_two_blanks('abc\n\n \n'))
541 548 nt.assert_true(isp.last_two_blanks('abc\n\n \n '))
542 549 nt.assert_true(isp.last_two_blanks('abc\n\n \n \n'))
543 550 nt.assert_true(isp.last_two_blanks('abc\nd\n\n\n'))
544 551 nt.assert_true(isp.last_two_blanks('abc\nd\ne\nf\n\n\n'))
545 552
546 553
547 554 class CellMagicsCommon(object):
548 555
549 556 def test_whole_cell(self):
550 557 src = "%%cellm line\nbody\n"
551 558 out = self.sp.transform_cell(src)
552 559 ref = u"get_ipython().run_cell_magic({u}'cellm', {u}'line', {u}'body')\n"
553 560 nt.assert_equal(out, py3compat.u_format(ref))
554 561
555 562 def test_cellmagic_help(self):
556 563 self.sp.push('%%cellm?')
557 564 nt.assert_false(self.sp.push_accepts_more())
558 565
559 566 def tearDown(self):
560 567 self.sp.reset()
561 568
562 569
563 570 class CellModeCellMagics(CellMagicsCommon, unittest.TestCase):
564 571 sp = isp.IPythonInputSplitter(line_input_checker=False)
565 572
566 573 def test_incremental(self):
567 574 sp = self.sp
568 575 sp.push('%%cellm firstline\n')
569 576 nt.assert_true(sp.push_accepts_more()) #1
570 577 sp.push('line2\n')
571 578 nt.assert_true(sp.push_accepts_more()) #2
572 579 sp.push('\n')
573 580 # This should accept a blank line and carry on until the cell is reset
574 581 nt.assert_true(sp.push_accepts_more()) #3
575 582
576 583 class LineModeCellMagics(CellMagicsCommon, unittest.TestCase):
577 584 sp = isp.IPythonInputSplitter(line_input_checker=True)
578 585
579 586 def test_incremental(self):
580 587 sp = self.sp
581 588 sp.push('%%cellm line2\n')
582 589 nt.assert_true(sp.push_accepts_more()) #1
583 590 sp.push('\n')
584 591 # In this case, a blank line should end the cell magic
585 592 nt.assert_false(sp.push_accepts_more()) #2
@@ -1,207 +1,223 b''
1 1 # coding: utf-8
2 2 """test the IPython Kernel"""
3 3
4 4 #-------------------------------------------------------------------------------
5 5 # Copyright (C) 2013 The IPython Development Team
6 6 #
7 7 # Distributed under the terms of the BSD License. The full license is in
8 8 # the file COPYING, distributed as part of this software.
9 9 #-------------------------------------------------------------------------------
10 10
11 11 #-------------------------------------------------------------------------------
12 12 # Imports
13 13 #-------------------------------------------------------------------------------
14 14
15 15 import io
16 16 import os.path
17 17 import sys
18 18
19 19 import nose.tools as nt
20 20
21 21 from IPython.testing import decorators as dec, tools as tt
22 22 from IPython.utils import py3compat
23 23 from IPython.utils.path import locate_profile
24 24 from IPython.utils.tempdir import TemporaryDirectory
25 25
26 26 from .utils import (new_kernel, kernel, TIMEOUT, assemble_output, execute,
27 27 flush_channels, wait_for_idle)
28 28
29 29 #-------------------------------------------------------------------------------
30 30 # Tests
31 31 #-------------------------------------------------------------------------------
32 32
33 33
34 34 def _check_mp_mode(kc, expected=False, stream="stdout"):
35 35 execute(kc=kc, code="import sys")
36 36 flush_channels(kc)
37 37 msg_id, content = execute(kc=kc, code="print (sys.%s._check_mp_mode())" % stream)
38 38 stdout, stderr = assemble_output(kc.iopub_channel)
39 39 nt.assert_equal(eval(stdout.strip()), expected)
40 40
41 41
42 42 # printing tests
43 43
44 44 def test_simple_print():
45 45 """simple print statement in kernel"""
46 46 with kernel() as kc:
47 47 iopub = kc.iopub_channel
48 48 msg_id, content = execute(kc=kc, code="print ('hi')")
49 49 stdout, stderr = assemble_output(iopub)
50 50 nt.assert_equal(stdout, 'hi\n')
51 51 nt.assert_equal(stderr, '')
52 52 _check_mp_mode(kc, expected=False)
53 53
54 54
55 55 def test_sys_path():
56 56 """test that sys.path doesn't get messed up by default"""
57 57 with kernel() as kc:
58 58 msg_id, content = execute(kc=kc, code="import sys; print (repr(sys.path[0]))")
59 59 stdout, stderr = assemble_output(kc.iopub_channel)
60 60 nt.assert_equal(stdout, "''\n")
61 61
62 62 def test_sys_path_profile_dir():
63 63 """test that sys.path doesn't get messed up when `--profile-dir` is specified"""
64 64
65 65 with new_kernel(['--profile-dir', locate_profile('default')]) as kc:
66 66 msg_id, content = execute(kc=kc, code="import sys; print (repr(sys.path[0]))")
67 67 stdout, stderr = assemble_output(kc.iopub_channel)
68 68 nt.assert_equal(stdout, "''\n")
69 69
70 70 @dec.knownfailureif(sys.platform == 'win32', "subprocess prints fail on Windows")
71 71 def test_subprocess_print():
72 72 """printing from forked mp.Process"""
73 73 with new_kernel() as kc:
74 74 iopub = kc.iopub_channel
75 75
76 76 _check_mp_mode(kc, expected=False)
77 77 flush_channels(kc)
78 78 np = 5
79 79 code = '\n'.join([
80 80 "from __future__ import print_function",
81 81 "import multiprocessing as mp",
82 82 "pool = [mp.Process(target=print, args=('hello', i,)) for i in range(%i)]" % np,
83 83 "for p in pool: p.start()",
84 84 "for p in pool: p.join()"
85 85 ])
86 86
87 87 expected = '\n'.join([
88 88 "hello %s" % i for i in range(np)
89 89 ]) + '\n'
90 90
91 91 msg_id, content = execute(kc=kc, code=code)
92 92 stdout, stderr = assemble_output(iopub)
93 93 nt.assert_equal(stdout.count("hello"), np, stdout)
94 94 for n in range(np):
95 95 nt.assert_equal(stdout.count(str(n)), 1, stdout)
96 96 nt.assert_equal(stderr, '')
97 97 _check_mp_mode(kc, expected=False)
98 98 _check_mp_mode(kc, expected=False, stream="stderr")
99 99
100 100
101 101 def test_subprocess_noprint():
102 102 """mp.Process without print doesn't trigger iostream mp_mode"""
103 103 with kernel() as kc:
104 104 iopub = kc.iopub_channel
105 105
106 106 np = 5
107 107 code = '\n'.join([
108 108 "import multiprocessing as mp",
109 109 "pool = [mp.Process(target=range, args=(i,)) for i in range(%i)]" % np,
110 110 "for p in pool: p.start()",
111 111 "for p in pool: p.join()"
112 112 ])
113 113
114 114 msg_id, content = execute(kc=kc, code=code)
115 115 stdout, stderr = assemble_output(iopub)
116 116 nt.assert_equal(stdout, '')
117 117 nt.assert_equal(stderr, '')
118 118
119 119 _check_mp_mode(kc, expected=False)
120 120 _check_mp_mode(kc, expected=False, stream="stderr")
121 121
122 122
123 123 @dec.knownfailureif(sys.platform == 'win32', "subprocess prints fail on Windows")
124 124 def test_subprocess_error():
125 125 """error in mp.Process doesn't crash"""
126 126 with new_kernel() as kc:
127 127 iopub = kc.iopub_channel
128 128
129 129 code = '\n'.join([
130 130 "import multiprocessing as mp",
131 131 "p = mp.Process(target=int, args=('hi',))",
132 132 "p.start()",
133 133 "p.join()",
134 134 ])
135 135
136 136 msg_id, content = execute(kc=kc, code=code)
137 137 stdout, stderr = assemble_output(iopub)
138 138 nt.assert_equal(stdout, '')
139 139 nt.assert_true("ValueError" in stderr, stderr)
140 140
141 141 _check_mp_mode(kc, expected=False)
142 142 _check_mp_mode(kc, expected=False, stream="stderr")
143 143
144 144 # raw_input tests
145 145
146 146 def test_raw_input():
147 147 """test [raw_]input"""
148 148 with kernel() as kc:
149 149 iopub = kc.iopub_channel
150 150
151 151 input_f = "input" if py3compat.PY3 else "raw_input"
152 152 theprompt = "prompt> "
153 153 code = 'print({input_f}("{theprompt}"))'.format(**locals())
154 154 msg_id = kc.execute(code, allow_stdin=True)
155 155 msg = kc.get_stdin_msg(block=True, timeout=TIMEOUT)
156 156 nt.assert_equal(msg['header']['msg_type'], u'input_request')
157 157 content = msg['content']
158 158 nt.assert_equal(content['prompt'], theprompt)
159 159 text = "some text"
160 160 kc.input(text)
161 161 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
162 162 nt.assert_equal(reply['content']['status'], 'ok')
163 163 stdout, stderr = assemble_output(iopub)
164 164 nt.assert_equal(stdout, text + "\n")
165 165
166 166
167 167 @dec.skipif(py3compat.PY3)
168 168 def test_eval_input():
169 169 """test input() on Python 2"""
170 170 with kernel() as kc:
171 171 iopub = kc.iopub_channel
172 172
173 173 input_f = "input" if py3compat.PY3 else "raw_input"
174 174 theprompt = "prompt> "
175 175 code = 'print(input("{theprompt}"))'.format(**locals())
176 176 msg_id = kc.execute(code, allow_stdin=True)
177 177 msg = kc.get_stdin_msg(block=True, timeout=TIMEOUT)
178 178 nt.assert_equal(msg['header']['msg_type'], u'input_request')
179 179 content = msg['content']
180 180 nt.assert_equal(content['prompt'], theprompt)
181 181 kc.input("1+1")
182 182 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
183 183 nt.assert_equal(reply['content']['status'], 'ok')
184 184 stdout, stderr = assemble_output(iopub)
185 185 nt.assert_equal(stdout, "2\n")
186 186
187 187
188 188 def test_save_history():
189 189 # Saving history from the kernel with %hist -f was failing because of
190 190 # unicode problems on Python 2.
191 191 with kernel() as kc, TemporaryDirectory() as td:
192 192 file = os.path.join(td, 'hist.out')
193 193 execute(u'a=1', kc=kc)
194 194 wait_for_idle(kc)
195 195 execute(u'b=u"abcΓΎ"', kc=kc)
196 196 wait_for_idle(kc)
197 197 _, reply = execute("%hist -f " + file, kc=kc)
198 198 nt.assert_equal(reply['status'], 'ok')
199 199 with io.open(file, encoding='utf-8') as f:
200 200 content = f.read()
201 201 nt.assert_in(u'a=1', content)
202 202 nt.assert_in(u'b=u"abcΓΎ"', content)
203 203
204 204 def test_help_output():
205 205 """ipython kernel --help-all works"""
206 206 tt.help_all_output_test('kernel')
207 207
208 def test_is_complete():
209 with kernel() as kc:
210 # There are more test cases for this in core - here we just check
211 # that the kernel exposes the interface correctly.
212 kc.is_complete('2+2')
213 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
214 assert reply['content']['complete']
215
216 # SyntaxError should mean it's complete
217 kc.is_complete('raise = 2')
218 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
219 assert reply['content']['complete']
220
221 kc.is_complete('a = [1,\n2,')
222 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
223 assert not reply['content']['complete']
@@ -1,302 +1,306 b''
1 1 """The IPython kernel implementation"""
2 2
3 3 import getpass
4 4 import sys
5 5 import traceback
6 6
7 7 from IPython.core import release
8 8 from IPython.utils.py3compat import builtin_mod, PY3
9 9 from IPython.utils.tokenutil import token_at_cursor
10 10 from IPython.utils.traitlets import Instance, Type, Any
11 11 from IPython.utils.decorators import undoc
12 12
13 13 from .kernelbase import Kernel as KernelBase
14 14 from .serialize import serialize_object, unpack_apply_message
15 15 from .zmqshell import ZMQInteractiveShell
16 16
17 17 class IPythonKernel(KernelBase):
18 18 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
19 19 shell_class = Type(ZMQInteractiveShell)
20 20
21 21 user_module = Any()
22 22 def _user_module_changed(self, name, old, new):
23 23 if self.shell is not None:
24 24 self.shell.user_module = new
25 25
26 26 user_ns = Instance(dict, args=None, allow_none=True)
27 27 def _user_ns_changed(self, name, old, new):
28 28 if self.shell is not None:
29 29 self.shell.user_ns = new
30 30 self.shell.init_user_ns()
31 31
32 32 # A reference to the Python builtin 'raw_input' function.
33 33 # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3)
34 34 _sys_raw_input = Any()
35 35 _sys_eval_input = Any()
36 36
37 37 def __init__(self, **kwargs):
38 38 super(IPythonKernel, self).__init__(**kwargs)
39 39
40 40 # Initialize the InteractiveShell subclass
41 41 self.shell = self.shell_class.instance(parent=self,
42 42 profile_dir = self.profile_dir,
43 43 user_module = self.user_module,
44 44 user_ns = self.user_ns,
45 45 kernel = self,
46 46 )
47 47 self.shell.displayhook.session = self.session
48 48 self.shell.displayhook.pub_socket = self.iopub_socket
49 49 self.shell.displayhook.topic = self._topic('execute_result')
50 50 self.shell.display_pub.session = self.session
51 51 self.shell.display_pub.pub_socket = self.iopub_socket
52 52 self.shell.data_pub.session = self.session
53 53 self.shell.data_pub.pub_socket = self.iopub_socket
54 54
55 55 # TMP - hack while developing
56 56 self.shell._reply_content = None
57 57
58 58 comm_msg_types = [ 'comm_open', 'comm_msg', 'comm_close' ]
59 59 comm_manager = self.shell.comm_manager
60 60 for msg_type in comm_msg_types:
61 61 self.shell_handlers[msg_type] = getattr(comm_manager, msg_type)
62 62
63 63 # Kernel info fields
64 64 implementation = 'ipython'
65 65 implementation_version = release.version
66 66 language = 'python'
67 67 language_version = sys.version.split()[0]
68 68 @property
69 69 def banner(self):
70 70 return self.shell.banner
71 71
72 72 def start(self):
73 73 self.shell.exit_now = False
74 74 super(IPythonKernel, self).start()
75 75
76 76 def set_parent(self, ident, parent):
77 77 """Overridden from parent to tell the display hook and output streams
78 78 about the parent message.
79 79 """
80 80 super(IPythonKernel, self).set_parent(ident, parent)
81 81 self.shell.set_parent(parent)
82 82
83 83 def _forward_input(self, allow_stdin=False):
84 84 """Forward raw_input and getpass to the current frontend.
85 85
86 86 via input_request
87 87 """
88 88 self._allow_stdin = allow_stdin
89 89
90 90 if PY3:
91 91 self._sys_raw_input = builtin_mod.input
92 92 builtin_mod.input = self.raw_input
93 93 else:
94 94 self._sys_raw_input = builtin_mod.raw_input
95 95 self._sys_eval_input = builtin_mod.input
96 96 builtin_mod.raw_input = self.raw_input
97 97 builtin_mod.input = lambda prompt='': eval(self.raw_input(prompt))
98 98 self._save_getpass = getpass.getpass
99 99 getpass.getpass = self.getpass
100 100
101 101 def _restore_input(self):
102 102 """Restore raw_input, getpass"""
103 103 if PY3:
104 104 builtin_mod.input = self._sys_raw_input
105 105 else:
106 106 builtin_mod.raw_input = self._sys_raw_input
107 107 builtin_mod.input = self._sys_eval_input
108 108
109 109 getpass.getpass = self._save_getpass
110 110
111 111 @property
112 112 def execution_count(self):
113 113 return self.shell.execution_count
114 114
115 115 @execution_count.setter
116 116 def execution_count(self, value):
117 117 # Ignore the incrememnting done by KernelBase, in favour of our shell's
118 118 # execution counter.
119 119 pass
120 120
121 121 def do_execute(self, code, silent, store_history=True,
122 122 user_expressions=None, allow_stdin=False):
123 123 shell = self.shell # we'll need this a lot here
124 124
125 125 self._forward_input(allow_stdin)
126 126
127 127 reply_content = {}
128 128 # FIXME: the shell calls the exception handler itself.
129 129 shell._reply_content = None
130 130 try:
131 131 shell.run_cell(code, store_history=store_history, silent=silent)
132 132 except:
133 133 status = u'error'
134 134 # FIXME: this code right now isn't being used yet by default,
135 135 # because the run_cell() call above directly fires off exception
136 136 # reporting. This code, therefore, is only active in the scenario
137 137 # where runlines itself has an unhandled exception. We need to
138 138 # uniformize this, for all exception construction to come from a
139 139 # single location in the codbase.
140 140 etype, evalue, tb = sys.exc_info()
141 141 tb_list = traceback.format_exception(etype, evalue, tb)
142 142 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
143 143 else:
144 144 status = u'ok'
145 145 finally:
146 146 self._restore_input()
147 147
148 148 reply_content[u'status'] = status
149 149
150 150 # Return the execution counter so clients can display prompts
151 151 reply_content['execution_count'] = shell.execution_count - 1
152 152
153 153 # FIXME - fish exception info out of shell, possibly left there by
154 154 # runlines. We'll need to clean up this logic later.
155 155 if shell._reply_content is not None:
156 156 reply_content.update(shell._reply_content)
157 157 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
158 158 reply_content['engine_info'] = e_info
159 159 # reset after use
160 160 shell._reply_content = None
161 161
162 162 if 'traceback' in reply_content:
163 163 self.log.info("Exception in execute request:\n%s", '\n'.join(reply_content['traceback']))
164 164
165 165
166 166 # At this point, we can tell whether the main code execution succeeded
167 167 # or not. If it did, we proceed to evaluate user_expressions
168 168 if reply_content['status'] == 'ok':
169 169 reply_content[u'user_expressions'] = \
170 170 shell.user_expressions(user_expressions or {})
171 171 else:
172 172 # If there was an error, don't even try to compute expressions
173 173 reply_content[u'user_expressions'] = {}
174 174
175 175 # Payloads should be retrieved regardless of outcome, so we can both
176 176 # recover partial output (that could have been generated early in a
177 177 # block, before an error) and clear the payload system always.
178 178 reply_content[u'payload'] = shell.payload_manager.read_payload()
179 179 # Be agressive about clearing the payload because we don't want
180 180 # it to sit in memory until the next execute_request comes in.
181 181 shell.payload_manager.clear_payload()
182 182
183 183 return reply_content
184 184
185 185 def do_complete(self, code, cursor_pos):
186 186 txt, matches = self.shell.complete('', code, cursor_pos)
187 187 return {'matches' : matches,
188 188 'cursor_end' : cursor_pos,
189 189 'cursor_start' : cursor_pos - len(txt),
190 190 'metadata' : {},
191 191 'status' : 'ok'}
192 192
193 193 def do_inspect(self, code, cursor_pos, detail_level=0):
194 194 name = token_at_cursor(code, cursor_pos)
195 195 info = self.shell.object_inspect(name)
196 196
197 197 reply_content = {'status' : 'ok'}
198 198 reply_content['data'] = data = {}
199 199 reply_content['metadata'] = {}
200 200 reply_content['found'] = info['found']
201 201 if info['found']:
202 202 info_text = self.shell.object_inspect_text(
203 203 name,
204 204 detail_level=detail_level,
205 205 )
206 206 data['text/plain'] = info_text
207 207
208 208 return reply_content
209 209
210 210 def do_history(self, hist_access_type, output, raw, session=None, start=None,
211 211 stop=None, n=None, pattern=None, unique=False):
212 212 if hist_access_type == 'tail':
213 213 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
214 214 include_latest=True)
215 215
216 216 elif hist_access_type == 'range':
217 217 hist = self.shell.history_manager.get_range(session, start, stop,
218 218 raw=raw, output=output)
219 219
220 220 elif hist_access_type == 'search':
221 221 hist = self.shell.history_manager.search(
222 222 pattern, raw=raw, output=output, n=n, unique=unique)
223 223 else:
224 224 hist = []
225 225
226 226 return {'history' : list(hist)}
227 227
228 228 def do_shutdown(self, restart):
229 229 self.shell.exit_now = True
230 230 return dict(status='ok', restart=restart)
231 231
232 def do_is_complete(self, code):
233 complete = self.shell.input_transformer_manager.is_complete(code)
234 return {'complete': complete}
235
232 236 def do_apply(self, content, bufs, msg_id, reply_metadata):
233 237 shell = self.shell
234 238 try:
235 239 working = shell.user_ns
236 240
237 241 prefix = "_"+str(msg_id).replace("-","")+"_"
238 242
239 243 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
240 244
241 245 fname = getattr(f, '__name__', 'f')
242 246
243 247 fname = prefix+"f"
244 248 argname = prefix+"args"
245 249 kwargname = prefix+"kwargs"
246 250 resultname = prefix+"result"
247 251
248 252 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
249 253 # print ns
250 254 working.update(ns)
251 255 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
252 256 try:
253 257 exec(code, shell.user_global_ns, shell.user_ns)
254 258 result = working.get(resultname)
255 259 finally:
256 260 for key in ns:
257 261 working.pop(key)
258 262
259 263 result_buf = serialize_object(result,
260 264 buffer_threshold=self.session.buffer_threshold,
261 265 item_threshold=self.session.item_threshold,
262 266 )
263 267
264 268 except:
265 269 # invoke IPython traceback formatting
266 270 shell.showtraceback()
267 271 # FIXME - fish exception info out of shell, possibly left there by
268 272 # run_code. We'll need to clean up this logic later.
269 273 reply_content = {}
270 274 if shell._reply_content is not None:
271 275 reply_content.update(shell._reply_content)
272 276 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
273 277 reply_content['engine_info'] = e_info
274 278 # reset after use
275 279 shell._reply_content = None
276 280
277 281 self.send_response(self.iopub_socket, u'error', reply_content,
278 282 ident=self._topic('error'))
279 283 self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback']))
280 284 result_buf = []
281 285
282 286 if reply_content['ename'] == 'UnmetDependency':
283 287 reply_metadata['dependencies_met'] = False
284 288 else:
285 289 reply_content = {'status' : 'ok'}
286 290
287 291 return reply_content, result_buf
288 292
289 293 def do_clear(self):
290 294 self.shell.reset(False)
291 295 return dict(status='ok')
292 296
293 297
294 298 # This exists only for backwards compatibility - use IPythonKernel instead
295 299
296 300 @undoc
297 301 class Kernel(IPythonKernel):
298 302 def __init__(self, *args, **kwargs):
299 303 import warnings
300 304 warnings.warn('Kernel is a deprecated alias of IPython.kernel.zmq.ipkernel.IPythonKernel',
301 305 DeprecationWarning)
302 306 super(Kernel, self).__init__(*args, **kwargs) No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now