##// END OF EJS Templates
Merge pull request #7377 from juhasch/skip-exceptions2...
Min RK -
r19989:e2778c5f merge
parent child Browse files
Show More
@@ -1,672 +1,677 b''
1 1 // Copyright (c) IPython Development Team.
2 2 // Distributed under the terms of the Modified BSD License.
3 3 /**
4 4 *
5 5 *
6 6 * @module codecell
7 7 * @namespace codecell
8 8 * @class CodeCell
9 9 */
10 10
11 11
12 12 define([
13 13 'base/js/namespace',
14 14 'jquery',
15 15 'base/js/utils',
16 16 'base/js/keyboard',
17 17 'services/config',
18 18 'notebook/js/cell',
19 19 'notebook/js/outputarea',
20 20 'notebook/js/completer',
21 21 'notebook/js/celltoolbar',
22 22 'codemirror/lib/codemirror',
23 23 'codemirror/mode/python/python',
24 24 'notebook/js/codemirror-ipython'
25 25 ], function(IPython,
26 26 $,
27 27 utils,
28 28 keyboard,
29 29 configmod,
30 30 cell,
31 31 outputarea,
32 32 completer,
33 33 celltoolbar,
34 34 CodeMirror,
35 35 cmpython,
36 36 cmip
37 37 ) {
38 38 "use strict";
39 39
40 40 var Cell = cell.Cell;
41 41
42 42 /* local util for codemirror */
43 43 var posEq = function(a, b) {return a.line === b.line && a.ch === b.ch;};
44 44
45 45 /**
46 46 *
47 47 * function to delete until previous non blanking space character
48 48 * or first multiple of 4 tabstop.
49 49 * @private
50 50 */
51 51 CodeMirror.commands.delSpaceToPrevTabStop = function(cm){
52 52 var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
53 53 if (!posEq(from, to)) { cm.replaceRange("", from, to); return; }
54 54 var cur = cm.getCursor(), line = cm.getLine(cur.line);
55 55 var tabsize = cm.getOption('tabSize');
56 56 var chToPrevTabStop = cur.ch-(Math.ceil(cur.ch/tabsize)-1)*tabsize;
57 57 from = {ch:cur.ch-chToPrevTabStop,line:cur.line};
58 58 var select = cm.getRange(from,cur);
59 59 if( select.match(/^\ +$/) !== null){
60 60 cm.replaceRange("",from,cur);
61 61 } else {
62 62 cm.deleteH(-1,"char");
63 63 }
64 64 };
65 65
66 66 var keycodes = keyboard.keycodes;
67 67
68 68 var CodeCell = function (kernel, options) {
69 69 /**
70 70 * Constructor
71 71 *
72 72 * A Cell conceived to write code.
73 73 *
74 74 * Parameters:
75 75 * kernel: Kernel instance
76 76 * The kernel doesn't have to be set at creation time, in that case
77 77 * it will be null and set_kernel has to be called later.
78 78 * options: dictionary
79 79 * Dictionary of keyword arguments.
80 80 * events: $(Events) instance
81 81 * config: dictionary
82 82 * keyboard_manager: KeyboardManager instance
83 83 * notebook: Notebook instance
84 84 * tooltip: Tooltip instance
85 85 */
86 86 this.kernel = kernel || null;
87 87 this.notebook = options.notebook;
88 88 this.collapsed = false;
89 89 this.events = options.events;
90 90 this.tooltip = options.tooltip;
91 91 this.config = options.config;
92 92 this.class_config = new configmod.ConfigWithDefaults(this.config,
93 93 CodeCell.config_defaults, 'CodeCell');
94 94
95 95 // create all attributed in constructor function
96 96 // even if null for V8 VM optimisation
97 97 this.input_prompt_number = null;
98 98 this.celltoolbar = null;
99 99 this.output_area = null;
100 100 // Keep a stack of the 'active' output areas (where active means the
101 101 // output area that recieves output). When a user activates an output
102 102 // area, it gets pushed to the stack. Then, when the output area is
103 103 // deactivated, it's popped from the stack. When the stack is empty,
104 104 // the cell's output area is used.
105 105 this.active_output_areas = [];
106 106 var that = this;
107 107 Object.defineProperty(this, 'active_output_area', {
108 108 get: function() {
109 109 if (that.active_output_areas && that.active_output_areas.length > 0) {
110 110 return that.active_output_areas[that.active_output_areas.length-1];
111 111 } else {
112 112 return that.output_area;
113 113 }
114 114 },
115 115 });
116 116
117 117 this.last_msg_id = null;
118 118 this.completer = null;
119 119 this.widget_views = [];
120 120 this._widgets_live = true;
121 121
122 122 Cell.apply(this,[{
123 123 config: $.extend({}, CodeCell.options_default),
124 124 keyboard_manager: options.keyboard_manager,
125 125 events: this.events}]);
126 126
127 127 // Attributes we want to override in this subclass.
128 128 this.cell_type = "code";
129 129 this.element.focusout(
130 130 function() { that.auto_highlight(); }
131 131 );
132 132 };
133 133
134 134 CodeCell.options_default = {
135 135 cm_config : {
136 136 extraKeys: {
137 137 "Tab" : "indentMore",
138 138 "Shift-Tab" : "indentLess",
139 139 "Backspace" : "delSpaceToPrevTabStop",
140 140 "Cmd-/" : "toggleComment",
141 141 "Ctrl-/" : "toggleComment"
142 142 },
143 143 mode: 'ipython',
144 144 theme: 'ipython',
145 145 matchBrackets: true
146 146 }
147 147 };
148 148
149 149 CodeCell.config_defaults = {
150 150 highlight_modes : {
151 151 'magic_javascript' :{'reg':[/^%%javascript/]},
152 152 'magic_perl' :{'reg':[/^%%perl/]},
153 153 'magic_ruby' :{'reg':[/^%%ruby/]},
154 154 'magic_python' :{'reg':[/^%%python3?/]},
155 155 'magic_shell' :{'reg':[/^%%bash/]},
156 156 'magic_r' :{'reg':[/^%%R/]},
157 157 'magic_text/x-cython' :{'reg':[/^%%cython/]},
158 158 },
159 159 };
160 160
161 161 CodeCell.msg_cells = {};
162 162
163 163 CodeCell.prototype = Object.create(Cell.prototype);
164 164
165 165 /**
166 166 * @method push_output_area
167 167 */
168 168 CodeCell.prototype.push_output_area = function (output_area) {
169 169 this.active_output_areas.push(output_area);
170 170 };
171 171
172 172 /**
173 173 * @method pop_output_area
174 174 */
175 175 CodeCell.prototype.pop_output_area = function (output_area) {
176 176 var index = this.active_output_areas.lastIndexOf(output_area);
177 177 if (index > -1) {
178 178 this.active_output_areas.splice(index, 1);
179 179 }
180 180 };
181 181
182 182 /** @method create_element */
183 183 CodeCell.prototype.create_element = function () {
184 184 Cell.prototype.create_element.apply(this, arguments);
185 185
186 186 var cell = $('<div></div>').addClass('cell code_cell');
187 187 cell.attr('tabindex','2');
188 188
189 189 var input = $('<div></div>').addClass('input');
190 190 var prompt = $('<div/>').addClass('prompt input_prompt');
191 191 var inner_cell = $('<div/>').addClass('inner_cell');
192 192 this.celltoolbar = new celltoolbar.CellToolbar({
193 193 cell: this,
194 194 notebook: this.notebook});
195 195 inner_cell.append(this.celltoolbar.element);
196 196 var input_area = $('<div/>').addClass('input_area');
197 197 this.code_mirror = new CodeMirror(input_area.get(0), this.cm_config);
198 198 this.code_mirror.on('keydown', $.proxy(this.handle_keyevent,this));
199 199 $(this.code_mirror.getInputField()).attr("spellcheck", "false");
200 200 inner_cell.append(input_area);
201 201 input.append(prompt).append(inner_cell);
202 202
203 203 var widget_area = $('<div/>')
204 204 .addClass('widget-area')
205 205 .hide();
206 206 this.widget_area = widget_area;
207 207 var widget_prompt = $('<div/>')
208 208 .addClass('prompt')
209 209 .appendTo(widget_area);
210 210 var widget_subarea = $('<div/>')
211 211 .addClass('widget-subarea')
212 212 .appendTo(widget_area);
213 213 this.widget_subarea = widget_subarea;
214 214 var that = this;
215 215 var widget_clear_buton = $('<button />')
216 216 .addClass('close')
217 217 .html('&times;')
218 218 .click(function() {
219 219 widget_area.slideUp('', function(){
220 220 for (var i = 0; i < that.widget_views.length; i++) {
221 221 var view = that.widget_views[i];
222 222 view.remove();
223 223
224 224 // Remove widget live events.
225 225 view.off('comm:live', that._widget_live);
226 226 view.off('comm:dead', that._widget_dead);
227 227 }
228 228 that.widget_views = [];
229 229 widget_subarea.html('');
230 230 });
231 231 })
232 232 .appendTo(widget_prompt);
233 233
234 234 var output = $('<div></div>');
235 235 cell.append(input).append(widget_area).append(output);
236 236 this.element = cell;
237 237 this.output_area = new outputarea.OutputArea({
238 238 selector: output,
239 239 prompt_area: true,
240 240 events: this.events,
241 241 keyboard_manager: this.keyboard_manager});
242 242 this.completer = new completer.Completer(this, this.events);
243 243 };
244 244
245 245 /**
246 246 * Display a widget view in the cell.
247 247 */
248 248 CodeCell.prototype.display_widget_view = function(view_promise) {
249 249
250 250 // Display a dummy element
251 251 var dummy = $('<div/>');
252 252 this.widget_subarea.append(dummy);
253 253
254 254 // Display the view.
255 255 var that = this;
256 256 return view_promise.then(function(view) {
257 257 that.widget_area.show();
258 258 dummy.replaceWith(view.$el);
259 259 that.widget_views.push(view);
260 260
261 261 // Check the live state of the view's model.
262 262 if (view.model.comm_live) {
263 263 that._widget_live(view);
264 264 } else {
265 265 that._widget_dead(view);
266 266 }
267 267
268 268 // Listen to comm live events for the view.
269 269 view.on('comm:live', that._widget_live, that);
270 270 view.on('comm:dead', that._widget_dead, that);
271 271 return view;
272 272 });
273 273 };
274 274
275 275 /**
276 276 * Handles when a widget loses it's comm connection.
277 277 * @param {WidgetView} view
278 278 */
279 279 CodeCell.prototype._widget_dead = function(view) {
280 280 if (this._widgets_live) {
281 281 this._widgets_live = false;
282 282 this.widget_area.addClass('connection-problems');
283 283 }
284 284
285 285 };
286 286
287 287 /**
288 288 * Handles when a widget is connected to a live comm.
289 289 * @param {WidgetView} view
290 290 */
291 291 CodeCell.prototype._widget_live = function(view) {
292 292 if (!this._widgets_live) {
293 293 // Check that the other widgets are live too. O(N) operation.
294 294 // Abort the function at the first dead widget found.
295 295 for (var i = 0; i < this.widget_views.length; i++) {
296 296 if (!this.widget_views[i].model.comm_live) return;
297 297 }
298 298 this._widgets_live = true;
299 299 this.widget_area.removeClass('connection-problems');
300 300 }
301 301 };
302 302
303 303 /** @method bind_events */
304 304 CodeCell.prototype.bind_events = function () {
305 305 Cell.prototype.bind_events.apply(this);
306 306 var that = this;
307 307
308 308 this.element.focusout(
309 309 function() { that.auto_highlight(); }
310 310 );
311 311 };
312 312
313 313
314 314 /**
315 315 * This method gets called in CodeMirror's onKeyDown/onKeyPress
316 316 * handlers and is used to provide custom key handling. Its return
317 317 * value is used to determine if CodeMirror should ignore the event:
318 318 * true = ignore, false = don't ignore.
319 319 * @method handle_codemirror_keyevent
320 320 */
321 321
322 322 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
323 323
324 324 var that = this;
325 325 // whatever key is pressed, first, cancel the tooltip request before
326 326 // they are sent, and remove tooltip if any, except for tab again
327 327 var tooltip_closed = null;
328 328 if (event.type === 'keydown' && event.which !== keycodes.tab ) {
329 329 tooltip_closed = this.tooltip.remove_and_cancel_tooltip();
330 330 }
331 331
332 332 var cur = editor.getCursor();
333 333 if (event.keyCode === keycodes.enter){
334 334 this.auto_highlight();
335 335 }
336 336
337 337 if (event.which === keycodes.down && event.type === 'keypress' && this.tooltip.time_before_tooltip >= 0) {
338 338 // triger on keypress (!) otherwise inconsistent event.which depending on plateform
339 339 // browser and keyboard layout !
340 340 // Pressing '(' , request tooltip, don't forget to reappend it
341 341 // The second argument says to hide the tooltip if the docstring
342 342 // is actually empty
343 343 this.tooltip.pending(that, true);
344 344 } else if ( tooltip_closed && event.which === keycodes.esc && event.type === 'keydown') {
345 345 // If tooltip is active, cancel it. The call to
346 346 // remove_and_cancel_tooltip above doesn't pass, force=true.
347 347 // Because of this it won't actually close the tooltip
348 348 // if it is in sticky mode. Thus, we have to check again if it is open
349 349 // and close it with force=true.
350 350 if (!this.tooltip._hidden) {
351 351 this.tooltip.remove_and_cancel_tooltip(true);
352 352 }
353 353 // If we closed the tooltip, don't let CM or the global handlers
354 354 // handle this event.
355 355 event.codemirrorIgnore = true;
356 356 event.preventDefault();
357 357 return true;
358 358 } else if (event.keyCode === keycodes.tab && event.type === 'keydown' && event.shiftKey) {
359 359 if (editor.somethingSelected() || editor.getSelections().length !== 1){
360 360 var anchor = editor.getCursor("anchor");
361 361 var head = editor.getCursor("head");
362 362 if( anchor.line !== head.line){
363 363 return false;
364 364 }
365 365 }
366 366 this.tooltip.request(that);
367 367 event.codemirrorIgnore = true;
368 368 event.preventDefault();
369 369 return true;
370 370 } else if (event.keyCode === keycodes.tab && event.type === 'keydown') {
371 371 // Tab completion.
372 372 this.tooltip.remove_and_cancel_tooltip();
373 373
374 374 // completion does not work on multicursor, it might be possible though in some cases
375 375 if (editor.somethingSelected() || editor.getSelections().length > 1) {
376 376 return false;
377 377 }
378 378 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
379 379 if (pre_cursor.trim() === "") {
380 380 // Don't autocomplete if the part of the line before the cursor
381 381 // is empty. In this case, let CodeMirror handle indentation.
382 382 return false;
383 383 } else {
384 384 event.codemirrorIgnore = true;
385 385 event.preventDefault();
386 386 this.completer.startCompletion();
387 387 return true;
388 388 }
389 389 }
390 390
391 391 // keyboard event wasn't one of those unique to code cells, let's see
392 392 // if it's one of the generic ones (i.e. check edit mode shortcuts)
393 393 return Cell.prototype.handle_codemirror_keyevent.apply(this, [editor, event]);
394 394 };
395 395
396 396 // Kernel related calls.
397 397
398 398 CodeCell.prototype.set_kernel = function (kernel) {
399 399 this.kernel = kernel;
400 400 };
401 401
402 402 /**
403 403 * Execute current code cell to the kernel
404 404 * @method execute
405 405 */
406 CodeCell.prototype.execute = function () {
406 CodeCell.prototype.execute = function (stop_on_error) {
407 407 if (!this.kernel || !this.kernel.is_connected()) {
408 408 console.log("Can't execute, kernel is not connected.");
409 409 return;
410 410 }
411 411
412 412 this.active_output_area.clear_output(false, true);
413
413
414 if (stop_on_error === undefined) {
415 stop_on_error = true;
416 }
417
414 418 // Clear widget area
415 419 for (var i = 0; i < this.widget_views.length; i++) {
416 420 var view = this.widget_views[i];
417 421 view.remove();
418 422
419 423 // Remove widget live events.
420 424 view.off('comm:live', this._widget_live);
421 425 view.off('comm:dead', this._widget_dead);
422 426 }
423 427 this.widget_views = [];
424 428 this.widget_subarea.html('');
425 429 this.widget_subarea.height('');
426 430 this.widget_area.height('');
427 431 this.widget_area.hide();
428 432
429 433 this.set_input_prompt('*');
430 434 this.element.addClass("running");
431 435 if (this.last_msg_id) {
432 436 this.kernel.clear_callbacks_for_msg(this.last_msg_id);
433 437 }
434 438 var callbacks = this.get_callbacks();
435 439
436 440 var old_msg_id = this.last_msg_id;
437 this.last_msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false, store_history: true});
441 this.last_msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false, store_history: true,
442 stop_on_error : stop_on_error});
438 443 if (old_msg_id) {
439 444 delete CodeCell.msg_cells[old_msg_id];
440 445 }
441 446 CodeCell.msg_cells[this.last_msg_id] = this;
442 447 this.render();
443 448 this.events.trigger('execute.CodeCell', {cell: this});
444 449 };
445 450
446 451 /**
447 452 * Construct the default callbacks for
448 453 * @method get_callbacks
449 454 */
450 455 CodeCell.prototype.get_callbacks = function () {
451 456 var that = this;
452 457 return {
453 458 shell : {
454 459 reply : $.proxy(this._handle_execute_reply, this),
455 460 payload : {
456 461 set_next_input : $.proxy(this._handle_set_next_input, this),
457 462 page : $.proxy(this._open_with_pager, this)
458 463 }
459 464 },
460 465 iopub : {
461 466 output : function() {
462 467 that.active_output_area.handle_output.apply(that.active_output_area, arguments);
463 468 },
464 469 clear_output : function() {
465 470 that.active_output_area.handle_clear_output.apply(that.active_output_area, arguments);
466 471 },
467 472 },
468 473 input : $.proxy(this._handle_input_request, this)
469 474 };
470 475 };
471 476
472 477 CodeCell.prototype._open_with_pager = function (payload) {
473 478 this.events.trigger('open_with_text.Pager', payload);
474 479 };
475 480
476 481 /**
477 482 * @method _handle_execute_reply
478 483 * @private
479 484 */
480 485 CodeCell.prototype._handle_execute_reply = function (msg) {
481 486 this.set_input_prompt(msg.content.execution_count);
482 487 this.element.removeClass("running");
483 488 this.events.trigger('set_dirty.Notebook', {value: true});
484 489 };
485 490
486 491 /**
487 492 * @method _handle_set_next_input
488 493 * @private
489 494 */
490 495 CodeCell.prototype._handle_set_next_input = function (payload) {
491 496 var data = {'cell': this, 'text': payload.text, replace: payload.replace};
492 497 this.events.trigger('set_next_input.Notebook', data);
493 498 };
494 499
495 500 /**
496 501 * @method _handle_input_request
497 502 * @private
498 503 */
499 504 CodeCell.prototype._handle_input_request = function (msg) {
500 505 this.active_output_area.append_raw_input(msg);
501 506 };
502 507
503 508
504 509 // Basic cell manipulation.
505 510
506 511 CodeCell.prototype.select = function () {
507 512 var cont = Cell.prototype.select.apply(this);
508 513 if (cont) {
509 514 this.code_mirror.refresh();
510 515 this.auto_highlight();
511 516 }
512 517 return cont;
513 518 };
514 519
515 520 CodeCell.prototype.render = function () {
516 521 var cont = Cell.prototype.render.apply(this);
517 522 // Always execute, even if we are already in the rendered state
518 523 return cont;
519 524 };
520 525
521 526 CodeCell.prototype.select_all = function () {
522 527 var start = {line: 0, ch: 0};
523 528 var nlines = this.code_mirror.lineCount();
524 529 var last_line = this.code_mirror.getLine(nlines-1);
525 530 var end = {line: nlines-1, ch: last_line.length};
526 531 this.code_mirror.setSelection(start, end);
527 532 };
528 533
529 534
530 535 CodeCell.prototype.collapse_output = function () {
531 536 this.output_area.collapse();
532 537 };
533 538
534 539
535 540 CodeCell.prototype.expand_output = function () {
536 541 this.output_area.expand();
537 542 this.output_area.unscroll_area();
538 543 };
539 544
540 545 CodeCell.prototype.scroll_output = function () {
541 546 this.output_area.expand();
542 547 this.output_area.scroll_if_long();
543 548 };
544 549
545 550 CodeCell.prototype.toggle_output = function () {
546 551 this.output_area.toggle_output();
547 552 };
548 553
549 554 CodeCell.prototype.toggle_output_scroll = function () {
550 555 this.output_area.toggle_scroll();
551 556 };
552 557
553 558
554 559 CodeCell.input_prompt_classical = function (prompt_value, lines_number) {
555 560 var ns;
556 561 if (prompt_value === undefined || prompt_value === null) {
557 562 ns = "&nbsp;";
558 563 } else {
559 564 ns = encodeURIComponent(prompt_value);
560 565 }
561 566 return 'In&nbsp;[' + ns + ']:';
562 567 };
563 568
564 569 CodeCell.input_prompt_continuation = function (prompt_value, lines_number) {
565 570 var html = [CodeCell.input_prompt_classical(prompt_value, lines_number)];
566 571 for(var i=1; i < lines_number; i++) {
567 572 html.push(['...:']);
568 573 }
569 574 return html.join('<br/>');
570 575 };
571 576
572 577 CodeCell.input_prompt_function = CodeCell.input_prompt_classical;
573 578
574 579
575 580 CodeCell.prototype.set_input_prompt = function (number) {
576 581 var nline = 1;
577 582 if (this.code_mirror !== undefined) {
578 583 nline = this.code_mirror.lineCount();
579 584 }
580 585 this.input_prompt_number = number;
581 586 var prompt_html = CodeCell.input_prompt_function(this.input_prompt_number, nline);
582 587 // This HTML call is okay because the user contents are escaped.
583 588 this.element.find('div.input_prompt').html(prompt_html);
584 589 };
585 590
586 591
587 592 CodeCell.prototype.clear_input = function () {
588 593 this.code_mirror.setValue('');
589 594 };
590 595
591 596
592 597 CodeCell.prototype.get_text = function () {
593 598 return this.code_mirror.getValue();
594 599 };
595 600
596 601
597 602 CodeCell.prototype.set_text = function (code) {
598 603 return this.code_mirror.setValue(code);
599 604 };
600 605
601 606
602 607 CodeCell.prototype.clear_output = function (wait) {
603 608 this.active_output_area.clear_output(wait);
604 609 this.set_input_prompt();
605 610 };
606 611
607 612
608 613 // JSON serialization
609 614
610 615 CodeCell.prototype.fromJSON = function (data) {
611 616 Cell.prototype.fromJSON.apply(this, arguments);
612 617 if (data.cell_type === 'code') {
613 618 if (data.source !== undefined) {
614 619 this.set_text(data.source);
615 620 // make this value the starting point, so that we can only undo
616 621 // to this state, instead of a blank cell
617 622 this.code_mirror.clearHistory();
618 623 this.auto_highlight();
619 624 }
620 625 this.set_input_prompt(data.execution_count);
621 626 this.output_area.trusted = data.metadata.trusted || false;
622 627 this.output_area.fromJSON(data.outputs);
623 628 if (data.metadata.collapsed !== undefined) {
624 629 if (data.metadata.collapsed) {
625 630 this.collapse_output();
626 631 } else {
627 632 this.expand_output();
628 633 }
629 634 }
630 635 }
631 636 };
632 637
633 638
634 639 CodeCell.prototype.toJSON = function () {
635 640 var data = Cell.prototype.toJSON.apply(this);
636 641 data.source = this.get_text();
637 642 // is finite protect against undefined and '*' value
638 643 if (isFinite(this.input_prompt_number)) {
639 644 data.execution_count = this.input_prompt_number;
640 645 } else {
641 646 data.execution_count = null;
642 647 }
643 648 var outputs = this.output_area.toJSON();
644 649 data.outputs = outputs;
645 650 data.metadata.trusted = this.output_area.trusted;
646 651 data.metadata.collapsed = this.output_area.collapsed;
647 652 return data;
648 653 };
649 654
650 655 /**
651 656 * handle cell level logic when a cell is unselected
652 657 * @method unselect
653 658 * @return is the action being taken
654 659 */
655 660 CodeCell.prototype.unselect = function () {
656 661 var cont = Cell.prototype.unselect.apply(this);
657 662 if (cont) {
658 663 // When a code cell is usnelected, make sure that the corresponding
659 664 // tooltip and completer to that cell is closed.
660 665 this.tooltip.remove_and_cancel_tooltip(true);
661 666 if (this.completer !== null) {
662 667 this.completer.close();
663 668 }
664 669 }
665 670 return cont;
666 671 };
667 672
668 673 // Backwards compatability.
669 674 IPython.CodeCell = CodeCell;
670 675
671 676 return {'CodeCell': CodeCell};
672 677 });
@@ -1,78 +1,115 b''
1 1 //
2 2 // Test code cell execution.
3 3 //
4 4 casper.notebook_test(function () {
5 5 this.evaluate(function () {
6 6 var cell = IPython.notebook.get_cell(0);
7 7 cell.set_text('a=10; print(a)');
8 8 cell.execute();
9 9 });
10 10
11 11 this.wait_for_output(0);
12 12
13 13 // refactor this into just a get_output(0)
14 14 this.then(function () {
15 15 var result = this.get_output_cell(0);
16 16 this.test.assertEquals(result.text, '10\n', 'cell execute (using js)');
17 17 });
18 18
19 19
20 20 // do it again with the keyboard shortcut
21 21 this.thenEvaluate(function () {
22 22 var cell = IPython.notebook.get_cell(0);
23 23 cell.set_text('a=11; print(a)');
24 24 cell.clear_output();
25 25 });
26 26
27 27 this.then(function(){
28 28
29 29 this.trigger_keydown('shift-enter');
30 30 });
31 31
32 32 this.wait_for_output(0);
33 33
34 34 this.then(function () {
35 35 var result = this.get_output_cell(0);
36 36 var num_cells = this.get_cells_length();
37 37 this.test.assertEquals(result.text, '11\n', 'cell execute (using ctrl-enter)');
38 38 this.test.assertEquals(num_cells, 2, 'shift-enter adds a new cell at the bottom')
39 39 });
40 40
41 41 // do it again with the keyboard shortcut
42 42 this.thenEvaluate(function () {
43 43 IPython.notebook.select(1);
44 44 IPython.notebook.delete_cell();
45 45 var cell = IPython.notebook.get_cell(0);
46 46 cell.set_text('a=12; print(a)');
47 47 cell.clear_output();
48 48 });
49 49
50 50 this.then(function(){
51 51 this.trigger_keydown('ctrl-enter');
52 52 });
53 53
54 54 this.wait_for_output(0);
55 55
56 56 this.then(function () {
57 57 var result = this.get_output_cell(0);
58 58 var num_cells = this.get_cells_length();
59 59 this.test.assertEquals(result.text, '12\n', 'cell execute (using shift-enter)');
60 60 this.test.assertEquals(num_cells, 1, 'ctrl-enter adds no new cell at the bottom')
61 61 });
62 62
63 63 // press the "play" triangle button in the toolbar
64 64 this.thenEvaluate(function () {
65 65 var cell = IPython.notebook.get_cell(0);
66 66 IPython.notebook.select(0);
67 67 cell.clear_output();
68 68 cell.set_text('a=13; print(a)');
69 69 $("button[data-jupyter-action='ipython.run-select-next']")[0].click()
70 70 });
71 71
72 72 this.wait_for_output(0);
73 73
74 74 this.then(function () {
75 75 var result = this.get_output_cell(0);
76 76 this.test.assertEquals(result.text, '13\n', 'cell execute (using "play" toolbar button)')
77 77 });
78
79 // run code with skip_exception
80 this.thenEvaluate(function () {
81 var cell0 = IPython.notebook.get_cell(0);
82 cell0.set_text('raise IOError');
83 IPython.notebook.insert_cell_below('code',0);
84 var cell1 = IPython.notebook.get_cell(1);
85 cell1.set_text('a=14; print(a)');
86 cell0.execute(false);
87 cell1.execute();
88 });
89
90 this.wait_for_output(1);
91
92 this.then(function () {
93 var result = this.get_output_cell(1);
94 this.test.assertEquals(result.text, '14\n', "cell execute, don't stop on error");
95 });
96
97 this.thenEvaluate(function () {
98 var cell0 = IPython.notebook.get_cell(0);
99 cell0.set_text('raise IOError');
100 IPython.notebook.insert_cell_below('code',0);
101 var cell1 = IPython.notebook.get_cell(1);
102 cell1.set_text('a=14; print(a)');
103 cell0.execute();
104 cell1.execute();
105 });
106
107 this.wait_for_output(0);
108
109 this.then(function () {
110 var outputs = this.evaluate(function() {
111 return IPython.notebook.get_cell(1).output_area.outputs;
112 })
113 this.test.assertEquals(outputs.length, 0, 'cell execute, stop on error (default)');
114 });
78 115 });
@@ -1,387 +1,390 b''
1 1 """Base class to manage the 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 from IPython.kernel.channels import major_protocol_version
8 8 from IPython.utils.py3compat import string_types, iteritems
9 9
10 10 import zmq
11 11
12 12 from IPython.utils.traitlets import (
13 13 Any, Instance, Type,
14 14 )
15 15
16 16 from .channelsabc import (ChannelABC, HBChannelABC)
17 17 from .clientabc import KernelClientABC
18 18 from .connect import ConnectionFileMixin
19 19
20 20
21 21 # some utilities to validate message structure, these might get moved elsewhere
22 22 # if they prove to have more generic utility
23 23
24 24 def validate_string_dict(dct):
25 25 """Validate that the input is a dict with string keys and values.
26 26
27 27 Raises ValueError if not."""
28 28 for k,v in iteritems(dct):
29 29 if not isinstance(k, string_types):
30 30 raise ValueError('key %r in dict must be a string' % k)
31 31 if not isinstance(v, string_types):
32 32 raise ValueError('value %r in dict must be a string' % v)
33 33
34 34
35 35 class KernelClient(ConnectionFileMixin):
36 36 """Communicates with a single kernel on any host via zmq channels.
37 37
38 38 There are four channels associated with each kernel:
39 39
40 40 * shell: for request/reply calls to the kernel.
41 41 * iopub: for the kernel to publish results to frontends.
42 42 * hb: for monitoring the kernel's heartbeat.
43 43 * stdin: for frontends to reply to raw_input calls in the kernel.
44 44
45 45 The methods of the channels are exposed as methods of the client itself
46 46 (KernelClient.execute, complete, history, etc.).
47 47 See the channels themselves for documentation of these methods.
48 48
49 49 """
50 50
51 51 # The PyZMQ Context to use for communication with the kernel.
52 52 context = Instance(zmq.Context)
53 53 def _context_default(self):
54 54 return zmq.Context.instance()
55 55
56 56 # The classes to use for the various channels
57 57 shell_channel_class = Type(ChannelABC)
58 58 iopub_channel_class = Type(ChannelABC)
59 59 stdin_channel_class = Type(ChannelABC)
60 60 hb_channel_class = Type(HBChannelABC)
61 61
62 62 # Protected traits
63 63 _shell_channel = Any
64 64 _iopub_channel = Any
65 65 _stdin_channel = Any
66 66 _hb_channel = Any
67 67
68 68 # flag for whether execute requests should be allowed to call raw_input:
69 69 allow_stdin = True
70 70
71 71 #--------------------------------------------------------------------------
72 72 # Channel proxy methods
73 73 #--------------------------------------------------------------------------
74 74
75 75 def _get_msg(channel, *args, **kwargs):
76 76 return channel.get_msg(*args, **kwargs)
77 77
78 78 def get_shell_msg(self, *args, **kwargs):
79 79 """Get a message from the shell channel"""
80 80 return self.shell_channel.get_msg(*args, **kwargs)
81 81
82 82 def get_iopub_msg(self, *args, **kwargs):
83 83 """Get a message from the iopub channel"""
84 84 return self.iopub_channel.get_msg(*args, **kwargs)
85 85
86 86 def get_stdin_msg(self, *args, **kwargs):
87 87 """Get a message from the stdin channel"""
88 88 return self.stdin_channel.get_msg(*args, **kwargs)
89 89
90 90 #--------------------------------------------------------------------------
91 91 # Channel management methods
92 92 #--------------------------------------------------------------------------
93 93
94 94 def start_channels(self, shell=True, iopub=True, stdin=True, hb=True):
95 95 """Starts the channels for this kernel.
96 96
97 97 This will create the channels if they do not exist and then start
98 98 them (their activity runs in a thread). If port numbers of 0 are
99 99 being used (random ports) then you must first call
100 100 :meth:`start_kernel`. If the channels have been stopped and you
101 101 call this, :class:`RuntimeError` will be raised.
102 102 """
103 103 if shell:
104 104 self.shell_channel.start()
105 105 self.kernel_info()
106 106 if iopub:
107 107 self.iopub_channel.start()
108 108 if stdin:
109 109 self.stdin_channel.start()
110 110 self.allow_stdin = True
111 111 else:
112 112 self.allow_stdin = False
113 113 if hb:
114 114 self.hb_channel.start()
115 115
116 116 def stop_channels(self):
117 117 """Stops all the running channels for this kernel.
118 118
119 119 This stops their event loops and joins their threads.
120 120 """
121 121 if self.shell_channel.is_alive():
122 122 self.shell_channel.stop()
123 123 if self.iopub_channel.is_alive():
124 124 self.iopub_channel.stop()
125 125 if self.stdin_channel.is_alive():
126 126 self.stdin_channel.stop()
127 127 if self.hb_channel.is_alive():
128 128 self.hb_channel.stop()
129 129
130 130 @property
131 131 def channels_running(self):
132 132 """Are any of the channels created and running?"""
133 133 return (self.shell_channel.is_alive() or self.iopub_channel.is_alive() or
134 134 self.stdin_channel.is_alive() or self.hb_channel.is_alive())
135 135
136 136 ioloop = None # Overridden in subclasses that use pyzmq event loop
137 137
138 138 @property
139 139 def shell_channel(self):
140 140 """Get the shell channel object for this kernel."""
141 141 if self._shell_channel is None:
142 142 url = self._make_url('shell')
143 143 self.log.debug("connecting shell channel to %s", url)
144 144 socket = self.connect_shell(identity=self.session.bsession)
145 145 self._shell_channel = self.shell_channel_class(
146 146 socket, self.session, self.ioloop
147 147 )
148 148 return self._shell_channel
149 149
150 150 @property
151 151 def iopub_channel(self):
152 152 """Get the iopub channel object for this kernel."""
153 153 if self._iopub_channel is None:
154 154 url = self._make_url('iopub')
155 155 self.log.debug("connecting iopub channel to %s", url)
156 156 socket = self.connect_iopub()
157 157 self._iopub_channel = self.iopub_channel_class(
158 158 socket, self.session, self.ioloop
159 159 )
160 160 return self._iopub_channel
161 161
162 162 @property
163 163 def stdin_channel(self):
164 164 """Get the stdin channel object for this kernel."""
165 165 if self._stdin_channel is None:
166 166 url = self._make_url('stdin')
167 167 self.log.debug("connecting stdin channel to %s", url)
168 168 socket = self.connect_stdin(identity=self.session.bsession)
169 169 self._stdin_channel = self.stdin_channel_class(
170 170 socket, self.session, self.ioloop
171 171 )
172 172 return self._stdin_channel
173 173
174 174 @property
175 175 def hb_channel(self):
176 176 """Get the hb channel object for this kernel."""
177 177 if self._hb_channel is None:
178 178 url = self._make_url('hb')
179 179 self.log.debug("connecting heartbeat channel to %s", url)
180 180 self._hb_channel = self.hb_channel_class(
181 181 self.context, self.session, url
182 182 )
183 183 return self._hb_channel
184 184
185 185 def is_alive(self):
186 186 """Is the kernel process still running?"""
187 187 if self._hb_channel is not None:
188 188 # We didn't start the kernel with this KernelManager so we
189 189 # use the heartbeat.
190 190 return self._hb_channel.is_beating()
191 191 else:
192 192 # no heartbeat and not local, we can't tell if it's running,
193 193 # so naively return True
194 194 return True
195 195
196 196
197 197 # Methods to send specific messages on channels
198 198 def execute(self, code, silent=False, store_history=True,
199 user_expressions=None, allow_stdin=None):
199 user_expressions=None, allow_stdin=None, stop_on_error=True):
200 200 """Execute code in the kernel.
201 201
202 202 Parameters
203 203 ----------
204 204 code : str
205 205 A string of Python code.
206 206
207 207 silent : bool, optional (default False)
208 208 If set, the kernel will execute the code as quietly possible, and
209 209 will force store_history to be False.
210 210
211 211 store_history : bool, optional (default True)
212 212 If set, the kernel will store command history. This is forced
213 213 to be False if silent is True.
214 214
215 215 user_expressions : dict, optional
216 216 A dict mapping names to expressions to be evaluated in the user's
217 217 dict. The expression values are returned as strings formatted using
218 218 :func:`repr`.
219 219
220 220 allow_stdin : bool, optional (default self.allow_stdin)
221 221 Flag for whether the kernel can send stdin requests to frontends.
222 222
223 223 Some frontends (e.g. the Notebook) do not support stdin requests.
224 224 If raw_input is called from code executed from such a frontend, a
225 225 StdinNotImplementedError will be raised.
226 226
227 stop_on_error: bool, optional (default True)
228 Flag whether to abort the execution queue, if an exception is encountered.
229
227 230 Returns
228 231 -------
229 232 The msg_id of the message sent.
230 233 """
231 234 if user_expressions is None:
232 235 user_expressions = {}
233 236 if allow_stdin is None:
234 237 allow_stdin = self.allow_stdin
235 238
236 239
237 240 # Don't waste network traffic if inputs are invalid
238 241 if not isinstance(code, string_types):
239 242 raise ValueError('code %r must be a string' % code)
240 243 validate_string_dict(user_expressions)
241 244
242 245 # Create class for content/msg creation. Related to, but possibly
243 246 # not in Session.
244 247 content = dict(code=code, silent=silent, store_history=store_history,
245 248 user_expressions=user_expressions,
246 allow_stdin=allow_stdin,
249 allow_stdin=allow_stdin, stop_on_error=stop_on_error
247 250 )
248 251 msg = self.session.msg('execute_request', content)
249 252 self.shell_channel.send(msg)
250 253 return msg['header']['msg_id']
251 254
252 255 def complete(self, code, cursor_pos=None):
253 256 """Tab complete text in the kernel's namespace.
254 257
255 258 Parameters
256 259 ----------
257 260 code : str
258 261 The context in which completion is requested.
259 262 Can be anything between a variable name and an entire cell.
260 263 cursor_pos : int, optional
261 264 The position of the cursor in the block of code where the completion was requested.
262 265 Default: ``len(code)``
263 266
264 267 Returns
265 268 -------
266 269 The msg_id of the message sent.
267 270 """
268 271 if cursor_pos is None:
269 272 cursor_pos = len(code)
270 273 content = dict(code=code, cursor_pos=cursor_pos)
271 274 msg = self.session.msg('complete_request', content)
272 275 self.shell_channel.send(msg)
273 276 return msg['header']['msg_id']
274 277
275 278 def inspect(self, code, cursor_pos=None, detail_level=0):
276 279 """Get metadata information about an object in the kernel's namespace.
277 280
278 281 It is up to the kernel to determine the appropriate object to inspect.
279 282
280 283 Parameters
281 284 ----------
282 285 code : str
283 286 The context in which info is requested.
284 287 Can be anything between a variable name and an entire cell.
285 288 cursor_pos : int, optional
286 289 The position of the cursor in the block of code where the info was requested.
287 290 Default: ``len(code)``
288 291 detail_level : int, optional
289 292 The level of detail for the introspection (0-2)
290 293
291 294 Returns
292 295 -------
293 296 The msg_id of the message sent.
294 297 """
295 298 if cursor_pos is None:
296 299 cursor_pos = len(code)
297 300 content = dict(code=code, cursor_pos=cursor_pos,
298 301 detail_level=detail_level,
299 302 )
300 303 msg = self.session.msg('inspect_request', content)
301 304 self.shell_channel.send(msg)
302 305 return msg['header']['msg_id']
303 306
304 307 def history(self, raw=True, output=False, hist_access_type='range', **kwargs):
305 308 """Get entries from the kernel's history list.
306 309
307 310 Parameters
308 311 ----------
309 312 raw : bool
310 313 If True, return the raw input.
311 314 output : bool
312 315 If True, then return the output as well.
313 316 hist_access_type : str
314 317 'range' (fill in session, start and stop params), 'tail' (fill in n)
315 318 or 'search' (fill in pattern param).
316 319
317 320 session : int
318 321 For a range request, the session from which to get lines. Session
319 322 numbers are positive integers; negative ones count back from the
320 323 current session.
321 324 start : int
322 325 The first line number of a history range.
323 326 stop : int
324 327 The final (excluded) line number of a history range.
325 328
326 329 n : int
327 330 The number of lines of history to get for a tail request.
328 331
329 332 pattern : str
330 333 The glob-syntax pattern for a search request.
331 334
332 335 Returns
333 336 -------
334 337 The msg_id of the message sent.
335 338 """
336 339 content = dict(raw=raw, output=output, hist_access_type=hist_access_type,
337 340 **kwargs)
338 341 msg = self.session.msg('history_request', content)
339 342 self.shell_channel.send(msg)
340 343 return msg['header']['msg_id']
341 344
342 345 def kernel_info(self):
343 346 """Request kernel info."""
344 347 msg = self.session.msg('kernel_info_request')
345 348 self.shell_channel.send(msg)
346 349 return msg['header']['msg_id']
347 350
348 351 def _handle_kernel_info_reply(self, msg):
349 352 """handle kernel info reply
350 353
351 354 sets protocol adaptation version. This might
352 355 be run from a separate thread.
353 356 """
354 357 adapt_version = int(msg['content']['protocol_version'].split('.')[0])
355 358 if adapt_version != major_protocol_version:
356 359 self.session.adapt_version = adapt_version
357 360
358 361 def shutdown(self, restart=False):
359 362 """Request an immediate kernel shutdown.
360 363
361 364 Upon receipt of the (empty) reply, client code can safely assume that
362 365 the kernel has shut down and it's safe to forcefully terminate it if
363 366 it's still alive.
364 367
365 368 The kernel will send the reply via a function registered with Python's
366 369 atexit module, ensuring it's truly done as the kernel is done with all
367 370 normal operation.
368 371 """
369 372 # Send quit message to kernel. Once we implement kernel-side setattr,
370 373 # this should probably be done that way, but for now this will do.
371 374 msg = self.session.msg('shutdown_request', {'restart':restart})
372 375 self.shell_channel.send(msg)
373 376 return msg['header']['msg_id']
374 377
375 378 def is_complete(self, code):
376 379 msg = self.session.msg('is_complete_request', {'code': code})
377 380 self.shell_channel.send(msg)
378 381 return msg['header']['msg_id']
379 382
380 383 def input(self, string):
381 384 """Send a string of raw input to the kernel."""
382 385 content = dict(value=string)
383 386 msg = self.session.msg('input_reply', content)
384 387 self.stdin_channel.send(msg)
385 388
386 389
387 390 KernelClientABC.register(KernelClient)
@@ -1,472 +1,490 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 import sys
8 8 from distutils.version import LooseVersion as V
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.utils.traitlets import (
17 17 HasTraits, TraitError, Bool, Unicode, Dict, Integer, List, Enum,
18 18 )
19 19 from IPython.utils.py3compat import string_types, iteritems
20 20
21 21 from .utils import TIMEOUT, start_global_kernel, flush_channels, execute
22 22
23 23 #-----------------------------------------------------------------------------
24 24 # Globals
25 25 #-----------------------------------------------------------------------------
26 26 KC = None
27 27
28 28 def setup():
29 29 global KC
30 30 KC = start_global_kernel()
31 31
32 32 #-----------------------------------------------------------------------------
33 33 # Message Spec References
34 34 #-----------------------------------------------------------------------------
35 35
36 36 class Reference(HasTraits):
37 37
38 38 """
39 39 Base class for message spec specification testing.
40 40
41 41 This class is the core of the message specification test. The
42 42 idea is that child classes implement trait attributes for each
43 43 message keys, so that message keys can be tested against these
44 44 traits using :meth:`check` method.
45 45
46 46 """
47 47
48 48 def check(self, d):
49 49 """validate a dict against our traits"""
50 50 for key in self.trait_names():
51 51 nt.assert_in(key, d)
52 52 # FIXME: always allow None, probably not a good idea
53 53 if d[key] is None:
54 54 continue
55 55 try:
56 56 setattr(self, key, d[key])
57 57 except TraitError as e:
58 58 assert False, str(e)
59 59
60 60
61 61 class Version(Unicode):
62 62 def __init__(self, *args, **kwargs):
63 63 self.min = kwargs.pop('min', None)
64 64 self.max = kwargs.pop('max', None)
65 65 kwargs['default_value'] = self.min
66 66 super(Version, self).__init__(*args, **kwargs)
67 67
68 68 def validate(self, obj, value):
69 69 if self.min and V(value) < V(self.min):
70 70 raise TraitError("bad version: %s < %s" % (value, self.min))
71 71 if self.max and (V(value) > V(self.max)):
72 72 raise TraitError("bad version: %s > %s" % (value, self.max))
73 73
74 74
75 75 class RMessage(Reference):
76 76 msg_id = Unicode()
77 77 msg_type = Unicode()
78 78 header = Dict()
79 79 parent_header = Dict()
80 80 content = Dict()
81 81
82 82 def check(self, d):
83 83 super(RMessage, self).check(d)
84 84 RHeader().check(self.header)
85 85 if self.parent_header:
86 86 RHeader().check(self.parent_header)
87 87
88 88 class RHeader(Reference):
89 89 msg_id = Unicode()
90 90 msg_type = Unicode()
91 91 session = Unicode()
92 92 username = Unicode()
93 93 version = Version(min='5.0')
94 94
95 95 mime_pat = re.compile(r'^[\w\-\+\.]+/[\w\-\+\.]+$')
96 96
97 97 class MimeBundle(Reference):
98 98 metadata = Dict()
99 99 data = Dict()
100 100 def _data_changed(self, name, old, new):
101 101 for k,v in iteritems(new):
102 102 assert mime_pat.match(k)
103 103 nt.assert_is_instance(v, string_types)
104 104
105 105 # shell replies
106 106
107 107 class ExecuteReply(Reference):
108 108 execution_count = Integer()
109 109 status = Enum((u'ok', u'error'))
110 110
111 111 def check(self, d):
112 112 Reference.check(self, d)
113 113 if d['status'] == 'ok':
114 114 ExecuteReplyOkay().check(d)
115 115 elif d['status'] == 'error':
116 116 ExecuteReplyError().check(d)
117 117
118 118
119 119 class ExecuteReplyOkay(Reference):
120 120 payload = List(Dict)
121 121 user_expressions = Dict()
122 122
123 123
124 124 class ExecuteReplyError(Reference):
125 125 ename = Unicode()
126 126 evalue = Unicode()
127 127 traceback = List(Unicode)
128 128
129 129
130 130 class InspectReply(MimeBundle):
131 131 found = Bool()
132 132
133 133
134 134 class ArgSpec(Reference):
135 135 args = List(Unicode)
136 136 varargs = Unicode()
137 137 varkw = Unicode()
138 138 defaults = List()
139 139
140 140
141 141 class Status(Reference):
142 142 execution_state = Enum((u'busy', u'idle', u'starting'))
143 143
144 144
145 145 class CompleteReply(Reference):
146 146 matches = List(Unicode)
147 147 cursor_start = Integer()
148 148 cursor_end = Integer()
149 149 status = Unicode()
150 150
151 151 class LanguageInfo(Reference):
152 152 name = Unicode('python')
153 153 version = Unicode(sys.version.split()[0])
154 154
155 155 class KernelInfoReply(Reference):
156 156 protocol_version = Version(min='5.0')
157 157 implementation = Unicode('ipython')
158 158 implementation_version = Version(min='2.1')
159 159 language_info = Dict()
160 160 banner = Unicode()
161 161
162 162 def check(self, d):
163 163 Reference.check(self, d)
164 164 LanguageInfo().check(d['language_info'])
165 165
166 166
167 167 class IsCompleteReply(Reference):
168 168 status = Enum((u'complete', u'incomplete', u'invalid', u'unknown'))
169 169
170 170 def check(self, d):
171 171 Reference.check(self, d)
172 172 if d['status'] == 'incomplete':
173 173 IsCompleteReplyIncomplete().check(d)
174 174
175 175 class IsCompleteReplyIncomplete(Reference):
176 176 indent = Unicode()
177 177
178 178
179 179 # IOPub messages
180 180
181 181 class ExecuteInput(Reference):
182 182 code = Unicode()
183 183 execution_count = Integer()
184 184
185 185
186 186 Error = ExecuteReplyError
187 187
188 188
189 189 class Stream(Reference):
190 190 name = Enum((u'stdout', u'stderr'))
191 191 text = Unicode()
192 192
193 193
194 194 class DisplayData(MimeBundle):
195 195 pass
196 196
197 197
198 198 class ExecuteResult(MimeBundle):
199 199 execution_count = Integer()
200 200
201 201 class HistoryReply(Reference):
202 202 history = List(List())
203 203
204 204
205 205 references = {
206 206 'execute_reply' : ExecuteReply(),
207 207 'inspect_reply' : InspectReply(),
208 208 'status' : Status(),
209 209 'complete_reply' : CompleteReply(),
210 210 'kernel_info_reply': KernelInfoReply(),
211 211 'is_complete_reply': IsCompleteReply(),
212 212 'execute_input' : ExecuteInput(),
213 213 'execute_result' : ExecuteResult(),
214 214 'history_reply' : HistoryReply(),
215 215 'error' : Error(),
216 216 'stream' : Stream(),
217 217 'display_data' : DisplayData(),
218 218 'header' : RHeader(),
219 219 }
220 220 """
221 221 Specifications of `content` part of the reply messages.
222 222 """
223 223
224 224
225 225 def validate_message(msg, msg_type=None, parent=None):
226 226 """validate a message
227 227
228 228 This is a generator, and must be iterated through to actually
229 229 trigger each test.
230 230
231 231 If msg_type and/or parent are given, the msg_type and/or parent msg_id
232 232 are compared with the given values.
233 233 """
234 234 RMessage().check(msg)
235 235 if msg_type:
236 236 nt.assert_equal(msg['msg_type'], msg_type)
237 237 if parent:
238 238 nt.assert_equal(msg['parent_header']['msg_id'], parent)
239 239 content = msg['content']
240 240 ref = references[msg['msg_type']]
241 241 ref.check(content)
242 242
243 243
244 244 #-----------------------------------------------------------------------------
245 245 # Tests
246 246 #-----------------------------------------------------------------------------
247 247
248 248 # Shell channel
249 249
250 250 def test_execute():
251 251 flush_channels()
252 252
253 253 msg_id = KC.execute(code='x=1')
254 254 reply = KC.get_shell_msg(timeout=TIMEOUT)
255 255 validate_message(reply, 'execute_reply', msg_id)
256 256
257 257
258 258 def test_execute_silent():
259 259 flush_channels()
260 260 msg_id, reply = execute(code='x=1', silent=True)
261 261
262 262 # flush status=idle
263 263 status = KC.iopub_channel.get_msg(timeout=TIMEOUT)
264 264 validate_message(status, 'status', msg_id)
265 265 nt.assert_equal(status['content']['execution_state'], 'idle')
266 266
267 267 nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1)
268 268 count = reply['execution_count']
269 269
270 270 msg_id, reply = execute(code='x=2', silent=True)
271 271
272 272 # flush status=idle
273 273 status = KC.iopub_channel.get_msg(timeout=TIMEOUT)
274 274 validate_message(status, 'status', msg_id)
275 275 nt.assert_equal(status['content']['execution_state'], 'idle')
276 276
277 277 nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1)
278 278 count_2 = reply['execution_count']
279 279 nt.assert_equal(count_2, count)
280 280
281 281
282 282 def test_execute_error():
283 283 flush_channels()
284 284
285 285 msg_id, reply = execute(code='1/0')
286 286 nt.assert_equal(reply['status'], 'error')
287 287 nt.assert_equal(reply['ename'], 'ZeroDivisionError')
288 288
289 289 error = KC.iopub_channel.get_msg(timeout=TIMEOUT)
290 290 validate_message(error, 'error', msg_id)
291 291
292 292
293 293 def test_execute_inc():
294 294 """execute request should increment execution_count"""
295 295 flush_channels()
296 296
297 297 msg_id, reply = execute(code='x=1')
298 298 count = reply['execution_count']
299 299
300 300 flush_channels()
301 301
302 302 msg_id, reply = execute(code='x=2')
303 303 count_2 = reply['execution_count']
304 304 nt.assert_equal(count_2, count+1)
305 305
306 def test_execute_stop_on_error():
307 """execute request should not abort execution queue with stop_on_error False"""
308 flush_channels()
309
310 KC.execute(code='raise IOError')
311 msg_id = KC.execute(code='print("Hello")')
312 KC.get_shell_msg(timeout=TIMEOUT)
313 reply = KC.get_shell_msg(timeout=TIMEOUT)
314 nt.assert_equal(reply['content']['status'], 'aborted')
315
316 flush_channels()
317
318 KC.execute(code='raise IOError', stop_on_error=False)
319 msg_id = KC.execute(code='print("Hello")')
320 KC.get_shell_msg(timeout=TIMEOUT)
321 reply = KC.get_shell_msg(timeout=TIMEOUT)
322 nt.assert_equal(reply['content']['status'], 'ok')
323
306 324
307 325 def test_user_expressions():
308 326 flush_channels()
309 327
310 328 msg_id, reply = execute(code='x=1', user_expressions=dict(foo='x+1'))
311 329 user_expressions = reply['user_expressions']
312 330 nt.assert_equal(user_expressions, {u'foo': {
313 331 u'status': u'ok',
314 332 u'data': {u'text/plain': u'2'},
315 333 u'metadata': {},
316 334 }})
317 335
318 336
319 337 def test_user_expressions_fail():
320 338 flush_channels()
321 339
322 340 msg_id, reply = execute(code='x=0', user_expressions=dict(foo='nosuchname'))
323 341 user_expressions = reply['user_expressions']
324 342 foo = user_expressions['foo']
325 343 nt.assert_equal(foo['status'], 'error')
326 344 nt.assert_equal(foo['ename'], 'NameError')
327 345
328 346
329 347 def test_oinfo():
330 348 flush_channels()
331 349
332 350 msg_id = KC.inspect('a')
333 351 reply = KC.get_shell_msg(timeout=TIMEOUT)
334 352 validate_message(reply, 'inspect_reply', msg_id)
335 353
336 354
337 355 def test_oinfo_found():
338 356 flush_channels()
339 357
340 358 msg_id, reply = execute(code='a=5')
341 359
342 360 msg_id = KC.inspect('a')
343 361 reply = KC.get_shell_msg(timeout=TIMEOUT)
344 362 validate_message(reply, 'inspect_reply', msg_id)
345 363 content = reply['content']
346 364 assert content['found']
347 365 text = content['data']['text/plain']
348 366 nt.assert_in('Type:', text)
349 367 nt.assert_in('Docstring:', text)
350 368
351 369
352 370 def test_oinfo_detail():
353 371 flush_channels()
354 372
355 373 msg_id, reply = execute(code='ip=get_ipython()')
356 374
357 375 msg_id = KC.inspect('ip.object_inspect', cursor_pos=10, detail_level=1)
358 376 reply = KC.get_shell_msg(timeout=TIMEOUT)
359 377 validate_message(reply, 'inspect_reply', msg_id)
360 378 content = reply['content']
361 379 assert content['found']
362 380 text = content['data']['text/plain']
363 381 nt.assert_in('Definition:', text)
364 382 nt.assert_in('Source:', text)
365 383
366 384
367 385 def test_oinfo_not_found():
368 386 flush_channels()
369 387
370 388 msg_id = KC.inspect('dne')
371 389 reply = KC.get_shell_msg(timeout=TIMEOUT)
372 390 validate_message(reply, 'inspect_reply', msg_id)
373 391 content = reply['content']
374 392 nt.assert_false(content['found'])
375 393
376 394
377 395 def test_complete():
378 396 flush_channels()
379 397
380 398 msg_id, reply = execute(code="alpha = albert = 5")
381 399
382 400 msg_id = KC.complete('al', 2)
383 401 reply = KC.get_shell_msg(timeout=TIMEOUT)
384 402 validate_message(reply, 'complete_reply', msg_id)
385 403 matches = reply['content']['matches']
386 404 for name in ('alpha', 'albert'):
387 405 nt.assert_in(name, matches)
388 406
389 407
390 408 def test_kernel_info_request():
391 409 flush_channels()
392 410
393 411 msg_id = KC.kernel_info()
394 412 reply = KC.get_shell_msg(timeout=TIMEOUT)
395 413 validate_message(reply, 'kernel_info_reply', msg_id)
396 414
397 415
398 416 def test_single_payload():
399 417 flush_channels()
400 418 msg_id, reply = execute(code="for i in range(3):\n"+
401 419 " x=range?\n")
402 420 payload = reply['payload']
403 421 next_input_pls = [pl for pl in payload if pl["source"] == "set_next_input"]
404 422 nt.assert_equal(len(next_input_pls), 1)
405 423
406 424 def test_is_complete():
407 425 flush_channels()
408 426
409 427 msg_id = KC.is_complete("a = 1")
410 428 reply = KC.get_shell_msg(timeout=TIMEOUT)
411 429 validate_message(reply, 'is_complete_reply', msg_id)
412 430
413 431 def test_history_range():
414 432 flush_channels()
415 433
416 434 msg_id_exec = KC.execute(code='x=1', store_history = True)
417 435 reply_exec = KC.get_shell_msg(timeout=TIMEOUT)
418 436
419 437 msg_id = KC.history(hist_access_type = 'range', raw = True, output = True, start = 1, stop = 2, session = 0)
420 438 reply = KC.get_shell_msg(timeout=TIMEOUT)
421 439 validate_message(reply, 'history_reply', msg_id)
422 440 content = reply['content']
423 441 nt.assert_equal(len(content['history']), 1)
424 442
425 443 def test_history_tail():
426 444 flush_channels()
427 445
428 446 msg_id_exec = KC.execute(code='x=1', store_history = True)
429 447 reply_exec = KC.get_shell_msg(timeout=TIMEOUT)
430 448
431 449 msg_id = KC.history(hist_access_type = 'tail', raw = True, output = True, n = 1, session = 0)
432 450 reply = KC.get_shell_msg(timeout=TIMEOUT)
433 451 validate_message(reply, 'history_reply', msg_id)
434 452 content = reply['content']
435 453 nt.assert_equal(len(content['history']), 1)
436 454
437 455 def test_history_search():
438 456 flush_channels()
439 457
440 458 msg_id_exec = KC.execute(code='x=1', store_history = True)
441 459 reply_exec = KC.get_shell_msg(timeout=TIMEOUT)
442 460
443 461 msg_id = KC.history(hist_access_type = 'search', raw = True, output = True, n = 1, pattern = '*', session = 0)
444 462 reply = KC.get_shell_msg(timeout=TIMEOUT)
445 463 validate_message(reply, 'history_reply', msg_id)
446 464 content = reply['content']
447 465 nt.assert_equal(len(content['history']), 1)
448 466
449 467 # IOPub channel
450 468
451 469
452 470 def test_stream():
453 471 flush_channels()
454 472
455 473 msg_id, reply = execute("print('hi')")
456 474
457 475 stdout = KC.iopub_channel.get_msg(timeout=TIMEOUT)
458 476 validate_message(stdout, 'stream', msg_id)
459 477 content = stdout['content']
460 478 nt.assert_equal(content['text'], u'hi\n')
461 479
462 480
463 481 def test_display_data():
464 482 flush_channels()
465 483
466 484 msg_id, reply = execute("from IPython.core.display import display; display(1)")
467 485
468 486 display = KC.iopub_channel.get_msg(timeout=TIMEOUT)
469 487 validate_message(display, 'display_data', parent=msg_id)
470 488 data = display['content']['data']
471 489 nt.assert_equal(data['text/plain'], u'1')
472 490
@@ -1,699 +1,701 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 # This should be overridden by wrapper kernels that implement any real
64 64 # language.
65 65 language_info = {}
66 66
67 67 # any links that should go in the help menu
68 68 help_links = List()
69 69
70 70 # Private interface
71 71
72 72 _darwin_app_nap = Bool(True, config=True,
73 73 help="""Whether to use appnope for compatiblity with OS X App Nap.
74 74
75 75 Only affects OS X >= 10.9.
76 76 """
77 77 )
78 78
79 79 # track associations with current request
80 80 _allow_stdin = Bool(False)
81 81 _parent_header = Dict()
82 82 _parent_ident = Any(b'')
83 83 # Time to sleep after flushing the stdout/err buffers in each execute
84 84 # cycle. While this introduces a hard limit on the minimal latency of the
85 85 # execute cycle, it helps prevent output synchronization problems for
86 86 # clients.
87 87 # Units are in seconds. The minimum zmq latency on local host is probably
88 88 # ~150 microseconds, set this to 500us for now. We may need to increase it
89 89 # a little if it's not enough after more interactive testing.
90 90 _execute_sleep = Float(0.0005, config=True)
91 91
92 92 # Frequency of the kernel's event loop.
93 93 # Units are in seconds, kernel subclasses for GUI toolkits may need to
94 94 # adapt to milliseconds.
95 95 _poll_interval = Float(0.05, config=True)
96 96
97 97 # If the shutdown was requested over the network, we leave here the
98 98 # necessary reply message so it can be sent by our registered atexit
99 99 # handler. This ensures that the reply is only sent to clients truly at
100 100 # the end of our shutdown process (which happens after the underlying
101 101 # IPython shell's own shutdown).
102 102 _shutdown_message = None
103 103
104 104 # This is a dict of port number that the kernel is listening on. It is set
105 105 # by record_ports and used by connect_request.
106 106 _recorded_ports = Dict()
107 107
108 108 # set of aborted msg_ids
109 109 aborted = Set()
110 110
111 111 # Track execution count here. For IPython, we override this to use the
112 112 # execution count we store in the shell.
113 113 execution_count = 0
114 114
115 115
116 116 def __init__(self, **kwargs):
117 117 super(Kernel, self).__init__(**kwargs)
118 118
119 119 # Build dict of handlers for message types
120 120 msg_types = [ 'execute_request', 'complete_request',
121 121 'inspect_request', 'history_request',
122 122 'kernel_info_request',
123 123 'connect_request', 'shutdown_request',
124 124 'apply_request', 'is_complete_request',
125 125 ]
126 126 self.shell_handlers = {}
127 127 for msg_type in msg_types:
128 128 self.shell_handlers[msg_type] = getattr(self, msg_type)
129 129
130 130 control_msg_types = msg_types + [ 'clear_request', 'abort_request' ]
131 131 self.control_handlers = {}
132 132 for msg_type in control_msg_types:
133 133 self.control_handlers[msg_type] = getattr(self, msg_type)
134 134
135 135
136 136 def dispatch_control(self, msg):
137 137 """dispatch control requests"""
138 138 idents,msg = self.session.feed_identities(msg, copy=False)
139 139 try:
140 140 msg = self.session.deserialize(msg, content=True, copy=False)
141 141 except:
142 142 self.log.error("Invalid Control Message", exc_info=True)
143 143 return
144 144
145 145 self.log.debug("Control received: %s", msg)
146 146
147 147 # Set the parent message for side effects.
148 148 self.set_parent(idents, msg)
149 149 self._publish_status(u'busy')
150 150
151 151 header = msg['header']
152 152 msg_type = header['msg_type']
153 153
154 154 handler = self.control_handlers.get(msg_type, None)
155 155 if handler is None:
156 156 self.log.error("UNKNOWN CONTROL MESSAGE TYPE: %r", msg_type)
157 157 else:
158 158 try:
159 159 handler(self.control_stream, idents, msg)
160 160 except Exception:
161 161 self.log.error("Exception in control handler:", exc_info=True)
162 162
163 163 sys.stdout.flush()
164 164 sys.stderr.flush()
165 165 self._publish_status(u'idle')
166 166
167 167 def dispatch_shell(self, stream, msg):
168 168 """dispatch shell requests"""
169 169 # flush control requests first
170 170 if self.control_stream:
171 171 self.control_stream.flush()
172 172
173 173 idents,msg = self.session.feed_identities(msg, copy=False)
174 174 try:
175 175 msg = self.session.deserialize(msg, content=True, copy=False)
176 176 except:
177 177 self.log.error("Invalid Message", exc_info=True)
178 178 return
179 179
180 180 # Set the parent message for side effects.
181 181 self.set_parent(idents, msg)
182 182 self._publish_status(u'busy')
183 183
184 184 header = msg['header']
185 185 msg_id = header['msg_id']
186 186 msg_type = msg['header']['msg_type']
187 187
188 188 # Print some info about this message and leave a '--->' marker, so it's
189 189 # easier to trace visually the message chain when debugging. Each
190 190 # handler prints its message at the end.
191 191 self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type)
192 192 self.log.debug(' Content: %s\n --->\n ', msg['content'])
193 193
194 194 if msg_id in self.aborted:
195 195 self.aborted.remove(msg_id)
196 196 # is it safe to assume a msg_id will not be resubmitted?
197 197 reply_type = msg_type.split('_')[0] + '_reply'
198 198 status = {'status' : 'aborted'}
199 199 md = {'engine' : self.ident}
200 200 md.update(status)
201 201 self.session.send(stream, reply_type, metadata=md,
202 202 content=status, parent=msg, ident=idents)
203 203 return
204 204
205 205 handler = self.shell_handlers.get(msg_type, None)
206 206 if handler is None:
207 207 self.log.error("UNKNOWN MESSAGE TYPE: %r", msg_type)
208 208 else:
209 209 # ensure default_int_handler during handler call
210 210 sig = signal(SIGINT, default_int_handler)
211 211 self.log.debug("%s: %s", msg_type, msg)
212 212 try:
213 213 handler(stream, idents, msg)
214 214 except Exception:
215 215 self.log.error("Exception in message handler:", exc_info=True)
216 216 finally:
217 217 signal(SIGINT, sig)
218 218
219 219 sys.stdout.flush()
220 220 sys.stderr.flush()
221 221 self._publish_status(u'idle')
222 222
223 223 def enter_eventloop(self):
224 224 """enter eventloop"""
225 225 self.log.info("entering eventloop %s", self.eventloop)
226 226 for stream in self.shell_streams:
227 227 # flush any pending replies,
228 228 # which may be skipped by entering the eventloop
229 229 stream.flush(zmq.POLLOUT)
230 230 # restore default_int_handler
231 231 signal(SIGINT, default_int_handler)
232 232 while self.eventloop is not None:
233 233 try:
234 234 self.eventloop(self)
235 235 except KeyboardInterrupt:
236 236 # Ctrl-C shouldn't crash the kernel
237 237 self.log.error("KeyboardInterrupt caught in kernel")
238 238 continue
239 239 else:
240 240 # eventloop exited cleanly, this means we should stop (right?)
241 241 self.eventloop = None
242 242 break
243 243 self.log.info("exiting eventloop")
244 244
245 245 def start(self):
246 246 """register dispatchers for streams"""
247 247 if self.control_stream:
248 248 self.control_stream.on_recv(self.dispatch_control, copy=False)
249 249
250 250 def make_dispatcher(stream):
251 251 def dispatcher(msg):
252 252 return self.dispatch_shell(stream, msg)
253 253 return dispatcher
254 254
255 255 for s in self.shell_streams:
256 256 s.on_recv(make_dispatcher(s), copy=False)
257 257
258 258 # publish idle status
259 259 self._publish_status('starting')
260 260
261 261 def do_one_iteration(self):
262 262 """step eventloop just once"""
263 263 if self.control_stream:
264 264 self.control_stream.flush()
265 265 for stream in self.shell_streams:
266 266 # handle at most one request per iteration
267 267 stream.flush(zmq.POLLIN, 1)
268 268 stream.flush(zmq.POLLOUT)
269 269
270 270
271 271 def record_ports(self, ports):
272 272 """Record the ports that this kernel is using.
273 273
274 274 The creator of the Kernel instance must call this methods if they
275 275 want the :meth:`connect_request` method to return the port numbers.
276 276 """
277 277 self._recorded_ports = ports
278 278
279 279 #---------------------------------------------------------------------------
280 280 # Kernel request handlers
281 281 #---------------------------------------------------------------------------
282 282
283 283 def _make_metadata(self, other=None):
284 284 """init metadata dict, for execute/apply_reply"""
285 285 new_md = {
286 286 'dependencies_met' : True,
287 287 'engine' : self.ident,
288 288 'started': datetime.now(),
289 289 }
290 290 if other:
291 291 new_md.update(other)
292 292 return new_md
293 293
294 294 def _publish_execute_input(self, code, parent, execution_count):
295 295 """Publish the code request on the iopub stream."""
296 296
297 297 self.session.send(self.iopub_socket, u'execute_input',
298 298 {u'code':code, u'execution_count': execution_count},
299 299 parent=parent, ident=self._topic('execute_input')
300 300 )
301 301
302 302 def _publish_status(self, status, parent=None):
303 303 """send status (busy/idle) on IOPub"""
304 304 self.session.send(self.iopub_socket,
305 305 u'status',
306 306 {u'execution_state': status},
307 307 parent=parent or self._parent_header,
308 308 ident=self._topic('status'),
309 309 )
310 310
311 311 def set_parent(self, ident, parent):
312 312 """Set the current parent_header
313 313
314 314 Side effects (IOPub messages) and replies are associated with
315 315 the request that caused them via the parent_header.
316 316
317 317 The parent identity is used to route input_request messages
318 318 on the stdin channel.
319 319 """
320 320 self._parent_ident = ident
321 321 self._parent_header = parent
322 322
323 323 def send_response(self, stream, msg_or_type, content=None, ident=None,
324 324 buffers=None, track=False, header=None, metadata=None):
325 325 """Send a response to the message we're currently processing.
326 326
327 327 This accepts all the parameters of :meth:`IPython.kernel.zmq.session.Session.send`
328 328 except ``parent``.
329 329
330 330 This relies on :meth:`set_parent` having been called for the current
331 331 message.
332 332 """
333 333 return self.session.send(stream, msg_or_type, content, self._parent_header,
334 334 ident, buffers, track, header, metadata)
335 335
336 336 def execute_request(self, stream, ident, parent):
337 337 """handle an execute_request"""
338 338
339 339 try:
340 340 content = parent[u'content']
341 341 code = py3compat.cast_unicode_py2(content[u'code'])
342 342 silent = content[u'silent']
343 343 store_history = content.get(u'store_history', not silent)
344 344 user_expressions = content.get('user_expressions', {})
345 345 allow_stdin = content.get('allow_stdin', False)
346 346 except:
347 347 self.log.error("Got bad msg: ")
348 348 self.log.error("%s", parent)
349 349 return
350 350
351 stop_on_error = content.get('stop_on_error', True)
352
351 353 md = self._make_metadata(parent['metadata'])
352 354
353 355 # Re-broadcast our input for the benefit of listening clients, and
354 356 # start computing output
355 357 if not silent:
356 358 self.execution_count += 1
357 359 self._publish_execute_input(code, parent, self.execution_count)
358 360
359 361 reply_content = self.do_execute(code, silent, store_history,
360 362 user_expressions, allow_stdin)
361 363
362 364 # Flush output before sending the reply.
363 365 sys.stdout.flush()
364 366 sys.stderr.flush()
365 367 # FIXME: on rare occasions, the flush doesn't seem to make it to the
366 368 # clients... This seems to mitigate the problem, but we definitely need
367 369 # to better understand what's going on.
368 370 if self._execute_sleep:
369 371 time.sleep(self._execute_sleep)
370 372
371 373 # Send the reply.
372 374 reply_content = json_clean(reply_content)
373 375
374 376 md['status'] = reply_content['status']
375 377 if reply_content['status'] == 'error' and \
376 378 reply_content['ename'] == 'UnmetDependency':
377 379 md['dependencies_met'] = False
378 380
379 381 reply_msg = self.session.send(stream, u'execute_reply',
380 382 reply_content, parent, metadata=md,
381 383 ident=ident)
382 384
383 385 self.log.debug("%s", reply_msg)
384 386
385 if not silent and reply_msg['content']['status'] == u'error':
387 if not silent and reply_msg['content']['status'] == u'error' and stop_on_error:
386 388 self._abort_queues()
387 389
388 390 def do_execute(self, code, silent, store_history=True,
389 user_experssions=None, allow_stdin=False):
391 user_expressions=None, allow_stdin=False):
390 392 """Execute user code. Must be overridden by subclasses.
391 393 """
392 394 raise NotImplementedError
393 395
394 396 def complete_request(self, stream, ident, parent):
395 397 content = parent['content']
396 398 code = content['code']
397 399 cursor_pos = content['cursor_pos']
398 400
399 401 matches = self.do_complete(code, cursor_pos)
400 402 matches = json_clean(matches)
401 403 completion_msg = self.session.send(stream, 'complete_reply',
402 404 matches, parent, ident)
403 405 self.log.debug("%s", completion_msg)
404 406
405 407 def do_complete(self, code, cursor_pos):
406 408 """Override in subclasses to find completions.
407 409 """
408 410 return {'matches' : [],
409 411 'cursor_end' : cursor_pos,
410 412 'cursor_start' : cursor_pos,
411 413 'metadata' : {},
412 414 'status' : 'ok'}
413 415
414 416 def inspect_request(self, stream, ident, parent):
415 417 content = parent['content']
416 418
417 419 reply_content = self.do_inspect(content['code'], content['cursor_pos'],
418 420 content.get('detail_level', 0))
419 421 # Before we send this object over, we scrub it for JSON usage
420 422 reply_content = json_clean(reply_content)
421 423 msg = self.session.send(stream, 'inspect_reply',
422 424 reply_content, parent, ident)
423 425 self.log.debug("%s", msg)
424 426
425 427 def do_inspect(self, code, cursor_pos, detail_level=0):
426 428 """Override in subclasses to allow introspection.
427 429 """
428 430 return {'status': 'ok', 'data':{}, 'metadata':{}, 'found':False}
429 431
430 432 def history_request(self, stream, ident, parent):
431 433 content = parent['content']
432 434
433 435 reply_content = self.do_history(**content)
434 436
435 437 reply_content = json_clean(reply_content)
436 438 msg = self.session.send(stream, 'history_reply',
437 439 reply_content, parent, ident)
438 440 self.log.debug("%s", msg)
439 441
440 442 def do_history(self, hist_access_type, output, raw, session=None, start=None,
441 443 stop=None, n=None, pattern=None, unique=False):
442 444 """Override in subclasses to access history.
443 445 """
444 446 return {'history': []}
445 447
446 448 def connect_request(self, stream, ident, parent):
447 449 if self._recorded_ports is not None:
448 450 content = self._recorded_ports.copy()
449 451 else:
450 452 content = {}
451 453 msg = self.session.send(stream, 'connect_reply',
452 454 content, parent, ident)
453 455 self.log.debug("%s", msg)
454 456
455 457 @property
456 458 def kernel_info(self):
457 459 return {
458 460 'protocol_version': release.kernel_protocol_version,
459 461 'implementation': self.implementation,
460 462 'implementation_version': self.implementation_version,
461 463 'language_info': self.language_info,
462 464 'banner': self.banner,
463 465 'help_links': self.help_links,
464 466 }
465 467
466 468 def kernel_info_request(self, stream, ident, parent):
467 469 msg = self.session.send(stream, 'kernel_info_reply',
468 470 self.kernel_info, parent, ident)
469 471 self.log.debug("%s", msg)
470 472
471 473 def shutdown_request(self, stream, ident, parent):
472 474 content = self.do_shutdown(parent['content']['restart'])
473 475 self.session.send(stream, u'shutdown_reply', content, parent, ident=ident)
474 476 # same content, but different msg_id for broadcasting on IOPub
475 477 self._shutdown_message = self.session.msg(u'shutdown_reply',
476 478 content, parent
477 479 )
478 480
479 481 self._at_shutdown()
480 482 # call sys.exit after a short delay
481 483 loop = ioloop.IOLoop.instance()
482 484 loop.add_timeout(time.time()+0.1, loop.stop)
483 485
484 486 def do_shutdown(self, restart):
485 487 """Override in subclasses to do things when the frontend shuts down the
486 488 kernel.
487 489 """
488 490 return {'status': 'ok', 'restart': restart}
489 491
490 492 def is_complete_request(self, stream, ident, parent):
491 493 content = parent['content']
492 494 code = content['code']
493 495
494 496 reply_content = self.do_is_complete(code)
495 497 reply_content = json_clean(reply_content)
496 498 reply_msg = self.session.send(stream, 'is_complete_reply',
497 499 reply_content, parent, ident)
498 500 self.log.debug("%s", reply_msg)
499 501
500 502 def do_is_complete(self, code):
501 503 """Override in subclasses to find completions.
502 504 """
503 505 return {'status' : 'unknown',
504 506 }
505 507
506 508 #---------------------------------------------------------------------------
507 509 # Engine methods
508 510 #---------------------------------------------------------------------------
509 511
510 512 def apply_request(self, stream, ident, parent):
511 513 try:
512 514 content = parent[u'content']
513 515 bufs = parent[u'buffers']
514 516 msg_id = parent['header']['msg_id']
515 517 except:
516 518 self.log.error("Got bad msg: %s", parent, exc_info=True)
517 519 return
518 520
519 521 md = self._make_metadata(parent['metadata'])
520 522
521 523 reply_content, result_buf = self.do_apply(content, bufs, msg_id, md)
522 524
523 525 # put 'ok'/'error' status in header, for scheduler introspection:
524 526 md['status'] = reply_content['status']
525 527
526 528 # flush i/o
527 529 sys.stdout.flush()
528 530 sys.stderr.flush()
529 531
530 532 self.session.send(stream, u'apply_reply', reply_content,
531 533 parent=parent, ident=ident,buffers=result_buf, metadata=md)
532 534
533 535 def do_apply(self, content, bufs, msg_id, reply_metadata):
534 536 """Override in subclasses to support the IPython parallel framework.
535 537 """
536 538 raise NotImplementedError
537 539
538 540 #---------------------------------------------------------------------------
539 541 # Control messages
540 542 #---------------------------------------------------------------------------
541 543
542 544 def abort_request(self, stream, ident, parent):
543 545 """abort a specific msg by id"""
544 546 msg_ids = parent['content'].get('msg_ids', None)
545 547 if isinstance(msg_ids, string_types):
546 548 msg_ids = [msg_ids]
547 549 if not msg_ids:
548 550 self._abort_queues()
549 551 for mid in msg_ids:
550 552 self.aborted.add(str(mid))
551 553
552 554 content = dict(status='ok')
553 555 reply_msg = self.session.send(stream, 'abort_reply', content=content,
554 556 parent=parent, ident=ident)
555 557 self.log.debug("%s", reply_msg)
556 558
557 559 def clear_request(self, stream, idents, parent):
558 560 """Clear our namespace."""
559 561 content = self.do_clear()
560 562 self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
561 563 content = content)
562 564
563 565 def do_clear(self):
564 566 """Override in subclasses to clear the namespace
565 567
566 568 This is only required for IPython.parallel.
567 569 """
568 570 raise NotImplementedError
569 571
570 572 #---------------------------------------------------------------------------
571 573 # Protected interface
572 574 #---------------------------------------------------------------------------
573 575
574 576 def _topic(self, topic):
575 577 """prefixed topic for IOPub messages"""
576 578 if self.int_id >= 0:
577 579 base = "engine.%i" % self.int_id
578 580 else:
579 581 base = "kernel.%s" % self.ident
580 582
581 583 return py3compat.cast_bytes("%s.%s" % (base, topic))
582 584
583 585 def _abort_queues(self):
584 586 for stream in self.shell_streams:
585 587 if stream:
586 588 self._abort_queue(stream)
587 589
588 590 def _abort_queue(self, stream):
589 591 poller = zmq.Poller()
590 592 poller.register(stream.socket, zmq.POLLIN)
591 593 while True:
592 594 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
593 595 if msg is None:
594 596 return
595 597
596 598 self.log.info("Aborting:")
597 599 self.log.info("%s", msg)
598 600 msg_type = msg['header']['msg_type']
599 601 reply_type = msg_type.split('_')[0] + '_reply'
600 602
601 603 status = {'status' : 'aborted'}
602 604 md = {'engine' : self.ident}
603 605 md.update(status)
604 606 reply_msg = self.session.send(stream, reply_type, metadata=md,
605 607 content=status, parent=msg, ident=idents)
606 608 self.log.debug("%s", reply_msg)
607 609 # We need to wait a bit for requests to come in. This can probably
608 610 # be set shorter for true asynchronous clients.
609 611 poller.poll(50)
610 612
611 613
612 614 def _no_raw_input(self):
613 615 """Raise StdinNotImplentedError if active frontend doesn't support
614 616 stdin."""
615 617 raise StdinNotImplementedError("raw_input was called, but this "
616 618 "frontend does not support stdin.")
617 619
618 620 def getpass(self, prompt=''):
619 621 """Forward getpass to frontends
620 622
621 623 Raises
622 624 ------
623 625 StdinNotImplentedError if active frontend doesn't support stdin.
624 626 """
625 627 if not self._allow_stdin:
626 628 raise StdinNotImplementedError(
627 629 "getpass was called, but this frontend does not support input requests."
628 630 )
629 631 return self._input_request(prompt,
630 632 self._parent_ident,
631 633 self._parent_header,
632 634 password=True,
633 635 )
634 636
635 637 def raw_input(self, prompt=''):
636 638 """Forward raw_input to frontends
637 639
638 640 Raises
639 641 ------
640 642 StdinNotImplentedError if active frontend doesn't support stdin.
641 643 """
642 644 if not self._allow_stdin:
643 645 raise StdinNotImplementedError(
644 646 "raw_input was called, but this frontend does not support input requests."
645 647 )
646 648 return self._input_request(prompt,
647 649 self._parent_ident,
648 650 self._parent_header,
649 651 password=False,
650 652 )
651 653
652 654 def _input_request(self, prompt, ident, parent, password=False):
653 655 # Flush output before making the request.
654 656 sys.stderr.flush()
655 657 sys.stdout.flush()
656 658 # flush the stdin socket, to purge stale replies
657 659 while True:
658 660 try:
659 661 self.stdin_socket.recv_multipart(zmq.NOBLOCK)
660 662 except zmq.ZMQError as e:
661 663 if e.errno == zmq.EAGAIN:
662 664 break
663 665 else:
664 666 raise
665 667
666 668 # Send the input request.
667 669 content = json_clean(dict(prompt=prompt, password=password))
668 670 self.session.send(self.stdin_socket, u'input_request', content, parent,
669 671 ident=ident)
670 672
671 673 # Await a response.
672 674 while True:
673 675 try:
674 676 ident, reply = self.session.recv(self.stdin_socket, 0)
675 677 except Exception:
676 678 self.log.warn("Invalid Message:", exc_info=True)
677 679 except KeyboardInterrupt:
678 680 # re-raise KeyboardInterrupt, to truncate traceback
679 681 raise KeyboardInterrupt
680 682 else:
681 683 break
682 684 try:
683 685 value = py3compat.unicode_to_str(reply['content']['value'])
684 686 except:
685 687 self.log.error("Bad input_reply: %s", parent)
686 688 value = ''
687 689 if value == '\x04':
688 690 # EOF
689 691 raise EOFError
690 692 return value
691 693
692 694 def _at_shutdown(self):
693 695 """Actions taken at shutdown by the kernel, called by python's atexit.
694 696 """
695 697 # io.rprint("Kernel at_shutdown") # dbg
696 698 if self._shutdown_message is not None:
697 699 self.session.send(self.iopub_socket, self._shutdown_message, ident=self._topic('shutdown'))
698 700 self.log.debug("%s", self._shutdown_message)
699 701 [ s.flush(zmq.POLLOUT) for s in self.shell_streams ]
@@ -1,1204 +1,1208 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
285 # A boolean flag, which, if True, does not abort the execution queue, if an exception is encountered.
286 # This allows the queued execution of multiple execute_requests, even if they generate exceptions.
287 'stop_on_error' : False,
284 288 }
285 289
286 290 .. versionchanged:: 5.0
287 291
288 292 ``user_variables`` removed, because it is redundant with user_expressions.
289 293
290 294 The ``code`` field contains a single string (possibly multiline) to be executed.
291 295
292 296 The ``user_expressions`` field deserves a detailed explanation. In the past, IPython had
293 297 the notion of a prompt string that allowed arbitrary code to be evaluated, and
294 298 this was put to good use by many in creating prompts that displayed system
295 299 status, path information, and even more esoteric uses like remote instrument
296 300 status acquired over the network. But now that IPython has a clean separation
297 301 between the kernel and the clients, the kernel has no prompt knowledge; prompts
298 302 are a frontend feature, and it should be even possible for different
299 303 frontends to display different prompts while interacting with the same kernel.
300 304 ``user_expressions`` can be used to retrieve this information.
301 305
302 306 Any error in evaluating any expression in ``user_expressions`` will result in
303 307 only that key containing a standard error message, of the form::
304 308
305 309 {
306 310 'status' : 'error',
307 311 'ename' : 'NameError',
308 312 'evalue' : 'foo',
309 313 'traceback' : ...
310 314 }
311 315
312 316 .. Note::
313 317
314 318 In order to obtain the current execution counter for the purposes of
315 319 displaying input prompts, frontends may make an execution request with an
316 320 empty code string and ``silent=True``.
317 321
318 322 Upon completion of the execution request, the kernel *always* sends a reply,
319 323 with a status code indicating what happened and additional data depending on
320 324 the outcome. See :ref:`below <execution_results>` for the possible return
321 325 codes and associated data.
322 326
323 327 .. seealso::
324 328
325 329 :ref:`execution_semantics`
326 330
327 331 .. _execution_counter:
328 332
329 333 Execution counter (prompt number)
330 334 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
331 335
332 336 The kernel should have a single, monotonically increasing counter of all execution
333 337 requests that are made with ``store_history=True``. This counter is used to populate
334 338 the ``In[n]`` and ``Out[n]`` prompts. The value of this counter will be returned as the
335 339 ``execution_count`` field of all ``execute_reply`` and ``execute_input`` messages.
336 340
337 341 .. _execution_results:
338 342
339 343 Execution results
340 344 ~~~~~~~~~~~~~~~~~
341 345
342 346 Message type: ``execute_reply``::
343 347
344 348 content = {
345 349 # One of: 'ok' OR 'error' OR 'abort'
346 350 'status' : str,
347 351
348 352 # The global kernel counter that increases by one with each request that
349 353 # stores history. This will typically be used by clients to display
350 354 # prompt numbers to the user. If the request did not store history, this will
351 355 # be the current value of the counter in the kernel.
352 356 'execution_count' : int,
353 357 }
354 358
355 359 When status is 'ok', the following extra fields are present::
356 360
357 361 {
358 362 # 'payload' will be a list of payload dicts, and is optional.
359 363 # payloads are considered deprecated.
360 364 # The only requirement of each payload dict is that it have a 'source' key,
361 365 # which is a string classifying the payload (e.g. 'page').
362 366
363 367 'payload' : list(dict),
364 368
365 369 # Results for the user_expressions.
366 370 'user_expressions' : dict,
367 371 }
368 372
369 373 .. versionchanged:: 5.0
370 374
371 375 ``user_variables`` is removed, use user_expressions instead.
372 376
373 377 When status is 'error', the following extra fields are present::
374 378
375 379 {
376 380 'ename' : str, # Exception name, as a string
377 381 'evalue' : str, # Exception value, as a string
378 382
379 383 # The traceback will contain a list of frames, represented each as a
380 384 # string. For now we'll stick to the existing design of ultraTB, which
381 385 # controls exception level of detail statefully. But eventually we'll
382 386 # want to grow into a model where more information is collected and
383 387 # packed into the traceback object, with clients deciding how little or
384 388 # how much of it to unpack. But for now, let's start with a simple list
385 389 # of strings, since that requires only minimal changes to ultratb as
386 390 # written.
387 391 'traceback' : list,
388 392 }
389 393
390 394
391 395 When status is 'abort', there are for now no additional data fields. This
392 396 happens when the kernel was interrupted by a signal.
393 397
394 398 Payloads
395 399 ********
396 400
397 401 .. admonition:: Execution payloads
398 402
399 403 Payloads are considered deprecated, though their replacement is not yet implemented.
400 404
401 405 Payloads are a way to trigger frontend actions from the kernel. Current payloads:
402 406
403 407 **page**: display data in a pager.
404 408
405 409 Pager output is used for introspection, or other displayed information that's not considered output.
406 410 Pager payloads are generally displayed in a separate pane, that can be viewed alongside code,
407 411 and are not included in notebook documents.
408 412
409 413 .. sourcecode:: python
410 414
411 415 {
412 416 "source": "page",
413 417 # mime-bundle of data to display in the pager.
414 418 # Must include text/plain.
415 419 "data": mimebundle,
416 420 # line offset to start from
417 421 "start": int,
418 422 }
419 423
420 424 **set_next_input**: create a new output
421 425
422 426 used to create new cells in the notebook,
423 427 or set the next input in a console interface.
424 428 The main example being ``%load``.
425 429
426 430 .. sourcecode:: python
427 431
428 432 {
429 433 "source": "set_next_input",
430 434 # the text contents of the cell to create
431 435 "text": "some cell content",
432 436 # If true, replace the current cell in document UIs instead of inserting
433 437 # a cell. Ignored in console UIs.
434 438 "replace": bool,
435 439 }
436 440
437 441 **edit**: open a file for editing.
438 442
439 443 Triggered by `%edit`. Only the QtConsole currently supports edit payloads.
440 444
441 445 .. sourcecode:: python
442 446
443 447 {
444 448 "source": "edit",
445 449 "filename": "/path/to/file.py", # the file to edit
446 450 "line_number": int, # the line number to start with
447 451 }
448 452
449 453 **ask_exit**: instruct the frontend to prompt the user for exit
450 454
451 455 Allows the kernel to request exit, e.g. via ``%exit`` in IPython.
452 456 Only for console frontends.
453 457
454 458 .. sourcecode:: python
455 459
456 460 {
457 461 "source": "ask_exit",
458 462 # whether the kernel should be left running, only closing the client
459 463 "keepkernel": bool,
460 464 }
461 465
462 466
463 467 .. _msging_inspection:
464 468
465 469 Introspection
466 470 -------------
467 471
468 472 Code can be inspected to show useful information to the user.
469 473 It is up to the Kernel to decide what information should be displayed, and its formatting.
470 474
471 475 Message type: ``inspect_request``::
472 476
473 477 content = {
474 478 # The code context in which introspection is requested
475 479 # this may be up to an entire multiline cell.
476 480 'code' : str,
477 481
478 482 # The cursor position within 'code' (in unicode characters) where inspection is requested
479 483 'cursor_pos' : int,
480 484
481 485 # The level of detail desired. In IPython, the default (0) is equivalent to typing
482 486 # 'x?' at the prompt, 1 is equivalent to 'x??'.
483 487 # The difference is up to kernels, but in IPython level 1 includes the source code
484 488 # if available.
485 489 'detail_level' : 0 or 1,
486 490 }
487 491
488 492 .. versionchanged:: 5.0
489 493
490 494 ``object_info_request`` renamed to ``inspect_request``.
491 495
492 496 .. versionchanged:: 5.0
493 497
494 498 ``name`` key replaced with ``code`` and ``cursor_pos``,
495 499 moving the lexing responsibility to the kernel.
496 500
497 501 The reply is a mime-bundle, like a `display_data`_ message,
498 502 which should be a formatted representation of information about the context.
499 503 In the notebook, this is used to show tooltips over function calls, etc.
500 504
501 505 Message type: ``inspect_reply``::
502 506
503 507 content = {
504 508 # 'ok' if the request succeeded or 'error', with error information as in all other replies.
505 509 'status' : 'ok',
506 510
507 511 # data can be empty if nothing is found
508 512 'data' : dict,
509 513 'metadata' : dict,
510 514 }
511 515
512 516 .. versionchanged:: 5.0
513 517
514 518 ``object_info_reply`` renamed to ``inspect_reply``.
515 519
516 520 .. versionchanged:: 5.0
517 521
518 522 Reply is changed from structured data to a mime bundle, allowing formatting decisions to be made by the kernel.
519 523
520 524 .. _msging_completion:
521 525
522 526 Completion
523 527 ----------
524 528
525 529 Message type: ``complete_request``::
526 530
527 531 content = {
528 532 # The code context in which completion is requested
529 533 # this may be up to an entire multiline cell, such as
530 534 # 'foo = a.isal'
531 535 'code' : str,
532 536
533 537 # The cursor position within 'code' (in unicode characters) where completion is requested
534 538 'cursor_pos' : int,
535 539 }
536 540
537 541 .. versionchanged:: 5.0
538 542
539 543 ``line``, ``block``, and ``text`` keys are removed in favor of a single ``code`` for context.
540 544 Lexing is up to the kernel.
541 545
542 546
543 547 Message type: ``complete_reply``::
544 548
545 549 content = {
546 550 # The list of all matches to the completion request, such as
547 551 # ['a.isalnum', 'a.isalpha'] for the above example.
548 552 'matches' : list,
549 553
550 554 # The range of text that should be replaced by the above matches when a completion is accepted.
551 555 # typically cursor_end is the same as cursor_pos in the request.
552 556 'cursor_start' : int,
553 557 'cursor_end' : int,
554 558
555 559 # Information that frontend plugins might use for extra display information about completions.
556 560 'metadata' : dict,
557 561
558 562 # status should be 'ok' unless an exception was raised during the request,
559 563 # in which case it should be 'error', along with the usual error message content
560 564 # in other messages.
561 565 'status' : 'ok'
562 566 }
563 567
564 568 .. versionchanged:: 5.0
565 569
566 570 - ``matched_text`` is removed in favor of ``cursor_start`` and ``cursor_end``.
567 571 - ``metadata`` is added for extended information.
568 572
569 573 .. _msging_history:
570 574
571 575 History
572 576 -------
573 577
574 578 For clients to explicitly request history from a kernel. The kernel has all
575 579 the actual execution history stored in a single location, so clients can
576 580 request it from the kernel when needed.
577 581
578 582 Message type: ``history_request``::
579 583
580 584 content = {
581 585
582 586 # If True, also return output history in the resulting dict.
583 587 'output' : bool,
584 588
585 589 # If True, return the raw input history, else the transformed input.
586 590 'raw' : bool,
587 591
588 592 # So far, this can be 'range', 'tail' or 'search'.
589 593 'hist_access_type' : str,
590 594
591 595 # If hist_access_type is 'range', get a range of input cells. session can
592 596 # be a positive session number, or a negative number to count back from
593 597 # the current session.
594 598 'session' : int,
595 599 # start and stop are line numbers within that session.
596 600 'start' : int,
597 601 'stop' : int,
598 602
599 603 # If hist_access_type is 'tail' or 'search', get the last n cells.
600 604 'n' : int,
601 605
602 606 # If hist_access_type is 'search', get cells matching the specified glob
603 607 # pattern (with * and ? as wildcards).
604 608 'pattern' : str,
605 609
606 610 # If hist_access_type is 'search' and unique is true, do not
607 611 # include duplicated history. Default is false.
608 612 'unique' : bool,
609 613
610 614 }
611 615
612 616 .. versionadded:: 4.0
613 617 The key ``unique`` for ``history_request``.
614 618
615 619 Message type: ``history_reply``::
616 620
617 621 content = {
618 622 # A list of 3 tuples, either:
619 623 # (session, line_number, input) or
620 624 # (session, line_number, (input, output)),
621 625 # depending on whether output was False or True, respectively.
622 626 'history' : list,
623 627 }
624 628
625 629 .. _msging_is_complete:
626 630
627 631 Code completeness
628 632 -----------------
629 633
630 634 .. versionadded:: 5.0
631 635
632 636 When the user enters a line in a console style interface, the console must
633 637 decide whether to immediately execute the current code, or whether to show a
634 638 continuation prompt for further input. For instance, in Python ``a = 5`` would
635 639 be executed immediately, while ``for i in range(5):`` would expect further input.
636 640
637 641 There are four possible replies:
638 642
639 643 - *complete* code is ready to be executed
640 644 - *incomplete* code should prompt for another line
641 645 - *invalid* code will typically be sent for execution, so that the user sees the
642 646 error soonest.
643 647 - *unknown* - if the kernel is not able to determine this. The frontend should
644 648 also handle the kernel not replying promptly. It may default to sending the
645 649 code for execution, or it may implement simple fallback heuristics for whether
646 650 to execute the code (e.g. execute after a blank line).
647 651
648 652 Frontends may have ways to override this, forcing the code to be sent for
649 653 execution or forcing a continuation prompt.
650 654
651 655 Message type: ``is_complete_request``::
652 656
653 657 content = {
654 658 # The code entered so far as a multiline string
655 659 'code' : str,
656 660 }
657 661
658 662 Message type: ``is_complete_reply``::
659 663
660 664 content = {
661 665 # One of 'complete', 'incomplete', 'invalid', 'unknown'
662 666 'status' : str,
663 667
664 668 # If status is 'incomplete', indent should contain the characters to use
665 669 # to indent the next line. This is only a hint: frontends may ignore it
666 670 # and use their own autoindentation rules. For other statuses, this
667 671 # field does not exist.
668 672 'indent': str,
669 673 }
670 674
671 675 Connect
672 676 -------
673 677
674 678 When a client connects to the request/reply socket of the kernel, it can issue
675 679 a connect request to get basic information about the kernel, such as the ports
676 680 the other ZeroMQ sockets are listening on. This allows clients to only have
677 681 to know about a single port (the shell channel) to connect to a kernel.
678 682
679 683 Message type: ``connect_request``::
680 684
681 685 content = {
682 686 }
683 687
684 688 Message type: ``connect_reply``::
685 689
686 690 content = {
687 691 'shell_port' : int, # The port the shell ROUTER socket is listening on.
688 692 'iopub_port' : int, # The port the PUB socket is listening on.
689 693 'stdin_port' : int, # The port the stdin ROUTER socket is listening on.
690 694 'hb_port' : int, # The port the heartbeat socket is listening on.
691 695 }
692 696
693 697 .. _msging_kernel_info:
694 698
695 699 Kernel info
696 700 -----------
697 701
698 702 If a client needs to know information about the kernel, it can
699 703 make a request of the kernel's information.
700 704 This message can be used to fetch core information of the
701 705 kernel, including language (e.g., Python), language version number and
702 706 IPython version number, and the IPython message spec version number.
703 707
704 708 Message type: ``kernel_info_request``::
705 709
706 710 content = {
707 711 }
708 712
709 713 Message type: ``kernel_info_reply``::
710 714
711 715 content = {
712 716 # Version of messaging protocol.
713 717 # The first integer indicates major version. It is incremented when
714 718 # there is any backward incompatible change.
715 719 # The second integer indicates minor version. It is incremented when
716 720 # there is any backward compatible change.
717 721 'protocol_version': 'X.Y.Z',
718 722
719 723 # The kernel implementation name
720 724 # (e.g. 'ipython' for the IPython kernel)
721 725 'implementation': str,
722 726
723 727 # Implementation version number.
724 728 # The version number of the kernel's implementation
725 729 # (e.g. IPython.__version__ for the IPython kernel)
726 730 'implementation_version': 'X.Y.Z',
727 731
728 732 # Information about the language of code for the kernel
729 733 'language_info': {
730 734 # Name of the programming language in which kernel is implemented.
731 735 # Kernel included in IPython returns 'python'.
732 736 'name': str,
733 737
734 738 # Language version number.
735 739 # It is Python version number (e.g., '2.7.3') for the kernel
736 740 # included in IPython.
737 741 'version': 'X.Y.Z',
738 742
739 743 # mimetype for script files in this language
740 744 'mimetype': str,
741 745
742 746 # Extension without the dot, e.g. 'py'
743 747 'file_extension': str,
744 748
745 749 # Pygments lexer, for highlighting
746 750 # Only needed if it differs from the top level 'language' field.
747 751 'pygments_lexer': str,
748 752
749 753 # Codemirror mode, for for highlighting in the notebook.
750 754 # Only needed if it differs from the top level 'language' field.
751 755 'codemirror_mode': str or dict,
752 756
753 757 # Nbconvert exporter, if notebooks written with this kernel should
754 758 # be exported with something other than the general 'script'
755 759 # exporter.
756 760 'nbconvert_exporter': str,
757 761 },
758 762
759 763 # A banner of information about the kernel,
760 764 # which may be desplayed in console environments.
761 765 'banner' : str,
762 766
763 767 # Optional: A list of dictionaries, each with keys 'text' and 'url'.
764 768 # These will be displayed in the help menu in the notebook UI.
765 769 'help_links': [
766 770 {'text': str, 'url': str}
767 771 ],
768 772 }
769 773
770 774 Refer to the lists of available `Pygments lexers <http://pygments.org/docs/lexers/>`_
771 775 and `codemirror modes <http://codemirror.net/mode/index.html>`_ for those fields.
772 776
773 777 .. versionchanged:: 5.0
774 778
775 779 Versions changed from lists of integers to strings.
776 780
777 781 .. versionchanged:: 5.0
778 782
779 783 ``ipython_version`` is removed.
780 784
781 785 .. versionchanged:: 5.0
782 786
783 787 ``language_info``, ``implementation``, ``implementation_version``, ``banner``
784 788 and ``help_links`` keys are added.
785 789
786 790 .. versionchanged:: 5.0
787 791
788 792 ``language_version`` moved to ``language_info.version``
789 793
790 794 .. versionchanged:: 5.0
791 795
792 796 ``language`` moved to ``language_info.name``
793 797
794 798 .. _msging_shutdown:
795 799
796 800 Kernel shutdown
797 801 ---------------
798 802
799 803 The clients can request the kernel to shut itself down; this is used in
800 804 multiple cases:
801 805
802 806 - when the user chooses to close the client application via a menu or window
803 807 control.
804 808 - when the user types 'exit' or 'quit' (or their uppercase magic equivalents).
805 809 - when the user chooses a GUI method (like the 'Ctrl-C' shortcut in the
806 810 IPythonQt client) to force a kernel restart to get a clean kernel without
807 811 losing client-side state like history or inlined figures.
808 812
809 813 The client sends a shutdown request to the kernel, and once it receives the
810 814 reply message (which is otherwise empty), it can assume that the kernel has
811 815 completed shutdown safely.
812 816
813 817 Upon their own shutdown, client applications will typically execute a last
814 818 minute sanity check and forcefully terminate any kernel that is still alive, to
815 819 avoid leaving stray processes in the user's machine.
816 820
817 821 Message type: ``shutdown_request``::
818 822
819 823 content = {
820 824 'restart' : bool # whether the shutdown is final, or precedes a restart
821 825 }
822 826
823 827 Message type: ``shutdown_reply``::
824 828
825 829 content = {
826 830 'restart' : bool # whether the shutdown is final, or precedes a restart
827 831 }
828 832
829 833 .. Note::
830 834
831 835 When the clients detect a dead kernel thanks to inactivity on the heartbeat
832 836 socket, they simply send a forceful process termination signal, since a dead
833 837 process is unlikely to respond in any useful way to messages.
834 838
835 839
836 840 Messages on the PUB/SUB socket
837 841 ==============================
838 842
839 843 Streams (stdout, stderr, etc)
840 844 ------------------------------
841 845
842 846 Message type: ``stream``::
843 847
844 848 content = {
845 849 # The name of the stream is one of 'stdout', 'stderr'
846 850 'name' : str,
847 851
848 852 # The text is an arbitrary string to be written to that stream
849 853 'text' : str,
850 854 }
851 855
852 856 .. versionchanged:: 5.0
853 857
854 858 'data' key renamed to 'text' for conistency with the notebook format.
855 859
856 860 Display Data
857 861 ------------
858 862
859 863 This type of message is used to bring back data that should be displayed (text,
860 864 html, svg, etc.) in the frontends. This data is published to all frontends.
861 865 Each message can have multiple representations of the data; it is up to the
862 866 frontend to decide which to use and how. A single message should contain all
863 867 possible representations of the same information. Each representation should
864 868 be a JSON'able data structure, and should be a valid MIME type.
865 869
866 870 Some questions remain about this design:
867 871
868 872 * Do we use this message type for execute_result/displayhook? Probably not, because
869 873 the displayhook also has to handle the Out prompt display. On the other hand
870 874 we could put that information into the metadata section.
871 875
872 876 .. _display_data:
873 877
874 878 Message type: ``display_data``::
875 879
876 880 content = {
877 881
878 882 # Who create the data
879 883 'source' : str,
880 884
881 885 # The data dict contains key/value pairs, where the keys are MIME
882 886 # types and the values are the raw data of the representation in that
883 887 # format.
884 888 'data' : dict,
885 889
886 890 # Any metadata that describes the data
887 891 'metadata' : dict
888 892 }
889 893
890 894
891 895 The ``metadata`` contains any metadata that describes the output.
892 896 Global keys are assumed to apply to the output as a whole.
893 897 The ``metadata`` dict can also contain mime-type keys, which will be sub-dictionaries,
894 898 which are interpreted as applying only to output of that type.
895 899 Third parties should put any data they write into a single dict
896 900 with a reasonably unique name to avoid conflicts.
897 901
898 902 The only metadata keys currently defined in IPython are the width and height
899 903 of images::
900 904
901 905 metadata = {
902 906 'image/png' : {
903 907 'width': 640,
904 908 'height': 480
905 909 }
906 910 }
907 911
908 912
909 913 .. versionchanged:: 5.0
910 914
911 915 `application/json` data should be unpacked JSON data,
912 916 not double-serialized as a JSON string.
913 917
914 918
915 919 Raw Data Publication
916 920 --------------------
917 921
918 922 ``display_data`` lets you publish *representations* of data, such as images and html.
919 923 This ``data_pub`` message lets you publish *actual raw data*, sent via message buffers.
920 924
921 925 data_pub messages are constructed via the :func:`IPython.lib.datapub.publish_data` function:
922 926
923 927 .. sourcecode:: python
924 928
925 929 from IPython.kernel.zmq.datapub import publish_data
926 930 ns = dict(x=my_array)
927 931 publish_data(ns)
928 932
929 933
930 934 Message type: ``data_pub``::
931 935
932 936 content = {
933 937 # the keys of the data dict, after it has been unserialized
934 938 'keys' : ['a', 'b']
935 939 }
936 940 # the namespace dict will be serialized in the message buffers,
937 941 # which will have a length of at least one
938 942 buffers = [b'pdict', ...]
939 943
940 944
941 945 The interpretation of a sequence of data_pub messages for a given parent request should be
942 946 to update a single namespace with subsequent results.
943 947
944 948 .. note::
945 949
946 950 No frontends directly handle data_pub messages at this time.
947 951 It is currently only used by the client/engines in :mod:`IPython.parallel`,
948 952 where engines may publish *data* to the Client,
949 953 of which the Client can then publish *representations* via ``display_data``
950 954 to various frontends.
951 955
952 956 Code inputs
953 957 -----------
954 958
955 959 To let all frontends know what code is being executed at any given time, these
956 960 messages contain a re-broadcast of the ``code`` portion of an
957 961 :ref:`execute_request <execute>`, along with the :ref:`execution_count
958 962 <execution_counter>`.
959 963
960 964 Message type: ``execute_input``::
961 965
962 966 content = {
963 967 'code' : str, # Source code to be executed, one or more lines
964 968
965 969 # The counter for this execution is also provided so that clients can
966 970 # display it, since IPython automatically creates variables called _iN
967 971 # (for input prompt In[N]).
968 972 'execution_count' : int
969 973 }
970 974
971 975 .. versionchanged:: 5.0
972 976
973 977 ``pyin`` is renamed to ``execute_input``.
974 978
975 979
976 980 Execution results
977 981 -----------------
978 982
979 983 Results of an execution are published as an ``execute_result``.
980 984 These are identical to `display_data`_ messages, with the addition of an ``execution_count`` key.
981 985
982 986 Results can have multiple simultaneous formats depending on its
983 987 configuration. A plain text representation should always be provided
984 988 in the ``text/plain`` mime-type. Frontends are free to display any or all of these
985 989 according to its capabilities.
986 990 Frontends should ignore mime-types they do not understand. The data itself is
987 991 any JSON object and depends on the format. It is often, but not always a string.
988 992
989 993 Message type: ``execute_result``::
990 994
991 995 content = {
992 996
993 997 # The counter for this execution is also provided so that clients can
994 998 # display it, since IPython automatically creates variables called _N
995 999 # (for prompt N).
996 1000 'execution_count' : int,
997 1001
998 1002 # data and metadata are identical to a display_data message.
999 1003 # the object being displayed is that passed to the display hook,
1000 1004 # i.e. the *result* of the execution.
1001 1005 'data' : dict,
1002 1006 'metadata' : dict,
1003 1007 }
1004 1008
1005 1009 Execution errors
1006 1010 ----------------
1007 1011
1008 1012 When an error occurs during code execution
1009 1013
1010 1014 Message type: ``error``::
1011 1015
1012 1016 content = {
1013 1017 # Similar content to the execute_reply messages for the 'error' case,
1014 1018 # except the 'status' field is omitted.
1015 1019 }
1016 1020
1017 1021 .. versionchanged:: 5.0
1018 1022
1019 1023 ``pyerr`` renamed to ``error``
1020 1024
1021 1025 Kernel status
1022 1026 -------------
1023 1027
1024 1028 This message type is used by frontends to monitor the status of the kernel.
1025 1029
1026 1030 Message type: ``status``::
1027 1031
1028 1032 content = {
1029 1033 # When the kernel starts to handle a message, it will enter the 'busy'
1030 1034 # state and when it finishes, it will enter the 'idle' state.
1031 1035 # The kernel will publish state 'starting' exactly once at process startup.
1032 1036 execution_state : ('busy', 'idle', 'starting')
1033 1037 }
1034 1038
1035 1039 .. versionchanged:: 5.0
1036 1040
1037 1041 Busy and idle messages should be sent before/after handling every message,
1038 1042 not just execution.
1039 1043
1040 1044 Clear output
1041 1045 ------------
1042 1046
1043 1047 This message type is used to clear the output that is visible on the frontend.
1044 1048
1045 1049 Message type: ``clear_output``::
1046 1050
1047 1051 content = {
1048 1052
1049 1053 # Wait to clear the output until new output is available. Clears the
1050 1054 # existing output immediately before the new output is displayed.
1051 1055 # Useful for creating simple animations with minimal flickering.
1052 1056 'wait' : bool,
1053 1057 }
1054 1058
1055 1059 .. versionchanged:: 4.1
1056 1060
1057 1061 ``stdout``, ``stderr``, and ``display`` boolean keys for selective clearing are removed,
1058 1062 and ``wait`` is added.
1059 1063 The selective clearing keys are ignored in v4 and the default behavior remains the same,
1060 1064 so v4 clear_output messages will be safely handled by a v4.1 frontend.
1061 1065
1062 1066
1063 1067 Messages on the stdin ROUTER/DEALER sockets
1064 1068 ===========================================
1065 1069
1066 1070 This is a socket where the request/reply pattern goes in the opposite direction:
1067 1071 from the kernel to a *single* frontend, and its purpose is to allow
1068 1072 ``raw_input`` and similar operations that read from ``sys.stdin`` on the kernel
1069 1073 to be fulfilled by the client. The request should be made to the frontend that
1070 1074 made the execution request that prompted ``raw_input`` to be called. For now we
1071 1075 will keep these messages as simple as possible, since they only mean to convey
1072 1076 the ``raw_input(prompt)`` call.
1073 1077
1074 1078 Message type: ``input_request``::
1075 1079
1076 1080 content = {
1077 1081 # the text to show at the prompt
1078 1082 'prompt' : str,
1079 1083 # Is the request for a password?
1080 1084 # If so, the frontend shouldn't echo input.
1081 1085 'password' : bool
1082 1086 }
1083 1087
1084 1088 Message type: ``input_reply``::
1085 1089
1086 1090 content = { 'value' : str }
1087 1091
1088 1092
1089 1093 When ``password`` is True, the frontend should not echo the input as it is entered.
1090 1094
1091 1095 .. versionchanged:: 5.0
1092 1096
1093 1097 ``password`` key added.
1094 1098
1095 1099 .. note::
1096 1100
1097 1101 The stdin socket of the client is required to have the same zmq IDENTITY
1098 1102 as the client's shell socket.
1099 1103 Because of this, the ``input_request`` must be sent with the same IDENTITY
1100 1104 routing prefix as the ``execute_reply`` in order for the frontend to receive
1101 1105 the message.
1102 1106
1103 1107 .. note::
1104 1108
1105 1109 We do not explicitly try to forward the raw ``sys.stdin`` object, because in
1106 1110 practice the kernel should behave like an interactive program. When a
1107 1111 program is opened on the console, the keyboard effectively takes over the
1108 1112 ``stdin`` file descriptor, and it can't be used for raw reading anymore.
1109 1113 Since the IPython kernel effectively behaves like a console program (albeit
1110 1114 one whose "keyboard" is actually living in a separate process and
1111 1115 transported over the zmq connection), raw ``stdin`` isn't expected to be
1112 1116 available.
1113 1117
1114 1118 .. _kernel_heartbeat:
1115 1119
1116 1120 Heartbeat for kernels
1117 1121 =====================
1118 1122
1119 1123 Clients send ping messages on a REQ socket, which are echoed right back
1120 1124 from the Kernel's REP socket. These are simple bytestrings, not full JSON messages described above.
1121 1125
1122 1126
1123 1127 Custom Messages
1124 1128 ===============
1125 1129
1126 1130 .. versionadded:: 4.1
1127 1131
1128 1132 IPython 2.0 (msgspec v4.1) adds a messaging system for developers to add their own objects with Frontend
1129 1133 and Kernel-side components, and allow them to communicate with each other.
1130 1134 To do this, IPython adds a notion of a ``Comm``, which exists on both sides,
1131 1135 and can communicate in either direction.
1132 1136
1133 1137 These messages are fully symmetrical - both the Kernel and the Frontend can send each message,
1134 1138 and no messages expect a reply.
1135 1139 The Kernel listens for these messages on the Shell channel,
1136 1140 and the Frontend listens for them on the IOPub channel.
1137 1141
1138 1142 Opening a Comm
1139 1143 --------------
1140 1144
1141 1145 Opening a Comm produces a ``comm_open`` message, to be sent to the other side::
1142 1146
1143 1147 {
1144 1148 'comm_id' : 'u-u-i-d',
1145 1149 'target_name' : 'my_comm',
1146 1150 'data' : {}
1147 1151 }
1148 1152
1149 1153 Every Comm has an ID and a target name.
1150 1154 The code handling the message on the receiving side is responsible for maintaining a mapping
1151 1155 of target_name keys to constructors.
1152 1156 After a ``comm_open`` message has been sent,
1153 1157 there should be a corresponding Comm instance on both sides.
1154 1158 The ``data`` key is always a dict and can be any extra JSON information used in initialization of the comm.
1155 1159
1156 1160 If the ``target_name`` key is not found on the receiving side,
1157 1161 then it should immediately reply with a ``comm_close`` message to avoid an inconsistent state.
1158 1162
1159 1163 Comm Messages
1160 1164 -------------
1161 1165
1162 1166 Comm messages are one-way communications to update comm state,
1163 1167 used for synchronizing widget state, or simply requesting actions of a comm's counterpart.
1164 1168
1165 1169 Essentially, each comm pair defines their own message specification implemented inside the ``data`` dict.
1166 1170
1167 1171 There are no expected replies (of course, one side can send another ``comm_msg`` in reply).
1168 1172
1169 1173 Message type: ``comm_msg``::
1170 1174
1171 1175 {
1172 1176 'comm_id' : 'u-u-i-d',
1173 1177 'data' : {}
1174 1178 }
1175 1179
1176 1180 Tearing Down Comms
1177 1181 ------------------
1178 1182
1179 1183 Since comms live on both sides, when a comm is destroyed the other side must be notified.
1180 1184 This is done with a ``comm_close`` message.
1181 1185
1182 1186 Message type: ``comm_close``::
1183 1187
1184 1188 {
1185 1189 'comm_id' : 'u-u-i-d',
1186 1190 'data' : {}
1187 1191 }
1188 1192
1189 1193 Output Side Effects
1190 1194 -------------------
1191 1195
1192 1196 Since comm messages can execute arbitrary user code,
1193 1197 handlers should set the parent header and publish status busy / idle,
1194 1198 just like an execute request.
1195 1199
1196 1200
1197 1201 To Do
1198 1202 =====
1199 1203
1200 1204 Missing things include:
1201 1205
1202 1206 * Important: finish thinking through the payload concept and API.
1203 1207
1204 1208 .. include:: ../links.txt
General Comments 0
You need to be logged in to leave comments. Login now