##// END OF EJS Templates
Merge pull request #6297 from takluyver/is-complete-request...
Min RK -
r18369:d236bec1 merge
parent child Browse files
Show More
@@ -1,638 +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
17 17 # Copyright (c) IPython Development Team.
18 18 # Distributed under the terms of the Modified BSD License.
19 19 import ast
20 20 import codeop
21 21 import re
22 22 import sys
23 23
24 24 from IPython.utils.py3compat import cast_unicode
25 25 from IPython.core.inputtransformer import (leading_indent,
26 26 classic_prompt,
27 27 ipy_prompt,
28 28 strip_encoding_cookie,
29 29 cellmagic,
30 30 assemble_logical_lines,
31 31 help_end,
32 32 escaped_commands,
33 33 assign_from_magic,
34 34 assign_from_system,
35 35 assemble_python_lines,
36 36 )
37 37
38 38 # These are available in this module for backwards compatibility.
39 39 from IPython.core.inputtransformer import (ESC_SHELL, ESC_SH_CAP, ESC_HELP,
40 40 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,
41 41 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN, ESC_SEQUENCES)
42 42
43 43 #-----------------------------------------------------------------------------
44 44 # Utilities
45 45 #-----------------------------------------------------------------------------
46 46
47 47 # FIXME: These are general-purpose utilities that later can be moved to the
48 48 # general ward. Kept here for now because we're being very strict about test
49 49 # coverage with this code, and this lets us ensure that we keep 100% coverage
50 50 # while developing.
51 51
52 52 # compiled regexps for autoindent management
53 53 dedent_re = re.compile('|'.join([
54 54 r'^\s+raise(\s.*)?$', # raise statement (+ space + other stuff, maybe)
55 55 r'^\s+raise\([^\)]*\).*$', # wacky raise with immediate open paren
56 56 r'^\s+return(\s.*)?$', # normal return (+ space + other stuff, maybe)
57 57 r'^\s+return\([^\)]*\).*$', # wacky return with immediate open paren
58 58 r'^\s+pass\s*$', # pass (optionally followed by trailing spaces)
59 59 r'^\s+break\s*$', # break (optionally followed by trailing spaces)
60 60 r'^\s+continue\s*$', # continue (optionally followed by trailing spaces)
61 61 ]))
62 62 ini_spaces_re = re.compile(r'^([ \t\r\f\v]+)')
63 63
64 64 # regexp to match pure comment lines so we don't accidentally insert 'if 1:'
65 65 # before pure comments
66 66 comment_line_re = re.compile('^\s*\#')
67 67
68 68
69 69 def num_ini_spaces(s):
70 70 """Return the number of initial spaces in a string.
71 71
72 72 Note that tabs are counted as a single space. For now, we do *not* support
73 73 mixing of tabs and spaces in the user's input.
74 74
75 75 Parameters
76 76 ----------
77 77 s : string
78 78
79 79 Returns
80 80 -------
81 81 n : int
82 82 """
83 83
84 84 ini_spaces = ini_spaces_re.match(s)
85 85 if ini_spaces:
86 86 return ini_spaces.end()
87 87 else:
88 88 return 0
89 89
90 90 def last_blank(src):
91 91 """Determine if the input source ends in a blank.
92 92
93 93 A blank is either a newline or a line consisting of whitespace.
94 94
95 95 Parameters
96 96 ----------
97 97 src : string
98 98 A single or multiline string.
99 99 """
100 100 if not src: return False
101 101 ll = src.splitlines()[-1]
102 102 return (ll == '') or ll.isspace()
103 103
104 104
105 105 last_two_blanks_re = re.compile(r'\n\s*\n\s*$', re.MULTILINE)
106 106 last_two_blanks_re2 = re.compile(r'.+\n\s*\n\s+$', re.MULTILINE)
107 107
108 108 def last_two_blanks(src):
109 109 """Determine if the input source ends in two blanks.
110 110
111 111 A blank is either a newline or a line consisting of whitespace.
112 112
113 113 Parameters
114 114 ----------
115 115 src : string
116 116 A single or multiline string.
117 117 """
118 118 if not src: return False
119 119 # The logic here is tricky: I couldn't get a regexp to work and pass all
120 120 # the tests, so I took a different approach: split the source by lines,
121 121 # grab the last two and prepend '###\n' as a stand-in for whatever was in
122 122 # the body before the last two lines. Then, with that structure, it's
123 123 # possible to analyze with two regexps. Not the most elegant solution, but
124 124 # it works. If anyone tries to change this logic, make sure to validate
125 125 # the whole test suite first!
126 126 new_src = '\n'.join(['###\n'] + src.splitlines()[-2:])
127 127 return (bool(last_two_blanks_re.match(new_src)) or
128 128 bool(last_two_blanks_re2.match(new_src)) )
129 129
130 130
131 131 def remove_comments(src):
132 132 """Remove all comments from input source.
133 133
134 134 Note: comments are NOT recognized inside of strings!
135 135
136 136 Parameters
137 137 ----------
138 138 src : string
139 139 A single or multiline input string.
140 140
141 141 Returns
142 142 -------
143 143 String with all Python comments removed.
144 144 """
145 145
146 146 return re.sub('#.*', '', src)
147 147
148 148
149 149 def get_input_encoding():
150 150 """Return the default standard input encoding.
151 151
152 152 If sys.stdin has no encoding, 'ascii' is returned."""
153 153 # There are strange environments for which sys.stdin.encoding is None. We
154 154 # ensure that a valid encoding is returned.
155 155 encoding = getattr(sys.stdin, 'encoding', None)
156 156 if encoding is None:
157 157 encoding = 'ascii'
158 158 return encoding
159 159
160 160 #-----------------------------------------------------------------------------
161 161 # Classes and functions for normal Python syntax handling
162 162 #-----------------------------------------------------------------------------
163 163
164 164 class InputSplitter(object):
165 165 r"""An object that can accumulate lines of Python source before execution.
166 166
167 167 This object is designed to be fed python source line-by-line, using
168 168 :meth:`push`. It will return on each push whether the currently pushed
169 169 code could be executed already. In addition, it provides a method called
170 170 :meth:`push_accepts_more` that can be used to query whether more input
171 171 can be pushed into a single interactive block.
172 172
173 173 This is a simple example of how an interactive terminal-based client can use
174 174 this tool::
175 175
176 176 isp = InputSplitter()
177 177 while isp.push_accepts_more():
178 178 indent = ' '*isp.indent_spaces
179 179 prompt = '>>> ' + indent
180 180 line = indent + raw_input(prompt)
181 181 isp.push(line)
182 182 print 'Input source was:\n', isp.source_reset(),
183 183 """
184 184 # Number of spaces of indentation computed from input that has been pushed
185 185 # so far. This is the attributes callers should query to get the current
186 186 # indentation level, in order to provide auto-indent facilities.
187 187 indent_spaces = 0
188 188 # String, indicating the default input encoding. It is computed by default
189 189 # at initialization time via get_input_encoding(), but it can be reset by a
190 190 # client with specific knowledge of the encoding.
191 191 encoding = ''
192 192 # String where the current full source input is stored, properly encoded.
193 193 # Reading this attribute is the normal way of querying the currently pushed
194 194 # source code, that has been properly encoded.
195 195 source = ''
196 196 # Code object corresponding to the current source. It is automatically
197 197 # synced to the source, so it can be queried at any time to obtain the code
198 198 # object; it will be None if the source doesn't compile to valid Python.
199 199 code = None
200 200
201 201 # Private attributes
202 202
203 203 # List with lines of input accumulated so far
204 204 _buffer = None
205 205 # Command compiler
206 206 _compile = None
207 207 # Mark when input has changed indentation all the way back to flush-left
208 208 _full_dedent = False
209 209 # Boolean indicating whether the current block is complete
210 210 _is_complete = None
211 # Boolean indicating whether the current block has an unrecoverable syntax error
212 _is_invalid = False
211 213
212 214 def __init__(self):
213 215 """Create a new InputSplitter instance.
214 216 """
215 217 self._buffer = []
216 218 self._compile = codeop.CommandCompiler()
217 219 self.encoding = get_input_encoding()
218 220
219 221 def reset(self):
220 222 """Reset the input buffer and associated state."""
221 223 self.indent_spaces = 0
222 224 self._buffer[:] = []
223 225 self.source = ''
224 226 self.code = None
225 227 self._is_complete = False
228 self._is_invalid = False
226 229 self._full_dedent = False
227 230
228 231 def source_reset(self):
229 232 """Return the input source and perform a full reset.
230 233 """
231 234 out = self.source
232 235 self.reset()
233 236 return out
234 237
238 def check_complete(self, source):
239 """Return whether a block of code is ready to execute, or should be continued
240
241 This is a non-stateful API, and will reset the state of this InputSplitter.
242
243 Parameters
244 ----------
245 source : string
246 Python input code, which can be multiline.
247
248 Returns
249 -------
250 status : str
251 One of 'complete', 'incomplete', or 'invalid' if source is not a
252 prefix of valid code.
253 indent_spaces : int or None
254 The number of spaces by which to indent the next line of code. If
255 status is not 'incomplete', this is None.
256 """
257 self.reset()
258 try:
259 self.push(source)
260 except SyntaxError:
261 # Transformers in IPythonInputSplitter can raise SyntaxError,
262 # which push() will not catch.
263 return 'invalid', None
264 else:
265 if self._is_invalid:
266 return 'invalid', None
267 elif self.push_accepts_more():
268 return 'incomplete', self.indent_spaces
269 else:
270 return 'complete', None
271 finally:
272 self.reset()
273
235 274 def push(self, lines):
236 275 """Push one or more lines of input.
237 276
238 277 This stores the given lines and returns a status code indicating
239 278 whether the code forms a complete Python block or not.
240 279
241 280 Any exceptions generated in compilation are swallowed, but if an
242 281 exception was produced, the method returns True.
243 282
244 283 Parameters
245 284 ----------
246 285 lines : string
247 286 One or more lines of Python input.
248 287
249 288 Returns
250 289 -------
251 290 is_complete : boolean
252 291 True if the current input source (the result of the current input
253 292 plus prior inputs) forms a complete Python execution block. Note that
254 293 this value is also stored as a private attribute (``_is_complete``), so it
255 294 can be queried at any time.
256 295 """
257 296 self._store(lines)
258 297 source = self.source
259 298
260 299 # Before calling _compile(), reset the code object to None so that if an
261 300 # exception is raised in compilation, we don't mislead by having
262 301 # inconsistent code/source attributes.
263 302 self.code, self._is_complete = None, None
303 self._is_invalid = False
264 304
265 305 # Honor termination lines properly
266 306 if source.endswith('\\\n'):
267 307 return False
268 308
269 309 self._update_indent(lines)
270 310 try:
271 311 self.code = self._compile(source, symbol="exec")
272 312 # Invalid syntax can produce any of a number of different errors from
273 313 # inside the compiler, so we have to catch them all. Syntax errors
274 314 # immediately produce a 'ready' block, so the invalid Python can be
275 315 # sent to the kernel for evaluation with possible ipython
276 316 # special-syntax conversion.
277 317 except (SyntaxError, OverflowError, ValueError, TypeError,
278 318 MemoryError):
279 319 self._is_complete = True
320 self._is_invalid = True
280 321 else:
281 322 # Compilation didn't produce any exceptions (though it may not have
282 323 # given a complete code object)
283 324 self._is_complete = self.code is not None
284 325
285 326 return self._is_complete
286 327
287 328 def push_accepts_more(self):
288 329 """Return whether a block of interactive input can accept more input.
289 330
290 331 This method is meant to be used by line-oriented frontends, who need to
291 332 guess whether a block is complete or not based solely on prior and
292 333 current input lines. The InputSplitter considers it has a complete
293 334 interactive block and will not accept more input when either:
294 335
295 336 * A SyntaxError is raised
296 337
297 338 * The code is complete and consists of a single line or a single
298 339 non-compound statement
299 340
300 341 * The code is complete and has a blank line at the end
301 342
302 343 If the current input produces a syntax error, this method immediately
303 344 returns False but does *not* raise the syntax error exception, as
304 345 typically clients will want to send invalid syntax to an execution
305 346 backend which might convert the invalid syntax into valid Python via
306 347 one of the dynamic IPython mechanisms.
307 348 """
308 349
309 350 # With incomplete input, unconditionally accept more
310 351 # A syntax error also sets _is_complete to True - see push()
311 352 if not self._is_complete:
312 353 #print("Not complete") # debug
313 354 return True
314 355
315 356 # The user can make any (complete) input execute by leaving a blank line
316 357 last_line = self.source.splitlines()[-1]
317 358 if (not last_line) or last_line.isspace():
318 359 #print("Blank line") # debug
319 360 return False
320 361
321 362 # If there's just a single line or AST node, and we're flush left, as is
322 363 # the case after a simple statement such as 'a=1', we want to execute it
323 364 # straight away.
324 365 if self.indent_spaces==0:
325 366 if len(self.source.splitlines()) <= 1:
326 367 return False
327 368
328 369 try:
329 370 code_ast = ast.parse(u''.join(self._buffer))
330 371 except Exception:
331 372 #print("Can't parse AST") # debug
332 373 return False
333 374 else:
334 375 if len(code_ast.body) == 1 and \
335 376 not hasattr(code_ast.body[0], 'body'):
336 377 #print("Simple statement") # debug
337 378 return False
338 379
339 380 # General fallback - accept more code
340 381 return True
341 382
342 383 #------------------------------------------------------------------------
343 384 # Private interface
344 385 #------------------------------------------------------------------------
345 386
346 387 def _find_indent(self, line):
347 388 """Compute the new indentation level for a single line.
348 389
349 390 Parameters
350 391 ----------
351 392 line : str
352 393 A single new line of non-whitespace, non-comment Python input.
353 394
354 395 Returns
355 396 -------
356 397 indent_spaces : int
357 398 New value for the indent level (it may be equal to self.indent_spaces
358 399 if indentation doesn't change.
359 400
360 401 full_dedent : boolean
361 402 Whether the new line causes a full flush-left dedent.
362 403 """
363 404 indent_spaces = self.indent_spaces
364 405 full_dedent = self._full_dedent
365 406
366 407 inisp = num_ini_spaces(line)
367 408 if inisp < indent_spaces:
368 409 indent_spaces = inisp
369 410 if indent_spaces <= 0:
370 411 #print 'Full dedent in text',self.source # dbg
371 412 full_dedent = True
372 413
373 414 if line.rstrip()[-1] == ':':
374 415 indent_spaces += 4
375 416 elif dedent_re.match(line):
376 417 indent_spaces -= 4
377 418 if indent_spaces <= 0:
378 419 full_dedent = True
379 420
380 421 # Safety
381 422 if indent_spaces < 0:
382 423 indent_spaces = 0
383 424 #print 'safety' # dbg
384 425
385 426 return indent_spaces, full_dedent
386 427
387 428 def _update_indent(self, lines):
388 429 for line in remove_comments(lines).splitlines():
389 430 if line and not line.isspace():
390 431 self.indent_spaces, self._full_dedent = self._find_indent(line)
391 432
392 433 def _store(self, lines, buffer=None, store='source'):
393 434 """Store one or more lines of input.
394 435
395 436 If input lines are not newline-terminated, a newline is automatically
396 437 appended."""
397 438
398 439 if buffer is None:
399 440 buffer = self._buffer
400 441
401 442 if lines.endswith('\n'):
402 443 buffer.append(lines)
403 444 else:
404 445 buffer.append(lines+'\n')
405 446 setattr(self, store, self._set_source(buffer))
406 447
407 448 def _set_source(self, buffer):
408 449 return u''.join(buffer)
409 450
410 451
411 452 class IPythonInputSplitter(InputSplitter):
412 453 """An input splitter that recognizes all of IPython's special syntax."""
413 454
414 455 # String with raw, untransformed input.
415 456 source_raw = ''
416 457
417 458 # Flag to track when a transformer has stored input that it hasn't given
418 459 # back yet.
419 460 transformer_accumulating = False
420 461
421 462 # Flag to track when assemble_python_lines has stored input that it hasn't
422 463 # given back yet.
423 464 within_python_line = False
424 465
425 466 # Private attributes
426 467
427 468 # List with lines of raw input accumulated so far.
428 469 _buffer_raw = None
429 470
430 471 def __init__(self, line_input_checker=True, physical_line_transforms=None,
431 472 logical_line_transforms=None, python_line_transforms=None):
432 473 super(IPythonInputSplitter, self).__init__()
433 474 self._buffer_raw = []
434 475 self._validate = True
435 476
436 477 if physical_line_transforms is not None:
437 478 self.physical_line_transforms = physical_line_transforms
438 479 else:
439 480 self.physical_line_transforms = [
440 481 leading_indent(),
441 482 classic_prompt(),
442 483 ipy_prompt(),
443 484 strip_encoding_cookie(),
444 485 cellmagic(end_on_blank_line=line_input_checker),
445 486 ]
446 487
447 488 self.assemble_logical_lines = assemble_logical_lines()
448 489 if logical_line_transforms is not None:
449 490 self.logical_line_transforms = logical_line_transforms
450 491 else:
451 492 self.logical_line_transforms = [
452 493 help_end(),
453 494 escaped_commands(),
454 495 assign_from_magic(),
455 496 assign_from_system(),
456 497 ]
457 498
458 499 self.assemble_python_lines = assemble_python_lines()
459 500 if python_line_transforms is not None:
460 501 self.python_line_transforms = python_line_transforms
461 502 else:
462 503 # We don't use any of these at present
463 504 self.python_line_transforms = []
464 505
465 506 @property
466 507 def transforms(self):
467 508 "Quick access to all transformers."
468 509 return self.physical_line_transforms + \
469 510 [self.assemble_logical_lines] + self.logical_line_transforms + \
470 511 [self.assemble_python_lines] + self.python_line_transforms
471 512
472 513 @property
473 514 def transforms_in_use(self):
474 515 """Transformers, excluding logical line transformers if we're in a
475 516 Python line."""
476 517 t = self.physical_line_transforms[:]
477 518 if not self.within_python_line:
478 519 t += [self.assemble_logical_lines] + self.logical_line_transforms
479 520 return t + [self.assemble_python_lines] + self.python_line_transforms
480 521
481 522 def reset(self):
482 523 """Reset the input buffer and associated state."""
483 524 super(IPythonInputSplitter, self).reset()
484 525 self._buffer_raw[:] = []
485 526 self.source_raw = ''
486 527 self.transformer_accumulating = False
487 528 self.within_python_line = False
488 529
489 530 for t in self.transforms:
490 531 try:
491 532 t.reset()
492 533 except SyntaxError:
493 534 # Nothing that calls reset() expects to handle transformer
494 535 # errors
495 536 pass
496 537
497 538 def flush_transformers(self):
498 539 def _flush(transform, outs):
499 540 """yield transformed lines
500 541
501 542 always strings, never None
502 543
503 544 transform: the current transform
504 545 outs: an iterable of previously transformed inputs.
505 546 Each may be multiline, which will be passed
506 547 one line at a time to transform.
507 548 """
508 549 for out in outs:
509 550 for line in out.splitlines():
510 551 # push one line at a time
511 552 tmp = transform.push(line)
512 553 if tmp is not None:
513 554 yield tmp
514 555
515 556 # reset the transform
516 557 tmp = transform.reset()
517 558 if tmp is not None:
518 559 yield tmp
519 560
520 561 out = []
521 562 for t in self.transforms_in_use:
522 563 out = _flush(t, out)
523 564
524 565 out = list(out)
525 566 if out:
526 567 self._store('\n'.join(out))
527 568
528 569 def raw_reset(self):
529 570 """Return raw input only and perform a full reset.
530 571 """
531 572 out = self.source_raw
532 573 self.reset()
533 574 return out
534 575
535 576 def source_reset(self):
536 577 try:
537 578 self.flush_transformers()
538 579 return self.source
539 580 finally:
540 581 self.reset()
541 582
542 583 def push_accepts_more(self):
543 584 if self.transformer_accumulating:
544 585 return True
545 586 else:
546 587 return super(IPythonInputSplitter, self).push_accepts_more()
547 588
548 589 def transform_cell(self, cell):
549 590 """Process and translate a cell of input.
550 591 """
551 592 self.reset()
552 593 try:
553 594 self.push(cell)
554 595 self.flush_transformers()
555 596 return self.source
556 597 finally:
557 598 self.reset()
558 599
559 600 def push(self, lines):
560 601 """Push one or more lines of IPython input.
561 602
562 603 This stores the given lines and returns a status code indicating
563 604 whether the code forms a complete Python block or not, after processing
564 605 all input lines for special IPython syntax.
565 606
566 607 Any exceptions generated in compilation are swallowed, but if an
567 608 exception was produced, the method returns True.
568 609
569 610 Parameters
570 611 ----------
571 612 lines : string
572 613 One or more lines of Python input.
573 614
574 615 Returns
575 616 -------
576 617 is_complete : boolean
577 618 True if the current input source (the result of the current input
578 619 plus prior inputs) forms a complete Python execution block. Note that
579 620 this value is also stored as a private attribute (_is_complete), so it
580 621 can be queried at any time.
581 622 """
582 623
583 624 # We must ensure all input is pure unicode
584 625 lines = cast_unicode(lines, self.encoding)
585 626
586 627 # ''.splitlines() --> [], but we need to push the empty line to transformers
587 628 lines_list = lines.splitlines()
588 629 if not lines_list:
589 630 lines_list = ['']
590 631
591 632 # Store raw source before applying any transformations to it. Note
592 633 # that this must be done *after* the reset() call that would otherwise
593 634 # flush the buffer.
594 635 self._store(lines, self._buffer_raw, 'source_raw')
595 636
596 637 for line in lines_list:
597 638 out = self.push_line(line)
598 639
599 640 return out
600 641
601 642 def push_line(self, line):
602 643 buf = self._buffer
603 644
604 645 def _accumulating(dbg):
605 646 #print(dbg)
606 647 self.transformer_accumulating = True
607 648 return False
608 649
609 650 for transformer in self.physical_line_transforms:
610 651 line = transformer.push(line)
611 652 if line is None:
612 653 return _accumulating(transformer)
613 654
614 655 if not self.within_python_line:
615 656 line = self.assemble_logical_lines.push(line)
616 657 if line is None:
617 658 return _accumulating('acc logical line')
618 659
619 660 for transformer in self.logical_line_transforms:
620 661 line = transformer.push(line)
621 662 if line is None:
622 663 return _accumulating(transformer)
623 664
624 665 line = self.assemble_python_lines.push(line)
625 666 if line is None:
626 667 self.within_python_line = True
627 668 return _accumulating('acc python line')
628 669 else:
629 670 self.within_python_line = False
630 671
631 672 for transformer in self.python_line_transforms:
632 673 line = transformer.push(line)
633 674 if line is None:
634 675 return _accumulating(transformer)
635 676
636 677 #print("transformers clear") #debug
637 678 self.transformer_accumulating = False
638 679 return super(IPythonInputSplitter, self).push(line)
@@ -1,597 +1,604 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Tests for the inputsplitter module."""
3 3
4 4 from __future__ import print_function
5 5
6 6 # Copyright (c) IPython Development Team.
7 7 # Distributed under the terms of the Modified BSD License.
8 8
9 9 import unittest
10 10 import sys
11 11
12 12 import nose.tools as nt
13 13
14 14 from IPython.core import inputsplitter as isp
15 15 from IPython.core.inputtransformer import InputTransformer
16 16 from IPython.core.tests.test_inputtransformer import syntax, syntax_ml
17 17 from IPython.testing import tools as tt
18 18 from IPython.utils import py3compat
19 19 from IPython.utils.py3compat import string_types, input
20 20
21 21 #-----------------------------------------------------------------------------
22 22 # Semi-complete examples (also used as tests)
23 23 #-----------------------------------------------------------------------------
24 24
25 25 # Note: at the bottom, there's a slightly more complete version of this that
26 26 # can be useful during development of code here.
27 27
28 28 def mini_interactive_loop(input_func):
29 29 """Minimal example of the logic of an interactive interpreter loop.
30 30
31 31 This serves as an example, and it is used by the test system with a fake
32 32 raw_input that simulates interactive input."""
33 33
34 34 from IPython.core.inputsplitter import InputSplitter
35 35
36 36 isp = InputSplitter()
37 37 # In practice, this input loop would be wrapped in an outside loop to read
38 38 # input indefinitely, until some exit/quit command was issued. Here we
39 39 # only illustrate the basic inner loop.
40 40 while isp.push_accepts_more():
41 41 indent = ' '*isp.indent_spaces
42 42 prompt = '>>> ' + indent
43 43 line = indent + input_func(prompt)
44 44 isp.push(line)
45 45
46 46 # Here we just return input so we can use it in a test suite, but a real
47 47 # interpreter would instead send it for execution somewhere.
48 48 src = isp.source_reset()
49 49 #print 'Input source was:\n', src # dbg
50 50 return src
51 51
52 52 #-----------------------------------------------------------------------------
53 53 # Test utilities, just for local use
54 54 #-----------------------------------------------------------------------------
55 55
56 56 def assemble(block):
57 57 """Assemble a block into multi-line sub-blocks."""
58 58 return ['\n'.join(sub_block)+'\n' for sub_block in block]
59 59
60 60
61 61 def pseudo_input(lines):
62 62 """Return a function that acts like raw_input but feeds the input list."""
63 63 ilines = iter(lines)
64 64 def raw_in(prompt):
65 65 try:
66 66 return next(ilines)
67 67 except StopIteration:
68 68 return ''
69 69 return raw_in
70 70
71 71 #-----------------------------------------------------------------------------
72 72 # Tests
73 73 #-----------------------------------------------------------------------------
74 74 def test_spaces():
75 75 tests = [('', 0),
76 76 (' ', 1),
77 77 ('\n', 0),
78 78 (' \n', 1),
79 79 ('x', 0),
80 80 (' x', 1),
81 81 (' x',2),
82 82 (' x',4),
83 83 # Note: tabs are counted as a single whitespace!
84 84 ('\tx', 1),
85 85 ('\t x', 2),
86 86 ]
87 87 tt.check_pairs(isp.num_ini_spaces, tests)
88 88
89 89
90 90 def test_remove_comments():
91 91 tests = [('text', 'text'),
92 92 ('text # comment', 'text '),
93 93 ('text # comment\n', 'text \n'),
94 94 ('text # comment \n', 'text \n'),
95 95 ('line # c \nline\n','line \nline\n'),
96 96 ('line # c \nline#c2 \nline\nline #c\n\n',
97 97 'line \nline\nline\nline \n\n'),
98 98 ]
99 99 tt.check_pairs(isp.remove_comments, tests)
100 100
101 101
102 102 def test_get_input_encoding():
103 103 encoding = isp.get_input_encoding()
104 104 nt.assert_true(isinstance(encoding, string_types))
105 105 # simple-minded check that at least encoding a simple string works with the
106 106 # encoding we got.
107 107 nt.assert_equal(u'test'.encode(encoding), b'test')
108 108
109 109
110 110 class NoInputEncodingTestCase(unittest.TestCase):
111 111 def setUp(self):
112 112 self.old_stdin = sys.stdin
113 113 class X: pass
114 114 fake_stdin = X()
115 115 sys.stdin = fake_stdin
116 116
117 117 def test(self):
118 118 # Verify that if sys.stdin has no 'encoding' attribute we do the right
119 119 # thing
120 120 enc = isp.get_input_encoding()
121 121 self.assertEqual(enc, 'ascii')
122 122
123 123 def tearDown(self):
124 124 sys.stdin = self.old_stdin
125 125
126 126
127 127 class InputSplitterTestCase(unittest.TestCase):
128 128 def setUp(self):
129 129 self.isp = isp.InputSplitter()
130 130
131 131 def test_reset(self):
132 132 isp = self.isp
133 133 isp.push('x=1')
134 134 isp.reset()
135 135 self.assertEqual(isp._buffer, [])
136 136 self.assertEqual(isp.indent_spaces, 0)
137 137 self.assertEqual(isp.source, '')
138 138 self.assertEqual(isp.code, None)
139 139 self.assertEqual(isp._is_complete, False)
140 140
141 141 def test_source(self):
142 142 self.isp._store('1')
143 143 self.isp._store('2')
144 144 self.assertEqual(self.isp.source, '1\n2\n')
145 145 self.assertTrue(len(self.isp._buffer)>0)
146 146 self.assertEqual(self.isp.source_reset(), '1\n2\n')
147 147 self.assertEqual(self.isp._buffer, [])
148 148 self.assertEqual(self.isp.source, '')
149 149
150 150 def test_indent(self):
151 151 isp = self.isp # shorthand
152 152 isp.push('x=1')
153 153 self.assertEqual(isp.indent_spaces, 0)
154 154 isp.push('if 1:\n x=1')
155 155 self.assertEqual(isp.indent_spaces, 4)
156 156 isp.push('y=2\n')
157 157 self.assertEqual(isp.indent_spaces, 0)
158 158
159 159 def test_indent2(self):
160 160 isp = self.isp
161 161 isp.push('if 1:')
162 162 self.assertEqual(isp.indent_spaces, 4)
163 163 isp.push(' x=1')
164 164 self.assertEqual(isp.indent_spaces, 4)
165 165 # Blank lines shouldn't change the indent level
166 166 isp.push(' '*2)
167 167 self.assertEqual(isp.indent_spaces, 4)
168 168
169 169 def test_indent3(self):
170 170 isp = self.isp
171 171 # When a multiline statement contains parens or multiline strings, we
172 172 # shouldn't get confused.
173 173 isp.push("if 1:")
174 174 isp.push(" x = (1+\n 2)")
175 175 self.assertEqual(isp.indent_spaces, 4)
176 176
177 177 def test_indent4(self):
178 178 isp = self.isp
179 179 # whitespace after ':' should not screw up indent level
180 180 isp.push('if 1: \n x=1')
181 181 self.assertEqual(isp.indent_spaces, 4)
182 182 isp.push('y=2\n')
183 183 self.assertEqual(isp.indent_spaces, 0)
184 184 isp.push('if 1:\t\n x=1')
185 185 self.assertEqual(isp.indent_spaces, 4)
186 186 isp.push('y=2\n')
187 187 self.assertEqual(isp.indent_spaces, 0)
188 188
189 189 def test_dedent_pass(self):
190 190 isp = self.isp # shorthand
191 191 # should NOT cause dedent
192 192 isp.push('if 1:\n passes = 5')
193 193 self.assertEqual(isp.indent_spaces, 4)
194 194 isp.push('if 1:\n pass')
195 195 self.assertEqual(isp.indent_spaces, 0)
196 196 isp.push('if 1:\n pass ')
197 197 self.assertEqual(isp.indent_spaces, 0)
198 198
199 199 def test_dedent_break(self):
200 200 isp = self.isp # shorthand
201 201 # should NOT cause dedent
202 202 isp.push('while 1:\n breaks = 5')
203 203 self.assertEqual(isp.indent_spaces, 4)
204 204 isp.push('while 1:\n break')
205 205 self.assertEqual(isp.indent_spaces, 0)
206 206 isp.push('while 1:\n break ')
207 207 self.assertEqual(isp.indent_spaces, 0)
208 208
209 209 def test_dedent_continue(self):
210 210 isp = self.isp # shorthand
211 211 # should NOT cause dedent
212 212 isp.push('while 1:\n continues = 5')
213 213 self.assertEqual(isp.indent_spaces, 4)
214 214 isp.push('while 1:\n continue')
215 215 self.assertEqual(isp.indent_spaces, 0)
216 216 isp.push('while 1:\n continue ')
217 217 self.assertEqual(isp.indent_spaces, 0)
218 218
219 219 def test_dedent_raise(self):
220 220 isp = self.isp # shorthand
221 221 # should NOT cause dedent
222 222 isp.push('if 1:\n raised = 4')
223 223 self.assertEqual(isp.indent_spaces, 4)
224 224 isp.push('if 1:\n raise TypeError()')
225 225 self.assertEqual(isp.indent_spaces, 0)
226 226 isp.push('if 1:\n raise')
227 227 self.assertEqual(isp.indent_spaces, 0)
228 228 isp.push('if 1:\n raise ')
229 229 self.assertEqual(isp.indent_spaces, 0)
230 230
231 231 def test_dedent_return(self):
232 232 isp = self.isp # shorthand
233 233 # should NOT cause dedent
234 234 isp.push('if 1:\n returning = 4')
235 235 self.assertEqual(isp.indent_spaces, 4)
236 236 isp.push('if 1:\n return 5 + 493')
237 237 self.assertEqual(isp.indent_spaces, 0)
238 238 isp.push('if 1:\n return')
239 239 self.assertEqual(isp.indent_spaces, 0)
240 240 isp.push('if 1:\n return ')
241 241 self.assertEqual(isp.indent_spaces, 0)
242 242 isp.push('if 1:\n return(0)')
243 243 self.assertEqual(isp.indent_spaces, 0)
244 244
245 245 def test_push(self):
246 246 isp = self.isp
247 247 self.assertTrue(isp.push('x=1'))
248 248
249 249 def test_push2(self):
250 250 isp = self.isp
251 251 self.assertFalse(isp.push('if 1:'))
252 252 for line in [' x=1', '# a comment', ' y=2']:
253 253 print(line)
254 254 self.assertTrue(isp.push(line))
255 255
256 256 def test_push3(self):
257 257 isp = self.isp
258 258 isp.push('if True:')
259 259 isp.push(' a = 1')
260 260 self.assertFalse(isp.push('b = [1,'))
261 261
262 262 def test_push_accepts_more(self):
263 263 isp = self.isp
264 264 isp.push('x=1')
265 265 self.assertFalse(isp.push_accepts_more())
266 266
267 267 def test_push_accepts_more2(self):
268 268 isp = self.isp
269 269 isp.push('if 1:')
270 270 self.assertTrue(isp.push_accepts_more())
271 271 isp.push(' x=1')
272 272 self.assertTrue(isp.push_accepts_more())
273 273 isp.push('')
274 274 self.assertFalse(isp.push_accepts_more())
275 275
276 276 def test_push_accepts_more3(self):
277 277 isp = self.isp
278 278 isp.push("x = (2+\n3)")
279 279 self.assertFalse(isp.push_accepts_more())
280 280
281 281 def test_push_accepts_more4(self):
282 282 isp = self.isp
283 283 # When a multiline statement contains parens or multiline strings, we
284 284 # shouldn't get confused.
285 285 # FIXME: we should be able to better handle de-dents in statements like
286 286 # multiline strings and multiline expressions (continued with \ or
287 287 # parens). Right now we aren't handling the indentation tracking quite
288 288 # correctly with this, though in practice it may not be too much of a
289 289 # problem. We'll need to see.
290 290 isp.push("if 1:")
291 291 isp.push(" x = (2+")
292 292 isp.push(" 3)")
293 293 self.assertTrue(isp.push_accepts_more())
294 294 isp.push(" y = 3")
295 295 self.assertTrue(isp.push_accepts_more())
296 296 isp.push('')
297 297 self.assertFalse(isp.push_accepts_more())
298 298
299 299 def test_push_accepts_more5(self):
300 300 isp = self.isp
301 301 isp.push('try:')
302 302 isp.push(' a = 5')
303 303 isp.push('except:')
304 304 isp.push(' raise')
305 305 # We want to be able to add an else: block at this point, so it should
306 306 # wait for a blank line.
307 307 self.assertTrue(isp.push_accepts_more())
308 308
309 309 def test_continuation(self):
310 310 isp = self.isp
311 311 isp.push("import os, \\")
312 312 self.assertTrue(isp.push_accepts_more())
313 313 isp.push("sys")
314 314 self.assertFalse(isp.push_accepts_more())
315 315
316 316 def test_syntax_error(self):
317 317 isp = self.isp
318 318 # Syntax errors immediately produce a 'ready' block, so the invalid
319 319 # Python can be sent to the kernel for evaluation with possible ipython
320 320 # special-syntax conversion.
321 321 isp.push('run foo')
322 322 self.assertFalse(isp.push_accepts_more())
323 323
324 324 def test_unicode(self):
325 325 self.isp.push(u"PΓ©rez")
326 326 self.isp.push(u'\xc3\xa9')
327 327 self.isp.push(u"u'\xc3\xa9'")
328 328
329 329 def test_line_continuation(self):
330 330 """ Test issue #2108."""
331 331 isp = self.isp
332 332 # A blank line after a line continuation should not accept more
333 333 isp.push("1 \\\n\n")
334 334 self.assertFalse(isp.push_accepts_more())
335 335 # Whitespace after a \ is a SyntaxError. The only way to test that
336 336 # here is to test that push doesn't accept more (as with
337 337 # test_syntax_error() above).
338 338 isp.push(r"1 \ ")
339 339 self.assertFalse(isp.push_accepts_more())
340 340 # Even if the line is continuable (c.f. the regular Python
341 341 # interpreter)
342 342 isp.push(r"(1 \ ")
343 343 self.assertFalse(isp.push_accepts_more())
344 344
345 def test_check_complete(self):
346 isp = self.isp
347 self.assertEqual(isp.check_complete("a = 1"), ('complete', None))
348 self.assertEqual(isp.check_complete("for a in range(5):"), ('incomplete', 4))
349 self.assertEqual(isp.check_complete("raise = 2"), ('invalid', None))
350 self.assertEqual(isp.check_complete("a = [1,\n2,"), ('incomplete', 0))
351
345 352 class InteractiveLoopTestCase(unittest.TestCase):
346 353 """Tests for an interactive loop like a python shell.
347 354 """
348 355 def check_ns(self, lines, ns):
349 356 """Validate that the given input lines produce the resulting namespace.
350 357
351 358 Note: the input lines are given exactly as they would be typed in an
352 359 auto-indenting environment, as mini_interactive_loop above already does
353 360 auto-indenting and prepends spaces to the input.
354 361 """
355 362 src = mini_interactive_loop(pseudo_input(lines))
356 363 test_ns = {}
357 364 exec(src, test_ns)
358 365 # We can't check that the provided ns is identical to the test_ns,
359 366 # because Python fills test_ns with extra keys (copyright, etc). But
360 367 # we can check that the given dict is *contained* in test_ns
361 368 for k,v in ns.items():
362 369 self.assertEqual(test_ns[k], v)
363 370
364 371 def test_simple(self):
365 372 self.check_ns(['x=1'], dict(x=1))
366 373
367 374 def test_simple2(self):
368 375 self.check_ns(['if 1:', 'x=2'], dict(x=2))
369 376
370 377 def test_xy(self):
371 378 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
372 379
373 380 def test_abc(self):
374 381 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
375 382
376 383 def test_multi(self):
377 384 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
378 385
379 386
380 387 class IPythonInputTestCase(InputSplitterTestCase):
381 388 """By just creating a new class whose .isp is a different instance, we
382 389 re-run the same test battery on the new input splitter.
383 390
384 391 In addition, this runs the tests over the syntax and syntax_ml dicts that
385 392 were tested by individual functions, as part of the OO interface.
386 393
387 394 It also makes some checks on the raw buffer storage.
388 395 """
389 396
390 397 def setUp(self):
391 398 self.isp = isp.IPythonInputSplitter()
392 399
393 400 def test_syntax(self):
394 401 """Call all single-line syntax tests from the main object"""
395 402 isp = self.isp
396 403 for example in syntax.values():
397 404 for raw, out_t in example:
398 405 if raw.startswith(' '):
399 406 continue
400 407
401 408 isp.push(raw+'\n')
402 409 out_raw = isp.source_raw
403 410 out = isp.source_reset()
404 411 self.assertEqual(out.rstrip(), out_t,
405 412 tt.pair_fail_msg.format("inputsplitter",raw, out_t, out))
406 413 self.assertEqual(out_raw.rstrip(), raw.rstrip())
407 414
408 415 def test_syntax_multiline(self):
409 416 isp = self.isp
410 417 for example in syntax_ml.values():
411 418 for line_pairs in example:
412 419 out_t_parts = []
413 420 raw_parts = []
414 421 for lraw, out_t_part in line_pairs:
415 422 if out_t_part is not None:
416 423 out_t_parts.append(out_t_part)
417 424
418 425 if lraw is not None:
419 426 isp.push(lraw)
420 427 raw_parts.append(lraw)
421 428
422 429 out_raw = isp.source_raw
423 430 out = isp.source_reset()
424 431 out_t = '\n'.join(out_t_parts).rstrip()
425 432 raw = '\n'.join(raw_parts).rstrip()
426 433 self.assertEqual(out.rstrip(), out_t)
427 434 self.assertEqual(out_raw.rstrip(), raw)
428 435
429 436 def test_syntax_multiline_cell(self):
430 437 isp = self.isp
431 438 for example in syntax_ml.values():
432 439
433 440 out_t_parts = []
434 441 for line_pairs in example:
435 442 raw = '\n'.join(r for r, _ in line_pairs if r is not None)
436 443 out_t = '\n'.join(t for _,t in line_pairs if t is not None)
437 444 out = isp.transform_cell(raw)
438 445 # Match ignoring trailing whitespace
439 446 self.assertEqual(out.rstrip(), out_t.rstrip())
440 447
441 448 def test_cellmagic_preempt(self):
442 449 isp = self.isp
443 450 for raw, name, line, cell in [
444 451 ("%%cellm a\nIn[1]:", u'cellm', u'a', u'In[1]:'),
445 452 ("%%cellm \nline\n>>> hi", u'cellm', u'', u'line\n>>> hi'),
446 453 (">>> %%cellm \nline\n>>> hi", u'cellm', u'', u'line\nhi'),
447 454 ("%%cellm \n>>> hi", u'cellm', u'', u'hi'),
448 455 ("%%cellm \nline1\nline2", u'cellm', u'', u'line1\nline2'),
449 456 ("%%cellm \nline1\\\\\nline2", u'cellm', u'', u'line1\\\\\nline2'),
450 457 ]:
451 458 expected = "get_ipython().run_cell_magic(%r, %r, %r)" % (
452 459 name, line, cell
453 460 )
454 461 out = isp.transform_cell(raw)
455 462 self.assertEqual(out.rstrip(), expected.rstrip())
456 463
457 464 def test_multiline_passthrough(self):
458 465 isp = self.isp
459 466 class CommentTransformer(InputTransformer):
460 467 def __init__(self):
461 468 self._lines = []
462 469
463 470 def push(self, line):
464 471 self._lines.append(line + '#')
465 472
466 473 def reset(self):
467 474 text = '\n'.join(self._lines)
468 475 self._lines = []
469 476 return text
470 477
471 478 isp.physical_line_transforms.insert(0, CommentTransformer())
472 479
473 480 for raw, expected in [
474 481 ("a=5", "a=5#"),
475 482 ("%ls foo", "get_ipython().magic(%r)" % u'ls foo#'),
476 483 ("!ls foo\n%ls bar", "get_ipython().system(%r)\nget_ipython().magic(%r)" % (
477 484 u'ls foo#', u'ls bar#'
478 485 )),
479 486 ("1\n2\n3\n%ls foo\n4\n5", "1#\n2#\n3#\nget_ipython().magic(%r)\n4#\n5#" % u'ls foo#'),
480 487 ]:
481 488 out = isp.transform_cell(raw)
482 489 self.assertEqual(out.rstrip(), expected.rstrip())
483 490
484 491 #-----------------------------------------------------------------------------
485 492 # Main - use as a script, mostly for developer experiments
486 493 #-----------------------------------------------------------------------------
487 494
488 495 if __name__ == '__main__':
489 496 # A simple demo for interactive experimentation. This code will not get
490 497 # picked up by any test suite.
491 498 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
492 499
493 500 # configure here the syntax to use, prompt and whether to autoindent
494 501 #isp, start_prompt = InputSplitter(), '>>> '
495 502 isp, start_prompt = IPythonInputSplitter(), 'In> '
496 503
497 504 autoindent = True
498 505 #autoindent = False
499 506
500 507 try:
501 508 while True:
502 509 prompt = start_prompt
503 510 while isp.push_accepts_more():
504 511 indent = ' '*isp.indent_spaces
505 512 if autoindent:
506 513 line = indent + input(prompt+indent)
507 514 else:
508 515 line = input(prompt)
509 516 isp.push(line)
510 517 prompt = '... '
511 518
512 519 # Here we just return input so we can use it in a test suite, but a
513 520 # real interpreter would instead send it for execution somewhere.
514 521 #src = isp.source; raise EOFError # dbg
515 522 raw = isp.source_raw
516 523 src = isp.source_reset()
517 524 print('Input source was:\n', src)
518 525 print('Raw source was:\n', raw)
519 526 except EOFError:
520 527 print('Bye')
521 528
522 529 # Tests for cell magics support
523 530
524 531 def test_last_blank():
525 532 nt.assert_false(isp.last_blank(''))
526 533 nt.assert_false(isp.last_blank('abc'))
527 534 nt.assert_false(isp.last_blank('abc\n'))
528 535 nt.assert_false(isp.last_blank('abc\na'))
529 536
530 537 nt.assert_true(isp.last_blank('\n'))
531 538 nt.assert_true(isp.last_blank('\n '))
532 539 nt.assert_true(isp.last_blank('abc\n '))
533 540 nt.assert_true(isp.last_blank('abc\n\n'))
534 541 nt.assert_true(isp.last_blank('abc\nd\n\n'))
535 542 nt.assert_true(isp.last_blank('abc\nd\ne\n\n'))
536 543 nt.assert_true(isp.last_blank('abc \n \n \n\n'))
537 544
538 545
539 546 def test_last_two_blanks():
540 547 nt.assert_false(isp.last_two_blanks(''))
541 548 nt.assert_false(isp.last_two_blanks('abc'))
542 549 nt.assert_false(isp.last_two_blanks('abc\n'))
543 550 nt.assert_false(isp.last_two_blanks('abc\n\na'))
544 551 nt.assert_false(isp.last_two_blanks('abc\n \n'))
545 552 nt.assert_false(isp.last_two_blanks('abc\n\n'))
546 553
547 554 nt.assert_true(isp.last_two_blanks('\n\n'))
548 555 nt.assert_true(isp.last_two_blanks('\n\n '))
549 556 nt.assert_true(isp.last_two_blanks('\n \n'))
550 557 nt.assert_true(isp.last_two_blanks('abc\n\n '))
551 558 nt.assert_true(isp.last_two_blanks('abc\n\n\n'))
552 559 nt.assert_true(isp.last_two_blanks('abc\n\n \n'))
553 560 nt.assert_true(isp.last_two_blanks('abc\n\n \n '))
554 561 nt.assert_true(isp.last_two_blanks('abc\n\n \n \n'))
555 562 nt.assert_true(isp.last_two_blanks('abc\nd\n\n\n'))
556 563 nt.assert_true(isp.last_two_blanks('abc\nd\ne\nf\n\n\n'))
557 564
558 565
559 566 class CellMagicsCommon(object):
560 567
561 568 def test_whole_cell(self):
562 569 src = "%%cellm line\nbody\n"
563 570 out = self.sp.transform_cell(src)
564 571 ref = u"get_ipython().run_cell_magic({u}'cellm', {u}'line', {u}'body')\n"
565 572 nt.assert_equal(out, py3compat.u_format(ref))
566 573
567 574 def test_cellmagic_help(self):
568 575 self.sp.push('%%cellm?')
569 576 nt.assert_false(self.sp.push_accepts_more())
570 577
571 578 def tearDown(self):
572 579 self.sp.reset()
573 580
574 581
575 582 class CellModeCellMagics(CellMagicsCommon, unittest.TestCase):
576 583 sp = isp.IPythonInputSplitter(line_input_checker=False)
577 584
578 585 def test_incremental(self):
579 586 sp = self.sp
580 587 sp.push('%%cellm firstline\n')
581 588 nt.assert_true(sp.push_accepts_more()) #1
582 589 sp.push('line2\n')
583 590 nt.assert_true(sp.push_accepts_more()) #2
584 591 sp.push('\n')
585 592 # This should accept a blank line and carry on until the cell is reset
586 593 nt.assert_true(sp.push_accepts_more()) #3
587 594
588 595 class LineModeCellMagics(CellMagicsCommon, unittest.TestCase):
589 596 sp = isp.IPythonInputSplitter(line_input_checker=True)
590 597
591 598 def test_incremental(self):
592 599 sp = self.sp
593 600 sp.push('%%cellm line2\n')
594 601 nt.assert_true(sp.push_accepts_more()) #1
595 602 sp.push('\n')
596 603 # In this case, a blank line should end the cell magic
597 604 nt.assert_false(sp.push_accepts_more()) #2
@@ -1,639 +1,644 b''
1 1 """Base classes to manage a Client's interaction with a running kernel"""
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 absolute_import
7 7
8 8 import atexit
9 9 import errno
10 10 from threading import Thread
11 11 import time
12 12
13 13 import zmq
14 14 # import ZMQError in top-level namespace, to avoid ugly attribute-error messages
15 15 # during garbage collection of threads at exit:
16 16 from zmq import ZMQError
17 17 from zmq.eventloop import ioloop, zmqstream
18 18
19 19 from IPython.core.release import kernel_protocol_version_info
20 20
21 21 from .channelsabc import (
22 22 ShellChannelABC, IOPubChannelABC,
23 23 HBChannelABC, StdInChannelABC,
24 24 )
25 25 from IPython.utils.py3compat import string_types, iteritems
26 26
27 27 #-----------------------------------------------------------------------------
28 28 # Constants and exceptions
29 29 #-----------------------------------------------------------------------------
30 30
31 31 major_protocol_version = kernel_protocol_version_info[0]
32 32
33 33 class InvalidPortNumber(Exception):
34 34 pass
35 35
36 36 #-----------------------------------------------------------------------------
37 37 # Utility functions
38 38 #-----------------------------------------------------------------------------
39 39
40 40 # some utilities to validate message structure, these might get moved elsewhere
41 41 # if they prove to have more generic utility
42 42
43 43 def validate_string_list(lst):
44 44 """Validate that the input is a list of strings.
45 45
46 46 Raises ValueError if not."""
47 47 if not isinstance(lst, list):
48 48 raise ValueError('input %r must be a list' % lst)
49 49 for x in lst:
50 50 if not isinstance(x, string_types):
51 51 raise ValueError('element %r in list must be a string' % x)
52 52
53 53
54 54 def validate_string_dict(dct):
55 55 """Validate that the input is a dict with string keys and values.
56 56
57 57 Raises ValueError if not."""
58 58 for k,v in iteritems(dct):
59 59 if not isinstance(k, string_types):
60 60 raise ValueError('key %r in dict must be a string' % k)
61 61 if not isinstance(v, string_types):
62 62 raise ValueError('value %r in dict must be a string' % v)
63 63
64 64
65 65 #-----------------------------------------------------------------------------
66 66 # ZMQ Socket Channel classes
67 67 #-----------------------------------------------------------------------------
68 68
69 69 class ZMQSocketChannel(Thread):
70 70 """The base class for the channels that use ZMQ sockets."""
71 71 context = None
72 72 session = None
73 73 socket = None
74 74 ioloop = None
75 75 stream = None
76 76 _address = None
77 77 _exiting = False
78 78 proxy_methods = []
79 79
80 80 def __init__(self, context, session, address):
81 81 """Create a channel.
82 82
83 83 Parameters
84 84 ----------
85 85 context : :class:`zmq.Context`
86 86 The ZMQ context to use.
87 87 session : :class:`session.Session`
88 88 The session to use.
89 89 address : zmq url
90 90 Standard (ip, port) tuple that the kernel is listening on.
91 91 """
92 92 super(ZMQSocketChannel, self).__init__()
93 93 self.daemon = True
94 94
95 95 self.context = context
96 96 self.session = session
97 97 if isinstance(address, tuple):
98 98 if address[1] == 0:
99 99 message = 'The port number for a channel cannot be 0.'
100 100 raise InvalidPortNumber(message)
101 101 address = "tcp://%s:%i" % address
102 102 self._address = address
103 103 atexit.register(self._notice_exit)
104 104
105 105 def _notice_exit(self):
106 106 self._exiting = True
107 107
108 108 def _run_loop(self):
109 109 """Run my loop, ignoring EINTR events in the poller"""
110 110 while True:
111 111 try:
112 112 self.ioloop.start()
113 113 except ZMQError as e:
114 114 if e.errno == errno.EINTR:
115 115 continue
116 116 else:
117 117 raise
118 118 except Exception:
119 119 if self._exiting:
120 120 break
121 121 else:
122 122 raise
123 123 else:
124 124 break
125 125
126 126 def stop(self):
127 127 """Stop the channel's event loop and join its thread.
128 128
129 129 This calls :meth:`~threading.Thread.join` and returns when the thread
130 130 terminates. :class:`RuntimeError` will be raised if
131 131 :meth:`~threading.Thread.start` is called again.
132 132 """
133 133 if self.ioloop is not None:
134 134 self.ioloop.stop()
135 135 self.join()
136 136 self.close()
137 137
138 138 def close(self):
139 139 if self.ioloop is not None:
140 140 try:
141 141 self.ioloop.close(all_fds=True)
142 142 except Exception:
143 143 pass
144 144 if self.socket is not None:
145 145 try:
146 146 self.socket.close(linger=0)
147 147 except Exception:
148 148 pass
149 149 self.socket = None
150 150
151 151 @property
152 152 def address(self):
153 153 """Get the channel's address as a zmq url string.
154 154
155 155 These URLS have the form: 'tcp://127.0.0.1:5555'.
156 156 """
157 157 return self._address
158 158
159 159 def _queue_send(self, msg):
160 160 """Queue a message to be sent from the IOLoop's thread.
161 161
162 162 Parameters
163 163 ----------
164 164 msg : message to send
165 165
166 166 This is threadsafe, as it uses IOLoop.add_callback to give the loop's
167 167 thread control of the action.
168 168 """
169 169 def thread_send():
170 170 self.session.send(self.stream, msg)
171 171 self.ioloop.add_callback(thread_send)
172 172
173 173 def _handle_recv(self, msg):
174 174 """Callback for stream.on_recv.
175 175
176 176 Unpacks message, and calls handlers with it.
177 177 """
178 178 ident,smsg = self.session.feed_identities(msg)
179 179 msg = self.session.unserialize(smsg)
180 180 self.call_handlers(msg)
181 181
182 182
183 183
184 184 class ShellChannel(ZMQSocketChannel):
185 185 """The shell channel for issuing request/replies to the kernel."""
186 186
187 187 command_queue = None
188 188 # flag for whether execute requests should be allowed to call raw_input:
189 189 allow_stdin = True
190 190 proxy_methods = [
191 191 'execute',
192 192 'complete',
193 193 'inspect',
194 194 'history',
195 195 'kernel_info',
196 196 'shutdown',
197 'is_complete',
197 198 ]
198 199
199 200 def __init__(self, context, session, address):
200 201 super(ShellChannel, self).__init__(context, session, address)
201 202 self.ioloop = ioloop.IOLoop()
202 203
203 204 def run(self):
204 205 """The thread's main activity. Call start() instead."""
205 206 self.socket = self.context.socket(zmq.DEALER)
206 207 self.socket.linger = 1000
207 208 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
208 209 self.socket.connect(self.address)
209 210 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
210 211 self.stream.on_recv(self._handle_recv)
211 212 self._run_loop()
212 213
213 214 def call_handlers(self, msg):
214 215 """This method is called in the ioloop thread when a message arrives.
215 216
216 217 Subclasses should override this method to handle incoming messages.
217 218 It is important to remember that this method is called in the thread
218 219 so that some logic must be done to ensure that the application level
219 220 handlers are called in the application thread.
220 221 """
221 222 raise NotImplementedError('call_handlers must be defined in a subclass.')
222 223
223 224 def execute(self, code, silent=False, store_history=True,
224 225 user_expressions=None, allow_stdin=None):
225 226 """Execute code in the kernel.
226 227
227 228 Parameters
228 229 ----------
229 230 code : str
230 231 A string of Python code.
231 232
232 233 silent : bool, optional (default False)
233 234 If set, the kernel will execute the code as quietly possible, and
234 235 will force store_history to be False.
235 236
236 237 store_history : bool, optional (default True)
237 238 If set, the kernel will store command history. This is forced
238 239 to be False if silent is True.
239 240
240 241 user_expressions : dict, optional
241 242 A dict mapping names to expressions to be evaluated in the user's
242 243 dict. The expression values are returned as strings formatted using
243 244 :func:`repr`.
244 245
245 246 allow_stdin : bool, optional (default self.allow_stdin)
246 247 Flag for whether the kernel can send stdin requests to frontends.
247 248
248 249 Some frontends (e.g. the Notebook) do not support stdin requests.
249 250 If raw_input is called from code executed from such a frontend, a
250 251 StdinNotImplementedError will be raised.
251 252
252 253 Returns
253 254 -------
254 255 The msg_id of the message sent.
255 256 """
256 257 if user_expressions is None:
257 258 user_expressions = {}
258 259 if allow_stdin is None:
259 260 allow_stdin = self.allow_stdin
260 261
261 262
262 263 # Don't waste network traffic if inputs are invalid
263 264 if not isinstance(code, string_types):
264 265 raise ValueError('code %r must be a string' % code)
265 266 validate_string_dict(user_expressions)
266 267
267 268 # Create class for content/msg creation. Related to, but possibly
268 269 # not in Session.
269 270 content = dict(code=code, silent=silent, store_history=store_history,
270 271 user_expressions=user_expressions,
271 272 allow_stdin=allow_stdin,
272 273 )
273 274 msg = self.session.msg('execute_request', content)
274 275 self._queue_send(msg)
275 276 return msg['header']['msg_id']
276 277
277 278 def complete(self, code, cursor_pos=None):
278 279 """Tab complete text in the kernel's namespace.
279 280
280 281 Parameters
281 282 ----------
282 283 code : str
283 284 The context in which completion is requested.
284 285 Can be anything between a variable name and an entire cell.
285 286 cursor_pos : int, optional
286 287 The position of the cursor in the block of code where the completion was requested.
287 288 Default: ``len(code)``
288 289
289 290 Returns
290 291 -------
291 292 The msg_id of the message sent.
292 293 """
293 294 if cursor_pos is None:
294 295 cursor_pos = len(code)
295 296 content = dict(code=code, cursor_pos=cursor_pos)
296 297 msg = self.session.msg('complete_request', content)
297 298 self._queue_send(msg)
298 299 return msg['header']['msg_id']
299 300
300 301 def inspect(self, code, cursor_pos=None, detail_level=0):
301 302 """Get metadata information about an object in the kernel's namespace.
302 303
303 304 It is up to the kernel to determine the appropriate object to inspect.
304 305
305 306 Parameters
306 307 ----------
307 308 code : str
308 309 The context in which info is requested.
309 310 Can be anything between a variable name and an entire cell.
310 311 cursor_pos : int, optional
311 312 The position of the cursor in the block of code where the info was requested.
312 313 Default: ``len(code)``
313 314 detail_level : int, optional
314 315 The level of detail for the introspection (0-2)
315 316
316 317 Returns
317 318 -------
318 319 The msg_id of the message sent.
319 320 """
320 321 if cursor_pos is None:
321 322 cursor_pos = len(code)
322 323 content = dict(code=code, cursor_pos=cursor_pos,
323 324 detail_level=detail_level,
324 325 )
325 326 msg = self.session.msg('inspect_request', content)
326 327 self._queue_send(msg)
327 328 return msg['header']['msg_id']
328 329
329 330 def history(self, raw=True, output=False, hist_access_type='range', **kwargs):
330 331 """Get entries from the kernel's history list.
331 332
332 333 Parameters
333 334 ----------
334 335 raw : bool
335 336 If True, return the raw input.
336 337 output : bool
337 338 If True, then return the output as well.
338 339 hist_access_type : str
339 340 'range' (fill in session, start and stop params), 'tail' (fill in n)
340 341 or 'search' (fill in pattern param).
341 342
342 343 session : int
343 344 For a range request, the session from which to get lines. Session
344 345 numbers are positive integers; negative ones count back from the
345 346 current session.
346 347 start : int
347 348 The first line number of a history range.
348 349 stop : int
349 350 The final (excluded) line number of a history range.
350 351
351 352 n : int
352 353 The number of lines of history to get for a tail request.
353 354
354 355 pattern : str
355 356 The glob-syntax pattern for a search request.
356 357
357 358 Returns
358 359 -------
359 360 The msg_id of the message sent.
360 361 """
361 362 content = dict(raw=raw, output=output, hist_access_type=hist_access_type,
362 363 **kwargs)
363 364 msg = self.session.msg('history_request', content)
364 365 self._queue_send(msg)
365 366 return msg['header']['msg_id']
366 367
367 368 def kernel_info(self):
368 369 """Request kernel info."""
369 370 msg = self.session.msg('kernel_info_request')
370 371 self._queue_send(msg)
371 372 return msg['header']['msg_id']
372 373
373 374 def _handle_kernel_info_reply(self, msg):
374 375 """handle kernel info reply
375 376
376 377 sets protocol adaptation version
377 378 """
378 379 adapt_version = int(msg['content']['protocol_version'].split('.')[0])
379 380 if adapt_version != major_protocol_version:
380 381 self.session.adapt_version = adapt_version
381 382
382 383 def shutdown(self, restart=False):
383 384 """Request an immediate kernel shutdown.
384 385
385 386 Upon receipt of the (empty) reply, client code can safely assume that
386 387 the kernel has shut down and it's safe to forcefully terminate it if
387 388 it's still alive.
388 389
389 390 The kernel will send the reply via a function registered with Python's
390 391 atexit module, ensuring it's truly done as the kernel is done with all
391 392 normal operation.
392 393 """
393 394 # Send quit message to kernel. Once we implement kernel-side setattr,
394 395 # this should probably be done that way, but for now this will do.
395 396 msg = self.session.msg('shutdown_request', {'restart':restart})
396 397 self._queue_send(msg)
397 398 return msg['header']['msg_id']
398 399
400 def is_complete(self, code):
401 msg = self.session.msg('is_complete_request', {'code': code})
402 self._queue_send(msg)
403 return msg['header']['msg_id']
399 404
400 405
401 406 class IOPubChannel(ZMQSocketChannel):
402 407 """The iopub channel which listens for messages that the kernel publishes.
403 408
404 409 This channel is where all output is published to frontends.
405 410 """
406 411
407 412 def __init__(self, context, session, address):
408 413 super(IOPubChannel, self).__init__(context, session, address)
409 414 self.ioloop = ioloop.IOLoop()
410 415
411 416 def run(self):
412 417 """The thread's main activity. Call start() instead."""
413 418 self.socket = self.context.socket(zmq.SUB)
414 419 self.socket.linger = 1000
415 420 self.socket.setsockopt(zmq.SUBSCRIBE,b'')
416 421 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
417 422 self.socket.connect(self.address)
418 423 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
419 424 self.stream.on_recv(self._handle_recv)
420 425 self._run_loop()
421 426
422 427 def call_handlers(self, msg):
423 428 """This method is called in the ioloop thread when a message arrives.
424 429
425 430 Subclasses should override this method to handle incoming messages.
426 431 It is important to remember that this method is called in the thread
427 432 so that some logic must be done to ensure that the application leve
428 433 handlers are called in the application thread.
429 434 """
430 435 raise NotImplementedError('call_handlers must be defined in a subclass.')
431 436
432 437 def flush(self, timeout=1.0):
433 438 """Immediately processes all pending messages on the iopub channel.
434 439
435 440 Callers should use this method to ensure that :meth:`call_handlers`
436 441 has been called for all messages that have been received on the
437 442 0MQ SUB socket of this channel.
438 443
439 444 This method is thread safe.
440 445
441 446 Parameters
442 447 ----------
443 448 timeout : float, optional
444 449 The maximum amount of time to spend flushing, in seconds. The
445 450 default is one second.
446 451 """
447 452 # We do the IOLoop callback process twice to ensure that the IOLoop
448 453 # gets to perform at least one full poll.
449 454 stop_time = time.time() + timeout
450 455 for i in range(2):
451 456 self._flushed = False
452 457 self.ioloop.add_callback(self._flush)
453 458 while not self._flushed and time.time() < stop_time:
454 459 time.sleep(0.01)
455 460
456 461 def _flush(self):
457 462 """Callback for :method:`self.flush`."""
458 463 self.stream.flush()
459 464 self._flushed = True
460 465
461 466
462 467 class StdInChannel(ZMQSocketChannel):
463 468 """The stdin channel to handle raw_input requests that the kernel makes."""
464 469
465 470 msg_queue = None
466 471 proxy_methods = ['input']
467 472
468 473 def __init__(self, context, session, address):
469 474 super(StdInChannel, self).__init__(context, session, address)
470 475 self.ioloop = ioloop.IOLoop()
471 476
472 477 def run(self):
473 478 """The thread's main activity. Call start() instead."""
474 479 self.socket = self.context.socket(zmq.DEALER)
475 480 self.socket.linger = 1000
476 481 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
477 482 self.socket.connect(self.address)
478 483 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
479 484 self.stream.on_recv(self._handle_recv)
480 485 self._run_loop()
481 486
482 487 def call_handlers(self, msg):
483 488 """This method is called in the ioloop thread when a message arrives.
484 489
485 490 Subclasses should override this method to handle incoming messages.
486 491 It is important to remember that this method is called in the thread
487 492 so that some logic must be done to ensure that the application leve
488 493 handlers are called in the application thread.
489 494 """
490 495 raise NotImplementedError('call_handlers must be defined in a subclass.')
491 496
492 497 def input(self, string):
493 498 """Send a string of raw input to the kernel."""
494 499 content = dict(value=string)
495 500 msg = self.session.msg('input_reply', content)
496 501 self._queue_send(msg)
497 502
498 503
499 504 class HBChannel(ZMQSocketChannel):
500 505 """The heartbeat channel which monitors the kernel heartbeat.
501 506
502 507 Note that the heartbeat channel is paused by default. As long as you start
503 508 this channel, the kernel manager will ensure that it is paused and un-paused
504 509 as appropriate.
505 510 """
506 511
507 512 time_to_dead = 3.0
508 513 socket = None
509 514 poller = None
510 515 _running = None
511 516 _pause = None
512 517 _beating = None
513 518
514 519 def __init__(self, context, session, address):
515 520 super(HBChannel, self).__init__(context, session, address)
516 521 self._running = False
517 522 self._pause =True
518 523 self.poller = zmq.Poller()
519 524
520 525 def _create_socket(self):
521 526 if self.socket is not None:
522 527 # close previous socket, before opening a new one
523 528 self.poller.unregister(self.socket)
524 529 self.socket.close()
525 530 self.socket = self.context.socket(zmq.REQ)
526 531 self.socket.linger = 1000
527 532 self.socket.connect(self.address)
528 533
529 534 self.poller.register(self.socket, zmq.POLLIN)
530 535
531 536 def _poll(self, start_time):
532 537 """poll for heartbeat replies until we reach self.time_to_dead.
533 538
534 539 Ignores interrupts, and returns the result of poll(), which
535 540 will be an empty list if no messages arrived before the timeout,
536 541 or the event tuple if there is a message to receive.
537 542 """
538 543
539 544 until_dead = self.time_to_dead - (time.time() - start_time)
540 545 # ensure poll at least once
541 546 until_dead = max(until_dead, 1e-3)
542 547 events = []
543 548 while True:
544 549 try:
545 550 events = self.poller.poll(1000 * until_dead)
546 551 except ZMQError as e:
547 552 if e.errno == errno.EINTR:
548 553 # ignore interrupts during heartbeat
549 554 # this may never actually happen
550 555 until_dead = self.time_to_dead - (time.time() - start_time)
551 556 until_dead = max(until_dead, 1e-3)
552 557 pass
553 558 else:
554 559 raise
555 560 except Exception:
556 561 if self._exiting:
557 562 break
558 563 else:
559 564 raise
560 565 else:
561 566 break
562 567 return events
563 568
564 569 def run(self):
565 570 """The thread's main activity. Call start() instead."""
566 571 self._create_socket()
567 572 self._running = True
568 573 self._beating = True
569 574
570 575 while self._running:
571 576 if self._pause:
572 577 # just sleep, and skip the rest of the loop
573 578 time.sleep(self.time_to_dead)
574 579 continue
575 580
576 581 since_last_heartbeat = 0.0
577 582 # io.rprint('Ping from HB channel') # dbg
578 583 # no need to catch EFSM here, because the previous event was
579 584 # either a recv or connect, which cannot be followed by EFSM
580 585 self.socket.send(b'ping')
581 586 request_time = time.time()
582 587 ready = self._poll(request_time)
583 588 if ready:
584 589 self._beating = True
585 590 # the poll above guarantees we have something to recv
586 591 self.socket.recv()
587 592 # sleep the remainder of the cycle
588 593 remainder = self.time_to_dead - (time.time() - request_time)
589 594 if remainder > 0:
590 595 time.sleep(remainder)
591 596 continue
592 597 else:
593 598 # nothing was received within the time limit, signal heart failure
594 599 self._beating = False
595 600 since_last_heartbeat = time.time() - request_time
596 601 self.call_handlers(since_last_heartbeat)
597 602 # and close/reopen the socket, because the REQ/REP cycle has been broken
598 603 self._create_socket()
599 604 continue
600 605
601 606 def pause(self):
602 607 """Pause the heartbeat."""
603 608 self._pause = True
604 609
605 610 def unpause(self):
606 611 """Unpause the heartbeat."""
607 612 self._pause = False
608 613
609 614 def is_beating(self):
610 615 """Is the heartbeat running and responsive (and not paused)."""
611 616 if self.is_alive() and not self._pause and self._beating:
612 617 return True
613 618 else:
614 619 return False
615 620
616 621 def stop(self):
617 622 """Stop the channel's event loop and join its thread."""
618 623 self._running = False
619 624 super(HBChannel, self).stop()
620 625
621 626 def call_handlers(self, since_last_heartbeat):
622 627 """This method is called in the ioloop thread when a message arrives.
623 628
624 629 Subclasses should override this method to handle incoming messages.
625 630 It is important to remember that this method is called in the thread
626 631 so that some logic must be done to ensure that the application level
627 632 handlers are called in the application thread.
628 633 """
629 634 raise NotImplementedError('call_handlers must be defined in a subclass.')
630 635
631 636
632 637 #---------------------------------------------------------------------#-----------------------------------------------------------------------------
633 638 # ABC Registration
634 639 #-----------------------------------------------------------------------------
635 640
636 641 ShellChannelABC.register(ShellChannel)
637 642 IOPubChannelABC.register(IOPubChannel)
638 643 HBChannelABC.register(HBChannel)
639 644 StdInChannelABC.register(StdInChannel)
@@ -1,207 +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 def test_is_complete():
209 with kernel() as kc:
210 # There are more test cases for this in core - here we just check
211 # that the kernel exposes the interface correctly.
212 kc.is_complete('2+2')
213 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
214 assert reply['content']['status'] == 'complete'
215
216 # SyntaxError should mean it's complete
217 kc.is_complete('raise = 2')
218 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
219 assert reply['content']['status'] == 'invalid'
220
221 kc.is_complete('a = [1,\n2,')
222 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
223 assert reply['content']['status'] == 'incomplete'
224 assert reply['content']['indent'] == '' No newline at end of file
@@ -1,409 +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 class IsCompleteReply(Reference):
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()
173
174
163 175 # IOPub messages
164 176
165 177 class ExecuteInput(Reference):
166 178 code = Unicode()
167 179 execution_count = Integer()
168 180
169 181
170 182 Error = ExecuteReplyError
171 183
172 184
173 185 class Stream(Reference):
174 186 name = Enum((u'stdout', u'stderr'))
175 187 text = Unicode()
176 188
177 189
178 190 class DisplayData(MimeBundle):
179 191 pass
180 192
181 193
182 194 class ExecuteResult(MimeBundle):
183 195 execution_count = Integer()
184 196
185 197
186 198 references = {
187 199 'execute_reply' : ExecuteReply(),
188 200 'inspect_reply' : InspectReply(),
189 201 'status' : Status(),
190 202 'complete_reply' : CompleteReply(),
191 203 'kernel_info_reply': KernelInfoReply(),
204 'is_complete_reply': IsCompleteReply(),
192 205 'execute_input' : ExecuteInput(),
193 206 'execute_result' : ExecuteResult(),
194 207 'error' : Error(),
195 208 'stream' : Stream(),
196 209 'display_data' : DisplayData(),
197 210 'header' : RHeader(),
198 211 }
199 212 """
200 213 Specifications of `content` part of the reply messages.
201 214 """
202 215
203 216
204 217 def validate_message(msg, msg_type=None, parent=None):
205 218 """validate a message
206 219
207 220 This is a generator, and must be iterated through to actually
208 221 trigger each test.
209 222
210 223 If msg_type and/or parent are given, the msg_type and/or parent msg_id
211 224 are compared with the given values.
212 225 """
213 226 RMessage().check(msg)
214 227 if msg_type:
215 228 nt.assert_equal(msg['msg_type'], msg_type)
216 229 if parent:
217 230 nt.assert_equal(msg['parent_header']['msg_id'], parent)
218 231 content = msg['content']
219 232 ref = references[msg['msg_type']]
220 233 ref.check(content)
221 234
222 235
223 236 #-----------------------------------------------------------------------------
224 237 # Tests
225 238 #-----------------------------------------------------------------------------
226 239
227 240 # Shell channel
228 241
229 242 def test_execute():
230 243 flush_channels()
231 244
232 245 msg_id = KC.execute(code='x=1')
233 246 reply = KC.get_shell_msg(timeout=TIMEOUT)
234 247 validate_message(reply, 'execute_reply', msg_id)
235 248
236 249
237 250 def test_execute_silent():
238 251 flush_channels()
239 252 msg_id, reply = execute(code='x=1', silent=True)
240 253
241 254 # flush status=idle
242 255 status = KC.iopub_channel.get_msg(timeout=TIMEOUT)
243 256 validate_message(status, 'status', msg_id)
244 257 nt.assert_equal(status['content']['execution_state'], 'idle')
245 258
246 259 nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1)
247 260 count = reply['execution_count']
248 261
249 262 msg_id, reply = execute(code='x=2', silent=True)
250 263
251 264 # flush status=idle
252 265 status = KC.iopub_channel.get_msg(timeout=TIMEOUT)
253 266 validate_message(status, 'status', msg_id)
254 267 nt.assert_equal(status['content']['execution_state'], 'idle')
255 268
256 269 nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1)
257 270 count_2 = reply['execution_count']
258 271 nt.assert_equal(count_2, count)
259 272
260 273
261 274 def test_execute_error():
262 275 flush_channels()
263 276
264 277 msg_id, reply = execute(code='1/0')
265 278 nt.assert_equal(reply['status'], 'error')
266 279 nt.assert_equal(reply['ename'], 'ZeroDivisionError')
267 280
268 281 error = KC.iopub_channel.get_msg(timeout=TIMEOUT)
269 282 validate_message(error, 'error', msg_id)
270 283
271 284
272 285 def test_execute_inc():
273 286 """execute request should increment execution_count"""
274 287 flush_channels()
275 288
276 289 msg_id, reply = execute(code='x=1')
277 290 count = reply['execution_count']
278 291
279 292 flush_channels()
280 293
281 294 msg_id, reply = execute(code='x=2')
282 295 count_2 = reply['execution_count']
283 296 nt.assert_equal(count_2, count+1)
284 297
285 298
286 299 def test_user_expressions():
287 300 flush_channels()
288 301
289 302 msg_id, reply = execute(code='x=1', user_expressions=dict(foo='x+1'))
290 303 user_expressions = reply['user_expressions']
291 304 nt.assert_equal(user_expressions, {u'foo': {
292 305 u'status': u'ok',
293 306 u'data': {u'text/plain': u'2'},
294 307 u'metadata': {},
295 308 }})
296 309
297 310
298 311 def test_user_expressions_fail():
299 312 flush_channels()
300 313
301 314 msg_id, reply = execute(code='x=0', user_expressions=dict(foo='nosuchname'))
302 315 user_expressions = reply['user_expressions']
303 316 foo = user_expressions['foo']
304 317 nt.assert_equal(foo['status'], 'error')
305 318 nt.assert_equal(foo['ename'], 'NameError')
306 319
307 320
308 321 def test_oinfo():
309 322 flush_channels()
310 323
311 324 msg_id = KC.inspect('a')
312 325 reply = KC.get_shell_msg(timeout=TIMEOUT)
313 326 validate_message(reply, 'inspect_reply', msg_id)
314 327
315 328
316 329 def test_oinfo_found():
317 330 flush_channels()
318 331
319 332 msg_id, reply = execute(code='a=5')
320 333
321 334 msg_id = KC.inspect('a')
322 335 reply = KC.get_shell_msg(timeout=TIMEOUT)
323 336 validate_message(reply, 'inspect_reply', msg_id)
324 337 content = reply['content']
325 338 assert content['found']
326 339 text = content['data']['text/plain']
327 340 nt.assert_in('Type:', text)
328 341 nt.assert_in('Docstring:', text)
329 342
330 343
331 344 def test_oinfo_detail():
332 345 flush_channels()
333 346
334 347 msg_id, reply = execute(code='ip=get_ipython()')
335 348
336 349 msg_id = KC.inspect('ip.object_inspect', cursor_pos=10, detail_level=1)
337 350 reply = KC.get_shell_msg(timeout=TIMEOUT)
338 351 validate_message(reply, 'inspect_reply', msg_id)
339 352 content = reply['content']
340 353 assert content['found']
341 354 text = content['data']['text/plain']
342 355 nt.assert_in('Definition:', text)
343 356 nt.assert_in('Source:', text)
344 357
345 358
346 359 def test_oinfo_not_found():
347 360 flush_channels()
348 361
349 362 msg_id = KC.inspect('dne')
350 363 reply = KC.get_shell_msg(timeout=TIMEOUT)
351 364 validate_message(reply, 'inspect_reply', msg_id)
352 365 content = reply['content']
353 366 nt.assert_false(content['found'])
354 367
355 368
356 369 def test_complete():
357 370 flush_channels()
358 371
359 372 msg_id, reply = execute(code="alpha = albert = 5")
360 373
361 374 msg_id = KC.complete('al', 2)
362 375 reply = KC.get_shell_msg(timeout=TIMEOUT)
363 376 validate_message(reply, 'complete_reply', msg_id)
364 377 matches = reply['content']['matches']
365 378 for name in ('alpha', 'albert'):
366 379 nt.assert_in(name, matches)
367 380
368 381
369 382 def test_kernel_info_request():
370 383 flush_channels()
371 384
372 385 msg_id = KC.kernel_info()
373 386 reply = KC.get_shell_msg(timeout=TIMEOUT)
374 387 validate_message(reply, 'kernel_info_reply', msg_id)
375 388
376 389
377 390 def test_single_payload():
378 391 flush_channels()
379 392 msg_id, reply = execute(code="for i in range(3):\n"+
380 393 " x=range?\n")
381 394 payload = reply['payload']
382 395 next_input_pls = [pl for pl in payload if pl["source"] == "set_next_input"]
383 396 nt.assert_equal(len(next_input_pls), 1)
384 397
398 def test_is_complete():
399 flush_channels()
400
401 msg_id = KC.is_complete("a = 1")
402 reply = KC.get_shell_msg(timeout=TIMEOUT)
403 validate_message(reply, 'is_complete_reply', msg_id)
385 404
386 405 # IOPub channel
387 406
388 407
389 408 def test_stream():
390 409 flush_channels()
391 410
392 411 msg_id, reply = execute("print('hi')")
393 412
394 413 stdout = KC.iopub_channel.get_msg(timeout=TIMEOUT)
395 414 validate_message(stdout, 'stream', msg_id)
396 415 content = stdout['content']
397 416 nt.assert_equal(content['text'], u'hi\n')
398 417
399 418
400 419 def test_display_data():
401 420 flush_channels()
402 421
403 422 msg_id, reply = execute("from IPython.core.display import display; display(1)")
404 423
405 424 display = KC.iopub_channel.get_msg(timeout=TIMEOUT)
406 425 validate_message(display, 'display_data', parent=msg_id)
407 426 data = display['content']['data']
408 427 nt.assert_equal(data['text/plain'], u'1')
409 428
@@ -1,305 +1,312 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 ..comm import CommManager
14 14 from .kernelbase import Kernel as KernelBase
15 15 from .serialize import serialize_object, unpack_apply_message
16 16 from .zmqshell import ZMQInteractiveShell
17 17
18 18 class IPythonKernel(KernelBase):
19 19 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
20 20 shell_class = Type(ZMQInteractiveShell)
21 21
22 22 user_module = Any()
23 23 def _user_module_changed(self, name, old, new):
24 24 if self.shell is not None:
25 25 self.shell.user_module = new
26 26
27 27 user_ns = Instance(dict, args=None, allow_none=True)
28 28 def _user_ns_changed(self, name, old, new):
29 29 if self.shell is not None:
30 30 self.shell.user_ns = new
31 31 self.shell.init_user_ns()
32 32
33 33 # A reference to the Python builtin 'raw_input' function.
34 34 # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3)
35 35 _sys_raw_input = Any()
36 36 _sys_eval_input = Any()
37 37
38 38 def __init__(self, **kwargs):
39 39 super(IPythonKernel, self).__init__(**kwargs)
40 40
41 41 # Initialize the InteractiveShell subclass
42 42 self.shell = self.shell_class.instance(parent=self,
43 43 profile_dir = self.profile_dir,
44 44 user_module = self.user_module,
45 45 user_ns = self.user_ns,
46 46 kernel = self,
47 47 )
48 48 self.shell.displayhook.session = self.session
49 49 self.shell.displayhook.pub_socket = self.iopub_socket
50 50 self.shell.displayhook.topic = self._topic('execute_result')
51 51 self.shell.display_pub.session = self.session
52 52 self.shell.display_pub.pub_socket = self.iopub_socket
53 53 self.shell.data_pub.session = self.session
54 54 self.shell.data_pub.pub_socket = self.iopub_socket
55 55
56 56 # TMP - hack while developing
57 57 self.shell._reply_content = None
58 58
59 59 self.comm_manager = CommManager(shell=self.shell, parent=self,
60 60 kernel=self)
61 61 self.shell.configurables.append(self.comm_manager)
62 62 comm_msg_types = [ 'comm_open', 'comm_msg', 'comm_close' ]
63 63 for msg_type in comm_msg_types:
64 64 self.shell_handlers[msg_type] = getattr(self.comm_manager, msg_type)
65 65
66 66 # Kernel info fields
67 67 implementation = 'ipython'
68 68 implementation_version = release.version
69 69 language = 'python'
70 70 language_version = sys.version.split()[0]
71 71 @property
72 72 def banner(self):
73 73 return self.shell.banner
74 74
75 75 def start(self):
76 76 self.shell.exit_now = False
77 77 super(IPythonKernel, self).start()
78 78
79 79 def set_parent(self, ident, parent):
80 80 """Overridden from parent to tell the display hook and output streams
81 81 about the parent message.
82 82 """
83 83 super(IPythonKernel, self).set_parent(ident, parent)
84 84 self.shell.set_parent(parent)
85 85
86 86 def _forward_input(self, allow_stdin=False):
87 87 """Forward raw_input and getpass to the current frontend.
88 88
89 89 via input_request
90 90 """
91 91 self._allow_stdin = allow_stdin
92 92
93 93 if PY3:
94 94 self._sys_raw_input = builtin_mod.input
95 95 builtin_mod.input = self.raw_input
96 96 else:
97 97 self._sys_raw_input = builtin_mod.raw_input
98 98 self._sys_eval_input = builtin_mod.input
99 99 builtin_mod.raw_input = self.raw_input
100 100 builtin_mod.input = lambda prompt='': eval(self.raw_input(prompt))
101 101 self._save_getpass = getpass.getpass
102 102 getpass.getpass = self.getpass
103 103
104 104 def _restore_input(self):
105 105 """Restore raw_input, getpass"""
106 106 if PY3:
107 107 builtin_mod.input = self._sys_raw_input
108 108 else:
109 109 builtin_mod.raw_input = self._sys_raw_input
110 110 builtin_mod.input = self._sys_eval_input
111 111
112 112 getpass.getpass = self._save_getpass
113 113
114 114 @property
115 115 def execution_count(self):
116 116 return self.shell.execution_count
117 117
118 118 @execution_count.setter
119 119 def execution_count(self, value):
120 120 # Ignore the incrememnting done by KernelBase, in favour of our shell's
121 121 # execution counter.
122 122 pass
123 123
124 124 def do_execute(self, code, silent, store_history=True,
125 125 user_expressions=None, allow_stdin=False):
126 126 shell = self.shell # we'll need this a lot here
127 127
128 128 self._forward_input(allow_stdin)
129 129
130 130 reply_content = {}
131 131 # FIXME: the shell calls the exception handler itself.
132 132 shell._reply_content = None
133 133 try:
134 134 shell.run_cell(code, store_history=store_history, silent=silent)
135 135 except:
136 136 status = u'error'
137 137 # FIXME: this code right now isn't being used yet by default,
138 138 # because the run_cell() call above directly fires off exception
139 139 # reporting. This code, therefore, is only active in the scenario
140 140 # where runlines itself has an unhandled exception. We need to
141 141 # uniformize this, for all exception construction to come from a
142 142 # single location in the codbase.
143 143 etype, evalue, tb = sys.exc_info()
144 144 tb_list = traceback.format_exception(etype, evalue, tb)
145 145 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
146 146 else:
147 147 status = u'ok'
148 148 finally:
149 149 self._restore_input()
150 150
151 151 reply_content[u'status'] = status
152 152
153 153 # Return the execution counter so clients can display prompts
154 154 reply_content['execution_count'] = shell.execution_count - 1
155 155
156 156 # FIXME - fish exception info out of shell, possibly left there by
157 157 # runlines. We'll need to clean up this logic later.
158 158 if shell._reply_content is not None:
159 159 reply_content.update(shell._reply_content)
160 160 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
161 161 reply_content['engine_info'] = e_info
162 162 # reset after use
163 163 shell._reply_content = None
164 164
165 165 if 'traceback' in reply_content:
166 166 self.log.info("Exception in execute request:\n%s", '\n'.join(reply_content['traceback']))
167 167
168 168
169 169 # At this point, we can tell whether the main code execution succeeded
170 170 # or not. If it did, we proceed to evaluate user_expressions
171 171 if reply_content['status'] == 'ok':
172 172 reply_content[u'user_expressions'] = \
173 173 shell.user_expressions(user_expressions or {})
174 174 else:
175 175 # If there was an error, don't even try to compute expressions
176 176 reply_content[u'user_expressions'] = {}
177 177
178 178 # Payloads should be retrieved regardless of outcome, so we can both
179 179 # recover partial output (that could have been generated early in a
180 180 # block, before an error) and clear the payload system always.
181 181 reply_content[u'payload'] = shell.payload_manager.read_payload()
182 182 # Be agressive about clearing the payload because we don't want
183 183 # it to sit in memory until the next execute_request comes in.
184 184 shell.payload_manager.clear_payload()
185 185
186 186 return reply_content
187 187
188 188 def do_complete(self, code, cursor_pos):
189 189 txt, matches = self.shell.complete('', code, cursor_pos)
190 190 return {'matches' : matches,
191 191 'cursor_end' : cursor_pos,
192 192 'cursor_start' : cursor_pos - len(txt),
193 193 'metadata' : {},
194 194 'status' : 'ok'}
195 195
196 196 def do_inspect(self, code, cursor_pos, detail_level=0):
197 197 name = token_at_cursor(code, cursor_pos)
198 198 info = self.shell.object_inspect(name)
199 199
200 200 reply_content = {'status' : 'ok'}
201 201 reply_content['data'] = data = {}
202 202 reply_content['metadata'] = {}
203 203 reply_content['found'] = info['found']
204 204 if info['found']:
205 205 info_text = self.shell.object_inspect_text(
206 206 name,
207 207 detail_level=detail_level,
208 208 )
209 209 data['text/plain'] = info_text
210 210
211 211 return reply_content
212 212
213 213 def do_history(self, hist_access_type, output, raw, session=None, start=None,
214 214 stop=None, n=None, pattern=None, unique=False):
215 215 if hist_access_type == 'tail':
216 216 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
217 217 include_latest=True)
218 218
219 219 elif hist_access_type == 'range':
220 220 hist = self.shell.history_manager.get_range(session, start, stop,
221 221 raw=raw, output=output)
222 222
223 223 elif hist_access_type == 'search':
224 224 hist = self.shell.history_manager.search(
225 225 pattern, raw=raw, output=output, n=n, unique=unique)
226 226 else:
227 227 hist = []
228 228
229 229 return {'history' : list(hist)}
230 230
231 231 def do_shutdown(self, restart):
232 232 self.shell.exit_now = True
233 233 return dict(status='ok', restart=restart)
234 234
235 def do_is_complete(self, code):
236 status, indent_spaces = self.shell.input_transformer_manager.check_complete(code)
237 r = {'status': status}
238 if status == 'incomplete':
239 r['indent'] = ' ' * indent_spaces
240 return r
241
235 242 def do_apply(self, content, bufs, msg_id, reply_metadata):
236 243 shell = self.shell
237 244 try:
238 245 working = shell.user_ns
239 246
240 247 prefix = "_"+str(msg_id).replace("-","")+"_"
241 248
242 249 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
243 250
244 251 fname = getattr(f, '__name__', 'f')
245 252
246 253 fname = prefix+"f"
247 254 argname = prefix+"args"
248 255 kwargname = prefix+"kwargs"
249 256 resultname = prefix+"result"
250 257
251 258 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
252 259 # print ns
253 260 working.update(ns)
254 261 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
255 262 try:
256 263 exec(code, shell.user_global_ns, shell.user_ns)
257 264 result = working.get(resultname)
258 265 finally:
259 266 for key in ns:
260 267 working.pop(key)
261 268
262 269 result_buf = serialize_object(result,
263 270 buffer_threshold=self.session.buffer_threshold,
264 271 item_threshold=self.session.item_threshold,
265 272 )
266 273
267 274 except:
268 275 # invoke IPython traceback formatting
269 276 shell.showtraceback()
270 277 # FIXME - fish exception info out of shell, possibly left there by
271 278 # run_code. We'll need to clean up this logic later.
272 279 reply_content = {}
273 280 if shell._reply_content is not None:
274 281 reply_content.update(shell._reply_content)
275 282 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
276 283 reply_content['engine_info'] = e_info
277 284 # reset after use
278 285 shell._reply_content = None
279 286
280 287 self.send_response(self.iopub_socket, u'error', reply_content,
281 288 ident=self._topic('error'))
282 289 self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback']))
283 290 result_buf = []
284 291
285 292 if reply_content['ename'] == 'UnmetDependency':
286 293 reply_metadata['dependencies_met'] = False
287 294 else:
288 295 reply_content = {'status' : 'ok'}
289 296
290 297 return reply_content, result_buf
291 298
292 299 def do_clear(self):
293 300 self.shell.reset(False)
294 301 return dict(status='ok')
295 302
296 303
297 304 # This exists only for backwards compatibility - use IPythonKernel instead
298 305
299 306 @undoc
300 307 class Kernel(IPythonKernel):
301 308 def __init__(self, *args, **kwargs):
302 309 import warnings
303 310 warnings.warn('Kernel is a deprecated alias of IPython.kernel.zmq.ipkernel.IPythonKernel',
304 311 DeprecationWarning)
305 312 super(Kernel, self).__init__(*args, **kwargs) No newline at end of file
@@ -1,676 +1,692 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 SingletonConfigurable
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(SingletonConfigurable):
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 'apply_request',
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
483 def is_complete_request(self, stream, ident, parent):
484 content = parent['content']
485 code = content['code']
486
487 reply_content = self.do_is_complete(code)
488 reply_content = json_clean(reply_content)
489 reply_msg = self.session.send(stream, 'is_complete_reply',
490 reply_content, parent, ident)
491 self.log.debug("%s", reply_msg)
492
493 def do_is_complete(self, code):
494 """Override in subclasses to find completions.
495 """
496 return {'status' : 'unknown',
497 }
482 498
483 499 #---------------------------------------------------------------------------
484 500 # Engine methods
485 501 #---------------------------------------------------------------------------
486 502
487 503 def apply_request(self, stream, ident, parent):
488 504 try:
489 505 content = parent[u'content']
490 506 bufs = parent[u'buffers']
491 507 msg_id = parent['header']['msg_id']
492 508 except:
493 509 self.log.error("Got bad msg: %s", parent, exc_info=True)
494 510 return
495 511
496 512 md = self._make_metadata(parent['metadata'])
497 513
498 514 reply_content, result_buf = self.do_apply(content, bufs, msg_id, md)
499 515
500 516 # put 'ok'/'error' status in header, for scheduler introspection:
501 517 md['status'] = reply_content['status']
502 518
503 519 # flush i/o
504 520 sys.stdout.flush()
505 521 sys.stderr.flush()
506 522
507 523 self.session.send(stream, u'apply_reply', reply_content,
508 524 parent=parent, ident=ident,buffers=result_buf, metadata=md)
509 525
510 526 def do_apply(self, content, bufs, msg_id, reply_metadata):
511 527 """Override in subclasses to support the IPython parallel framework.
512 528 """
513 529 raise NotImplementedError
514 530
515 531 #---------------------------------------------------------------------------
516 532 # Control messages
517 533 #---------------------------------------------------------------------------
518 534
519 535 def abort_request(self, stream, ident, parent):
520 536 """abort a specific msg by id"""
521 537 msg_ids = parent['content'].get('msg_ids', None)
522 538 if isinstance(msg_ids, string_types):
523 539 msg_ids = [msg_ids]
524 540 if not msg_ids:
525 541 self._abort_queues()
526 542 for mid in msg_ids:
527 543 self.aborted.add(str(mid))
528 544
529 545 content = dict(status='ok')
530 546 reply_msg = self.session.send(stream, 'abort_reply', content=content,
531 547 parent=parent, ident=ident)
532 548 self.log.debug("%s", reply_msg)
533 549
534 550 def clear_request(self, stream, idents, parent):
535 551 """Clear our namespace."""
536 552 content = self.do_clear()
537 553 self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
538 554 content = content)
539 555
540 556 def do_clear(self):
541 557 """Override in subclasses to clear the namespace
542 558
543 559 This is only required for IPython.parallel.
544 560 """
545 561 raise NotImplementedError
546 562
547 563 #---------------------------------------------------------------------------
548 564 # Protected interface
549 565 #---------------------------------------------------------------------------
550 566
551 567 def _topic(self, topic):
552 568 """prefixed topic for IOPub messages"""
553 569 if self.int_id >= 0:
554 570 base = "engine.%i" % self.int_id
555 571 else:
556 572 base = "kernel.%s" % self.ident
557 573
558 574 return py3compat.cast_bytes("%s.%s" % (base, topic))
559 575
560 576 def _abort_queues(self):
561 577 for stream in self.shell_streams:
562 578 if stream:
563 579 self._abort_queue(stream)
564 580
565 581 def _abort_queue(self, stream):
566 582 poller = zmq.Poller()
567 583 poller.register(stream.socket, zmq.POLLIN)
568 584 while True:
569 585 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
570 586 if msg is None:
571 587 return
572 588
573 589 self.log.info("Aborting:")
574 590 self.log.info("%s", msg)
575 591 msg_type = msg['header']['msg_type']
576 592 reply_type = msg_type.split('_')[0] + '_reply'
577 593
578 594 status = {'status' : 'aborted'}
579 595 md = {'engine' : self.ident}
580 596 md.update(status)
581 597 reply_msg = self.session.send(stream, reply_type, metadata=md,
582 598 content=status, parent=msg, ident=idents)
583 599 self.log.debug("%s", reply_msg)
584 600 # We need to wait a bit for requests to come in. This can probably
585 601 # be set shorter for true asynchronous clients.
586 602 poller.poll(50)
587 603
588 604
589 605 def _no_raw_input(self):
590 606 """Raise StdinNotImplentedError if active frontend doesn't support
591 607 stdin."""
592 608 raise StdinNotImplementedError("raw_input was called, but this "
593 609 "frontend does not support stdin.")
594 610
595 611 def getpass(self, prompt=''):
596 612 """Forward getpass to frontends
597 613
598 614 Raises
599 615 ------
600 616 StdinNotImplentedError if active frontend doesn't support stdin.
601 617 """
602 618 if not self._allow_stdin:
603 619 raise StdinNotImplementedError(
604 620 "getpass was called, but this frontend does not support input requests."
605 621 )
606 622 return self._input_request(prompt,
607 623 self._parent_ident,
608 624 self._parent_header,
609 625 password=True,
610 626 )
611 627
612 628 def raw_input(self, prompt=''):
613 629 """Forward raw_input to frontends
614 630
615 631 Raises
616 632 ------
617 633 StdinNotImplentedError if active frontend doesn't support stdin.
618 634 """
619 635 if not self._allow_stdin:
620 636 raise StdinNotImplementedError(
621 637 "raw_input was called, but this frontend does not support input requests."
622 638 )
623 639 return self._input_request(prompt,
624 640 self._parent_ident,
625 641 self._parent_header,
626 642 password=False,
627 643 )
628 644
629 645 def _input_request(self, prompt, ident, parent, password=False):
630 646 # Flush output before making the request.
631 647 sys.stderr.flush()
632 648 sys.stdout.flush()
633 649 # flush the stdin socket, to purge stale replies
634 650 while True:
635 651 try:
636 652 self.stdin_socket.recv_multipart(zmq.NOBLOCK)
637 653 except zmq.ZMQError as e:
638 654 if e.errno == zmq.EAGAIN:
639 655 break
640 656 else:
641 657 raise
642 658
643 659 # Send the input request.
644 660 content = json_clean(dict(prompt=prompt, password=password))
645 661 self.session.send(self.stdin_socket, u'input_request', content, parent,
646 662 ident=ident)
647 663
648 664 # Await a response.
649 665 while True:
650 666 try:
651 667 ident, reply = self.session.recv(self.stdin_socket, 0)
652 668 except Exception:
653 669 self.log.warn("Invalid Message:", exc_info=True)
654 670 except KeyboardInterrupt:
655 671 # re-raise KeyboardInterrupt, to truncate traceback
656 672 raise KeyboardInterrupt
657 673 else:
658 674 break
659 675 try:
660 676 value = py3compat.unicode_to_str(reply['content']['value'])
661 677 except:
662 678 self.log.error("Bad input_reply: %s", parent)
663 679 value = ''
664 680 if value == '\x04':
665 681 # EOF
666 682 raise EOFError
667 683 return value
668 684
669 685 def _at_shutdown(self):
670 686 """Actions taken at shutdown by the kernel, called by python's atexit.
671 687 """
672 688 # io.rprint("Kernel at_shutdown") # dbg
673 689 if self._shutdown_message is not None:
674 690 self.session.send(self.iopub_socket, self._shutdown_message, ident=self._topic('shutdown'))
675 691 self.log.debug("%s", self._shutdown_message)
676 692 [ s.flush(zmq.POLLOUT) for s in self.shell_streams ]
@@ -1,1070 +1,1115 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.0.
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 .. _msging_is_complete:
577
578 Code completeness
579 -----------------
580
581 .. versionadded:: 5.0
582
583 When the user enters a line in a console style interface, the console must
584 decide whether to immediately execute the current code, or whether to show a
585 continuation prompt for further input. For instance, in Python ``a = 5`` would
586 be executed immediately, while ``for i in range(5):`` would expect further input.
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
599 Frontends may have ways to override this, forcing the code to be sent for
600 execution or forcing a continuation prompt.
601
602 Message type: ``is_complete_request``::
603
604 content = {
605 # The code entered so far as a multiline string
606 'code' : str,
607 }
608
609 Message type: ``is_complete_reply``::
610
611 content = {
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,
620 }
576 621
577 622 Connect
578 623 -------
579 624
580 625 When a client connects to the request/reply socket of the kernel, it can issue
581 626 a connect request to get basic information about the kernel, such as the ports
582 627 the other ZeroMQ sockets are listening on. This allows clients to only have
583 628 to know about a single port (the shell channel) to connect to a kernel.
584 629
585 630 Message type: ``connect_request``::
586 631
587 632 content = {
588 633 }
589 634
590 635 Message type: ``connect_reply``::
591 636
592 637 content = {
593 638 'shell_port' : int, # The port the shell ROUTER socket is listening on.
594 639 'iopub_port' : int, # The port the PUB socket is listening on.
595 640 'stdin_port' : int, # The port the stdin ROUTER socket is listening on.
596 641 'hb_port' : int, # The port the heartbeat socket is listening on.
597 642 }
598 643
599 644 .. _msging_kernel_info:
600 645
601 646 Kernel info
602 647 -----------
603 648
604 649 If a client needs to know information about the kernel, it can
605 650 make a request of the kernel's information.
606 651 This message can be used to fetch core information of the
607 652 kernel, including language (e.g., Python), language version number and
608 653 IPython version number, and the IPython message spec version number.
609 654
610 655 Message type: ``kernel_info_request``::
611 656
612 657 content = {
613 658 }
614 659
615 660 Message type: ``kernel_info_reply``::
616 661
617 662 content = {
618 663 # Version of messaging protocol.
619 664 # The first integer indicates major version. It is incremented when
620 665 # there is any backward incompatible change.
621 666 # The second integer indicates minor version. It is incremented when
622 667 # there is any backward compatible change.
623 668 'protocol_version': 'X.Y.Z',
624 669
625 670 # The kernel implementation name
626 671 # (e.g. 'ipython' for the IPython kernel)
627 672 'implementation': str,
628 673
629 674 # Implementation version number.
630 675 # The version number of the kernel's implementation
631 676 # (e.g. IPython.__version__ for the IPython kernel)
632 677 'implementation_version': 'X.Y.Z',
633 678
634 679 # Programming language in which kernel is implemented.
635 680 # Kernel included in IPython returns 'python'.
636 681 'language': str,
637 682
638 683 # Language version number.
639 684 # It is Python version number (e.g., '2.7.3') for the kernel
640 685 # included in IPython.
641 686 'language_version': 'X.Y.Z',
642 687
643 688 # A banner of information about the kernel,
644 689 # which may be desplayed in console environments.
645 690 'banner' : str,
646 691 }
647 692
648 693 .. versionchanged:: 5.0
649 694
650 695 Versions changed from lists of integers to strings.
651 696
652 697 .. versionchanged:: 5.0
653 698
654 699 ``ipython_version`` is removed.
655 700
656 701 .. versionchanged:: 5.0
657 702
658 703 ``implementation``, ``implementation_version``, and ``banner`` keys are added.
659 704
660 705 .. _msging_shutdown:
661 706
662 707 Kernel shutdown
663 708 ---------------
664 709
665 710 The clients can request the kernel to shut itself down; this is used in
666 711 multiple cases:
667 712
668 713 - when the user chooses to close the client application via a menu or window
669 714 control.
670 715 - when the user types 'exit' or 'quit' (or their uppercase magic equivalents).
671 716 - when the user chooses a GUI method (like the 'Ctrl-C' shortcut in the
672 717 IPythonQt client) to force a kernel restart to get a clean kernel without
673 718 losing client-side state like history or inlined figures.
674 719
675 720 The client sends a shutdown request to the kernel, and once it receives the
676 721 reply message (which is otherwise empty), it can assume that the kernel has
677 722 completed shutdown safely.
678 723
679 724 Upon their own shutdown, client applications will typically execute a last
680 725 minute sanity check and forcefully terminate any kernel that is still alive, to
681 726 avoid leaving stray processes in the user's machine.
682 727
683 728 Message type: ``shutdown_request``::
684 729
685 730 content = {
686 731 'restart' : bool # whether the shutdown is final, or precedes a restart
687 732 }
688 733
689 734 Message type: ``shutdown_reply``::
690 735
691 736 content = {
692 737 'restart' : bool # whether the shutdown is final, or precedes a restart
693 738 }
694 739
695 740 .. Note::
696 741
697 742 When the clients detect a dead kernel thanks to inactivity on the heartbeat
698 743 socket, they simply send a forceful process termination signal, since a dead
699 744 process is unlikely to respond in any useful way to messages.
700 745
701 746
702 747 Messages on the PUB/SUB socket
703 748 ==============================
704 749
705 750 Streams (stdout, stderr, etc)
706 751 ------------------------------
707 752
708 753 Message type: ``stream``::
709 754
710 755 content = {
711 756 # The name of the stream is one of 'stdout', 'stderr'
712 757 'name' : str,
713 758
714 759 # The text is an arbitrary string to be written to that stream
715 760 'text' : str,
716 761 }
717 762
718 763 .. versionchanged:: 5.0
719 764
720 765 'data' key renamed to 'text' for conistency with the notebook format.
721 766
722 767 Display Data
723 768 ------------
724 769
725 770 This type of message is used to bring back data that should be displayed (text,
726 771 html, svg, etc.) in the frontends. This data is published to all frontends.
727 772 Each message can have multiple representations of the data; it is up to the
728 773 frontend to decide which to use and how. A single message should contain all
729 774 possible representations of the same information. Each representation should
730 775 be a JSON'able data structure, and should be a valid MIME type.
731 776
732 777 Some questions remain about this design:
733 778
734 779 * Do we use this message type for execute_result/displayhook? Probably not, because
735 780 the displayhook also has to handle the Out prompt display. On the other hand
736 781 we could put that information into the metadata section.
737 782
738 783 .. _display_data:
739 784
740 785 Message type: ``display_data``::
741 786
742 787 content = {
743 788
744 789 # Who create the data
745 790 'source' : str,
746 791
747 792 # The data dict contains key/value pairs, where the keys are MIME
748 793 # types and the values are the raw data of the representation in that
749 794 # format.
750 795 'data' : dict,
751 796
752 797 # Any metadata that describes the data
753 798 'metadata' : dict
754 799 }
755 800
756 801
757 802 The ``metadata`` contains any metadata that describes the output.
758 803 Global keys are assumed to apply to the output as a whole.
759 804 The ``metadata`` dict can also contain mime-type keys, which will be sub-dictionaries,
760 805 which are interpreted as applying only to output of that type.
761 806 Third parties should put any data they write into a single dict
762 807 with a reasonably unique name to avoid conflicts.
763 808
764 809 The only metadata keys currently defined in IPython are the width and height
765 810 of images::
766 811
767 812 metadata = {
768 813 'image/png' : {
769 814 'width': 640,
770 815 'height': 480
771 816 }
772 817 }
773 818
774 819
775 820 .. versionchanged:: 5.0
776 821
777 822 `application/json` data should be unpacked JSON data,
778 823 not double-serialized as a JSON string.
779 824
780 825
781 826 Raw Data Publication
782 827 --------------------
783 828
784 829 ``display_data`` lets you publish *representations* of data, such as images and html.
785 830 This ``data_pub`` message lets you publish *actual raw data*, sent via message buffers.
786 831
787 832 data_pub messages are constructed via the :func:`IPython.lib.datapub.publish_data` function:
788 833
789 834 .. sourcecode:: python
790 835
791 836 from IPython.kernel.zmq.datapub import publish_data
792 837 ns = dict(x=my_array)
793 838 publish_data(ns)
794 839
795 840
796 841 Message type: ``data_pub``::
797 842
798 843 content = {
799 844 # the keys of the data dict, after it has been unserialized
800 845 'keys' : ['a', 'b']
801 846 }
802 847 # the namespace dict will be serialized in the message buffers,
803 848 # which will have a length of at least one
804 849 buffers = [b'pdict', ...]
805 850
806 851
807 852 The interpretation of a sequence of data_pub messages for a given parent request should be
808 853 to update a single namespace with subsequent results.
809 854
810 855 .. note::
811 856
812 857 No frontends directly handle data_pub messages at this time.
813 858 It is currently only used by the client/engines in :mod:`IPython.parallel`,
814 859 where engines may publish *data* to the Client,
815 860 of which the Client can then publish *representations* via ``display_data``
816 861 to various frontends.
817 862
818 863 Code inputs
819 864 -----------
820 865
821 866 To let all frontends know what code is being executed at any given time, these
822 867 messages contain a re-broadcast of the ``code`` portion of an
823 868 :ref:`execute_request <execute>`, along with the :ref:`execution_count
824 869 <execution_counter>`.
825 870
826 871 Message type: ``execute_input``::
827 872
828 873 content = {
829 874 'code' : str, # Source code to be executed, one or more lines
830 875
831 876 # The counter for this execution is also provided so that clients can
832 877 # display it, since IPython automatically creates variables called _iN
833 878 # (for input prompt In[N]).
834 879 'execution_count' : int
835 880 }
836 881
837 882 .. versionchanged:: 5.0
838 883
839 884 ``pyin`` is renamed to ``execute_input``.
840 885
841 886
842 887 Execution results
843 888 -----------------
844 889
845 890 Results of an execution are published as an ``execute_result``.
846 891 These are identical to `display_data`_ messages, with the addition of an ``execution_count`` key.
847 892
848 893 Results can have multiple simultaneous formats depending on its
849 894 configuration. A plain text representation should always be provided
850 895 in the ``text/plain`` mime-type. Frontends are free to display any or all of these
851 896 according to its capabilities.
852 897 Frontends should ignore mime-types they do not understand. The data itself is
853 898 any JSON object and depends on the format. It is often, but not always a string.
854 899
855 900 Message type: ``execute_result``::
856 901
857 902 content = {
858 903
859 904 # The counter for this execution is also provided so that clients can
860 905 # display it, since IPython automatically creates variables called _N
861 906 # (for prompt N).
862 907 'execution_count' : int,
863 908
864 909 # data and metadata are identical to a display_data message.
865 910 # the object being displayed is that passed to the display hook,
866 911 # i.e. the *result* of the execution.
867 912 'data' : dict,
868 913 'metadata' : dict,
869 914 }
870 915
871 916 Execution errors
872 917 ----------------
873 918
874 919 When an error occurs during code execution
875 920
876 921 Message type: ``error``::
877 922
878 923 content = {
879 924 # Similar content to the execute_reply messages for the 'error' case,
880 925 # except the 'status' field is omitted.
881 926 }
882 927
883 928 .. versionchanged:: 5.0
884 929
885 930 ``pyerr`` renamed to ``error``
886 931
887 932 Kernel status
888 933 -------------
889 934
890 935 This message type is used by frontends to monitor the status of the kernel.
891 936
892 937 Message type: ``status``::
893 938
894 939 content = {
895 940 # When the kernel starts to handle a message, it will enter the 'busy'
896 941 # state and when it finishes, it will enter the 'idle' state.
897 942 # The kernel will publish state 'starting' exactly once at process startup.
898 943 execution_state : ('busy', 'idle', 'starting')
899 944 }
900 945
901 946 .. versionchanged:: 5.0
902 947
903 948 Busy and idle messages should be sent before/after handling every message,
904 949 not just execution.
905 950
906 951 Clear output
907 952 ------------
908 953
909 954 This message type is used to clear the output that is visible on the frontend.
910 955
911 956 Message type: ``clear_output``::
912 957
913 958 content = {
914 959
915 960 # Wait to clear the output until new output is available. Clears the
916 961 # existing output immediately before the new output is displayed.
917 962 # Useful for creating simple animations with minimal flickering.
918 963 'wait' : bool,
919 964 }
920 965
921 966 .. versionchanged:: 4.1
922 967
923 968 ``stdout``, ``stderr``, and ``display`` boolean keys for selective clearing are removed,
924 969 and ``wait`` is added.
925 970 The selective clearing keys are ignored in v4 and the default behavior remains the same,
926 971 so v4 clear_output messages will be safely handled by a v4.1 frontend.
927 972
928 973
929 974 Messages on the stdin ROUTER/DEALER sockets
930 975 ===========================================
931 976
932 977 This is a socket where the request/reply pattern goes in the opposite direction:
933 978 from the kernel to a *single* frontend, and its purpose is to allow
934 979 ``raw_input`` and similar operations that read from ``sys.stdin`` on the kernel
935 980 to be fulfilled by the client. The request should be made to the frontend that
936 981 made the execution request that prompted ``raw_input`` to be called. For now we
937 982 will keep these messages as simple as possible, since they only mean to convey
938 983 the ``raw_input(prompt)`` call.
939 984
940 985 Message type: ``input_request``::
941 986
942 987 content = {
943 988 # the text to show at the prompt
944 989 'prompt' : str,
945 990 # Is the request for a password?
946 991 # If so, the frontend shouldn't echo input.
947 992 'password' : bool
948 993 }
949 994
950 995 Message type: ``input_reply``::
951 996
952 997 content = { 'value' : str }
953 998
954 999
955 1000 When ``password`` is True, the frontend should not echo the input as it is entered.
956 1001
957 1002 .. versionchanged:: 5.0
958 1003
959 1004 ``password`` key added.
960 1005
961 1006 .. note::
962 1007
963 1008 The stdin socket of the client is required to have the same zmq IDENTITY
964 1009 as the client's shell socket.
965 1010 Because of this, the ``input_request`` must be sent with the same IDENTITY
966 1011 routing prefix as the ``execute_reply`` in order for the frontend to receive
967 1012 the message.
968 1013
969 1014 .. note::
970 1015
971 1016 We do not explicitly try to forward the raw ``sys.stdin`` object, because in
972 1017 practice the kernel should behave like an interactive program. When a
973 1018 program is opened on the console, the keyboard effectively takes over the
974 1019 ``stdin`` file descriptor, and it can't be used for raw reading anymore.
975 1020 Since the IPython kernel effectively behaves like a console program (albeit
976 1021 one whose "keyboard" is actually living in a separate process and
977 1022 transported over the zmq connection), raw ``stdin`` isn't expected to be
978 1023 available.
979 1024
980 1025 .. _kernel_heartbeat:
981 1026
982 1027 Heartbeat for kernels
983 1028 =====================
984 1029
985 1030 Clients send ping messages on a REQ socket, which are echoed right back
986 1031 from the Kernel's REP socket. These are simple bytestrings, not full JSON messages described above.
987 1032
988 1033
989 1034 Custom Messages
990 1035 ===============
991 1036
992 1037 .. versionadded:: 4.1
993 1038
994 1039 IPython 2.0 (msgspec v4.1) adds a messaging system for developers to add their own objects with Frontend
995 1040 and Kernel-side components, and allow them to communicate with each other.
996 1041 To do this, IPython adds a notion of a ``Comm``, which exists on both sides,
997 1042 and can communicate in either direction.
998 1043
999 1044 These messages are fully symmetrical - both the Kernel and the Frontend can send each message,
1000 1045 and no messages expect a reply.
1001 1046 The Kernel listens for these messages on the Shell channel,
1002 1047 and the Frontend listens for them on the IOPub channel.
1003 1048
1004 1049 Opening a Comm
1005 1050 --------------
1006 1051
1007 1052 Opening a Comm produces a ``comm_open`` message, to be sent to the other side::
1008 1053
1009 1054 {
1010 1055 'comm_id' : 'u-u-i-d',
1011 1056 'target_name' : 'my_comm',
1012 1057 'data' : {}
1013 1058 }
1014 1059
1015 1060 Every Comm has an ID and a target name.
1016 1061 The code handling the message on the receiving side is responsible for maintaining a mapping
1017 1062 of target_name keys to constructors.
1018 1063 After a ``comm_open`` message has been sent,
1019 1064 there should be a corresponding Comm instance on both sides.
1020 1065 The ``data`` key is always a dict and can be any extra JSON information used in initialization of the comm.
1021 1066
1022 1067 If the ``target_name`` key is not found on the receiving side,
1023 1068 then it should immediately reply with a ``comm_close`` message to avoid an inconsistent state.
1024 1069
1025 1070 Comm Messages
1026 1071 -------------
1027 1072
1028 1073 Comm messages are one-way communications to update comm state,
1029 1074 used for synchronizing widget state, or simply requesting actions of a comm's counterpart.
1030 1075
1031 1076 Essentially, each comm pair defines their own message specification implemented inside the ``data`` dict.
1032 1077
1033 1078 There are no expected replies (of course, one side can send another ``comm_msg`` in reply).
1034 1079
1035 1080 Message type: ``comm_msg``::
1036 1081
1037 1082 {
1038 1083 'comm_id' : 'u-u-i-d',
1039 1084 'data' : {}
1040 1085 }
1041 1086
1042 1087 Tearing Down Comms
1043 1088 ------------------
1044 1089
1045 1090 Since comms live on both sides, when a comm is destroyed the other side must be notified.
1046 1091 This is done with a ``comm_close`` message.
1047 1092
1048 1093 Message type: ``comm_close``::
1049 1094
1050 1095 {
1051 1096 'comm_id' : 'u-u-i-d',
1052 1097 'data' : {}
1053 1098 }
1054 1099
1055 1100 Output Side Effects
1056 1101 -------------------
1057 1102
1058 1103 Since comm messages can execute arbitrary user code,
1059 1104 handlers should set the parent header and publish status busy / idle,
1060 1105 just like an execute request.
1061 1106
1062 1107
1063 1108 To Do
1064 1109 =====
1065 1110
1066 1111 Missing things include:
1067 1112
1068 1113 * Important: finish thinking through the payload concept and API.
1069 1114
1070 1115 .. include:: ../links.txt
@@ -1,153 +1,164 b''
1 1 Making simple Python wrapper kernels
2 2 ====================================
3 3
4 4 .. versionadded:: 3.0
5 5
6 6 You can now re-use the kernel machinery in IPython to easily make new kernels.
7 7 This is useful for languages that have Python bindings, such as `Octave
8 8 <http://www.gnu.org/software/octave/>`_ (via
9 9 `Oct2Py <http://blink1073.github.io/oct2py/docs/index.html>`_), or languages
10 10 where the REPL can be controlled in a tty using `pexpect <http://pexpect.readthedocs.org/en/latest/>`_,
11 11 such as bash.
12 12
13 13 .. seealso::
14 14
15 15 `bash_kernel <https://github.com/takluyver/bash_kernel>`_
16 16 A simple kernel for bash, written using this machinery
17 17
18 18 Required steps
19 19 --------------
20 20
21 21 Subclass :class:`IPython.kernel.zmq.kernelbase.Kernel`, and implement the
22 22 following methods and attributes:
23 23
24 24 .. class:: MyKernel
25 25
26 26 .. attribute:: implementation
27 27 implementation_version
28 28 language
29 29 language_version
30 30 banner
31 31
32 32 Information for :ref:`msging_kernel_info` replies. 'Implementation' refers
33 33 to the kernel (e.g. IPython), and 'language' refers to the language it
34 34 interprets (e.g. Python). The 'banner' is displayed to the user in console
35 35 UIs before the first prompt. All of these values are strings.
36 36
37 37 .. method:: do_execute(code, silent, store_history=True, user_expressions=None, allow_stdin=False)
38 38
39 39 Execute user code.
40 40
41 41 :param str code: The code to be executed.
42 42 :param bool silent: Whether to display output.
43 43 :param bool store_history: Whether to record this code in history and
44 44 increase the execution count. If silent is True, this is implicitly
45 45 False.
46 46 :param dict user_expressions: Mapping of names to expressions to evaluate
47 47 after the code has run. You can ignore this if you need to.
48 48 :param bool allow_stdin: Whether the frontend can provide input on request
49 49 (e.g. for Python's :func:`raw_input`).
50 50
51 51 Your method should return a dict containing the fields described in
52 52 :ref:`execution_results`. To display output, it can send messages
53 53 using :meth:`~IPython.kernel.zmq.kernelbase.Kernel.send_response`.
54 54 See :doc:`messaging` for details of the different message types.
55 55
56 56 To launch your kernel, add this at the end of your module::
57 57
58 58 if __name__ == '__main__':
59 59 from IPython.kernel.zmq.kernelapp import IPKernelApp
60 60 IPKernelApp.launch_instance(kernel_class=MyKernel)
61 61
62 62 Example
63 63 -------
64 64
65 65 ``echokernel.py`` will simply echo any input it's given to stdout::
66 66
67 67 from IPython.kernel.zmq.kernelbase import Kernel
68 68
69 69 class EchoKernel(Kernel):
70 70 implementation = 'Echo'
71 71 implementation_version = '1.0'
72 72 language = 'no-op'
73 73 language_version = '0.1'
74 74 banner = "Echo kernel - as useful as a parrot"
75 75
76 76 def do_execute(self, code, silent, store_history=True, user_expressions=None,
77 77 allow_stdin=False):
78 78 if not silent:
79 79 stream_content = {'name': 'stdout', 'data':code}
80 80 self.send_response(self.iopub_socket, 'stream', stream_content)
81 81
82 82 return {'status': 'ok',
83 83 # The base class increments the execution count
84 84 'execution_count': self.execution_count,
85 85 'payload': [],
86 86 'user_expressions': {},
87 87 }
88 88
89 89 if __name__ == '__main__':
90 90 from IPython.kernel.zmq.kernelapp import IPKernelApp
91 91 IPKernelApp.launch_instance(kernel_class=EchoKernel)
92 92
93 93 Here's the Kernel spec ``kernel.json`` file for this::
94 94
95 95 {"argv":["python","-m","echokernel", "-f", "{connection_file}"],
96 96 "display_name":"Echo",
97 97 "language":"no-op"
98 98 }
99 99
100 100
101 101 Optional steps
102 102 --------------
103 103
104 104 You can override a number of other methods to improve the functionality of your
105 105 kernel. All of these methods should return a dictionary as described in the
106 106 relevant section of the :doc:`messaging spec <messaging>`.
107 107
108 108 .. class:: MyKernel
109 109
110 110 .. method:: do_complete(code, cusor_pos)
111 111
112 112 Code completion
113 113
114 114 :param str code: The code already present
115 115 :param int cursor_pos: The position in the code where completion is requested
116 116
117 117 .. seealso::
118 118
119 119 :ref:`msging_completion` messages
120 120
121 121 .. method:: do_inspect(code, cusor_pos, detail_level=0)
122 122
123 123 Object introspection
124 124
125 125 :param str code: The code
126 126 :param int cursor_pos: The position in the code where introspection is requested
127 127 :param int detail_level: 0 or 1 for more or less detail. In IPython, 1 gets
128 128 the source code.
129 129
130 130 .. seealso::
131 131
132 132 :ref:`msging_inspection` messages
133 133
134 134 .. method:: do_history(hist_access_type, output, raw, session=None, start=None, stop=None, n=None, pattern=None, unique=False)
135 135
136 136 History access. Only the relevant parameters for the type of history
137 137 request concerned will be passed, so your method definition must have defaults
138 138 for all the arguments shown with defaults here.
139 139
140 140 .. seealso::
141 141
142 142 :ref:`msging_history` messages
143 143
144 .. method:: do_is_complete(code)
145
146 Is code entered in a console-like interface complete and ready to execute,
147 or should a continuation prompt be shown?
148
149 :param str code: The code entered so far - possibly multiple lines
150
151 .. seealso::
152
153 :ref:`msging_is_complete` messages
154
144 155 .. method:: do_shutdown(restart)
145 156
146 157 Shutdown the kernel. You only need to handle your own clean up - the kernel
147 158 machinery will take care of cleaning up its own things before stopping.
148 159
149 160 :param bool restart: Whether the kernel will be started again afterwards
150 161
151 162 .. seealso::
152 163
153 164 :ref:`msging_shutdown` messages
General Comments 0
You need to be logged in to leave comments. Login now