##// END OF EJS Templates
Four possible states for completion reply, & indent hint
Thomas Kluyver -
Show More
@@ -1,654 +1,679 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 # Boolean indicating whether the current block has an unrecoverable syntax error
228 _is_invalid = False
227 229
228 230 def __init__(self):
229 231 """Create a new InputSplitter instance.
230 232 """
231 233 self._buffer = []
232 234 self._compile = codeop.CommandCompiler()
233 235 self.encoding = get_input_encoding()
234 236
235 237 def reset(self):
236 238 """Reset the input buffer and associated state."""
237 239 self.indent_spaces = 0
238 240 self._buffer[:] = []
239 241 self.source = ''
240 242 self.code = None
241 243 self._is_complete = False
244 self._is_invalid = False
242 245 self._full_dedent = False
243 246
244 247 def source_reset(self):
245 248 """Return the input source and perform a full reset.
246 249 """
247 250 out = self.source
248 251 self.reset()
249 252 return out
250 253
251 def is_complete(self, source):
254 def check_complete(self, source):
252 255 """Return whether a block of code is ready to execute, or should be continued
253 256
254 257 This is a non-stateful API, and will reset the state of this InputSplitter.
258
259 Parameters
260 ----------
261 source : string
262 Python input code, which can be multiline.
263
264 Returns
265 -------
266 status : str
267 One of 'complete', 'incomplete', or 'invalid' if source is not a
268 prefix of valid code.
269 indent_spaces : int or None
270 The number of spaces by which to indent the next line of code. If
271 status is not 'incomplete', this is None.
255 272 """
256 273 self.reset()
257 274 try:
258 275 self.push(source)
259 return not self.push_accepts_more()
260 276 except SyntaxError:
261 277 # Transformers in IPythonInputSplitter can raise SyntaxError,
262 278 # which push() will not catch.
263 return True
279 return 'invalid', None
280 else:
281 if self._is_invalid:
282 return 'invalid', None
283 elif self.push_accepts_more():
284 return 'incomplete', self.indent_spaces
285 else:
286 return 'complete', None
264 287 finally:
265 288 self.reset()
266 289
267 290 def push(self, lines):
268 291 """Push one or more lines of input.
269 292
270 293 This stores the given lines and returns a status code indicating
271 294 whether the code forms a complete Python block or not.
272 295
273 296 Any exceptions generated in compilation are swallowed, but if an
274 297 exception was produced, the method returns True.
275 298
276 299 Parameters
277 300 ----------
278 301 lines : string
279 302 One or more lines of Python input.
280 303
281 304 Returns
282 305 -------
283 306 is_complete : boolean
284 307 True if the current input source (the result of the current input
285 308 plus prior inputs) forms a complete Python execution block. Note that
286 309 this value is also stored as a private attribute (``_is_complete``), so it
287 310 can be queried at any time.
288 311 """
289 312 self._store(lines)
290 313 source = self.source
291 314
292 315 # Before calling _compile(), reset the code object to None so that if an
293 316 # exception is raised in compilation, we don't mislead by having
294 317 # inconsistent code/source attributes.
295 318 self.code, self._is_complete = None, None
319 self._is_invalid = False
296 320
297 321 # Honor termination lines properly
298 322 if source.endswith('\\\n'):
299 323 return False
300 324
301 325 self._update_indent(lines)
302 326 try:
303 327 self.code = self._compile(source, symbol="exec")
304 328 # Invalid syntax can produce any of a number of different errors from
305 329 # inside the compiler, so we have to catch them all. Syntax errors
306 330 # immediately produce a 'ready' block, so the invalid Python can be
307 331 # sent to the kernel for evaluation with possible ipython
308 332 # special-syntax conversion.
309 333 except (SyntaxError, OverflowError, ValueError, TypeError,
310 334 MemoryError):
311 335 self._is_complete = True
336 self._is_invalid = True
312 337 else:
313 338 # Compilation didn't produce any exceptions (though it may not have
314 339 # given a complete code object)
315 340 self._is_complete = self.code is not None
316 341
317 342 return self._is_complete
318 343
319 344 def push_accepts_more(self):
320 345 """Return whether a block of interactive input can accept more input.
321 346
322 347 This method is meant to be used by line-oriented frontends, who need to
323 348 guess whether a block is complete or not based solely on prior and
324 349 current input lines. The InputSplitter considers it has a complete
325 350 interactive block and will not accept more input when either:
326 351
327 352 * A SyntaxError is raised
328 353
329 354 * The code is complete and consists of a single line or a single
330 355 non-compound statement
331 356
332 357 * The code is complete and has a blank line at the end
333 358
334 359 If the current input produces a syntax error, this method immediately
335 360 returns False but does *not* raise the syntax error exception, as
336 361 typically clients will want to send invalid syntax to an execution
337 362 backend which might convert the invalid syntax into valid Python via
338 363 one of the dynamic IPython mechanisms.
339 364 """
340 365
341 366 # With incomplete input, unconditionally accept more
342 367 # A syntax error also sets _is_complete to True - see push()
343 368 if not self._is_complete:
344 369 #print("Not complete") # debug
345 370 return True
346 371
347 372 # The user can make any (complete) input execute by leaving a blank line
348 373 last_line = self.source.splitlines()[-1]
349 374 if (not last_line) or last_line.isspace():
350 375 #print("Blank line") # debug
351 376 return False
352 377
353 378 # If there's just a single line or AST node, and we're flush left, as is
354 379 # the case after a simple statement such as 'a=1', we want to execute it
355 380 # straight away.
356 381 if self.indent_spaces==0:
357 382 if len(self.source.splitlines()) <= 1:
358 383 return False
359 384
360 385 try:
361 386 code_ast = ast.parse(u''.join(self._buffer))
362 387 except Exception:
363 388 #print("Can't parse AST") # debug
364 389 return False
365 390 else:
366 391 if len(code_ast.body) == 1 and \
367 392 not hasattr(code_ast.body[0], 'body'):
368 393 #print("Simple statement") # debug
369 394 return False
370 395
371 396 # General fallback - accept more code
372 397 return True
373 398
374 399 #------------------------------------------------------------------------
375 400 # Private interface
376 401 #------------------------------------------------------------------------
377 402
378 403 def _find_indent(self, line):
379 404 """Compute the new indentation level for a single line.
380 405
381 406 Parameters
382 407 ----------
383 408 line : str
384 409 A single new line of non-whitespace, non-comment Python input.
385 410
386 411 Returns
387 412 -------
388 413 indent_spaces : int
389 414 New value for the indent level (it may be equal to self.indent_spaces
390 415 if indentation doesn't change.
391 416
392 417 full_dedent : boolean
393 418 Whether the new line causes a full flush-left dedent.
394 419 """
395 420 indent_spaces = self.indent_spaces
396 421 full_dedent = self._full_dedent
397 422
398 423 inisp = num_ini_spaces(line)
399 424 if inisp < indent_spaces:
400 425 indent_spaces = inisp
401 426 if indent_spaces <= 0:
402 427 #print 'Full dedent in text',self.source # dbg
403 428 full_dedent = True
404 429
405 430 if line.rstrip()[-1] == ':':
406 431 indent_spaces += 4
407 432 elif dedent_re.match(line):
408 433 indent_spaces -= 4
409 434 if indent_spaces <= 0:
410 435 full_dedent = True
411 436
412 437 # Safety
413 438 if indent_spaces < 0:
414 439 indent_spaces = 0
415 440 #print 'safety' # dbg
416 441
417 442 return indent_spaces, full_dedent
418 443
419 444 def _update_indent(self, lines):
420 445 for line in remove_comments(lines).splitlines():
421 446 if line and not line.isspace():
422 447 self.indent_spaces, self._full_dedent = self._find_indent(line)
423 448
424 449 def _store(self, lines, buffer=None, store='source'):
425 450 """Store one or more lines of input.
426 451
427 452 If input lines are not newline-terminated, a newline is automatically
428 453 appended."""
429 454
430 455 if buffer is None:
431 456 buffer = self._buffer
432 457
433 458 if lines.endswith('\n'):
434 459 buffer.append(lines)
435 460 else:
436 461 buffer.append(lines+'\n')
437 462 setattr(self, store, self._set_source(buffer))
438 463
439 464 def _set_source(self, buffer):
440 465 return u''.join(buffer)
441 466
442 467
443 468 class IPythonInputSplitter(InputSplitter):
444 469 """An input splitter that recognizes all of IPython's special syntax."""
445 470
446 471 # String with raw, untransformed input.
447 472 source_raw = ''
448 473
449 474 # Flag to track when a transformer has stored input that it hasn't given
450 475 # back yet.
451 476 transformer_accumulating = False
452 477
453 478 # Flag to track when assemble_python_lines has stored input that it hasn't
454 479 # given back yet.
455 480 within_python_line = False
456 481
457 482 # Private attributes
458 483
459 484 # List with lines of raw input accumulated so far.
460 485 _buffer_raw = None
461 486
462 487 def __init__(self, line_input_checker=True, physical_line_transforms=None,
463 488 logical_line_transforms=None, python_line_transforms=None):
464 489 super(IPythonInputSplitter, self).__init__()
465 490 self._buffer_raw = []
466 491 self._validate = True
467 492
468 493 if physical_line_transforms is not None:
469 494 self.physical_line_transforms = physical_line_transforms
470 495 else:
471 496 self.physical_line_transforms = [
472 497 leading_indent(),
473 498 classic_prompt(),
474 499 ipy_prompt(),
475 500 strip_encoding_cookie(),
476 501 cellmagic(end_on_blank_line=line_input_checker),
477 502 ]
478 503
479 504 self.assemble_logical_lines = assemble_logical_lines()
480 505 if logical_line_transforms is not None:
481 506 self.logical_line_transforms = logical_line_transforms
482 507 else:
483 508 self.logical_line_transforms = [
484 509 help_end(),
485 510 escaped_commands(),
486 511 assign_from_magic(),
487 512 assign_from_system(),
488 513 ]
489 514
490 515 self.assemble_python_lines = assemble_python_lines()
491 516 if python_line_transforms is not None:
492 517 self.python_line_transforms = python_line_transforms
493 518 else:
494 519 # We don't use any of these at present
495 520 self.python_line_transforms = []
496 521
497 522 @property
498 523 def transforms(self):
499 524 "Quick access to all transformers."
500 525 return self.physical_line_transforms + \
501 526 [self.assemble_logical_lines] + self.logical_line_transforms + \
502 527 [self.assemble_python_lines] + self.python_line_transforms
503 528
504 529 @property
505 530 def transforms_in_use(self):
506 531 """Transformers, excluding logical line transformers if we're in a
507 532 Python line."""
508 533 t = self.physical_line_transforms[:]
509 534 if not self.within_python_line:
510 535 t += [self.assemble_logical_lines] + self.logical_line_transforms
511 536 return t + [self.assemble_python_lines] + self.python_line_transforms
512 537
513 538 def reset(self):
514 539 """Reset the input buffer and associated state."""
515 540 super(IPythonInputSplitter, self).reset()
516 541 self._buffer_raw[:] = []
517 542 self.source_raw = ''
518 543 self.transformer_accumulating = False
519 544 self.within_python_line = False
520 545
521 546 for t in self.transforms:
522 547 try:
523 548 t.reset()
524 549 except SyntaxError:
525 550 # Nothing that calls reset() expects to handle transformer
526 551 # errors
527 552 pass
528 553
529 554 def flush_transformers(self):
530 555 def _flush(transform, out):
531 556 if out is not None:
532 557 tmp = transform.push(out)
533 558 return tmp or transform.reset() or None
534 559 else:
535 560 return transform.reset() or None
536 561
537 562 out = None
538 563 for t in self.transforms_in_use:
539 564 out = _flush(t, out)
540 565
541 566 if out is not None:
542 567 self._store(out)
543 568
544 569 def raw_reset(self):
545 570 """Return raw input only and perform a full reset.
546 571 """
547 572 out = self.source_raw
548 573 self.reset()
549 574 return out
550 575
551 576 def source_reset(self):
552 577 try:
553 578 self.flush_transformers()
554 579 return self.source
555 580 finally:
556 581 self.reset()
557 582
558 583 def push_accepts_more(self):
559 584 if self.transformer_accumulating:
560 585 return True
561 586 else:
562 587 return super(IPythonInputSplitter, self).push_accepts_more()
563 588
564 589 def transform_cell(self, cell):
565 590 """Process and translate a cell of input.
566 591 """
567 592 self.reset()
568 593 try:
569 594 self.push(cell)
570 595 self.flush_transformers()
571 596 return self.source
572 597 finally:
573 598 self.reset()
574 599
575 600 def push(self, lines):
576 601 """Push one or more lines of IPython input.
577 602
578 603 This stores the given lines and returns a status code indicating
579 604 whether the code forms a complete Python block or not, after processing
580 605 all input lines for special IPython syntax.
581 606
582 607 Any exceptions generated in compilation are swallowed, but if an
583 608 exception was produced, the method returns True.
584 609
585 610 Parameters
586 611 ----------
587 612 lines : string
588 613 One or more lines of Python input.
589 614
590 615 Returns
591 616 -------
592 617 is_complete : boolean
593 618 True if the current input source (the result of the current input
594 619 plus prior inputs) forms a complete Python execution block. Note that
595 620 this value is also stored as a private attribute (_is_complete), so it
596 621 can be queried at any time.
597 622 """
598 623
599 624 # We must ensure all input is pure unicode
600 625 lines = cast_unicode(lines, self.encoding)
601 626
602 627 # ''.splitlines() --> [], but we need to push the empty line to transformers
603 628 lines_list = lines.splitlines()
604 629 if not lines_list:
605 630 lines_list = ['']
606 631
607 632 # Store raw source before applying any transformations to it. Note
608 633 # that this must be done *after* the reset() call that would otherwise
609 634 # flush the buffer.
610 635 self._store(lines, self._buffer_raw, 'source_raw')
611 636
612 637 for line in lines_list:
613 638 out = self.push_line(line)
614 639
615 640 return out
616 641
617 642 def push_line(self, line):
618 643 buf = self._buffer
619 644
620 645 def _accumulating(dbg):
621 646 #print(dbg)
622 647 self.transformer_accumulating = True
623 648 return False
624 649
625 650 for transformer in self.physical_line_transforms:
626 651 line = transformer.push(line)
627 652 if line is None:
628 653 return _accumulating(transformer)
629 654
630 655 if not self.within_python_line:
631 656 line = self.assemble_logical_lines.push(line)
632 657 if line is None:
633 658 return _accumulating('acc logical line')
634 659
635 660 for transformer in self.logical_line_transforms:
636 661 line = transformer.push(line)
637 662 if line is None:
638 663 return _accumulating(transformer)
639 664
640 665 line = self.assemble_python_lines.push(line)
641 666 if line is None:
642 667 self.within_python_line = True
643 668 return _accumulating('acc python line')
644 669 else:
645 670 self.within_python_line = False
646 671
647 672 for transformer in self.python_line_transforms:
648 673 line = transformer.push(line)
649 674 if line is None:
650 675 return _accumulating(transformer)
651 676
652 677 #print("transformers clear") #debug
653 678 self.transformer_accumulating = False
654 679 return super(IPythonInputSplitter, self).push(line)
@@ -1,592 +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):
358 def test_check_complete(self):
359 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,")
360 self.assertEqual(isp.check_complete("a = 1"), ('complete', None))
361 self.assertEqual(isp.check_complete("for a in range(5):"), ('incomplete', 4))
362 self.assertEqual(isp.check_complete("raise = 2"), ('invalid', None))
363 self.assertEqual(isp.check_complete("a = [1,\n2,"), ('incomplete', 0))
364 364
365 365 class InteractiveLoopTestCase(unittest.TestCase):
366 366 """Tests for an interactive loop like a python shell.
367 367 """
368 368 def check_ns(self, lines, ns):
369 369 """Validate that the given input lines produce the resulting namespace.
370 370
371 371 Note: the input lines are given exactly as they would be typed in an
372 372 auto-indenting environment, as mini_interactive_loop above already does
373 373 auto-indenting and prepends spaces to the input.
374 374 """
375 375 src = mini_interactive_loop(pseudo_input(lines))
376 376 test_ns = {}
377 377 exec(src, test_ns)
378 378 # We can't check that the provided ns is identical to the test_ns,
379 379 # because Python fills test_ns with extra keys (copyright, etc). But
380 380 # we can check that the given dict is *contained* in test_ns
381 381 for k,v in ns.items():
382 382 self.assertEqual(test_ns[k], v)
383 383
384 384 def test_simple(self):
385 385 self.check_ns(['x=1'], dict(x=1))
386 386
387 387 def test_simple2(self):
388 388 self.check_ns(['if 1:', 'x=2'], dict(x=2))
389 389
390 390 def test_xy(self):
391 391 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
392 392
393 393 def test_abc(self):
394 394 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
395 395
396 396 def test_multi(self):
397 397 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
398 398
399 399
400 400 class IPythonInputTestCase(InputSplitterTestCase):
401 401 """By just creating a new class whose .isp is a different instance, we
402 402 re-run the same test battery on the new input splitter.
403 403
404 404 In addition, this runs the tests over the syntax and syntax_ml dicts that
405 405 were tested by individual functions, as part of the OO interface.
406 406
407 407 It also makes some checks on the raw buffer storage.
408 408 """
409 409
410 410 def setUp(self):
411 411 self.isp = isp.IPythonInputSplitter()
412 412
413 413 def test_syntax(self):
414 414 """Call all single-line syntax tests from the main object"""
415 415 isp = self.isp
416 416 for example in syntax.values():
417 417 for raw, out_t in example:
418 418 if raw.startswith(' '):
419 419 continue
420 420
421 421 isp.push(raw+'\n')
422 422 out_raw = isp.source_raw
423 423 out = isp.source_reset()
424 424 self.assertEqual(out.rstrip(), out_t,
425 425 tt.pair_fail_msg.format("inputsplitter",raw, out_t, out))
426 426 self.assertEqual(out_raw.rstrip(), raw.rstrip())
427 427
428 428 def test_syntax_multiline(self):
429 429 isp = self.isp
430 430 for example in syntax_ml.values():
431 431 for line_pairs in example:
432 432 out_t_parts = []
433 433 raw_parts = []
434 434 for lraw, out_t_part in line_pairs:
435 435 if out_t_part is not None:
436 436 out_t_parts.append(out_t_part)
437 437
438 438 if lraw is not None:
439 439 isp.push(lraw)
440 440 raw_parts.append(lraw)
441 441
442 442 out_raw = isp.source_raw
443 443 out = isp.source_reset()
444 444 out_t = '\n'.join(out_t_parts).rstrip()
445 445 raw = '\n'.join(raw_parts).rstrip()
446 446 self.assertEqual(out.rstrip(), out_t)
447 447 self.assertEqual(out_raw.rstrip(), raw)
448 448
449 449 def test_syntax_multiline_cell(self):
450 450 isp = self.isp
451 451 for example in syntax_ml.values():
452 452
453 453 out_t_parts = []
454 454 for line_pairs in example:
455 455 raw = '\n'.join(r for r, _ in line_pairs if r is not None)
456 456 out_t = '\n'.join(t for _,t in line_pairs if t is not None)
457 457 out = isp.transform_cell(raw)
458 458 # Match ignoring trailing whitespace
459 459 self.assertEqual(out.rstrip(), out_t.rstrip())
460 460
461 461 def test_cellmagic_preempt(self):
462 462 isp = self.isp
463 463 for raw, name, line, cell in [
464 464 ("%%cellm a\nIn[1]:", u'cellm', u'a', u'In[1]:'),
465 465 ("%%cellm \nline\n>>> hi", u'cellm', u'', u'line\n>>> hi'),
466 466 (">>> %%cellm \nline\n>>> hi", u'cellm', u'', u'line\nhi'),
467 467 ("%%cellm \n>>> hi", u'cellm', u'', u'hi'),
468 468 ("%%cellm \nline1\nline2", u'cellm', u'', u'line1\nline2'),
469 469 ("%%cellm \nline1\\\\\nline2", u'cellm', u'', u'line1\\\\\nline2'),
470 470 ]:
471 471 expected = "get_ipython().run_cell_magic(%r, %r, %r)" % (
472 472 name, line, cell
473 473 )
474 474 out = isp.transform_cell(raw)
475 475 self.assertEqual(out.rstrip(), expected.rstrip())
476 476
477 477
478 478
479 479 #-----------------------------------------------------------------------------
480 480 # Main - use as a script, mostly for developer experiments
481 481 #-----------------------------------------------------------------------------
482 482
483 483 if __name__ == '__main__':
484 484 # A simple demo for interactive experimentation. This code will not get
485 485 # picked up by any test suite.
486 486 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
487 487
488 488 # configure here the syntax to use, prompt and whether to autoindent
489 489 #isp, start_prompt = InputSplitter(), '>>> '
490 490 isp, start_prompt = IPythonInputSplitter(), 'In> '
491 491
492 492 autoindent = True
493 493 #autoindent = False
494 494
495 495 try:
496 496 while True:
497 497 prompt = start_prompt
498 498 while isp.push_accepts_more():
499 499 indent = ' '*isp.indent_spaces
500 500 if autoindent:
501 501 line = indent + input(prompt+indent)
502 502 else:
503 503 line = input(prompt)
504 504 isp.push(line)
505 505 prompt = '... '
506 506
507 507 # Here we just return input so we can use it in a test suite, but a
508 508 # real interpreter would instead send it for execution somewhere.
509 509 #src = isp.source; raise EOFError # dbg
510 510 raw = isp.source_raw
511 511 src = isp.source_reset()
512 512 print('Input source was:\n', src)
513 513 print('Raw source was:\n', raw)
514 514 except EOFError:
515 515 print('Bye')
516 516
517 517 # Tests for cell magics support
518 518
519 519 def test_last_blank():
520 520 nt.assert_false(isp.last_blank(''))
521 521 nt.assert_false(isp.last_blank('abc'))
522 522 nt.assert_false(isp.last_blank('abc\n'))
523 523 nt.assert_false(isp.last_blank('abc\na'))
524 524
525 525 nt.assert_true(isp.last_blank('\n'))
526 526 nt.assert_true(isp.last_blank('\n '))
527 527 nt.assert_true(isp.last_blank('abc\n '))
528 528 nt.assert_true(isp.last_blank('abc\n\n'))
529 529 nt.assert_true(isp.last_blank('abc\nd\n\n'))
530 530 nt.assert_true(isp.last_blank('abc\nd\ne\n\n'))
531 531 nt.assert_true(isp.last_blank('abc \n \n \n\n'))
532 532
533 533
534 534 def test_last_two_blanks():
535 535 nt.assert_false(isp.last_two_blanks(''))
536 536 nt.assert_false(isp.last_two_blanks('abc'))
537 537 nt.assert_false(isp.last_two_blanks('abc\n'))
538 538 nt.assert_false(isp.last_two_blanks('abc\n\na'))
539 539 nt.assert_false(isp.last_two_blanks('abc\n \n'))
540 540 nt.assert_false(isp.last_two_blanks('abc\n\n'))
541 541
542 542 nt.assert_true(isp.last_two_blanks('\n\n'))
543 543 nt.assert_true(isp.last_two_blanks('\n\n '))
544 544 nt.assert_true(isp.last_two_blanks('\n \n'))
545 545 nt.assert_true(isp.last_two_blanks('abc\n\n '))
546 546 nt.assert_true(isp.last_two_blanks('abc\n\n\n'))
547 547 nt.assert_true(isp.last_two_blanks('abc\n\n \n'))
548 548 nt.assert_true(isp.last_two_blanks('abc\n\n \n '))
549 549 nt.assert_true(isp.last_two_blanks('abc\n\n \n \n'))
550 550 nt.assert_true(isp.last_two_blanks('abc\nd\n\n\n'))
551 551 nt.assert_true(isp.last_two_blanks('abc\nd\ne\nf\n\n\n'))
552 552
553 553
554 554 class CellMagicsCommon(object):
555 555
556 556 def test_whole_cell(self):
557 557 src = "%%cellm line\nbody\n"
558 558 out = self.sp.transform_cell(src)
559 559 ref = u"get_ipython().run_cell_magic({u}'cellm', {u}'line', {u}'body')\n"
560 560 nt.assert_equal(out, py3compat.u_format(ref))
561 561
562 562 def test_cellmagic_help(self):
563 563 self.sp.push('%%cellm?')
564 564 nt.assert_false(self.sp.push_accepts_more())
565 565
566 566 def tearDown(self):
567 567 self.sp.reset()
568 568
569 569
570 570 class CellModeCellMagics(CellMagicsCommon, unittest.TestCase):
571 571 sp = isp.IPythonInputSplitter(line_input_checker=False)
572 572
573 573 def test_incremental(self):
574 574 sp = self.sp
575 575 sp.push('%%cellm firstline\n')
576 576 nt.assert_true(sp.push_accepts_more()) #1
577 577 sp.push('line2\n')
578 578 nt.assert_true(sp.push_accepts_more()) #2
579 579 sp.push('\n')
580 580 # This should accept a blank line and carry on until the cell is reset
581 581 nt.assert_true(sp.push_accepts_more()) #3
582 582
583 583 class LineModeCellMagics(CellMagicsCommon, unittest.TestCase):
584 584 sp = isp.IPythonInputSplitter(line_input_checker=True)
585 585
586 586 def test_incremental(self):
587 587 sp = self.sp
588 588 sp.push('%%cellm line2\n')
589 589 nt.assert_true(sp.push_accepts_more()) #1
590 590 sp.push('\n')
591 591 # In this case, a blank line should end the cell magic
592 592 nt.assert_false(sp.push_accepts_more()) #2
@@ -1,223 +1,224 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 208 def test_is_complete():
209 209 with kernel() as kc:
210 210 # There are more test cases for this in core - here we just check
211 211 # that the kernel exposes the interface correctly.
212 212 kc.is_complete('2+2')
213 213 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
214 assert reply['content']['complete']
214 assert reply['content']['status'] == 'complete'
215 215
216 216 # SyntaxError should mean it's complete
217 217 kc.is_complete('raise = 2')
218 218 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
219 assert reply['content']['complete']
219 assert reply['content']['status'] == 'invalid'
220 220
221 221 kc.is_complete('a = [1,\n2,')
222 222 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
223 assert not reply['content']['complete']
223 assert reply['content']['status'] == 'incomplete'
224 assert reply['content']['indent'] == '' No newline at end of file
@@ -1,420 +1,428 b''
1 1 """Test suite for our zeromq-based message specification."""
2 2
3 3 # Copyright (c) IPython Development Team.
4 4 # Distributed under the terms of the Modified BSD License.
5 5
6 6 import re
7 7 from distutils.version import LooseVersion as V
8 8 from subprocess import PIPE
9 9 try:
10 10 from queue import Empty # Py 3
11 11 except ImportError:
12 12 from Queue import Empty # Py 2
13 13
14 14 import nose.tools as nt
15 15
16 16 from IPython.kernel import KernelManager
17 17
18 18 from IPython.utils.traitlets import (
19 19 HasTraits, TraitError, Bool, Unicode, Dict, Integer, List, Enum, Any,
20 20 )
21 21 from IPython.utils.py3compat import string_types, iteritems
22 22
23 23 from .utils import TIMEOUT, start_global_kernel, flush_channels, execute
24 24
25 25 #-----------------------------------------------------------------------------
26 26 # Globals
27 27 #-----------------------------------------------------------------------------
28 28 KC = None
29 29
30 30 def setup():
31 31 global KC
32 32 KC = start_global_kernel()
33 33
34 34 #-----------------------------------------------------------------------------
35 35 # Message Spec References
36 36 #-----------------------------------------------------------------------------
37 37
38 38 class Reference(HasTraits):
39 39
40 40 """
41 41 Base class for message spec specification testing.
42 42
43 43 This class is the core of the message specification test. The
44 44 idea is that child classes implement trait attributes for each
45 45 message keys, so that message keys can be tested against these
46 46 traits using :meth:`check` method.
47 47
48 48 """
49 49
50 50 def check(self, d):
51 51 """validate a dict against our traits"""
52 52 for key in self.trait_names():
53 53 nt.assert_in(key, d)
54 54 # FIXME: always allow None, probably not a good idea
55 55 if d[key] is None:
56 56 continue
57 57 try:
58 58 setattr(self, key, d[key])
59 59 except TraitError as e:
60 60 assert False, str(e)
61 61
62 62
63 63 class Version(Unicode):
64 64 def __init__(self, *args, **kwargs):
65 65 self.min = kwargs.pop('min', None)
66 66 self.max = kwargs.pop('max', None)
67 67 kwargs['default_value'] = self.min
68 68 super(Version, self).__init__(*args, **kwargs)
69 69
70 70 def validate(self, obj, value):
71 71 if self.min and V(value) < V(self.min):
72 72 raise TraitError("bad version: %s < %s" % (value, self.min))
73 73 if self.max and (V(value) > V(self.max)):
74 74 raise TraitError("bad version: %s > %s" % (value, self.max))
75 75
76 76
77 77 class RMessage(Reference):
78 78 msg_id = Unicode()
79 79 msg_type = Unicode()
80 80 header = Dict()
81 81 parent_header = Dict()
82 82 content = Dict()
83 83
84 84 def check(self, d):
85 85 super(RMessage, self).check(d)
86 86 RHeader().check(self.header)
87 87 if self.parent_header:
88 88 RHeader().check(self.parent_header)
89 89
90 90 class RHeader(Reference):
91 91 msg_id = Unicode()
92 92 msg_type = Unicode()
93 93 session = Unicode()
94 94 username = Unicode()
95 95 version = Version(min='5.0')
96 96
97 97 mime_pat = re.compile(r'^[\w\-\+\.]+/[\w\-\+\.]+$')
98 98
99 99 class MimeBundle(Reference):
100 100 metadata = Dict()
101 101 data = Dict()
102 102 def _data_changed(self, name, old, new):
103 103 for k,v in iteritems(new):
104 104 assert mime_pat.match(k)
105 105 nt.assert_is_instance(v, string_types)
106 106
107 107 # shell replies
108 108
109 109 class ExecuteReply(Reference):
110 110 execution_count = Integer()
111 111 status = Enum((u'ok', u'error'))
112 112
113 113 def check(self, d):
114 114 Reference.check(self, d)
115 115 if d['status'] == 'ok':
116 116 ExecuteReplyOkay().check(d)
117 117 elif d['status'] == 'error':
118 118 ExecuteReplyError().check(d)
119 119
120 120
121 121 class ExecuteReplyOkay(Reference):
122 122 payload = List(Dict)
123 123 user_expressions = Dict()
124 124
125 125
126 126 class ExecuteReplyError(Reference):
127 127 ename = Unicode()
128 128 evalue = Unicode()
129 129 traceback = List(Unicode)
130 130
131 131
132 132 class InspectReply(MimeBundle):
133 133 found = Bool()
134 134
135 135
136 136 class ArgSpec(Reference):
137 137 args = List(Unicode)
138 138 varargs = Unicode()
139 139 varkw = Unicode()
140 140 defaults = List()
141 141
142 142
143 143 class Status(Reference):
144 144 execution_state = Enum((u'busy', u'idle', u'starting'))
145 145
146 146
147 147 class CompleteReply(Reference):
148 148 matches = List(Unicode)
149 149 cursor_start = Integer()
150 150 cursor_end = Integer()
151 151 status = Unicode()
152 152
153 153
154 154 class KernelInfoReply(Reference):
155 155 protocol_version = Version(min='5.0')
156 156 implementation = Unicode('ipython')
157 157 implementation_version = Version(min='2.1')
158 158 language_version = Version(min='2.7')
159 159 language = Unicode('python')
160 160 banner = Unicode()
161 161
162 162
163 163 class IsCompleteReply(Reference):
164 complete = Bool()
164 status = Enum((u'complete', u'incomplete', u'invalid', u'unknown'))
165
166 def check(self, d):
167 Reference.check(self, d)
168 if d['status'] == 'incomplete':
169 IsCompleteReplyIncomplete().check(d)
170
171 class IsCompleteReplyIncomplete(Reference):
172 indent = Unicode()
165 173
166 174
167 175 # IOPub messages
168 176
169 177 class ExecuteInput(Reference):
170 178 code = Unicode()
171 179 execution_count = Integer()
172 180
173 181
174 182 Error = ExecuteReplyError
175 183
176 184
177 185 class Stream(Reference):
178 186 name = Enum((u'stdout', u'stderr'))
179 187 data = Unicode()
180 188
181 189
182 190 class DisplayData(MimeBundle):
183 191 pass
184 192
185 193
186 194 class ExecuteResult(MimeBundle):
187 195 execution_count = Integer()
188 196
189 197
190 198 references = {
191 199 'execute_reply' : ExecuteReply(),
192 200 'inspect_reply' : InspectReply(),
193 201 'status' : Status(),
194 202 'complete_reply' : CompleteReply(),
195 203 'kernel_info_reply': KernelInfoReply(),
196 204 'is_complete_reply': IsCompleteReply(),
197 205 'execute_input' : ExecuteInput(),
198 206 'execute_result' : ExecuteResult(),
199 207 'error' : Error(),
200 208 'stream' : Stream(),
201 209 'display_data' : DisplayData(),
202 210 'header' : RHeader(),
203 211 }
204 212 """
205 213 Specifications of `content` part of the reply messages.
206 214 """
207 215
208 216
209 217 def validate_message(msg, msg_type=None, parent=None):
210 218 """validate a message
211 219
212 220 This is a generator, and must be iterated through to actually
213 221 trigger each test.
214 222
215 223 If msg_type and/or parent are given, the msg_type and/or parent msg_id
216 224 are compared with the given values.
217 225 """
218 226 RMessage().check(msg)
219 227 if msg_type:
220 228 nt.assert_equal(msg['msg_type'], msg_type)
221 229 if parent:
222 230 nt.assert_equal(msg['parent_header']['msg_id'], parent)
223 231 content = msg['content']
224 232 ref = references[msg['msg_type']]
225 233 ref.check(content)
226 234
227 235
228 236 #-----------------------------------------------------------------------------
229 237 # Tests
230 238 #-----------------------------------------------------------------------------
231 239
232 240 # Shell channel
233 241
234 242 def test_execute():
235 243 flush_channels()
236 244
237 245 msg_id = KC.execute(code='x=1')
238 246 reply = KC.get_shell_msg(timeout=TIMEOUT)
239 247 validate_message(reply, 'execute_reply', msg_id)
240 248
241 249
242 250 def test_execute_silent():
243 251 flush_channels()
244 252 msg_id, reply = execute(code='x=1', silent=True)
245 253
246 254 # flush status=idle
247 255 status = KC.iopub_channel.get_msg(timeout=TIMEOUT)
248 256 validate_message(status, 'status', msg_id)
249 257 nt.assert_equal(status['content']['execution_state'], 'idle')
250 258
251 259 nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1)
252 260 count = reply['execution_count']
253 261
254 262 msg_id, reply = execute(code='x=2', silent=True)
255 263
256 264 # flush status=idle
257 265 status = KC.iopub_channel.get_msg(timeout=TIMEOUT)
258 266 validate_message(status, 'status', msg_id)
259 267 nt.assert_equal(status['content']['execution_state'], 'idle')
260 268
261 269 nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1)
262 270 count_2 = reply['execution_count']
263 271 nt.assert_equal(count_2, count)
264 272
265 273
266 274 def test_execute_error():
267 275 flush_channels()
268 276
269 277 msg_id, reply = execute(code='1/0')
270 278 nt.assert_equal(reply['status'], 'error')
271 279 nt.assert_equal(reply['ename'], 'ZeroDivisionError')
272 280
273 281 error = KC.iopub_channel.get_msg(timeout=TIMEOUT)
274 282 validate_message(error, 'error', msg_id)
275 283
276 284
277 285 def test_execute_inc():
278 286 """execute request should increment execution_count"""
279 287 flush_channels()
280 288
281 289 msg_id, reply = execute(code='x=1')
282 290 count = reply['execution_count']
283 291
284 292 flush_channels()
285 293
286 294 msg_id, reply = execute(code='x=2')
287 295 count_2 = reply['execution_count']
288 296 nt.assert_equal(count_2, count+1)
289 297
290 298
291 299 def test_user_expressions():
292 300 flush_channels()
293 301
294 302 msg_id, reply = execute(code='x=1', user_expressions=dict(foo='x+1'))
295 303 user_expressions = reply['user_expressions']
296 304 nt.assert_equal(user_expressions, {u'foo': {
297 305 u'status': u'ok',
298 306 u'data': {u'text/plain': u'2'},
299 307 u'metadata': {},
300 308 }})
301 309
302 310
303 311 def test_user_expressions_fail():
304 312 flush_channels()
305 313
306 314 msg_id, reply = execute(code='x=0', user_expressions=dict(foo='nosuchname'))
307 315 user_expressions = reply['user_expressions']
308 316 foo = user_expressions['foo']
309 317 nt.assert_equal(foo['status'], 'error')
310 318 nt.assert_equal(foo['ename'], 'NameError')
311 319
312 320
313 321 def test_oinfo():
314 322 flush_channels()
315 323
316 324 msg_id = KC.inspect('a')
317 325 reply = KC.get_shell_msg(timeout=TIMEOUT)
318 326 validate_message(reply, 'inspect_reply', msg_id)
319 327
320 328
321 329 def test_oinfo_found():
322 330 flush_channels()
323 331
324 332 msg_id, reply = execute(code='a=5')
325 333
326 334 msg_id = KC.inspect('a')
327 335 reply = KC.get_shell_msg(timeout=TIMEOUT)
328 336 validate_message(reply, 'inspect_reply', msg_id)
329 337 content = reply['content']
330 338 assert content['found']
331 339 text = content['data']['text/plain']
332 340 nt.assert_in('Type:', text)
333 341 nt.assert_in('Docstring:', text)
334 342
335 343
336 344 def test_oinfo_detail():
337 345 flush_channels()
338 346
339 347 msg_id, reply = execute(code='ip=get_ipython()')
340 348
341 349 msg_id = KC.inspect('ip.object_inspect', cursor_pos=10, detail_level=1)
342 350 reply = KC.get_shell_msg(timeout=TIMEOUT)
343 351 validate_message(reply, 'inspect_reply', msg_id)
344 352 content = reply['content']
345 353 assert content['found']
346 354 text = content['data']['text/plain']
347 355 nt.assert_in('Definition:', text)
348 356 nt.assert_in('Source:', text)
349 357
350 358
351 359 def test_oinfo_not_found():
352 360 flush_channels()
353 361
354 362 msg_id = KC.inspect('dne')
355 363 reply = KC.get_shell_msg(timeout=TIMEOUT)
356 364 validate_message(reply, 'inspect_reply', msg_id)
357 365 content = reply['content']
358 366 nt.assert_false(content['found'])
359 367
360 368
361 369 def test_complete():
362 370 flush_channels()
363 371
364 372 msg_id, reply = execute(code="alpha = albert = 5")
365 373
366 374 msg_id = KC.complete('al', 2)
367 375 reply = KC.get_shell_msg(timeout=TIMEOUT)
368 376 validate_message(reply, 'complete_reply', msg_id)
369 377 matches = reply['content']['matches']
370 378 for name in ('alpha', 'albert'):
371 379 nt.assert_in(name, matches)
372 380
373 381
374 382 def test_kernel_info_request():
375 383 flush_channels()
376 384
377 385 msg_id = KC.kernel_info()
378 386 reply = KC.get_shell_msg(timeout=TIMEOUT)
379 387 validate_message(reply, 'kernel_info_reply', msg_id)
380 388
381 389
382 390 def test_single_payload():
383 391 flush_channels()
384 392 msg_id, reply = execute(code="for i in range(3):\n"+
385 393 " x=range?\n")
386 394 payload = reply['payload']
387 395 next_input_pls = [pl for pl in payload if pl["source"] == "set_next_input"]
388 396 nt.assert_equal(len(next_input_pls), 1)
389 397
390 398 def test_is_complete():
391 399 flush_channels()
392 400
393 401 msg_id = KC.is_complete("a = 1")
394 402 reply = KC.get_shell_msg(timeout=TIMEOUT)
395 403 validate_message(reply, 'is_complete_reply', msg_id)
396 404
397 405 # IOPub channel
398 406
399 407
400 408 def test_stream():
401 409 flush_channels()
402 410
403 411 msg_id, reply = execute("print('hi')")
404 412
405 413 stdout = KC.iopub_channel.get_msg(timeout=TIMEOUT)
406 414 validate_message(stdout, 'stream', msg_id)
407 415 content = stdout['content']
408 416 nt.assert_equal(content['data'], u'hi\n')
409 417
410 418
411 419 def test_display_data():
412 420 flush_channels()
413 421
414 422 msg_id, reply = execute("from IPython.core.display import display; display(1)")
415 423
416 424 display = KC.iopub_channel.get_msg(timeout=TIMEOUT)
417 425 validate_message(display, 'display_data', parent=msg_id)
418 426 data = display['content']['data']
419 427 nt.assert_equal(data['text/plain'], u'1')
420 428
@@ -1,306 +1,309 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 232 def do_is_complete(self, code):
233 complete = self.shell.input_transformer_manager.is_complete(code)
234 return {'complete': complete}
233 status, indent_spaces = self.shell.input_transformer_manager.check_complete(code)
234 r = {'status': status}
235 if status == 'incomplete':
236 r['indent'] = ' ' * indent_spaces
237 return r
235 238
236 239 def do_apply(self, content, bufs, msg_id, reply_metadata):
237 240 shell = self.shell
238 241 try:
239 242 working = shell.user_ns
240 243
241 244 prefix = "_"+str(msg_id).replace("-","")+"_"
242 245
243 246 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
244 247
245 248 fname = getattr(f, '__name__', 'f')
246 249
247 250 fname = prefix+"f"
248 251 argname = prefix+"args"
249 252 kwargname = prefix+"kwargs"
250 253 resultname = prefix+"result"
251 254
252 255 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
253 256 # print ns
254 257 working.update(ns)
255 258 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
256 259 try:
257 260 exec(code, shell.user_global_ns, shell.user_ns)
258 261 result = working.get(resultname)
259 262 finally:
260 263 for key in ns:
261 264 working.pop(key)
262 265
263 266 result_buf = serialize_object(result,
264 267 buffer_threshold=self.session.buffer_threshold,
265 268 item_threshold=self.session.item_threshold,
266 269 )
267 270
268 271 except:
269 272 # invoke IPython traceback formatting
270 273 shell.showtraceback()
271 274 # FIXME - fish exception info out of shell, possibly left there by
272 275 # run_code. We'll need to clean up this logic later.
273 276 reply_content = {}
274 277 if shell._reply_content is not None:
275 278 reply_content.update(shell._reply_content)
276 279 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
277 280 reply_content['engine_info'] = e_info
278 281 # reset after use
279 282 shell._reply_content = None
280 283
281 284 self.send_response(self.iopub_socket, u'error', reply_content,
282 285 ident=self._topic('error'))
283 286 self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback']))
284 287 result_buf = []
285 288
286 289 if reply_content['ename'] == 'UnmetDependency':
287 290 reply_metadata['dependencies_met'] = False
288 291 else:
289 292 reply_content = {'status' : 'ok'}
290 293
291 294 return reply_content, result_buf
292 295
293 296 def do_clear(self):
294 297 self.shell.reset(False)
295 298 return dict(status='ok')
296 299
297 300
298 301 # This exists only for backwards compatibility - use IPythonKernel instead
299 302
300 303 @undoc
301 304 class Kernel(IPythonKernel):
302 305 def __init__(self, *args, **kwargs):
303 306 import warnings
304 307 warnings.warn('Kernel is a deprecated alias of IPython.kernel.zmq.ipkernel.IPythonKernel',
305 308 DeprecationWarning)
306 309 super(Kernel, self).__init__(*args, **kwargs) No newline at end of file
@@ -1,693 +1,693 b''
1 1 """Base class for a kernel that talks to frontends over 0MQ."""
2 2
3 3 # Copyright (c) IPython Development Team.
4 4 # Distributed under the terms of the Modified BSD License.
5 5
6 6 from __future__ import print_function
7 7
8 8 import sys
9 9 import time
10 10 import logging
11 11 import uuid
12 12
13 13 from datetime import datetime
14 14 from signal import (
15 15 signal, default_int_handler, SIGINT
16 16 )
17 17
18 18 import zmq
19 19 from zmq.eventloop import ioloop
20 20 from zmq.eventloop.zmqstream import ZMQStream
21 21
22 22 from IPython.config.configurable import Configurable
23 23 from IPython.core.error import StdinNotImplementedError
24 24 from IPython.core import release
25 25 from IPython.utils import py3compat
26 26 from IPython.utils.py3compat import unicode_type, string_types
27 27 from IPython.utils.jsonutil import json_clean
28 28 from IPython.utils.traitlets import (
29 29 Any, Instance, Float, Dict, List, Set, Integer, Unicode, Bool,
30 30 )
31 31
32 32 from .session import Session
33 33
34 34
35 35 class Kernel(Configurable):
36 36
37 37 #---------------------------------------------------------------------------
38 38 # Kernel interface
39 39 #---------------------------------------------------------------------------
40 40
41 41 # attribute to override with a GUI
42 42 eventloop = Any(None)
43 43 def _eventloop_changed(self, name, old, new):
44 44 """schedule call to eventloop from IOLoop"""
45 45 loop = ioloop.IOLoop.instance()
46 46 loop.add_callback(self.enter_eventloop)
47 47
48 48 session = Instance(Session)
49 49 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
50 50 shell_streams = List()
51 51 control_stream = Instance(ZMQStream)
52 52 iopub_socket = Instance(zmq.Socket)
53 53 stdin_socket = Instance(zmq.Socket)
54 54 log = Instance(logging.Logger)
55 55
56 56 # identities:
57 57 int_id = Integer(-1)
58 58 ident = Unicode()
59 59
60 60 def _ident_default(self):
61 61 return unicode_type(uuid.uuid4())
62 62
63 63 # Private interface
64 64
65 65 _darwin_app_nap = Bool(True, config=True,
66 66 help="""Whether to use appnope for compatiblity with OS X App Nap.
67 67
68 68 Only affects OS X >= 10.9.
69 69 """
70 70 )
71 71
72 72 # track associations with current request
73 73 _allow_stdin = Bool(False)
74 74 _parent_header = Dict()
75 75 _parent_ident = Any(b'')
76 76 # Time to sleep after flushing the stdout/err buffers in each execute
77 77 # cycle. While this introduces a hard limit on the minimal latency of the
78 78 # execute cycle, it helps prevent output synchronization problems for
79 79 # clients.
80 80 # Units are in seconds. The minimum zmq latency on local host is probably
81 81 # ~150 microseconds, set this to 500us for now. We may need to increase it
82 82 # a little if it's not enough after more interactive testing.
83 83 _execute_sleep = Float(0.0005, config=True)
84 84
85 85 # Frequency of the kernel's event loop.
86 86 # Units are in seconds, kernel subclasses for GUI toolkits may need to
87 87 # adapt to milliseconds.
88 88 _poll_interval = Float(0.05, config=True)
89 89
90 90 # If the shutdown was requested over the network, we leave here the
91 91 # necessary reply message so it can be sent by our registered atexit
92 92 # handler. This ensures that the reply is only sent to clients truly at
93 93 # the end of our shutdown process (which happens after the underlying
94 94 # IPython shell's own shutdown).
95 95 _shutdown_message = None
96 96
97 97 # This is a dict of port number that the kernel is listening on. It is set
98 98 # by record_ports and used by connect_request.
99 99 _recorded_ports = Dict()
100 100
101 101 # set of aborted msg_ids
102 102 aborted = Set()
103 103
104 104 # Track execution count here. For IPython, we override this to use the
105 105 # execution count we store in the shell.
106 106 execution_count = 0
107 107
108 108
109 109 def __init__(self, **kwargs):
110 110 super(Kernel, self).__init__(**kwargs)
111 111
112 112 # Build dict of handlers for message types
113 113 msg_types = [ 'execute_request', 'complete_request',
114 114 'inspect_request', 'history_request',
115 115 'kernel_info_request',
116 116 'connect_request', 'shutdown_request',
117 117 'apply_request', 'is_complete_request',
118 118 ]
119 119 self.shell_handlers = {}
120 120 for msg_type in msg_types:
121 121 self.shell_handlers[msg_type] = getattr(self, msg_type)
122 122
123 123 control_msg_types = msg_types + [ 'clear_request', 'abort_request' ]
124 124 self.control_handlers = {}
125 125 for msg_type in control_msg_types:
126 126 self.control_handlers[msg_type] = getattr(self, msg_type)
127 127
128 128
129 129 def dispatch_control(self, msg):
130 130 """dispatch control requests"""
131 131 idents,msg = self.session.feed_identities(msg, copy=False)
132 132 try:
133 133 msg = self.session.unserialize(msg, content=True, copy=False)
134 134 except:
135 135 self.log.error("Invalid Control Message", exc_info=True)
136 136 return
137 137
138 138 self.log.debug("Control received: %s", msg)
139 139
140 140 # Set the parent message for side effects.
141 141 self.set_parent(idents, msg)
142 142 self._publish_status(u'busy')
143 143
144 144 header = msg['header']
145 145 msg_type = header['msg_type']
146 146
147 147 handler = self.control_handlers.get(msg_type, None)
148 148 if handler is None:
149 149 self.log.error("UNKNOWN CONTROL MESSAGE TYPE: %r", msg_type)
150 150 else:
151 151 try:
152 152 handler(self.control_stream, idents, msg)
153 153 except Exception:
154 154 self.log.error("Exception in control handler:", exc_info=True)
155 155
156 156 sys.stdout.flush()
157 157 sys.stderr.flush()
158 158 self._publish_status(u'idle')
159 159
160 160 def dispatch_shell(self, stream, msg):
161 161 """dispatch shell requests"""
162 162 # flush control requests first
163 163 if self.control_stream:
164 164 self.control_stream.flush()
165 165
166 166 idents,msg = self.session.feed_identities(msg, copy=False)
167 167 try:
168 168 msg = self.session.unserialize(msg, content=True, copy=False)
169 169 except:
170 170 self.log.error("Invalid Message", exc_info=True)
171 171 return
172 172
173 173 # Set the parent message for side effects.
174 174 self.set_parent(idents, msg)
175 175 self._publish_status(u'busy')
176 176
177 177 header = msg['header']
178 178 msg_id = header['msg_id']
179 179 msg_type = msg['header']['msg_type']
180 180
181 181 # Print some info about this message and leave a '--->' marker, so it's
182 182 # easier to trace visually the message chain when debugging. Each
183 183 # handler prints its message at the end.
184 184 self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type)
185 185 self.log.debug(' Content: %s\n --->\n ', msg['content'])
186 186
187 187 if msg_id in self.aborted:
188 188 self.aborted.remove(msg_id)
189 189 # is it safe to assume a msg_id will not be resubmitted?
190 190 reply_type = msg_type.split('_')[0] + '_reply'
191 191 status = {'status' : 'aborted'}
192 192 md = {'engine' : self.ident}
193 193 md.update(status)
194 194 self.session.send(stream, reply_type, metadata=md,
195 195 content=status, parent=msg, ident=idents)
196 196 return
197 197
198 198 handler = self.shell_handlers.get(msg_type, None)
199 199 if handler is None:
200 200 self.log.error("UNKNOWN MESSAGE TYPE: %r", msg_type)
201 201 else:
202 202 # ensure default_int_handler during handler call
203 203 sig = signal(SIGINT, default_int_handler)
204 204 self.log.debug("%s: %s", msg_type, msg)
205 205 try:
206 206 handler(stream, idents, msg)
207 207 except Exception:
208 208 self.log.error("Exception in message handler:", exc_info=True)
209 209 finally:
210 210 signal(SIGINT, sig)
211 211
212 212 sys.stdout.flush()
213 213 sys.stderr.flush()
214 214 self._publish_status(u'idle')
215 215
216 216 def enter_eventloop(self):
217 217 """enter eventloop"""
218 218 self.log.info("entering eventloop %s", self.eventloop)
219 219 for stream in self.shell_streams:
220 220 # flush any pending replies,
221 221 # which may be skipped by entering the eventloop
222 222 stream.flush(zmq.POLLOUT)
223 223 # restore default_int_handler
224 224 signal(SIGINT, default_int_handler)
225 225 while self.eventloop is not None:
226 226 try:
227 227 self.eventloop(self)
228 228 except KeyboardInterrupt:
229 229 # Ctrl-C shouldn't crash the kernel
230 230 self.log.error("KeyboardInterrupt caught in kernel")
231 231 continue
232 232 else:
233 233 # eventloop exited cleanly, this means we should stop (right?)
234 234 self.eventloop = None
235 235 break
236 236 self.log.info("exiting eventloop")
237 237
238 238 def start(self):
239 239 """register dispatchers for streams"""
240 240 if self.control_stream:
241 241 self.control_stream.on_recv(self.dispatch_control, copy=False)
242 242
243 243 def make_dispatcher(stream):
244 244 def dispatcher(msg):
245 245 return self.dispatch_shell(stream, msg)
246 246 return dispatcher
247 247
248 248 for s in self.shell_streams:
249 249 s.on_recv(make_dispatcher(s), copy=False)
250 250
251 251 # publish idle status
252 252 self._publish_status('starting')
253 253
254 254 def do_one_iteration(self):
255 255 """step eventloop just once"""
256 256 if self.control_stream:
257 257 self.control_stream.flush()
258 258 for stream in self.shell_streams:
259 259 # handle at most one request per iteration
260 260 stream.flush(zmq.POLLIN, 1)
261 261 stream.flush(zmq.POLLOUT)
262 262
263 263
264 264 def record_ports(self, ports):
265 265 """Record the ports that this kernel is using.
266 266
267 267 The creator of the Kernel instance must call this methods if they
268 268 want the :meth:`connect_request` method to return the port numbers.
269 269 """
270 270 self._recorded_ports = ports
271 271
272 272 #---------------------------------------------------------------------------
273 273 # Kernel request handlers
274 274 #---------------------------------------------------------------------------
275 275
276 276 def _make_metadata(self, other=None):
277 277 """init metadata dict, for execute/apply_reply"""
278 278 new_md = {
279 279 'dependencies_met' : True,
280 280 'engine' : self.ident,
281 281 'started': datetime.now(),
282 282 }
283 283 if other:
284 284 new_md.update(other)
285 285 return new_md
286 286
287 287 def _publish_execute_input(self, code, parent, execution_count):
288 288 """Publish the code request on the iopub stream."""
289 289
290 290 self.session.send(self.iopub_socket, u'execute_input',
291 291 {u'code':code, u'execution_count': execution_count},
292 292 parent=parent, ident=self._topic('execute_input')
293 293 )
294 294
295 295 def _publish_status(self, status, parent=None):
296 296 """send status (busy/idle) on IOPub"""
297 297 self.session.send(self.iopub_socket,
298 298 u'status',
299 299 {u'execution_state': status},
300 300 parent=parent or self._parent_header,
301 301 ident=self._topic('status'),
302 302 )
303 303
304 304 def set_parent(self, ident, parent):
305 305 """Set the current parent_header
306 306
307 307 Side effects (IOPub messages) and replies are associated with
308 308 the request that caused them via the parent_header.
309 309
310 310 The parent identity is used to route input_request messages
311 311 on the stdin channel.
312 312 """
313 313 self._parent_ident = ident
314 314 self._parent_header = parent
315 315
316 316 def send_response(self, stream, msg_or_type, content=None, ident=None,
317 317 buffers=None, track=False, header=None, metadata=None):
318 318 """Send a response to the message we're currently processing.
319 319
320 320 This accepts all the parameters of :meth:`IPython.kernel.zmq.session.Session.send`
321 321 except ``parent``.
322 322
323 323 This relies on :meth:`set_parent` having been called for the current
324 324 message.
325 325 """
326 326 return self.session.send(stream, msg_or_type, content, self._parent_header,
327 327 ident, buffers, track, header, metadata)
328 328
329 329 def execute_request(self, stream, ident, parent):
330 330 """handle an execute_request"""
331 331
332 332 try:
333 333 content = parent[u'content']
334 334 code = py3compat.cast_unicode_py2(content[u'code'])
335 335 silent = content[u'silent']
336 336 store_history = content.get(u'store_history', not silent)
337 337 user_expressions = content.get('user_expressions', {})
338 338 allow_stdin = content.get('allow_stdin', False)
339 339 except:
340 340 self.log.error("Got bad msg: ")
341 341 self.log.error("%s", parent)
342 342 return
343 343
344 344 md = self._make_metadata(parent['metadata'])
345 345
346 346 # Re-broadcast our input for the benefit of listening clients, and
347 347 # start computing output
348 348 if not silent:
349 349 self.execution_count += 1
350 350 self._publish_execute_input(code, parent, self.execution_count)
351 351
352 352 reply_content = self.do_execute(code, silent, store_history,
353 353 user_expressions, allow_stdin)
354 354
355 355 # Flush output before sending the reply.
356 356 sys.stdout.flush()
357 357 sys.stderr.flush()
358 358 # FIXME: on rare occasions, the flush doesn't seem to make it to the
359 359 # clients... This seems to mitigate the problem, but we definitely need
360 360 # to better understand what's going on.
361 361 if self._execute_sleep:
362 362 time.sleep(self._execute_sleep)
363 363
364 364 # Send the reply.
365 365 reply_content = json_clean(reply_content)
366 366
367 367 md['status'] = reply_content['status']
368 368 if reply_content['status'] == 'error' and \
369 369 reply_content['ename'] == 'UnmetDependency':
370 370 md['dependencies_met'] = False
371 371
372 372 reply_msg = self.session.send(stream, u'execute_reply',
373 373 reply_content, parent, metadata=md,
374 374 ident=ident)
375 375
376 376 self.log.debug("%s", reply_msg)
377 377
378 378 if not silent and reply_msg['content']['status'] == u'error':
379 379 self._abort_queues()
380 380
381 381 def do_execute(self, code, silent, store_history=True,
382 382 user_experssions=None, allow_stdin=False):
383 383 """Execute user code. Must be overridden by subclasses.
384 384 """
385 385 raise NotImplementedError
386 386
387 387 def complete_request(self, stream, ident, parent):
388 388 content = parent['content']
389 389 code = content['code']
390 390 cursor_pos = content['cursor_pos']
391 391
392 392 matches = self.do_complete(code, cursor_pos)
393 393 matches = json_clean(matches)
394 394 completion_msg = self.session.send(stream, 'complete_reply',
395 395 matches, parent, ident)
396 396 self.log.debug("%s", completion_msg)
397 397
398 398 def do_complete(self, code, cursor_pos):
399 399 """Override in subclasses to find completions.
400 400 """
401 401 return {'matches' : [],
402 402 'cursor_end' : cursor_pos,
403 403 'cursor_start' : cursor_pos,
404 404 'metadata' : {},
405 405 'status' : 'ok'}
406 406
407 407 def inspect_request(self, stream, ident, parent):
408 408 content = parent['content']
409 409
410 410 reply_content = self.do_inspect(content['code'], content['cursor_pos'],
411 411 content.get('detail_level', 0))
412 412 # Before we send this object over, we scrub it for JSON usage
413 413 reply_content = json_clean(reply_content)
414 414 msg = self.session.send(stream, 'inspect_reply',
415 415 reply_content, parent, ident)
416 416 self.log.debug("%s", msg)
417 417
418 418 def do_inspect(self, code, cursor_pos, detail_level=0):
419 419 """Override in subclasses to allow introspection.
420 420 """
421 421 return {'status': 'ok', 'data':{}, 'metadata':{}, 'found':False}
422 422
423 423 def history_request(self, stream, ident, parent):
424 424 content = parent['content']
425 425
426 426 reply_content = self.do_history(**content)
427 427
428 428 reply_content = json_clean(reply_content)
429 429 msg = self.session.send(stream, 'history_reply',
430 430 reply_content, parent, ident)
431 431 self.log.debug("%s", msg)
432 432
433 433 def do_history(self, hist_access_type, output, raw, session=None, start=None,
434 434 stop=None, n=None, pattern=None, unique=False):
435 435 """Override in subclasses to access history.
436 436 """
437 437 return {'history': []}
438 438
439 439 def connect_request(self, stream, ident, parent):
440 440 if self._recorded_ports is not None:
441 441 content = self._recorded_ports.copy()
442 442 else:
443 443 content = {}
444 444 msg = self.session.send(stream, 'connect_reply',
445 445 content, parent, ident)
446 446 self.log.debug("%s", msg)
447 447
448 448 @property
449 449 def kernel_info(self):
450 450 return {
451 451 'protocol_version': release.kernel_protocol_version,
452 452 'implementation': self.implementation,
453 453 'implementation_version': self.implementation_version,
454 454 'language': self.language,
455 455 'language_version': self.language_version,
456 456 'banner': self.banner,
457 457 }
458 458
459 459 def kernel_info_request(self, stream, ident, parent):
460 460 msg = self.session.send(stream, 'kernel_info_reply',
461 461 self.kernel_info, parent, ident)
462 462 self.log.debug("%s", msg)
463 463
464 464 def shutdown_request(self, stream, ident, parent):
465 465 content = self.do_shutdown(parent['content']['restart'])
466 466 self.session.send(stream, u'shutdown_reply', content, parent, ident=ident)
467 467 # same content, but different msg_id for broadcasting on IOPub
468 468 self._shutdown_message = self.session.msg(u'shutdown_reply',
469 469 content, parent
470 470 )
471 471
472 472 self._at_shutdown()
473 473 # call sys.exit after a short delay
474 474 loop = ioloop.IOLoop.instance()
475 475 loop.add_timeout(time.time()+0.1, loop.stop)
476 476
477 477 def do_shutdown(self, restart):
478 478 """Override in subclasses to do things when the frontend shuts down the
479 479 kernel.
480 480 """
481 481 return {'status': 'ok', 'restart': restart}
482 482
483 483 def is_complete_request(self, stream, ident, parent):
484 484 content = parent['content']
485 485 code = content['code']
486 486
487 487 reply_content = self.do_is_complete(code)
488 488 reply_content = json_clean(reply_content)
489 489 reply_msg = self.session.send(stream, 'is_complete_reply',
490 490 reply_content, parent, ident)
491 491 self.log.debug("%s", reply_msg)
492 492
493 493 def do_is_complete(self, code):
494 494 """Override in subclasses to find completions.
495 495 """
496 return {'complete' : True,
496 return {'status' : 'unknown',
497 497 }
498 498
499 499 #---------------------------------------------------------------------------
500 500 # Engine methods
501 501 #---------------------------------------------------------------------------
502 502
503 503 def apply_request(self, stream, ident, parent):
504 504 try:
505 505 content = parent[u'content']
506 506 bufs = parent[u'buffers']
507 507 msg_id = parent['header']['msg_id']
508 508 except:
509 509 self.log.error("Got bad msg: %s", parent, exc_info=True)
510 510 return
511 511
512 512 md = self._make_metadata(parent['metadata'])
513 513
514 514 reply_content, result_buf = self.do_apply(content, bufs, msg_id, md)
515 515
516 516 # put 'ok'/'error' status in header, for scheduler introspection:
517 517 md['status'] = reply_content['status']
518 518
519 519 # flush i/o
520 520 sys.stdout.flush()
521 521 sys.stderr.flush()
522 522
523 523 self.session.send(stream, u'apply_reply', reply_content,
524 524 parent=parent, ident=ident,buffers=result_buf, metadata=md)
525 525
526 526 def do_apply(self, content, bufs, msg_id, reply_metadata):
527 527 """Override in subclasses to support the IPython parallel framework.
528 528 """
529 529 raise NotImplementedError
530 530
531 531 #---------------------------------------------------------------------------
532 532 # Control messages
533 533 #---------------------------------------------------------------------------
534 534
535 535 def abort_request(self, stream, ident, parent):
536 536 """abort a specifig msg by id"""
537 537 msg_ids = parent['content'].get('msg_ids', None)
538 538 if isinstance(msg_ids, string_types):
539 539 msg_ids = [msg_ids]
540 540 if not msg_ids:
541 541 self.abort_queues()
542 542 for mid in msg_ids:
543 543 self.aborted.add(str(mid))
544 544
545 545 content = dict(status='ok')
546 546 reply_msg = self.session.send(stream, 'abort_reply', content=content,
547 547 parent=parent, ident=ident)
548 548 self.log.debug("%s", reply_msg)
549 549
550 550 def clear_request(self, stream, idents, parent):
551 551 """Clear our namespace."""
552 552 content = self.do_clear()
553 553 self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
554 554 content = content)
555 555
556 556 def do_clear(self):
557 557 """Override in subclasses to clear the namespace
558 558
559 559 This is only required for IPython.parallel.
560 560 """
561 561 raise NotImplementedError
562 562
563 563 #---------------------------------------------------------------------------
564 564 # Protected interface
565 565 #---------------------------------------------------------------------------
566 566
567 567 def _topic(self, topic):
568 568 """prefixed topic for IOPub messages"""
569 569 if self.int_id >= 0:
570 570 base = "engine.%i" % self.int_id
571 571 else:
572 572 base = "kernel.%s" % self.ident
573 573
574 574 return py3compat.cast_bytes("%s.%s" % (base, topic))
575 575
576 576 def _abort_queues(self):
577 577 for stream in self.shell_streams:
578 578 if stream:
579 579 self._abort_queue(stream)
580 580
581 581 def _abort_queue(self, stream):
582 582 poller = zmq.Poller()
583 583 poller.register(stream.socket, zmq.POLLIN)
584 584 while True:
585 585 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
586 586 if msg is None:
587 587 return
588 588
589 589 self.log.info("Aborting:")
590 590 self.log.info("%s", msg)
591 591 msg_type = msg['header']['msg_type']
592 592 reply_type = msg_type.split('_')[0] + '_reply'
593 593
594 594 status = {'status' : 'aborted'}
595 595 md = {'engine' : self.ident}
596 596 md.update(status)
597 597 reply_msg = self.session.send(stream, reply_type, metadata=md,
598 598 content=status, parent=msg, ident=idents)
599 599 self.log.debug("%s", reply_msg)
600 600 # We need to wait a bit for requests to come in. This can probably
601 601 # be set shorter for true asynchronous clients.
602 602 poller.poll(50)
603 603
604 604
605 605 def _no_raw_input(self):
606 606 """Raise StdinNotImplentedError if active frontend doesn't support
607 607 stdin."""
608 608 raise StdinNotImplementedError("raw_input was called, but this "
609 609 "frontend does not support stdin.")
610 610
611 611 def getpass(self, prompt=''):
612 612 """Forward getpass to frontends
613 613
614 614 Raises
615 615 ------
616 616 StdinNotImplentedError if active frontend doesn't support stdin.
617 617 """
618 618 if not self._allow_stdin:
619 619 raise StdinNotImplementedError(
620 620 "getpass was called, but this frontend does not support input requests."
621 621 )
622 622 return self._input_request(prompt,
623 623 self._parent_ident,
624 624 self._parent_header,
625 625 password=True,
626 626 )
627 627
628 628 def raw_input(self, prompt=''):
629 629 """Forward raw_input to frontends
630 630
631 631 Raises
632 632 ------
633 633 StdinNotImplentedError if active frontend doesn't support stdin.
634 634 """
635 635 if not self._allow_stdin:
636 636 raise StdinNotImplementedError(
637 637 "raw_input was called, but this frontend does not support input requests."
638 638 )
639 639 return self._input_request(prompt,
640 640 self._parent_ident,
641 641 self._parent_header,
642 642 password=False,
643 643 )
644 644
645 645 def _input_request(self, prompt, ident, parent, password=False):
646 646 # Flush output before making the request.
647 647 sys.stderr.flush()
648 648 sys.stdout.flush()
649 649 # flush the stdin socket, to purge stale replies
650 650 while True:
651 651 try:
652 652 self.stdin_socket.recv_multipart(zmq.NOBLOCK)
653 653 except zmq.ZMQError as e:
654 654 if e.errno == zmq.EAGAIN:
655 655 break
656 656 else:
657 657 raise
658 658
659 659 # Send the input request.
660 660 content = json_clean(dict(prompt=prompt, password=password))
661 661 self.session.send(self.stdin_socket, u'input_request', content, parent,
662 662 ident=ident)
663 663
664 664 # Await a response.
665 665 while True:
666 666 try:
667 667 ident, reply = self.session.recv(self.stdin_socket, 0)
668 668 except Exception:
669 669 self.log.warn("Invalid Message:", exc_info=True)
670 670 except KeyboardInterrupt:
671 671 # re-raise KeyboardInterrupt, to truncate traceback
672 672 raise KeyboardInterrupt
673 673 else:
674 674 break
675 675 try:
676 676 value = py3compat.unicode_to_str(reply['content']['value'])
677 677 except:
678 678 self.log.error("Bad input_reply: %s", parent)
679 679 value = ''
680 680 if value == '\x04':
681 681 # EOF
682 682 raise EOFError
683 683 return value
684 684
685 685 def _at_shutdown(self):
686 686 """Actions taken at shutdown by the kernel, called by python's atexit.
687 687 """
688 688 # io.rprint("Kernel at_shutdown") # dbg
689 689 if self._shutdown_message is not None:
690 690 self.session.send(self.iopub_socket, self._shutdown_message, ident=self._topic('shutdown'))
691 691 self.log.debug("%s", self._shutdown_message)
692 692 [ s.flush(zmq.POLLOUT) for s in self.shell_streams ]
693 693
@@ -1,1095 +1,1111 b''
1 1 .. _messaging:
2 2
3 3 ======================
4 4 Messaging in IPython
5 5 ======================
6 6
7 7
8 8 Versioning
9 9 ==========
10 10
11 11 The IPython message specification is versioned independently of IPython.
12 12 The current version of the specification is 5.1.
13 13
14 14
15 15 Introduction
16 16 ============
17 17
18 18 This document explains the basic communications design and messaging
19 19 specification for how the various IPython objects interact over a network
20 20 transport. The current implementation uses the ZeroMQ_ library for messaging
21 21 within and between hosts.
22 22
23 23 .. Note::
24 24
25 25 This document should be considered the authoritative description of the
26 26 IPython messaging protocol, and all developers are strongly encouraged to
27 27 keep it updated as the implementation evolves, so that we have a single
28 28 common reference for all protocol details.
29 29
30 30 The basic design is explained in the following diagram:
31 31
32 32 .. image:: figs/frontend-kernel.png
33 33 :width: 450px
34 34 :alt: IPython kernel/frontend messaging architecture.
35 35 :align: center
36 36 :target: ../_images/frontend-kernel.png
37 37
38 38 A single kernel can be simultaneously connected to one or more frontends. The
39 39 kernel has three sockets that serve the following functions:
40 40
41 41 1. Shell: this single ROUTER socket allows multiple incoming connections from
42 42 frontends, and this is the socket where requests for code execution, object
43 43 information, prompts, etc. are made to the kernel by any frontend. The
44 44 communication on this socket is a sequence of request/reply actions from
45 45 each frontend and the kernel.
46 46
47 47 2. IOPub: this socket is the 'broadcast channel' where the kernel publishes all
48 48 side effects (stdout, stderr, etc.) as well as the requests coming from any
49 49 client over the shell socket and its own requests on the stdin socket. There
50 50 are a number of actions in Python which generate side effects: :func:`print`
51 51 writes to ``sys.stdout``, errors generate tracebacks, etc. Additionally, in
52 52 a multi-client scenario, we want all frontends to be able to know what each
53 53 other has sent to the kernel (this can be useful in collaborative scenarios,
54 54 for example). This socket allows both side effects and the information
55 55 about communications taking place with one client over the shell channel
56 56 to be made available to all clients in a uniform manner.
57 57
58 58 3. stdin: this ROUTER socket is connected to all frontends, and it allows
59 59 the kernel to request input from the active frontend when :func:`raw_input` is called.
60 60 The frontend that executed the code has a DEALER socket that acts as a 'virtual keyboard'
61 61 for the kernel while this communication is happening (illustrated in the
62 62 figure by the black outline around the central keyboard). In practice,
63 63 frontends may display such kernel requests using a special input widget or
64 64 otherwise indicating that the user is to type input for the kernel instead
65 65 of normal commands in the frontend.
66 66
67 67 All messages are tagged with enough information (details below) for clients
68 68 to know which messages come from their own interaction with the kernel and
69 69 which ones are from other clients, so they can display each type
70 70 appropriately.
71 71
72 72 4. Control: This channel is identical to Shell, but operates on a separate socket,
73 73 to allow important messages to avoid queueing behind execution requests (e.g. shutdown or abort).
74 74
75 75 The actual format of the messages allowed on each of these channels is
76 76 specified below. Messages are dicts of dicts with string keys and values that
77 77 are reasonably representable in JSON. Our current implementation uses JSON
78 78 explicitly as its message format, but this shouldn't be considered a permanent
79 79 feature. As we've discovered that JSON has non-trivial performance issues due
80 80 to excessive copying, we may in the future move to a pure pickle-based raw
81 81 message format. However, it should be possible to easily convert from the raw
82 82 objects to JSON, since we may have non-python clients (e.g. a web frontend).
83 83 As long as it's easy to make a JSON version of the objects that is a faithful
84 84 representation of all the data, we can communicate with such clients.
85 85
86 86 .. Note::
87 87
88 88 Not all of these have yet been fully fleshed out, but the key ones are, see
89 89 kernel and frontend files for actual implementation details.
90 90
91 91 General Message Format
92 92 ======================
93 93
94 94 A message is defined by the following four-dictionary structure::
95 95
96 96 {
97 97 # The message header contains a pair of unique identifiers for the
98 98 # originating session and the actual message id, in addition to the
99 99 # username for the process that generated the message. This is useful in
100 100 # collaborative settings where multiple users may be interacting with the
101 101 # same kernel simultaneously, so that frontends can label the various
102 102 # messages in a meaningful way.
103 103 'header' : {
104 104 'msg_id' : uuid,
105 105 'username' : str,
106 106 'session' : uuid,
107 107 # All recognized message type strings are listed below.
108 108 'msg_type' : str,
109 109 # the message protocol version
110 110 'version' : '5.0',
111 111 },
112 112
113 113 # In a chain of messages, the header from the parent is copied so that
114 114 # clients can track where messages come from.
115 115 'parent_header' : dict,
116 116
117 117 # Any metadata associated with the message.
118 118 'metadata' : dict,
119 119
120 120 # The actual content of the message must be a dict, whose structure
121 121 # depends on the message type.
122 122 'content' : dict,
123 123 }
124 124
125 125 .. versionchanged:: 5.0
126 126
127 127 ``version`` key added to the header.
128 128
129 129 .. _wire_protocol:
130 130
131 131 The Wire Protocol
132 132 =================
133 133
134 134
135 135 This message format exists at a high level,
136 136 but does not describe the actual *implementation* at the wire level in zeromq.
137 137 The canonical implementation of the message spec is our :class:`~IPython.kernel.zmq.session.Session` class.
138 138
139 139 .. note::
140 140
141 141 This section should only be relevant to non-Python consumers of the protocol.
142 142 Python consumers should simply import and use IPython's own implementation of the wire protocol
143 143 in the :class:`IPython.kernel.zmq.session.Session` object.
144 144
145 145 Every message is serialized to a sequence of at least six blobs of bytes:
146 146
147 147 .. sourcecode:: python
148 148
149 149 [
150 150 b'u-u-i-d', # zmq identity(ies)
151 151 b'<IDS|MSG>', # delimiter
152 152 b'baddad42', # HMAC signature
153 153 b'{header}', # serialized header dict
154 154 b'{parent_header}', # serialized parent header dict
155 155 b'{metadata}', # serialized metadata dict
156 156 b'{content}, # serialized content dict
157 157 b'blob', # extra raw data buffer(s)
158 158 ...
159 159 ]
160 160
161 161 The front of the message is the ZeroMQ routing prefix,
162 162 which can be zero or more socket identities.
163 163 This is every piece of the message prior to the delimiter key ``<IDS|MSG>``.
164 164 In the case of IOPub, there should be just one prefix component,
165 165 which is the topic for IOPub subscribers, e.g. ``execute_result``, ``display_data``.
166 166
167 167 .. note::
168 168
169 169 In most cases, the IOPub topics are irrelevant and completely ignored,
170 170 because frontends just subscribe to all topics.
171 171 The convention used in the IPython kernel is to use the msg_type as the topic,
172 172 and possibly extra information about the message, e.g. ``execute_result`` or ``stream.stdout``
173 173
174 174 After the delimiter is the `HMAC`_ signature of the message, used for authentication.
175 175 If authentication is disabled, this should be an empty string.
176 176 By default, the hashing function used for computing these signatures is sha256.
177 177
178 178 .. _HMAC: http://en.wikipedia.org/wiki/HMAC
179 179
180 180 .. note::
181 181
182 182 To disable authentication and signature checking,
183 183 set the `key` field of a connection file to an empty string.
184 184
185 185 The signature is the HMAC hex digest of the concatenation of:
186 186
187 187 - A shared key (typically the ``key`` field of a connection file)
188 188 - The serialized header dict
189 189 - The serialized parent header dict
190 190 - The serialized metadata dict
191 191 - The serialized content dict
192 192
193 193 In Python, this is implemented via:
194 194
195 195 .. sourcecode:: python
196 196
197 197 # once:
198 198 digester = HMAC(key, digestmod=hashlib.sha256)
199 199
200 200 # for each message
201 201 d = digester.copy()
202 202 for serialized_dict in (header, parent, metadata, content):
203 203 d.update(serialized_dict)
204 204 signature = d.hexdigest()
205 205
206 206 After the signature is the actual message, always in four frames of bytes.
207 207 The four dictionaries that compose a message are serialized separately,
208 208 in the order of header, parent header, metadata, and content.
209 209 These can be serialized by any function that turns a dict into bytes.
210 210 The default and most common serialization is JSON, but msgpack and pickle
211 211 are common alternatives.
212 212
213 213 After the serialized dicts are zero to many raw data buffers,
214 214 which can be used by message types that support binary data (mainly apply and data_pub).
215 215
216 216
217 217 Python functional API
218 218 =====================
219 219
220 220 As messages are dicts, they map naturally to a ``func(**kw)`` call form. We
221 221 should develop, at a few key points, functional forms of all the requests that
222 222 take arguments in this manner and automatically construct the necessary dict
223 223 for sending.
224 224
225 225 In addition, the Python implementation of the message specification extends
226 226 messages upon deserialization to the following form for convenience::
227 227
228 228 {
229 229 'header' : dict,
230 230 # The msg's unique identifier and type are always stored in the header,
231 231 # but the Python implementation copies them to the top level.
232 232 'msg_id' : uuid,
233 233 'msg_type' : str,
234 234 'parent_header' : dict,
235 235 'content' : dict,
236 236 'metadata' : dict,
237 237 }
238 238
239 239 All messages sent to or received by any IPython process should have this
240 240 extended structure.
241 241
242 242
243 243 Messages on the shell ROUTER/DEALER sockets
244 244 ===========================================
245 245
246 246 .. _execute:
247 247
248 248 Execute
249 249 -------
250 250
251 251 This message type is used by frontends to ask the kernel to execute code on
252 252 behalf of the user, in a namespace reserved to the user's variables (and thus
253 253 separate from the kernel's own internal code and variables).
254 254
255 255 Message type: ``execute_request``::
256 256
257 257 content = {
258 258 # Source code to be executed by the kernel, one or more lines.
259 259 'code' : str,
260 260
261 261 # A boolean flag which, if True, signals the kernel to execute
262 262 # this code as quietly as possible.
263 263 # silent=True forces store_history to be False,
264 264 # and will *not*:
265 265 # - broadcast output on the IOPUB channel
266 266 # - have an execute_result
267 267 # The default is False.
268 268 'silent' : bool,
269 269
270 270 # A boolean flag which, if True, signals the kernel to populate history
271 271 # The default is True if silent is False. If silent is True, store_history
272 272 # is forced to be False.
273 273 'store_history' : bool,
274 274
275 275 # A dict mapping names to expressions to be evaluated in the
276 276 # user's dict. The rich display-data representation of each will be evaluated after execution.
277 277 # See the display_data content for the structure of the representation data.
278 278 'user_expressions' : dict,
279 279
280 280 # Some frontends do not support stdin requests.
281 281 # If raw_input is called from code executed from such a frontend,
282 282 # a StdinNotImplementedError will be raised.
283 283 'allow_stdin' : True,
284 284 }
285 285
286 286 .. versionchanged:: 5.0
287 287
288 288 ``user_variables`` removed, because it is redundant with user_expressions.
289 289
290 290 The ``code`` field contains a single string (possibly multiline) to be executed.
291 291
292 292 The ``user_expressions`` field deserves a detailed explanation. In the past, IPython had
293 293 the notion of a prompt string that allowed arbitrary code to be evaluated, and
294 294 this was put to good use by many in creating prompts that displayed system
295 295 status, path information, and even more esoteric uses like remote instrument
296 296 status acquired over the network. But now that IPython has a clean separation
297 297 between the kernel and the clients, the kernel has no prompt knowledge; prompts
298 298 are a frontend feature, and it should be even possible for different
299 299 frontends to display different prompts while interacting with the same kernel.
300 300 ``user_expressions`` can be used to retrieve this information.
301 301
302 302 Any error in evaluating any expression in ``user_expressions`` will result in
303 303 only that key containing a standard error message, of the form::
304 304
305 305 {
306 306 'status' : 'error',
307 307 'ename' : 'NameError',
308 308 'evalue' : 'foo',
309 309 'traceback' : ...
310 310 }
311 311
312 312 .. Note::
313 313
314 314 In order to obtain the current execution counter for the purposes of
315 315 displaying input prompts, frontends may make an execution request with an
316 316 empty code string and ``silent=True``.
317 317
318 318 Upon completion of the execution request, the kernel *always* sends a reply,
319 319 with a status code indicating what happened and additional data depending on
320 320 the outcome. See :ref:`below <execution_results>` for the possible return
321 321 codes and associated data.
322 322
323 323 .. seealso::
324 324
325 325 :ref:`execution_semantics`
326 326
327 327 .. _execution_counter:
328 328
329 329 Execution counter (prompt number)
330 330 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
331 331
332 332 The kernel should have a single, monotonically increasing counter of all execution
333 333 requests that are made with ``store_history=True``. This counter is used to populate
334 334 the ``In[n]`` and ``Out[n]`` prompts. The value of this counter will be returned as the
335 335 ``execution_count`` field of all ``execute_reply`` and ``execute_input`` messages.
336 336
337 337 .. _execution_results:
338 338
339 339 Execution results
340 340 ~~~~~~~~~~~~~~~~~
341 341
342 342 Message type: ``execute_reply``::
343 343
344 344 content = {
345 345 # One of: 'ok' OR 'error' OR 'abort'
346 346 'status' : str,
347 347
348 348 # The global kernel counter that increases by one with each request that
349 349 # stores history. This will typically be used by clients to display
350 350 # prompt numbers to the user. If the request did not store history, this will
351 351 # be the current value of the counter in the kernel.
352 352 'execution_count' : int,
353 353 }
354 354
355 355 When status is 'ok', the following extra fields are present::
356 356
357 357 {
358 358 # 'payload' will be a list of payload dicts.
359 359 # Each execution payload is a dict with string keys that may have been
360 360 # produced by the code being executed. It is retrieved by the kernel at
361 361 # the end of the execution and sent back to the front end, which can take
362 362 # action on it as needed.
363 363 # The only requirement of each payload dict is that it have a 'source' key,
364 364 # which is a string classifying the payload (e.g. 'pager').
365 365 'payload' : list(dict),
366 366
367 367 # Results for the user_expressions.
368 368 'user_expressions' : dict,
369 369 }
370 370
371 371 .. versionchanged:: 5.0
372 372
373 373 ``user_variables`` is removed, use user_expressions instead.
374 374
375 375 .. admonition:: Execution payloads
376 376
377 377 The notion of an 'execution payload' is different from a return value of a
378 378 given set of code, which normally is just displayed on the execute_result stream
379 379 through the PUB socket. The idea of a payload is to allow special types of
380 380 code, typically magics, to populate a data container in the IPython kernel
381 381 that will be shipped back to the caller via this channel. The kernel
382 382 has an API for this in the PayloadManager::
383 383
384 384 ip.payload_manager.write_payload(payload_dict)
385 385
386 386 which appends a dictionary to the list of payloads.
387 387
388 388 The payload API is not yet stabilized,
389 389 and should probably not be supported by non-Python kernels at this time.
390 390 In such cases, the payload list should always be empty.
391 391
392 392
393 393 When status is 'error', the following extra fields are present::
394 394
395 395 {
396 396 'ename' : str, # Exception name, as a string
397 397 'evalue' : str, # Exception value, as a string
398 398
399 399 # The traceback will contain a list of frames, represented each as a
400 400 # string. For now we'll stick to the existing design of ultraTB, which
401 401 # controls exception level of detail statefully. But eventually we'll
402 402 # want to grow into a model where more information is collected and
403 403 # packed into the traceback object, with clients deciding how little or
404 404 # how much of it to unpack. But for now, let's start with a simple list
405 405 # of strings, since that requires only minimal changes to ultratb as
406 406 # written.
407 407 'traceback' : list,
408 408 }
409 409
410 410
411 411 When status is 'abort', there are for now no additional data fields. This
412 412 happens when the kernel was interrupted by a signal.
413 413
414 414 .. _msging_inspection:
415 415
416 416 Introspection
417 417 -------------
418 418
419 419 Code can be inspected to show useful information to the user.
420 420 It is up to the Kernel to decide what information should be displayed, and its formatting.
421 421
422 422 Message type: ``inspect_request``::
423 423
424 424 content = {
425 425 # The code context in which introspection is requested
426 426 # this may be up to an entire multiline cell.
427 427 'code' : str,
428 428
429 429 # The cursor position within 'code' (in unicode characters) where inspection is requested
430 430 'cursor_pos' : int,
431 431
432 432 # The level of detail desired. In IPython, the default (0) is equivalent to typing
433 433 # 'x?' at the prompt, 1 is equivalent to 'x??'.
434 434 # The difference is up to kernels, but in IPython level 1 includes the source code
435 435 # if available.
436 436 'detail_level' : 0 or 1,
437 437 }
438 438
439 439 .. versionchanged:: 5.0
440 440
441 441 ``object_info_request`` renamed to ``inspect_request``.
442 442
443 443 .. versionchanged:: 5.0
444 444
445 445 ``name`` key replaced with ``code`` and ``cursor_pos``,
446 446 moving the lexing responsibility to the kernel.
447 447
448 448 The reply is a mime-bundle, like a `display_data`_ message,
449 449 which should be a formatted representation of information about the context.
450 450 In the notebook, this is used to show tooltips over function calls, etc.
451 451
452 452 Message type: ``inspect_reply``::
453 453
454 454 content = {
455 455 # 'ok' if the request succeeded or 'error', with error information as in all other replies.
456 456 'status' : 'ok',
457 457
458 458 # data can be empty if nothing is found
459 459 'data' : dict,
460 460 'metadata' : dict,
461 461 }
462 462
463 463 .. versionchanged:: 5.0
464 464
465 465 ``object_info_reply`` renamed to ``inspect_reply``.
466 466
467 467 .. versionchanged:: 5.0
468 468
469 469 Reply is changed from structured data to a mime bundle, allowing formatting decisions to be made by the kernel.
470 470
471 471 .. _msging_completion:
472 472
473 473 Completion
474 474 ----------
475 475
476 476 Message type: ``complete_request``::
477 477
478 478 content = {
479 479 # The code context in which completion is requested
480 480 # this may be up to an entire multiline cell, such as
481 481 # 'foo = a.isal'
482 482 'code' : str,
483 483
484 484 # The cursor position within 'code' (in unicode characters) where completion is requested
485 485 'cursor_pos' : int,
486 486 }
487 487
488 488 .. versionchanged:: 5.0
489 489
490 490 ``line``, ``block``, and ``text`` keys are removed in favor of a single ``code`` for context.
491 491 Lexing is up to the kernel.
492 492
493 493
494 494 Message type: ``complete_reply``::
495 495
496 496 content = {
497 497 # The list of all matches to the completion request, such as
498 498 # ['a.isalnum', 'a.isalpha'] for the above example.
499 499 'matches' : list,
500 500
501 501 # The range of text that should be replaced by the above matches when a completion is accepted.
502 502 # typically cursor_end is the same as cursor_pos in the request.
503 503 'cursor_start' : int,
504 504 'cursor_end' : int,
505 505
506 506 # Information that frontend plugins might use for extra display information about completions.
507 507 'metadata' : dict,
508 508
509 509 # status should be 'ok' unless an exception was raised during the request,
510 510 # in which case it should be 'error', along with the usual error message content
511 511 # in other messages.
512 512 'status' : 'ok'
513 513 }
514 514
515 515 .. versionchanged:: 5.0
516 516
517 517 - ``matched_text`` is removed in favor of ``cursor_start`` and ``cursor_end``.
518 518 - ``metadata`` is added for extended information.
519 519
520 520 .. _msging_history:
521 521
522 522 History
523 523 -------
524 524
525 525 For clients to explicitly request history from a kernel. The kernel has all
526 526 the actual execution history stored in a single location, so clients can
527 527 request it from the kernel when needed.
528 528
529 529 Message type: ``history_request``::
530 530
531 531 content = {
532 532
533 533 # If True, also return output history in the resulting dict.
534 534 'output' : bool,
535 535
536 536 # If True, return the raw input history, else the transformed input.
537 537 'raw' : bool,
538 538
539 539 # So far, this can be 'range', 'tail' or 'search'.
540 540 'hist_access_type' : str,
541 541
542 542 # If hist_access_type is 'range', get a range of input cells. session can
543 543 # be a positive session number, or a negative number to count back from
544 544 # the current session.
545 545 'session' : int,
546 546 # start and stop are line numbers within that session.
547 547 'start' : int,
548 548 'stop' : int,
549 549
550 550 # If hist_access_type is 'tail' or 'search', get the last n cells.
551 551 'n' : int,
552 552
553 553 # If hist_access_type is 'search', get cells matching the specified glob
554 554 # pattern (with * and ? as wildcards).
555 555 'pattern' : str,
556 556
557 557 # If hist_access_type is 'search' and unique is true, do not
558 558 # include duplicated history. Default is false.
559 559 'unique' : bool,
560 560
561 561 }
562 562
563 563 .. versionadded:: 4.0
564 564 The key ``unique`` for ``history_request``.
565 565
566 566 Message type: ``history_reply``::
567 567
568 568 content = {
569 569 # A list of 3 tuples, either:
570 570 # (session, line_number, input) or
571 571 # (session, line_number, (input, output)),
572 572 # depending on whether output was False or True, respectively.
573 573 'history' : list,
574 574 }
575 575
576 576 .. _msging_is_complete:
577 577
578 578 Code completeness
579 579 -----------------
580 580
581 581 .. versionadded:: 5.1
582 582
583 583 When the user enters a line in a console style interface, the console must
584 584 decide whether to immediately execute the current code, or whether to show a
585 585 continuation prompt for further input. For instance, in Python ``a = 5`` would
586 586 be executed immediately, while ``for i in range(5):`` would expect further input.
587 587
588 There are four possible replies:
589
590 - *complete* code is ready to be executed
591 - *incomplete* code should prompt for another line
592 - *invalid* code will typically be sent for execution, so that the user sees the
593 error soonest.
594 - *unknown* - if the kernel is not able to determine this. The frontend should
595 also handle the kernel not replying promptly. It may default to sending the
596 code for execution, or it may implement simple fallback heuristics for whether
597 to execute the code (e.g. execute after a blank line).
598
588 599 Frontends may have ways to override this, forcing the code to be sent for
589 execution or forcing a continuation prompt. If the kernel does not reply promptly,
590 the frontend will probably default to sending the code to be executed.
600 execution or forcing a continuation prompt.
591 601
592 602 Message type: ``is_complete_request``::
593 603
594 604 content = {
595 605 # The code entered so far as a multiline string
596 606 'code' : str,
597 607 }
598 608
599 Message type: ``complete_reply``::
609 Message type: ``is_complete_reply``::
600 610
601 611 content = {
602 # True if the code is ready to execute, False if not
603 'complete' : bool,
612 # One of 'complete', 'incomplete', 'invalid', 'unknown'
613 'status' : str,
614
615 # If status is 'incomplete', indent should contain the characters to use
616 # to indent the next line. This is only a hint: frontends may ignore it
617 # and use their own autoindentation rules. For other statuses, this
618 # field does not exist.
619 'indent': str,
604 620 }
605 621
606 622 Connect
607 623 -------
608 624
609 625 When a client connects to the request/reply socket of the kernel, it can issue
610 626 a connect request to get basic information about the kernel, such as the ports
611 627 the other ZeroMQ sockets are listening on. This allows clients to only have
612 628 to know about a single port (the shell channel) to connect to a kernel.
613 629
614 630 Message type: ``connect_request``::
615 631
616 632 content = {
617 633 }
618 634
619 635 Message type: ``connect_reply``::
620 636
621 637 content = {
622 638 'shell_port' : int, # The port the shell ROUTER socket is listening on.
623 639 'iopub_port' : int, # The port the PUB socket is listening on.
624 640 'stdin_port' : int, # The port the stdin ROUTER socket is listening on.
625 641 'hb_port' : int, # The port the heartbeat socket is listening on.
626 642 }
627 643
628 644 .. _msging_kernel_info:
629 645
630 646 Kernel info
631 647 -----------
632 648
633 649 If a client needs to know information about the kernel, it can
634 650 make a request of the kernel's information.
635 651 This message can be used to fetch core information of the
636 652 kernel, including language (e.g., Python), language version number and
637 653 IPython version number, and the IPython message spec version number.
638 654
639 655 Message type: ``kernel_info_request``::
640 656
641 657 content = {
642 658 }
643 659
644 660 Message type: ``kernel_info_reply``::
645 661
646 662 content = {
647 663 # Version of messaging protocol.
648 664 # The first integer indicates major version. It is incremented when
649 665 # there is any backward incompatible change.
650 666 # The second integer indicates minor version. It is incremented when
651 667 # there is any backward compatible change.
652 668 'protocol_version': 'X.Y.Z',
653 669
654 670 # The kernel implementation name
655 671 # (e.g. 'ipython' for the IPython kernel)
656 672 'implementation': str,
657 673
658 674 # Implementation version number.
659 675 # The version number of the kernel's implementation
660 676 # (e.g. IPython.__version__ for the IPython kernel)
661 677 'implementation_version': 'X.Y.Z',
662 678
663 679 # Programming language in which kernel is implemented.
664 680 # Kernel included in IPython returns 'python'.
665 681 'language': str,
666 682
667 683 # Language version number.
668 684 # It is Python version number (e.g., '2.7.3') for the kernel
669 685 # included in IPython.
670 686 'language_version': 'X.Y.Z',
671 687
672 688 # A banner of information about the kernel,
673 689 # which may be desplayed in console environments.
674 690 'banner' : str,
675 691 }
676 692
677 693 .. versionchanged:: 5.0
678 694
679 695 Versions changed from lists of integers to strings.
680 696
681 697 .. versionchanged:: 5.0
682 698
683 699 ``ipython_version`` is removed.
684 700
685 701 .. versionchanged:: 5.0
686 702
687 703 ``implementation``, ``implementation_version``, and ``banner`` keys are added.
688 704
689 705 .. _msging_shutdown:
690 706
691 707 Kernel shutdown
692 708 ---------------
693 709
694 710 The clients can request the kernel to shut itself down; this is used in
695 711 multiple cases:
696 712
697 713 - when the user chooses to close the client application via a menu or window
698 714 control.
699 715 - when the user types 'exit' or 'quit' (or their uppercase magic equivalents).
700 716 - when the user chooses a GUI method (like the 'Ctrl-C' shortcut in the
701 717 IPythonQt client) to force a kernel restart to get a clean kernel without
702 718 losing client-side state like history or inlined figures.
703 719
704 720 The client sends a shutdown request to the kernel, and once it receives the
705 721 reply message (which is otherwise empty), it can assume that the kernel has
706 722 completed shutdown safely.
707 723
708 724 Upon their own shutdown, client applications will typically execute a last
709 725 minute sanity check and forcefully terminate any kernel that is still alive, to
710 726 avoid leaving stray processes in the user's machine.
711 727
712 728 Message type: ``shutdown_request``::
713 729
714 730 content = {
715 731 'restart' : bool # whether the shutdown is final, or precedes a restart
716 732 }
717 733
718 734 Message type: ``shutdown_reply``::
719 735
720 736 content = {
721 737 'restart' : bool # whether the shutdown is final, or precedes a restart
722 738 }
723 739
724 740 .. Note::
725 741
726 742 When the clients detect a dead kernel thanks to inactivity on the heartbeat
727 743 socket, they simply send a forceful process termination signal, since a dead
728 744 process is unlikely to respond in any useful way to messages.
729 745
730 746
731 747 Messages on the PUB/SUB socket
732 748 ==============================
733 749
734 750 Streams (stdout, stderr, etc)
735 751 ------------------------------
736 752
737 753 Message type: ``stream``::
738 754
739 755 content = {
740 756 # The name of the stream is one of 'stdout', 'stderr'
741 757 'name' : str,
742 758
743 759 # The data is an arbitrary string to be written to that stream
744 760 'data' : str,
745 761 }
746 762
747 763 Display Data
748 764 ------------
749 765
750 766 This type of message is used to bring back data that should be displayed (text,
751 767 html, svg, etc.) in the frontends. This data is published to all frontends.
752 768 Each message can have multiple representations of the data; it is up to the
753 769 frontend to decide which to use and how. A single message should contain all
754 770 possible representations of the same information. Each representation should
755 771 be a JSON'able data structure, and should be a valid MIME type.
756 772
757 773 Some questions remain about this design:
758 774
759 775 * Do we use this message type for execute_result/displayhook? Probably not, because
760 776 the displayhook also has to handle the Out prompt display. On the other hand
761 777 we could put that information into the metadata section.
762 778
763 779 .. _display_data:
764 780
765 781 Message type: ``display_data``::
766 782
767 783 content = {
768 784
769 785 # Who create the data
770 786 'source' : str,
771 787
772 788 # The data dict contains key/value pairs, where the keys are MIME
773 789 # types and the values are the raw data of the representation in that
774 790 # format.
775 791 'data' : dict,
776 792
777 793 # Any metadata that describes the data
778 794 'metadata' : dict
779 795 }
780 796
781 797
782 798 The ``metadata`` contains any metadata that describes the output.
783 799 Global keys are assumed to apply to the output as a whole.
784 800 The ``metadata`` dict can also contain mime-type keys, which will be sub-dictionaries,
785 801 which are interpreted as applying only to output of that type.
786 802 Third parties should put any data they write into a single dict
787 803 with a reasonably unique name to avoid conflicts.
788 804
789 805 The only metadata keys currently defined in IPython are the width and height
790 806 of images::
791 807
792 808 metadata = {
793 809 'image/png' : {
794 810 'width': 640,
795 811 'height': 480
796 812 }
797 813 }
798 814
799 815
800 816 .. versionchanged:: 5.0
801 817
802 818 `application/json` data should be unpacked JSON data,
803 819 not double-serialized as a JSON string.
804 820
805 821
806 822 Raw Data Publication
807 823 --------------------
808 824
809 825 ``display_data`` lets you publish *representations* of data, such as images and html.
810 826 This ``data_pub`` message lets you publish *actual raw data*, sent via message buffers.
811 827
812 828 data_pub messages are constructed via the :func:`IPython.lib.datapub.publish_data` function:
813 829
814 830 .. sourcecode:: python
815 831
816 832 from IPython.kernel.zmq.datapub import publish_data
817 833 ns = dict(x=my_array)
818 834 publish_data(ns)
819 835
820 836
821 837 Message type: ``data_pub``::
822 838
823 839 content = {
824 840 # the keys of the data dict, after it has been unserialized
825 841 'keys' : ['a', 'b']
826 842 }
827 843 # the namespace dict will be serialized in the message buffers,
828 844 # which will have a length of at least one
829 845 buffers = [b'pdict', ...]
830 846
831 847
832 848 The interpretation of a sequence of data_pub messages for a given parent request should be
833 849 to update a single namespace with subsequent results.
834 850
835 851 .. note::
836 852
837 853 No frontends directly handle data_pub messages at this time.
838 854 It is currently only used by the client/engines in :mod:`IPython.parallel`,
839 855 where engines may publish *data* to the Client,
840 856 of which the Client can then publish *representations* via ``display_data``
841 857 to various frontends.
842 858
843 859 Code inputs
844 860 -----------
845 861
846 862 To let all frontends know what code is being executed at any given time, these
847 863 messages contain a re-broadcast of the ``code`` portion of an
848 864 :ref:`execute_request <execute>`, along with the :ref:`execution_count
849 865 <execution_counter>`.
850 866
851 867 Message type: ``execute_input``::
852 868
853 869 content = {
854 870 'code' : str, # Source code to be executed, one or more lines
855 871
856 872 # The counter for this execution is also provided so that clients can
857 873 # display it, since IPython automatically creates variables called _iN
858 874 # (for input prompt In[N]).
859 875 'execution_count' : int
860 876 }
861 877
862 878 .. versionchanged:: 5.0
863 879
864 880 ``pyin`` is renamed to ``execute_input``.
865 881
866 882
867 883 Execution results
868 884 -----------------
869 885
870 886 Results of an execution are published as an ``execute_result``.
871 887 These are identical to `display_data`_ messages, with the addition of an ``execution_count`` key.
872 888
873 889 Results can have multiple simultaneous formats depending on its
874 890 configuration. A plain text representation should always be provided
875 891 in the ``text/plain`` mime-type. Frontends are free to display any or all of these
876 892 according to its capabilities.
877 893 Frontends should ignore mime-types they do not understand. The data itself is
878 894 any JSON object and depends on the format. It is often, but not always a string.
879 895
880 896 Message type: ``execute_result``::
881 897
882 898 content = {
883 899
884 900 # The counter for this execution is also provided so that clients can
885 901 # display it, since IPython automatically creates variables called _N
886 902 # (for prompt N).
887 903 'execution_count' : int,
888 904
889 905 # data and metadata are identical to a display_data message.
890 906 # the object being displayed is that passed to the display hook,
891 907 # i.e. the *result* of the execution.
892 908 'data' : dict,
893 909 'metadata' : dict,
894 910 }
895 911
896 912 Execution errors
897 913 ----------------
898 914
899 915 When an error occurs during code execution
900 916
901 917 Message type: ``error``::
902 918
903 919 content = {
904 920 # Similar content to the execute_reply messages for the 'error' case,
905 921 # except the 'status' field is omitted.
906 922 }
907 923
908 924 .. versionchanged:: 5.0
909 925
910 926 ``pyerr`` renamed to ``error``
911 927
912 928 Kernel status
913 929 -------------
914 930
915 931 This message type is used by frontends to monitor the status of the kernel.
916 932
917 933 Message type: ``status``::
918 934
919 935 content = {
920 936 # When the kernel starts to handle a message, it will enter the 'busy'
921 937 # state and when it finishes, it will enter the 'idle' state.
922 938 # The kernel will publish state 'starting' exactly once at process startup.
923 939 execution_state : ('busy', 'idle', 'starting')
924 940 }
925 941
926 942 .. versionchanged:: 5.0
927 943
928 944 Busy and idle messages should be sent before/after handling every message,
929 945 not just execution.
930 946
931 947 Clear output
932 948 ------------
933 949
934 950 This message type is used to clear the output that is visible on the frontend.
935 951
936 952 Message type: ``clear_output``::
937 953
938 954 content = {
939 955
940 956 # Wait to clear the output until new output is available. Clears the
941 957 # existing output immediately before the new output is displayed.
942 958 # Useful for creating simple animations with minimal flickering.
943 959 'wait' : bool,
944 960 }
945 961
946 962 .. versionchanged:: 4.1
947 963
948 964 ``stdout``, ``stderr``, and ``display`` boolean keys for selective clearing are removed,
949 965 and ``wait`` is added.
950 966 The selective clearing keys are ignored in v4 and the default behavior remains the same,
951 967 so v4 clear_output messages will be safely handled by a v4.1 frontend.
952 968
953 969
954 970 Messages on the stdin ROUTER/DEALER sockets
955 971 ===========================================
956 972
957 973 This is a socket where the request/reply pattern goes in the opposite direction:
958 974 from the kernel to a *single* frontend, and its purpose is to allow
959 975 ``raw_input`` and similar operations that read from ``sys.stdin`` on the kernel
960 976 to be fulfilled by the client. The request should be made to the frontend that
961 977 made the execution request that prompted ``raw_input`` to be called. For now we
962 978 will keep these messages as simple as possible, since they only mean to convey
963 979 the ``raw_input(prompt)`` call.
964 980
965 981 Message type: ``input_request``::
966 982
967 983 content = {
968 984 # the text to show at the prompt
969 985 'prompt' : str,
970 986 # Is the request for a password?
971 987 # If so, the frontend shouldn't echo input.
972 988 'password' : bool
973 989 }
974 990
975 991 Message type: ``input_reply``::
976 992
977 993 content = { 'value' : str }
978 994
979 995
980 996 When ``password`` is True, the frontend should not echo the input as it is entered.
981 997
982 998 .. versionchanged:: 5.0
983 999
984 1000 ``password`` key added.
985 1001
986 1002 .. note::
987 1003
988 1004 The stdin socket of the client is required to have the same zmq IDENTITY
989 1005 as the client's shell socket.
990 1006 Because of this, the ``input_request`` must be sent with the same IDENTITY
991 1007 routing prefix as the ``execute_reply`` in order for the frontend to receive
992 1008 the message.
993 1009
994 1010 .. note::
995 1011
996 1012 We do not explicitly try to forward the raw ``sys.stdin`` object, because in
997 1013 practice the kernel should behave like an interactive program. When a
998 1014 program is opened on the console, the keyboard effectively takes over the
999 1015 ``stdin`` file descriptor, and it can't be used for raw reading anymore.
1000 1016 Since the IPython kernel effectively behaves like a console program (albeit
1001 1017 one whose "keyboard" is actually living in a separate process and
1002 1018 transported over the zmq connection), raw ``stdin`` isn't expected to be
1003 1019 available.
1004 1020
1005 1021 .. _kernel_heartbeat:
1006 1022
1007 1023 Heartbeat for kernels
1008 1024 =====================
1009 1025
1010 1026 Clients send ping messages on a REQ socket, which are echoed right back
1011 1027 from the Kernel's REP socket. These are simple bytestrings, not full JSON messages described above.
1012 1028
1013 1029
1014 1030 Custom Messages
1015 1031 ===============
1016 1032
1017 1033 .. versionadded:: 4.1
1018 1034
1019 1035 IPython 2.0 (msgspec v4.1) adds a messaging system for developers to add their own objects with Frontend
1020 1036 and Kernel-side components, and allow them to communicate with each other.
1021 1037 To do this, IPython adds a notion of a ``Comm``, which exists on both sides,
1022 1038 and can communicate in either direction.
1023 1039
1024 1040 These messages are fully symmetrical - both the Kernel and the Frontend can send each message,
1025 1041 and no messages expect a reply.
1026 1042 The Kernel listens for these messages on the Shell channel,
1027 1043 and the Frontend listens for them on the IOPub channel.
1028 1044
1029 1045 Opening a Comm
1030 1046 --------------
1031 1047
1032 1048 Opening a Comm produces a ``comm_open`` message, to be sent to the other side::
1033 1049
1034 1050 {
1035 1051 'comm_id' : 'u-u-i-d',
1036 1052 'target_name' : 'my_comm',
1037 1053 'data' : {}
1038 1054 }
1039 1055
1040 1056 Every Comm has an ID and a target name.
1041 1057 The code handling the message on the receiving side is responsible for maintaining a mapping
1042 1058 of target_name keys to constructors.
1043 1059 After a ``comm_open`` message has been sent,
1044 1060 there should be a corresponding Comm instance on both sides.
1045 1061 The ``data`` key is always a dict and can be any extra JSON information used in initialization of the comm.
1046 1062
1047 1063 If the ``target_name`` key is not found on the receiving side,
1048 1064 then it should immediately reply with a ``comm_close`` message to avoid an inconsistent state.
1049 1065
1050 1066 Comm Messages
1051 1067 -------------
1052 1068
1053 1069 Comm messages are one-way communications to update comm state,
1054 1070 used for synchronizing widget state, or simply requesting actions of a comm's counterpart.
1055 1071
1056 1072 Essentially, each comm pair defines their own message specification implemented inside the ``data`` dict.
1057 1073
1058 1074 There are no expected replies (of course, one side can send another ``comm_msg`` in reply).
1059 1075
1060 1076 Message type: ``comm_msg``::
1061 1077
1062 1078 {
1063 1079 'comm_id' : 'u-u-i-d',
1064 1080 'data' : {}
1065 1081 }
1066 1082
1067 1083 Tearing Down Comms
1068 1084 ------------------
1069 1085
1070 1086 Since comms live on both sides, when a comm is destroyed the other side must be notified.
1071 1087 This is done with a ``comm_close`` message.
1072 1088
1073 1089 Message type: ``comm_close``::
1074 1090
1075 1091 {
1076 1092 'comm_id' : 'u-u-i-d',
1077 1093 'data' : {}
1078 1094 }
1079 1095
1080 1096 Output Side Effects
1081 1097 -------------------
1082 1098
1083 1099 Since comm messages can execute arbitrary user code,
1084 1100 handlers should set the parent header and publish status busy / idle,
1085 1101 just like an execute request.
1086 1102
1087 1103
1088 1104 To Do
1089 1105 =====
1090 1106
1091 1107 Missing things include:
1092 1108
1093 1109 * Important: finish thinking through the payload concept and API.
1094 1110
1095 1111 .. include:: ../links.txt
General Comments 0
You need to be logged in to leave comments. Login now