##// END OF EJS Templates
Added wait flag to clear_output.
Jonathan Frederic -
Show More
@@ -1,671 +1,675 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Top-level display functions for displaying object in different formats.
3 3
4 4 Authors:
5 5
6 6 * Brian Granger
7 7 """
8 8
9 9 #-----------------------------------------------------------------------------
10 10 # Copyright (C) 2013 The IPython Development Team
11 11 #
12 12 # Distributed under the terms of the BSD License. The full license is in
13 13 # the file COPYING, distributed as part of this software.
14 14 #-----------------------------------------------------------------------------
15 15
16 16 #-----------------------------------------------------------------------------
17 17 # Imports
18 18 #-----------------------------------------------------------------------------
19 19
20 20 from __future__ import print_function
21 21
22 22 import os
23 23 import struct
24 24
25 25 from IPython.utils.py3compat import string_types, cast_bytes_py2, cast_unicode
26 26
27 27 from .displaypub import publish_display_data
28 28
29 29 #-----------------------------------------------------------------------------
30 30 # utility functions
31 31 #-----------------------------------------------------------------------------
32 32
33 33 def _safe_exists(path):
34 34 """Check path, but don't let exceptions raise"""
35 35 try:
36 36 return os.path.exists(path)
37 37 except Exception:
38 38 return False
39 39
40 40 def _merge(d1, d2):
41 41 """Like update, but merges sub-dicts instead of clobbering at the top level.
42 42
43 43 Updates d1 in-place
44 44 """
45 45
46 46 if not isinstance(d2, dict) or not isinstance(d1, dict):
47 47 return d2
48 48 for key, value in d2.items():
49 49 d1[key] = _merge(d1.get(key), value)
50 50 return d1
51 51
52 52 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
53 53 """internal implementation of all display_foo methods
54 54
55 55 Parameters
56 56 ----------
57 57 mimetype : str
58 58 The mimetype to be published (e.g. 'image/png')
59 59 objs : tuple of objects
60 60 The Python objects to display, or if raw=True raw text data to
61 61 display.
62 62 raw : bool
63 63 Are the data objects raw data or Python objects that need to be
64 64 formatted before display? [default: False]
65 65 metadata : dict (optional)
66 66 Metadata to be associated with the specific mimetype output.
67 67 """
68 68 if metadata:
69 69 metadata = {mimetype: metadata}
70 70 if raw:
71 71 # turn list of pngdata into list of { 'image/png': pngdata }
72 72 objs = [ {mimetype: obj} for obj in objs ]
73 73 display(*objs, raw=raw, metadata=metadata, include=[mimetype])
74 74
75 75 #-----------------------------------------------------------------------------
76 76 # Main functions
77 77 #-----------------------------------------------------------------------------
78 78
79 79 def display(*objs, **kwargs):
80 80 """Display a Python object in all frontends.
81 81
82 82 By default all representations will be computed and sent to the frontends.
83 83 Frontends can decide which representation is used and how.
84 84
85 85 Parameters
86 86 ----------
87 87 objs : tuple of objects
88 88 The Python objects to display.
89 89 raw : bool, optional
90 90 Are the objects to be displayed already mimetype-keyed dicts of raw display data,
91 91 or Python objects that need to be formatted before display? [default: False]
92 92 include : list or tuple, optional
93 93 A list of format type strings (MIME types) to include in the
94 94 format data dict. If this is set *only* the format types included
95 95 in this list will be computed.
96 96 exclude : list or tuple, optional
97 97 A list of format type strings (MIME types) to exclude in the format
98 98 data dict. If this is set all format types will be computed,
99 99 except for those included in this argument.
100 100 metadata : dict, optional
101 101 A dictionary of metadata to associate with the output.
102 102 mime-type keys in this dictionary will be associated with the individual
103 103 representation formats, if they exist.
104 104 """
105 105 raw = kwargs.get('raw', False)
106 106 include = kwargs.get('include')
107 107 exclude = kwargs.get('exclude')
108 108 metadata = kwargs.get('metadata')
109 109
110 110 from IPython.core.interactiveshell import InteractiveShell
111 111
112 112 if raw:
113 113 for obj in objs:
114 114 publish_display_data('display', obj, metadata)
115 115 else:
116 116 format = InteractiveShell.instance().display_formatter.format
117 117 for obj in objs:
118 118 format_dict, md_dict = format(obj, include=include, exclude=exclude)
119 119 if metadata:
120 120 # kwarg-specified metadata gets precedence
121 121 _merge(md_dict, metadata)
122 122 publish_display_data('display', format_dict, md_dict)
123 123
124 124
125 125 def display_pretty(*objs, **kwargs):
126 126 """Display the pretty (default) representation of an object.
127 127
128 128 Parameters
129 129 ----------
130 130 objs : tuple of objects
131 131 The Python objects to display, or if raw=True raw text data to
132 132 display.
133 133 raw : bool
134 134 Are the data objects raw data or Python objects that need to be
135 135 formatted before display? [default: False]
136 136 metadata : dict (optional)
137 137 Metadata to be associated with the specific mimetype output.
138 138 """
139 139 _display_mimetype('text/plain', objs, **kwargs)
140 140
141 141
142 142 def display_html(*objs, **kwargs):
143 143 """Display the HTML representation of an object.
144 144
145 145 Parameters
146 146 ----------
147 147 objs : tuple of objects
148 148 The Python objects to display, or if raw=True raw HTML data to
149 149 display.
150 150 raw : bool
151 151 Are the data objects raw data or Python objects that need to be
152 152 formatted before display? [default: False]
153 153 metadata : dict (optional)
154 154 Metadata to be associated with the specific mimetype output.
155 155 """
156 156 _display_mimetype('text/html', objs, **kwargs)
157 157
158 158
159 159 def display_svg(*objs, **kwargs):
160 160 """Display the SVG representation of an object.
161 161
162 162 Parameters
163 163 ----------
164 164 objs : tuple of objects
165 165 The Python objects to display, or if raw=True raw svg data to
166 166 display.
167 167 raw : bool
168 168 Are the data objects raw data or Python objects that need to be
169 169 formatted before display? [default: False]
170 170 metadata : dict (optional)
171 171 Metadata to be associated with the specific mimetype output.
172 172 """
173 173 _display_mimetype('image/svg+xml', objs, **kwargs)
174 174
175 175
176 176 def display_png(*objs, **kwargs):
177 177 """Display the PNG representation of an object.
178 178
179 179 Parameters
180 180 ----------
181 181 objs : tuple of objects
182 182 The Python objects to display, or if raw=True raw png data to
183 183 display.
184 184 raw : bool
185 185 Are the data objects raw data or Python objects that need to be
186 186 formatted before display? [default: False]
187 187 metadata : dict (optional)
188 188 Metadata to be associated with the specific mimetype output.
189 189 """
190 190 _display_mimetype('image/png', objs, **kwargs)
191 191
192 192
193 193 def display_jpeg(*objs, **kwargs):
194 194 """Display the JPEG representation of an object.
195 195
196 196 Parameters
197 197 ----------
198 198 objs : tuple of objects
199 199 The Python objects to display, or if raw=True raw JPEG data to
200 200 display.
201 201 raw : bool
202 202 Are the data objects raw data or Python objects that need to be
203 203 formatted before display? [default: False]
204 204 metadata : dict (optional)
205 205 Metadata to be associated with the specific mimetype output.
206 206 """
207 207 _display_mimetype('image/jpeg', objs, **kwargs)
208 208
209 209
210 210 def display_latex(*objs, **kwargs):
211 211 """Display the LaTeX representation of an object.
212 212
213 213 Parameters
214 214 ----------
215 215 objs : tuple of objects
216 216 The Python objects to display, or if raw=True raw latex data to
217 217 display.
218 218 raw : bool
219 219 Are the data objects raw data or Python objects that need to be
220 220 formatted before display? [default: False]
221 221 metadata : dict (optional)
222 222 Metadata to be associated with the specific mimetype output.
223 223 """
224 224 _display_mimetype('text/latex', objs, **kwargs)
225 225
226 226
227 227 def display_json(*objs, **kwargs):
228 228 """Display the JSON representation of an object.
229 229
230 230 Note that not many frontends support displaying JSON.
231 231
232 232 Parameters
233 233 ----------
234 234 objs : tuple of objects
235 235 The Python objects to display, or if raw=True raw json data to
236 236 display.
237 237 raw : bool
238 238 Are the data objects raw data or Python objects that need to be
239 239 formatted before display? [default: False]
240 240 metadata : dict (optional)
241 241 Metadata to be associated with the specific mimetype output.
242 242 """
243 243 _display_mimetype('application/json', objs, **kwargs)
244 244
245 245
246 246 def display_javascript(*objs, **kwargs):
247 247 """Display the Javascript representation of an object.
248 248
249 249 Parameters
250 250 ----------
251 251 objs : tuple of objects
252 252 The Python objects to display, or if raw=True raw javascript data to
253 253 display.
254 254 raw : bool
255 255 Are the data objects raw data or Python objects that need to be
256 256 formatted before display? [default: False]
257 257 metadata : dict (optional)
258 258 Metadata to be associated with the specific mimetype output.
259 259 """
260 260 _display_mimetype('application/javascript', objs, **kwargs)
261 261
262 262 #-----------------------------------------------------------------------------
263 263 # Smart classes
264 264 #-----------------------------------------------------------------------------
265 265
266 266
267 267 class DisplayObject(object):
268 268 """An object that wraps data to be displayed."""
269 269
270 270 _read_flags = 'r'
271 271
272 272 def __init__(self, data=None, url=None, filename=None):
273 273 """Create a display object given raw data.
274 274
275 275 When this object is returned by an expression or passed to the
276 276 display function, it will result in the data being displayed
277 277 in the frontend. The MIME type of the data should match the
278 278 subclasses used, so the Png subclass should be used for 'image/png'
279 279 data. If the data is a URL, the data will first be downloaded
280 280 and then displayed. If
281 281
282 282 Parameters
283 283 ----------
284 284 data : unicode, str or bytes
285 285 The raw data or a URL or file to load the data from
286 286 url : unicode
287 287 A URL to download the data from.
288 288 filename : unicode
289 289 Path to a local file to load the data from.
290 290 """
291 291 if data is not None and isinstance(data, string_types):
292 292 if data.startswith('http') and url is None:
293 293 url = data
294 294 filename = None
295 295 data = None
296 296 elif _safe_exists(data) and filename is None:
297 297 url = None
298 298 filename = data
299 299 data = None
300 300
301 301 self.data = data
302 302 self.url = url
303 303 self.filename = None if filename is None else unicode(filename)
304 304
305 305 self.reload()
306 306
307 307 def reload(self):
308 308 """Reload the raw data from file or URL."""
309 309 if self.filename is not None:
310 310 with open(self.filename, self._read_flags) as f:
311 311 self.data = f.read()
312 312 elif self.url is not None:
313 313 try:
314 314 import urllib2
315 315 response = urllib2.urlopen(self.url)
316 316 self.data = response.read()
317 317 # extract encoding from header, if there is one:
318 318 encoding = None
319 319 for sub in response.headers['content-type'].split(';'):
320 320 sub = sub.strip()
321 321 if sub.startswith('charset'):
322 322 encoding = sub.split('=')[-1].strip()
323 323 break
324 324 # decode data, if an encoding was specified
325 325 if encoding:
326 326 self.data = self.data.decode(encoding, 'replace')
327 327 except:
328 328 self.data = None
329 329
330 330 class Pretty(DisplayObject):
331 331
332 332 def _repr_pretty_(self):
333 333 return self.data
334 334
335 335
336 336 class HTML(DisplayObject):
337 337
338 338 def _repr_html_(self):
339 339 return self.data
340 340
341 341 def __html__(self):
342 342 """
343 343 This method exists to inform other HTML-using modules (e.g. Markupsafe,
344 344 htmltag, etc) that this object is HTML and does not need things like
345 345 special characters (<>&) escaped.
346 346 """
347 347 return self._repr_html_()
348 348
349 349
350 350 class Math(DisplayObject):
351 351
352 352 def _repr_latex_(self):
353 353 s = self.data.strip('$')
354 354 return "$$%s$$" % s
355 355
356 356
357 357 class Latex(DisplayObject):
358 358
359 359 def _repr_latex_(self):
360 360 return self.data
361 361
362 362
363 363 class SVG(DisplayObject):
364 364
365 365 # wrap data in a property, which extracts the <svg> tag, discarding
366 366 # document headers
367 367 _data = None
368 368
369 369 @property
370 370 def data(self):
371 371 return self._data
372 372
373 373 @data.setter
374 374 def data(self, svg):
375 375 if svg is None:
376 376 self._data = None
377 377 return
378 378 # parse into dom object
379 379 from xml.dom import minidom
380 380 svg = cast_bytes_py2(svg)
381 381 x = minidom.parseString(svg)
382 382 # get svg tag (should be 1)
383 383 found_svg = x.getElementsByTagName('svg')
384 384 if found_svg:
385 385 svg = found_svg[0].toxml()
386 386 else:
387 387 # fallback on the input, trust the user
388 388 # but this is probably an error.
389 389 pass
390 390 svg = cast_unicode(svg)
391 391 self._data = svg
392 392
393 393 def _repr_svg_(self):
394 394 return self.data
395 395
396 396
397 397 class JSON(DisplayObject):
398 398
399 399 def _repr_json_(self):
400 400 return self.data
401 401
402 402 css_t = """$("head").append($("<link/>").attr({
403 403 rel: "stylesheet",
404 404 type: "text/css",
405 405 href: "%s"
406 406 }));
407 407 """
408 408
409 409 lib_t1 = """$.getScript("%s", function () {
410 410 """
411 411 lib_t2 = """});
412 412 """
413 413
414 414 class Javascript(DisplayObject):
415 415
416 416 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
417 417 """Create a Javascript display object given raw data.
418 418
419 419 When this object is returned by an expression or passed to the
420 420 display function, it will result in the data being displayed
421 421 in the frontend. If the data is a URL, the data will first be
422 422 downloaded and then displayed.
423 423
424 424 In the Notebook, the containing element will be available as `element`,
425 425 and jQuery will be available. The output area starts hidden, so if
426 426 the js appends content to `element` that should be visible, then
427 427 it must call `container.show()` to unhide the area.
428 428
429 429 Parameters
430 430 ----------
431 431 data : unicode, str or bytes
432 432 The Javascript source code or a URL to download it from.
433 433 url : unicode
434 434 A URL to download the data from.
435 435 filename : unicode
436 436 Path to a local file to load the data from.
437 437 lib : list or str
438 438 A sequence of Javascript library URLs to load asynchronously before
439 439 running the source code. The full URLs of the libraries should
440 440 be given. A single Javascript library URL can also be given as a
441 441 string.
442 442 css: : list or str
443 443 A sequence of css files to load before running the source code.
444 444 The full URLs of the css files should be given. A single css URL
445 445 can also be given as a string.
446 446 """
447 447 if isinstance(lib, basestring):
448 448 lib = [lib]
449 449 elif lib is None:
450 450 lib = []
451 451 if isinstance(css, basestring):
452 452 css = [css]
453 453 elif css is None:
454 454 css = []
455 455 if not isinstance(lib, (list,tuple)):
456 456 raise TypeError('expected sequence, got: %r' % lib)
457 457 if not isinstance(css, (list,tuple)):
458 458 raise TypeError('expected sequence, got: %r' % css)
459 459 self.lib = lib
460 460 self.css = css
461 461 super(Javascript, self).__init__(data=data, url=url, filename=filename)
462 462
463 463 def _repr_javascript_(self):
464 464 r = ''
465 465 for c in self.css:
466 466 r += css_t % c
467 467 for l in self.lib:
468 468 r += lib_t1 % l
469 469 r += self.data
470 470 r += lib_t2*len(self.lib)
471 471 return r
472 472
473 473 # constants for identifying png/jpeg data
474 474 _PNG = b'\x89PNG\r\n\x1a\n'
475 475 _JPEG = b'\xff\xd8'
476 476
477 477 def _pngxy(data):
478 478 """read the (width, height) from a PNG header"""
479 479 ihdr = data.index(b'IHDR')
480 480 # next 8 bytes are width/height
481 481 w4h4 = data[ihdr+4:ihdr+12]
482 482 return struct.unpack('>ii', w4h4)
483 483
484 484 def _jpegxy(data):
485 485 """read the (width, height) from a JPEG header"""
486 486 # adapted from http://www.64lines.com/jpeg-width-height
487 487
488 488 idx = 4
489 489 while True:
490 490 block_size = struct.unpack('>H', data[idx:idx+2])[0]
491 491 idx = idx + block_size
492 492 if data[idx:idx+2] == b'\xFF\xC0':
493 493 # found Start of Frame
494 494 iSOF = idx
495 495 break
496 496 else:
497 497 # read another block
498 498 idx += 2
499 499
500 500 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
501 501 return w, h
502 502
503 503 class Image(DisplayObject):
504 504
505 505 _read_flags = 'rb'
506 506 _FMT_JPEG = u'jpeg'
507 507 _FMT_PNG = u'png'
508 508 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG]
509 509
510 510 def __init__(self, data=None, url=None, filename=None, format=u'png', embed=None, width=None, height=None, retina=False):
511 511 """Create a PNG/JPEG image object given raw data.
512 512
513 513 When this object is returned by an input cell or passed to the
514 514 display function, it will result in the image being displayed
515 515 in the frontend.
516 516
517 517 Parameters
518 518 ----------
519 519 data : unicode, str or bytes
520 520 The raw image data or a URL or filename to load the data from.
521 521 This always results in embedded image data.
522 522 url : unicode
523 523 A URL to download the data from. If you specify `url=`,
524 524 the image data will not be embedded unless you also specify `embed=True`.
525 525 filename : unicode
526 526 Path to a local file to load the data from.
527 527 Images from a file are always embedded.
528 528 format : unicode
529 529 The format of the image data (png/jpeg/jpg). If a filename or URL is given
530 530 for format will be inferred from the filename extension.
531 531 embed : bool
532 532 Should the image data be embedded using a data URI (True) or be
533 533 loaded using an <img> tag. Set this to True if you want the image
534 534 to be viewable later with no internet connection in the notebook.
535 535
536 536 Default is `True`, unless the keyword argument `url` is set, then
537 537 default value is `False`.
538 538
539 539 Note that QtConsole is not able to display images if `embed` is set to `False`
540 540 width : int
541 541 Width to which to constrain the image in html
542 542 height : int
543 543 Height to which to constrain the image in html
544 544 retina : bool
545 545 Automatically set the width and height to half of the measured
546 546 width and height.
547 547 This only works for embedded images because it reads the width/height
548 548 from image data.
549 549 For non-embedded images, you can just set the desired display width
550 550 and height directly.
551 551
552 552 Examples
553 553 --------
554 554 # embedded image data, works in qtconsole and notebook
555 555 # when passed positionally, the first arg can be any of raw image data,
556 556 # a URL, or a filename from which to load image data.
557 557 # The result is always embedding image data for inline images.
558 558 Image('http://www.google.fr/images/srpr/logo3w.png')
559 559 Image('/path/to/image.jpg')
560 560 Image(b'RAW_PNG_DATA...')
561 561
562 562 # Specifying Image(url=...) does not embed the image data,
563 563 # it only generates `<img>` tag with a link to the source.
564 564 # This will not work in the qtconsole or offline.
565 565 Image(url='http://www.google.fr/images/srpr/logo3w.png')
566 566
567 567 """
568 568 if filename is not None:
569 569 ext = self._find_ext(filename)
570 570 elif url is not None:
571 571 ext = self._find_ext(url)
572 572 elif data is None:
573 573 raise ValueError("No image data found. Expecting filename, url, or data.")
574 574 elif isinstance(data, string_types) and (
575 575 data.startswith('http') or _safe_exists(data)
576 576 ):
577 577 ext = self._find_ext(data)
578 578 else:
579 579 ext = None
580 580
581 581 if ext is not None:
582 582 format = ext.lower()
583 583 if ext == u'jpg' or ext == u'jpeg':
584 584 format = self._FMT_JPEG
585 585 if ext == u'png':
586 586 format = self._FMT_PNG
587 587 elif isinstance(data, bytes) and format == 'png':
588 588 # infer image type from image data header,
589 589 # only if format might not have been specified.
590 590 if data[:2] == _JPEG:
591 591 format = 'jpeg'
592 592
593 593 self.format = unicode(format).lower()
594 594 self.embed = embed if embed is not None else (url is None)
595 595
596 596 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
597 597 raise ValueError("Cannot embed the '%s' image format" % (self.format))
598 598 self.width = width
599 599 self.height = height
600 600 self.retina = retina
601 601 super(Image, self).__init__(data=data, url=url, filename=filename)
602 602
603 603 if retina:
604 604 self._retina_shape()
605 605
606 606 def _retina_shape(self):
607 607 """load pixel-doubled width and height from image data"""
608 608 if not self.embed:
609 609 return
610 610 if self.format == 'png':
611 611 w, h = _pngxy(self.data)
612 612 elif self.format == 'jpeg':
613 613 w, h = _jpegxy(self.data)
614 614 else:
615 615 # retina only supports png
616 616 return
617 617 self.width = w // 2
618 618 self.height = h // 2
619 619
620 620 def reload(self):
621 621 """Reload the raw data from file or URL."""
622 622 if self.embed:
623 623 super(Image,self).reload()
624 624 if self.retina:
625 625 self._retina_shape()
626 626
627 627 def _repr_html_(self):
628 628 if not self.embed:
629 629 width = height = ''
630 630 if self.width:
631 631 width = ' width="%d"' % self.width
632 632 if self.height:
633 633 height = ' height="%d"' % self.height
634 634 return u'<img src="%s"%s%s/>' % (self.url, width, height)
635 635
636 636 def _data_and_metadata(self):
637 637 """shortcut for returning metadata with shape information, if defined"""
638 638 md = {}
639 639 if self.width:
640 640 md['width'] = self.width
641 641 if self.height:
642 642 md['height'] = self.height
643 643 if md:
644 644 return self.data, md
645 645 else:
646 646 return self.data
647 647
648 648 def _repr_png_(self):
649 649 if self.embed and self.format == u'png':
650 650 return self._data_and_metadata()
651 651
652 652 def _repr_jpeg_(self):
653 653 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
654 654 return self._data_and_metadata()
655 655
656 656 def _find_ext(self, s):
657 657 return unicode(s.split('.')[-1].lower())
658 658
659 659
660 def clear_output():
661 """Clear the output of the current cell receiving output."""
660 def clear_output(wait=False):
661 """Clear the output of the current cell receiving output.
662
663 Parameters
664 ----------
665 wait : bool [default: false]
666 Wait to clear the output until new output is available to replace it."""
662 667 from IPython.core.interactiveshell import InteractiveShell
663 668 if InteractiveShell.initialized():
664 InteractiveShell.instance().display_pub.clear_output()
669 InteractiveShell.instance().display_pub.clear_output(wait)
665 670 else:
666 671 from IPython.utils import io
667 672 print('\033[2K\r', file=io.stdout, end='')
668 673 io.stdout.flush()
669 674 print('\033[2K\r', file=io.stderr, end='')
670 675 io.stderr.flush()
671
@@ -1,176 +1,176 b''
1 1 """An interface for publishing rich data to frontends.
2 2
3 3 There are two components of the display system:
4 4
5 5 * Display formatters, which take a Python object and compute the
6 6 representation of the object in various formats (text, HTML, SVG, etc.).
7 7 * The display publisher that is used to send the representation data to the
8 8 various frontends.
9 9
10 10 This module defines the logic display publishing. The display publisher uses
11 11 the ``display_data`` message type that is defined in the IPython messaging
12 12 spec.
13 13
14 14 Authors:
15 15
16 16 * Brian Granger
17 17 """
18 18
19 19 #-----------------------------------------------------------------------------
20 20 # Copyright (C) 2008-2011 The IPython Development Team
21 21 #
22 22 # Distributed under the terms of the BSD License. The full license is in
23 23 # the file COPYING, distributed as part of this software.
24 24 #-----------------------------------------------------------------------------
25 25
26 26 #-----------------------------------------------------------------------------
27 27 # Imports
28 28 #-----------------------------------------------------------------------------
29 29
30 30 from __future__ import print_function
31 31
32 32 from IPython.config.configurable import Configurable
33 33 from IPython.utils import io
34 34 from IPython.utils.traitlets import List
35 35
36 36 #-----------------------------------------------------------------------------
37 37 # Main payload class
38 38 #-----------------------------------------------------------------------------
39 39
40 40 class DisplayPublisher(Configurable):
41 41 """A traited class that publishes display data to frontends.
42 42
43 43 Instances of this class are created by the main IPython object and should
44 44 be accessed there.
45 45 """
46 46
47 47 def _validate_data(self, source, data, metadata=None):
48 48 """Validate the display data.
49 49
50 50 Parameters
51 51 ----------
52 52 source : str
53 53 The fully dotted name of the callable that created the data, like
54 54 :func:`foo.bar.my_formatter`.
55 55 data : dict
56 56 The formata data dictionary.
57 57 metadata : dict
58 58 Any metadata for the data.
59 59 """
60 60
61 61 if not isinstance(source, basestring):
62 62 raise TypeError('source must be a str, got: %r' % source)
63 63 if not isinstance(data, dict):
64 64 raise TypeError('data must be a dict, got: %r' % data)
65 65 if metadata is not None:
66 66 if not isinstance(metadata, dict):
67 67 raise TypeError('metadata must be a dict, got: %r' % data)
68 68
69 69 def publish(self, source, data, metadata=None):
70 70 """Publish data and metadata to all frontends.
71 71
72 72 See the ``display_data`` message in the messaging documentation for
73 73 more details about this message type.
74 74
75 75 The following MIME types are currently implemented:
76 76
77 77 * text/plain
78 78 * text/html
79 79 * text/latex
80 80 * application/json
81 81 * application/javascript
82 82 * image/png
83 83 * image/jpeg
84 84 * image/svg+xml
85 85
86 86 Parameters
87 87 ----------
88 88 source : str
89 89 A string that give the function or method that created the data,
90 90 such as 'IPython.core.page'.
91 91 data : dict
92 92 A dictionary having keys that are valid MIME types (like
93 93 'text/plain' or 'image/svg+xml') and values that are the data for
94 94 that MIME type. The data itself must be a JSON'able data
95 95 structure. Minimally all data should have the 'text/plain' data,
96 96 which can be displayed by all frontends. If more than the plain
97 97 text is given, it is up to the frontend to decide which
98 98 representation to use.
99 99 metadata : dict
100 100 A dictionary for metadata related to the data. This can contain
101 101 arbitrary key, value pairs that frontends can use to interpret
102 102 the data. Metadata specific to each mime-type can be specified
103 103 in the metadata dict with the same mime-type keys as
104 104 the data itself.
105 105 """
106 106
107 107 # The default is to simply write the plain text data using io.stdout.
108 108 if 'text/plain' in data:
109 109 print(data['text/plain'], file=io.stdout)
110 110
111 def clear_output(self):
111 def clear_output(self, wait=False):
112 112 """Clear the output of the cell receiving output."""
113 113 print('\033[2K\r', file=io.stdout, end='')
114 114 io.stdout.flush()
115 115 print('\033[2K\r', file=io.stderr, end='')
116 116 io.stderr.flush()
117 117
118 118
119 119 class CapturingDisplayPublisher(DisplayPublisher):
120 120 """A DisplayPublisher that stores"""
121 121 outputs = List()
122 122
123 123 def publish(self, source, data, metadata=None):
124 124 self.outputs.append((source, data, metadata))
125 125
126 def clear_output(self):
127 super(CapturingDisplayPublisher, self).clear_output()
126 def clear_output(self, wait=False):
127 super(CapturingDisplayPublisher, self).clear_output(wait)
128 128 if other:
129 129 # empty the list, *do not* reassign a new list
130 130 del self.outputs[:]
131 131
132 132
133 133 def publish_display_data(source, data, metadata=None):
134 134 """Publish data and metadata to all frontends.
135 135
136 136 See the ``display_data`` message in the messaging documentation for
137 137 more details about this message type.
138 138
139 139 The following MIME types are currently implemented:
140 140
141 141 * text/plain
142 142 * text/html
143 143 * text/latex
144 144 * application/json
145 145 * application/javascript
146 146 * image/png
147 147 * image/jpeg
148 148 * image/svg+xml
149 149
150 150 Parameters
151 151 ----------
152 152 source : str
153 153 A string that give the function or method that created the data,
154 154 such as 'IPython.core.page'.
155 155 data : dict
156 156 A dictionary having keys that are valid MIME types (like
157 157 'text/plain' or 'image/svg+xml') and values that are the data for
158 158 that MIME type. The data itself must be a JSON'able data
159 159 structure. Minimally all data should have the 'text/plain' data,
160 160 which can be displayed by all frontends. If more than the plain
161 161 text is given, it is up to the frontend to decide which
162 162 representation to use.
163 163 metadata : dict
164 164 A dictionary for metadata related to the data. This can contain
165 165 arbitrary key, value pairs that frontends can use to interpret
166 166 the data. mime-type keys matching those in data can be used
167 167 to specify metadata about particular representations.
168 168 """
169 169 from IPython.core.interactiveshell import InteractiveShell
170 170 InteractiveShell.instance().display_pub.publish(
171 171 source,
172 172 data,
173 173 metadata
174 174 )
175 175
176 176
@@ -1,441 +1,441 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // CodeCell
10 10 //============================================================================
11 11 /**
12 12 * An extendable module that provide base functionnality to create cell for notebook.
13 13 * @module IPython
14 14 * @namespace IPython
15 15 * @submodule CodeCell
16 16 */
17 17
18 18
19 19 /* local util for codemirror */
20 20 var posEq = function(a, b) {return a.line == b.line && a.ch == b.ch;}
21 21
22 22 /**
23 23 *
24 24 * function to delete until previous non blanking space character
25 25 * or first multiple of 4 tabstop.
26 26 * @private
27 27 */
28 28 CodeMirror.commands.delSpaceToPrevTabStop = function(cm){
29 29 var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
30 30 if (!posEq(from, to)) {cm.replaceRange("", from, to); return}
31 31 var cur = cm.getCursor(), line = cm.getLine(cur.line);
32 32 var tabsize = cm.getOption('tabSize');
33 33 var chToPrevTabStop = cur.ch-(Math.ceil(cur.ch/tabsize)-1)*tabsize;
34 34 var from = {ch:cur.ch-chToPrevTabStop,line:cur.line}
35 35 var select = cm.getRange(from,cur)
36 36 if( select.match(/^\ +$/) != null){
37 37 cm.replaceRange("",from,cur)
38 38 } else {
39 39 cm.deleteH(-1,"char")
40 40 }
41 41 };
42 42
43 43
44 44 var IPython = (function (IPython) {
45 45 "use strict";
46 46
47 47 var utils = IPython.utils;
48 48 var key = IPython.utils.keycodes;
49 49
50 50 /**
51 51 * A Cell conceived to write code.
52 52 *
53 53 * The kernel doesn't have to be set at creation time, in that case
54 54 * it will be null and set_kernel has to be called later.
55 55 * @class CodeCell
56 56 * @extends IPython.Cell
57 57 *
58 58 * @constructor
59 59 * @param {Object|null} kernel
60 60 * @param {object|undefined} [options]
61 61 * @param [options.cm_config] {object} config to pass to CodeMirror
62 62 */
63 63 var CodeCell = function (kernel, options) {
64 64 this.kernel = kernel || null;
65 65 this.code_mirror = null;
66 66 this.input_prompt_number = null;
67 67 this.collapsed = false;
68 68 this.cell_type = "code";
69 69
70 70
71 71 var cm_overwrite_options = {
72 72 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
73 73 };
74 74
75 75 options = this.mergeopt(CodeCell, options, {cm_config:cm_overwrite_options});
76 76
77 77 IPython.Cell.apply(this,[options]);
78 78
79 79 var that = this;
80 80 this.element.focusout(
81 81 function() { that.auto_highlight(); }
82 82 );
83 83 };
84 84
85 85 CodeCell.options_default = {
86 86 cm_config : {
87 87 extraKeys: {
88 88 "Tab" : "indentMore",
89 89 "Shift-Tab" : "indentLess",
90 90 "Backspace" : "delSpaceToPrevTabStop",
91 91 "Cmd-/" : "toggleComment",
92 92 "Ctrl-/" : "toggleComment"
93 93 },
94 94 mode: 'ipython',
95 95 theme: 'ipython',
96 96 matchBrackets: true
97 97 }
98 98 };
99 99
100 100
101 101 CodeCell.prototype = new IPython.Cell();
102 102
103 103 /**
104 104 * @method auto_highlight
105 105 */
106 106 CodeCell.prototype.auto_highlight = function () {
107 107 this._auto_highlight(IPython.config.cell_magic_highlight)
108 108 };
109 109
110 110 /** @method create_element */
111 111 CodeCell.prototype.create_element = function () {
112 112 IPython.Cell.prototype.create_element.apply(this, arguments);
113 113
114 114 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell');
115 115 cell.attr('tabindex','2');
116 116
117 117 this.celltoolbar = new IPython.CellToolbar(this);
118 118
119 119 var input = $('<div></div>').addClass('input');
120 120 var vbox = $('<div/>').addClass('vbox box-flex1')
121 121 input.append($('<div/>').addClass('prompt input_prompt'));
122 122 vbox.append(this.celltoolbar.element);
123 123 var input_area = $('<div/>').addClass('input_area');
124 124 this.code_mirror = CodeMirror(input_area.get(0), this.cm_config);
125 125 $(this.code_mirror.getInputField()).attr("spellcheck", "false");
126 126 vbox.append(input_area);
127 127 input.append(vbox);
128 128 var output = $('<div></div>');
129 129 cell.append(input).append(output);
130 130 this.element = cell;
131 131 this.output_area = new IPython.OutputArea(output, true);
132 132
133 133 // construct a completer only if class exist
134 134 // otherwise no print view
135 135 if (IPython.Completer !== undefined)
136 136 {
137 137 this.completer = new IPython.Completer(this);
138 138 }
139 139 };
140 140
141 141 /**
142 142 * This method gets called in CodeMirror's onKeyDown/onKeyPress
143 143 * handlers and is used to provide custom key handling. Its return
144 144 * value is used to determine if CodeMirror should ignore the event:
145 145 * true = ignore, false = don't ignore.
146 146 * @method handle_codemirror_keyevent
147 147 */
148 148 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
149 149
150 150 var that = this;
151 151 // whatever key is pressed, first, cancel the tooltip request before
152 152 // they are sent, and remove tooltip if any, except for tab again
153 153 if (event.type === 'keydown' && event.which != key.TAB ) {
154 154 IPython.tooltip.remove_and_cancel_tooltip();
155 155 };
156 156
157 157 var cur = editor.getCursor();
158 158 if (event.keyCode === key.ENTER){
159 159 this.auto_highlight();
160 160 }
161 161
162 162 if (event.keyCode === key.ENTER && (event.shiftKey || event.ctrlKey)) {
163 163 // Always ignore shift-enter in CodeMirror as we handle it.
164 164 return true;
165 165 } else if (event.which === 40 && event.type === 'keypress' && IPython.tooltip.time_before_tooltip >= 0) {
166 166 // triger on keypress (!) otherwise inconsistent event.which depending on plateform
167 167 // browser and keyboard layout !
168 168 // Pressing '(' , request tooltip, don't forget to reappend it
169 169 // The second argument says to hide the tooltip if the docstring
170 170 // is actually empty
171 171 IPython.tooltip.pending(that, true);
172 172 } else if (event.which === key.UPARROW && event.type === 'keydown') {
173 173 // If we are not at the top, let CM handle the up arrow and
174 174 // prevent the global keydown handler from handling it.
175 175 if (!that.at_top()) {
176 176 event.stop();
177 177 return false;
178 178 } else {
179 179 return true;
180 180 };
181 181 } else if (event.which === key.ESC) {
182 182 return IPython.tooltip.remove_and_cancel_tooltip(true);
183 183 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
184 184 // If we are not at the bottom, let CM handle the down arrow and
185 185 // prevent the global keydown handler from handling it.
186 186 if (!that.at_bottom()) {
187 187 event.stop();
188 188 return false;
189 189 } else {
190 190 return true;
191 191 };
192 192 } else if (event.keyCode === key.TAB && event.type == 'keydown' && event.shiftKey) {
193 193 if (editor.somethingSelected()){
194 194 var anchor = editor.getCursor("anchor");
195 195 var head = editor.getCursor("head");
196 196 if( anchor.line != head.line){
197 197 return false;
198 198 }
199 199 }
200 200 IPython.tooltip.request(that);
201 201 event.stop();
202 202 return true;
203 203 } else if (event.keyCode === key.TAB && event.type == 'keydown') {
204 204 // Tab completion.
205 205 //Do not trim here because of tooltip
206 206 if (editor.somethingSelected()){return false}
207 207 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
208 208 if (pre_cursor.trim() === "") {
209 209 // Don't autocomplete if the part of the line before the cursor
210 210 // is empty. In this case, let CodeMirror handle indentation.
211 211 return false;
212 212 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && IPython.config.tooltip_on_tab ) {
213 213 IPython.tooltip.request(that);
214 214 // Prevent the event from bubbling up.
215 215 event.stop();
216 216 // Prevent CodeMirror from handling the tab.
217 217 return true;
218 218 } else {
219 219 event.stop();
220 220 this.completer.startCompletion();
221 221 return true;
222 222 };
223 223 } else {
224 224 // keypress/keyup also trigger on TAB press, and we don't want to
225 225 // use those to disable tab completion.
226 226 return false;
227 227 };
228 228 return false;
229 229 };
230 230
231 231
232 232 // Kernel related calls.
233 233
234 234 CodeCell.prototype.set_kernel = function (kernel) {
235 235 this.kernel = kernel;
236 236 }
237 237
238 238 /**
239 239 * Execute current code cell to the kernel
240 240 * @method execute
241 241 */
242 242 CodeCell.prototype.execute = function () {
243 243 this.output_area.clear_output();
244 244 this.set_input_prompt('*');
245 245 this.element.addClass("running");
246 246 var callbacks = {
247 247 'execute_reply': $.proxy(this._handle_execute_reply, this),
248 248 'output': $.proxy(this.output_area.handle_output, this.output_area),
249 249 'clear_output': $.proxy(this.output_area.handle_clear_output, this.output_area),
250 250 'set_next_input': $.proxy(this._handle_set_next_input, this),
251 251 'input_request': $.proxy(this._handle_input_request, this)
252 252 };
253 253 var msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false, store_history: true});
254 254 };
255 255
256 256 /**
257 257 * @method _handle_execute_reply
258 258 * @private
259 259 */
260 260 CodeCell.prototype._handle_execute_reply = function (content) {
261 261 this.set_input_prompt(content.execution_count);
262 262 this.element.removeClass("running");
263 263 $([IPython.events]).trigger('set_dirty.Notebook', {value: true});
264 264 }
265 265
266 266 /**
267 267 * @method _handle_set_next_input
268 268 * @private
269 269 */
270 270 CodeCell.prototype._handle_set_next_input = function (text) {
271 271 var data = {'cell': this, 'text': text}
272 272 $([IPython.events]).trigger('set_next_input.Notebook', data);
273 273 }
274 274
275 275 /**
276 276 * @method _handle_input_request
277 277 * @private
278 278 */
279 279 CodeCell.prototype._handle_input_request = function (content) {
280 280 this.output_area.append_raw_input(content);
281 281 }
282 282
283 283
284 284 // Basic cell manipulation.
285 285
286 286 CodeCell.prototype.select = function () {
287 287 IPython.Cell.prototype.select.apply(this);
288 288 this.code_mirror.refresh();
289 289 this.code_mirror.focus();
290 290 this.auto_highlight();
291 291 // We used to need an additional refresh() after the focus, but
292 292 // it appears that this has been fixed in CM. This bug would show
293 293 // up on FF when a newly loaded markdown cell was edited.
294 294 };
295 295
296 296
297 297 CodeCell.prototype.select_all = function () {
298 298 var start = {line: 0, ch: 0};
299 299 var nlines = this.code_mirror.lineCount();
300 300 var last_line = this.code_mirror.getLine(nlines-1);
301 301 var end = {line: nlines-1, ch: last_line.length};
302 302 this.code_mirror.setSelection(start, end);
303 303 };
304 304
305 305
306 306 CodeCell.prototype.collapse = function () {
307 307 this.collapsed = true;
308 308 this.output_area.collapse();
309 309 };
310 310
311 311
312 312 CodeCell.prototype.expand = function () {
313 313 this.collapsed = false;
314 314 this.output_area.expand();
315 315 };
316 316
317 317
318 318 CodeCell.prototype.toggle_output = function () {
319 319 this.collapsed = Boolean(1 - this.collapsed);
320 320 this.output_area.toggle_output();
321 321 };
322 322
323 323
324 324 CodeCell.prototype.toggle_output_scroll = function () {
325 325 this.output_area.toggle_scroll();
326 326 };
327 327
328 328
329 329 CodeCell.input_prompt_classical = function (prompt_value, lines_number) {
330 330 var ns = prompt_value || "&nbsp;";
331 331 return 'In&nbsp;[' + ns + ']:'
332 332 };
333 333
334 334 CodeCell.input_prompt_continuation = function (prompt_value, lines_number) {
335 335 var html = [CodeCell.input_prompt_classical(prompt_value, lines_number)];
336 336 for(var i=1; i < lines_number; i++){html.push(['...:'])};
337 337 return html.join('</br>')
338 338 };
339 339
340 340 CodeCell.input_prompt_function = CodeCell.input_prompt_classical;
341 341
342 342
343 343 CodeCell.prototype.set_input_prompt = function (number) {
344 344 var nline = 1
345 345 if( this.code_mirror != undefined) {
346 346 nline = this.code_mirror.lineCount();
347 347 }
348 348 this.input_prompt_number = number;
349 349 var prompt_html = CodeCell.input_prompt_function(this.input_prompt_number, nline);
350 350 this.element.find('div.input_prompt').html(prompt_html);
351 351 };
352 352
353 353
354 354 CodeCell.prototype.clear_input = function () {
355 355 this.code_mirror.setValue('');
356 356 };
357 357
358 358
359 359 CodeCell.prototype.get_text = function () {
360 360 return this.code_mirror.getValue();
361 361 };
362 362
363 363
364 364 CodeCell.prototype.set_text = function (code) {
365 365 return this.code_mirror.setValue(code);
366 366 };
367 367
368 368
369 369 CodeCell.prototype.at_top = function () {
370 370 var cursor = this.code_mirror.getCursor();
371 371 if (cursor.line === 0 && cursor.ch === 0) {
372 372 return true;
373 373 } else {
374 374 return false;
375 375 }
376 376 };
377 377
378 378
379 379 CodeCell.prototype.at_bottom = function () {
380 380 var cursor = this.code_mirror.getCursor();
381 381 if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
382 382 return true;
383 383 } else {
384 384 return false;
385 385 }
386 386 };
387 387
388 388
389 CodeCell.prototype.clear_output = function () {
390 this.output_area.clear_output();
389 CodeCell.prototype.clear_output = function (wait) {
390 this.output_area.clear_output(wait);
391 391 };
392 392
393 393
394 394 // JSON serialization
395 395
396 396 CodeCell.prototype.fromJSON = function (data) {
397 397 IPython.Cell.prototype.fromJSON.apply(this, arguments);
398 398 if (data.cell_type === 'code') {
399 399 if (data.input !== undefined) {
400 400 this.set_text(data.input);
401 401 // make this value the starting point, so that we can only undo
402 402 // to this state, instead of a blank cell
403 403 this.code_mirror.clearHistory();
404 404 this.auto_highlight();
405 405 }
406 406 if (data.prompt_number !== undefined) {
407 407 this.set_input_prompt(data.prompt_number);
408 408 } else {
409 409 this.set_input_prompt();
410 410 };
411 411 this.output_area.fromJSON(data.outputs);
412 412 if (data.collapsed !== undefined) {
413 413 if (data.collapsed) {
414 414 this.collapse();
415 415 } else {
416 416 this.expand();
417 417 };
418 418 };
419 419 };
420 420 };
421 421
422 422
423 423 CodeCell.prototype.toJSON = function () {
424 424 var data = IPython.Cell.prototype.toJSON.apply(this);
425 425 data.input = this.get_text();
426 426 data.cell_type = 'code';
427 427 if (this.input_prompt_number) {
428 428 data.prompt_number = this.input_prompt_number;
429 429 };
430 430 var outputs = this.output_area.toJSON();
431 431 data.outputs = outputs;
432 432 data.language = 'python';
433 433 data.collapsed = this.collapsed;
434 434 return data;
435 435 };
436 436
437 437
438 438 IPython.CodeCell = CodeCell;
439 439
440 440 return IPython;
441 441 }(IPython));
@@ -1,651 +1,668 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // OutputArea
10 10 //============================================================================
11 11
12 12 /**
13 13 * @module IPython
14 14 * @namespace IPython
15 15 * @submodule OutputArea
16 16 */
17 17 var IPython = (function (IPython) {
18 18 "use strict";
19 19
20 20 var utils = IPython.utils;
21 21
22 22 /**
23 23 * @class OutputArea
24 24 *
25 25 * @constructor
26 26 */
27 27
28 28 var OutputArea = function (selector, prompt_area) {
29 29 this.selector = selector;
30 30 this.wrapper = $(selector);
31 31 this.outputs = [];
32 32 this.collapsed = false;
33 33 this.scrolled = false;
34 this.clear_out_timeout = null;
34 this.clear_queued = null;
35 35 if (prompt_area === undefined) {
36 36 this.prompt_area = true;
37 37 } else {
38 38 this.prompt_area = prompt_area;
39 39 }
40 40 this.create_elements();
41 41 this.style();
42 42 this.bind_events();
43 43 };
44 44
45 45 OutputArea.prototype.create_elements = function () {
46 46 this.element = $("<div/>");
47 47 this.collapse_button = $("<div/>");
48 48 this.prompt_overlay = $("<div/>");
49 49 this.wrapper.append(this.prompt_overlay);
50 50 this.wrapper.append(this.element);
51 51 this.wrapper.append(this.collapse_button);
52 52 };
53 53
54 54
55 55 OutputArea.prototype.style = function () {
56 56 this.collapse_button.hide();
57 57 this.prompt_overlay.hide();
58 58
59 59 this.wrapper.addClass('output_wrapper');
60 60 this.element.addClass('output vbox');
61 61
62 62 this.collapse_button.addClass("btn output_collapsed");
63 63 this.collapse_button.attr('title', 'click to expand output');
64 64 this.collapse_button.html('. . .');
65 65
66 66 this.prompt_overlay.addClass('out_prompt_overlay prompt');
67 67 this.prompt_overlay.attr('title', 'click to expand output; double click to hide output');
68 68
69 69 this.collapse();
70 70 };
71 71
72 72 /**
73 73 * Should the OutputArea scroll?
74 74 * Returns whether the height (in lines) exceeds a threshold.
75 75 *
76 76 * @private
77 77 * @method _should_scroll
78 78 * @param [lines=100]{Integer}
79 79 * @return {Bool}
80 80 *
81 81 */
82 82 OutputArea.prototype._should_scroll = function (lines) {
83 83 if (lines <=0 ){ return }
84 84 if (!lines) {
85 85 lines = 100;
86 86 }
87 87 // line-height from http://stackoverflow.com/questions/1185151
88 88 var fontSize = this.element.css('font-size');
89 89 var lineHeight = Math.floor(parseInt(fontSize.replace('px','')) * 1.5);
90 90
91 91 return (this.element.height() > lines * lineHeight);
92 92 };
93 93
94 94
95 95 OutputArea.prototype.bind_events = function () {
96 96 var that = this;
97 97 this.prompt_overlay.dblclick(function () { that.toggle_output(); });
98 98 this.prompt_overlay.click(function () { that.toggle_scroll(); });
99 99
100 100 this.element.resize(function () {
101 101 // FIXME: Firefox on Linux misbehaves, so automatic scrolling is disabled
102 102 if ( IPython.utils.browser[0] === "Firefox" ) {
103 103 return;
104 104 }
105 105 // maybe scroll output,
106 106 // if it's grown large enough and hasn't already been scrolled.
107 107 if ( !that.scrolled && that._should_scroll(OutputArea.auto_scroll_threshold)) {
108 108 that.scroll_area();
109 109 }
110 110 });
111 111 this.collapse_button.click(function () {
112 112 that.expand();
113 113 });
114 114 };
115 115
116 116
117 117 OutputArea.prototype.collapse = function () {
118 118 if (!this.collapsed) {
119 119 this.element.hide();
120 120 this.prompt_overlay.hide();
121 121 if (this.element.html()){
122 122 this.collapse_button.show();
123 123 }
124 124 this.collapsed = true;
125 125 }
126 126 };
127 127
128 128
129 129 OutputArea.prototype.expand = function () {
130 130 if (this.collapsed) {
131 131 this.collapse_button.hide();
132 132 this.element.show();
133 133 this.prompt_overlay.show();
134 134 this.collapsed = false;
135 135 }
136 136 };
137 137
138 138
139 139 OutputArea.prototype.toggle_output = function () {
140 140 if (this.collapsed) {
141 141 this.expand();
142 142 } else {
143 143 this.collapse();
144 144 }
145 145 };
146 146
147 147
148 148 OutputArea.prototype.scroll_area = function () {
149 149 this.element.addClass('output_scroll');
150 150 this.prompt_overlay.attr('title', 'click to unscroll output; double click to hide');
151 151 this.scrolled = true;
152 152 };
153 153
154 154
155 155 OutputArea.prototype.unscroll_area = function () {
156 156 this.element.removeClass('output_scroll');
157 157 this.prompt_overlay.attr('title', 'click to scroll output; double click to hide');
158 158 this.scrolled = false;
159 159 };
160 160
161 161 /**
162 162 * Threshold to trigger autoscroll when the OutputArea is resized,
163 163 * typically when new outputs are added.
164 164 *
165 165 * Behavior is undefined if autoscroll is lower than minimum_scroll_threshold,
166 166 * unless it is < 0, in which case autoscroll will never be triggered
167 167 *
168 168 * @property auto_scroll_threshold
169 169 * @type Number
170 170 * @default 100
171 171 *
172 172 **/
173 173 OutputArea.auto_scroll_threshold = 100;
174 174
175 175
176 176 /**
177 177 * Lower limit (in lines) for OutputArea to be made scrollable. OutputAreas
178 178 * shorter than this are never scrolled.
179 179 *
180 180 * @property minimum_scroll_threshold
181 181 * @type Number
182 182 * @default 20
183 183 *
184 184 **/
185 185 OutputArea.minimum_scroll_threshold = 20;
186 186
187 187
188 188 /**
189 189 *
190 190 * Scroll OutputArea if height supperior than a threshold (in lines).
191 191 *
192 192 * Threshold is a maximum number of lines. If unspecified, defaults to
193 193 * OutputArea.minimum_scroll_threshold.
194 194 *
195 195 * Negative threshold will prevent the OutputArea from ever scrolling.
196 196 *
197 197 * @method scroll_if_long
198 198 *
199 199 * @param [lines=20]{Number} Default to 20 if not set,
200 200 * behavior undefined for value of `0`.
201 201 *
202 202 **/
203 203 OutputArea.prototype.scroll_if_long = function (lines) {
204 204 var n = lines | OutputArea.minimum_scroll_threshold;
205 205 if(n <= 0){
206 206 return
207 207 }
208 208
209 209 if (this._should_scroll(n)) {
210 210 // only allow scrolling long-enough output
211 211 this.scroll_area();
212 212 }
213 213 };
214 214
215 215
216 216 OutputArea.prototype.toggle_scroll = function () {
217 217 if (this.scrolled) {
218 218 this.unscroll_area();
219 219 } else {
220 220 // only allow scrolling long-enough output
221 221 this.scroll_if_long();
222 222 }
223 223 };
224 224
225 225
226 226 // typeset with MathJax if MathJax is available
227 227 OutputArea.prototype.typeset = function () {
228 228 if (window.MathJax){
229 229 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
230 230 }
231 231 };
232 232
233 233
234 234 OutputArea.prototype.handle_output = function (msg_type, content) {
235 235 var json = {};
236 236 json.output_type = msg_type;
237 237 if (msg_type === "stream") {
238 238 json.text = content.data;
239 239 json.stream = content.name;
240 240 } else if (msg_type === "display_data") {
241 241 json = this.convert_mime_types(json, content.data);
242 242 json.metadata = this.convert_mime_types({}, content.metadata);
243 243 } else if (msg_type === "pyout") {
244 244 json.prompt_number = content.execution_count;
245 245 json = this.convert_mime_types(json, content.data);
246 246 json.metadata = this.convert_mime_types({}, content.metadata);
247 247 } else if (msg_type === "pyerr") {
248 248 json.ename = content.ename;
249 249 json.evalue = content.evalue;
250 250 json.traceback = content.traceback;
251 251 }
252 252 // append with dynamic=true
253 253 this.append_output(json, true);
254 254 };
255 255
256 256
257 257 OutputArea.prototype.convert_mime_types = function (json, data) {
258 258 if (data === undefined) {
259 259 return json;
260 260 }
261 261 if (data['text/plain'] !== undefined) {
262 262 json.text = data['text/plain'];
263 263 }
264 264 if (data['text/html'] !== undefined) {
265 265 json.html = data['text/html'];
266 266 }
267 267 if (data['image/svg+xml'] !== undefined) {
268 268 json.svg = data['image/svg+xml'];
269 269 }
270 270 if (data['image/png'] !== undefined) {
271 271 json.png = data['image/png'];
272 272 }
273 273 if (data['image/jpeg'] !== undefined) {
274 274 json.jpeg = data['image/jpeg'];
275 275 }
276 276 if (data['text/latex'] !== undefined) {
277 277 json.latex = data['text/latex'];
278 278 }
279 279 if (data['application/json'] !== undefined) {
280 280 json.json = data['application/json'];
281 281 }
282 282 if (data['application/javascript'] !== undefined) {
283 283 json.javascript = data['application/javascript'];
284 284 }
285 285 return json;
286 286 };
287 287
288 288
289 289 OutputArea.prototype.append_output = function (json, dynamic) {
290 290 // If dynamic is true, javascript output will be eval'd.
291 291 this.expand();
292
293 // Clear the output if clear is queued.
294 if (this.clear_queued) {
295 this.clear_output(false);
296 }
297
292 298 if (json.output_type === 'pyout') {
293 299 this.append_pyout(json, dynamic);
294 300 } else if (json.output_type === 'pyerr') {
295 301 this.append_pyerr(json);
296 302 } else if (json.output_type === 'display_data') {
297 303 this.append_display_data(json, dynamic);
298 304 } else if (json.output_type === 'stream') {
299 305 this.append_stream(json);
300 306 }
301 307 this.outputs.push(json);
302 308 this.element.height('auto');
303 309 var that = this;
304 310 setTimeout(function(){that.element.trigger('resize');}, 100);
305 311 };
306 312
307 313
308 314 OutputArea.prototype.create_output_area = function () {
309 315 var oa = $("<div/>").addClass("output_area");
310 316 if (this.prompt_area) {
311 317 oa.append($('<div/>').addClass('prompt'));
312 318 }
313 319 return oa;
314 320 };
315 321
316 322 OutputArea.prototype._append_javascript_error = function (err, container) {
317 323 // display a message when a javascript error occurs in display output
318 324 var msg = "Javascript error adding output!"
319 325 console.log(msg, err);
320 326 if ( container === undefined ) return;
321 327 container.append(
322 328 $('<div/>').html(msg + "<br/>" +
323 329 err.toString() +
324 330 '<br/>See your browser Javascript console for more details.'
325 331 ).addClass('js-error')
326 332 );
327 333 container.show();
328 334 };
329 335
330 336 OutputArea.prototype._safe_append = function (toinsert) {
331 337 // safely append an item to the document
332 338 // this is an object created by user code,
333 339 // and may have errors, which should not be raised
334 340 // under any circumstances.
335 341 try {
336 342 this.element.append(toinsert);
337 343 } catch(err) {
338 344 console.log(err);
339 345 this._append_javascript_error(err, this.element);
340 346 }
341 347 };
342 348
343 349
344 350 OutputArea.prototype.append_pyout = function (json, dynamic) {
345 351 var n = json.prompt_number || ' ';
346 352 var toinsert = this.create_output_area();
347 353 if (this.prompt_area) {
348 354 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
349 355 }
350 356 this.append_mime_type(json, toinsert, dynamic);
351 357 this._safe_append(toinsert);
352 358 // If we just output latex, typeset it.
353 359 if ((json.latex !== undefined) || (json.html !== undefined)) {
354 360 this.typeset();
355 361 }
356 362 };
357 363
358 364
359 365 OutputArea.prototype.append_pyerr = function (json) {
360 366 var tb = json.traceback;
361 367 if (tb !== undefined && tb.length > 0) {
362 368 var s = '';
363 369 var len = tb.length;
364 370 for (var i=0; i<len; i++) {
365 371 s = s + tb[i] + '\n';
366 372 }
367 373 s = s + '\n';
368 374 var toinsert = this.create_output_area();
369 375 this.append_text(s, {}, toinsert);
370 376 this._safe_append(toinsert);
371 377 }
372 378 };
373 379
374 380
375 381 OutputArea.prototype.append_stream = function (json) {
376 382 // temporary fix: if stream undefined (json file written prior to this patch),
377 383 // default to most likely stdout:
378 384 if (json.stream == undefined){
379 385 json.stream = 'stdout';
380 386 }
381 387 var text = json.text;
382 388 var subclass = "output_"+json.stream;
383 389 if (this.outputs.length > 0){
384 390 // have at least one output to consider
385 391 var last = this.outputs[this.outputs.length-1];
386 392 if (last.output_type == 'stream' && json.stream == last.stream){
387 393 // latest output was in the same stream,
388 394 // so append directly into its pre tag
389 395 // escape ANSI & HTML specials:
390 396 var pre = this.element.find('div.'+subclass).last().find('pre');
391 397 var html = utils.fixCarriageReturn(
392 398 pre.html() + utils.fixConsole(text));
393 399 pre.html(html);
394 400 return;
395 401 }
396 402 }
397 403
398 404 if (!text.replace("\r", "")) {
399 405 // text is nothing (empty string, \r, etc.)
400 406 // so don't append any elements, which might add undesirable space
401 407 return;
402 408 }
403 409
404 410 // If we got here, attach a new div
405 411 var toinsert = this.create_output_area();
406 412 this.append_text(text, {}, toinsert, "output_stream "+subclass);
407 413 this._safe_append(toinsert);
408 414 };
409 415
410 416
411 417 OutputArea.prototype.append_display_data = function (json, dynamic) {
412 418 var toinsert = this.create_output_area();
413 419 this.append_mime_type(json, toinsert, dynamic);
414 420 this._safe_append(toinsert);
415 421 // If we just output latex, typeset it.
416 422 if ( (json.latex !== undefined) || (json.html !== undefined) ) {
417 423 this.typeset();
418 424 }
419 425 };
420 426
421 427 OutputArea.display_order = ['javascript','html','latex','svg','png','jpeg','text'];
422 428
423 429 OutputArea.prototype.append_mime_type = function (json, element, dynamic) {
424 430 for(var type_i in OutputArea.display_order){
425 431 var type = OutputArea.display_order[type_i];
426 432 if(json[type] != undefined ){
427 433 var md = {};
428 434 if (json.metadata && json.metadata[type]) {
429 435 md = json.metadata[type];
430 436 };
431 437 if(type == 'javascript'){
432 438 if (dynamic) {
433 439 this.append_javascript(json.javascript, md, element, dynamic);
434 440 }
435 441 } else {
436 442 this['append_'+type](json[type], md, element);
437 443 }
438 444 return;
439 445 }
440 446 }
441 447 };
442 448
443 449
444 450 OutputArea.prototype.append_html = function (html, md, element) {
445 451 var toinsert = $("<div/>").addClass("output_subarea output_html rendered_html");
446 452 toinsert.append(html);
447 453 element.append(toinsert);
448 454 };
449 455
450 456
451 457 OutputArea.prototype.append_javascript = function (js, md, container) {
452 458 // We just eval the JS code, element appears in the local scope.
453 459 var element = $("<div/>").addClass("output_subarea");
454 460 container.append(element);
455 461 // Div for js shouldn't be drawn, as it will add empty height to the area.
456 462 container.hide();
457 463 // If the Javascript appends content to `element` that should be drawn, then
458 464 // it must also call `container.show()`.
459 465 try {
460 466 eval(js);
461 467 } catch(err) {
462 468 this._append_javascript_error(err, container);
463 469 }
464 470 };
465 471
466 472
467 473 OutputArea.prototype.append_text = function (data, md, element, extra_class) {
468 474 var toinsert = $("<div/>").addClass("output_subarea output_text");
469 475 // escape ANSI & HTML specials in plaintext:
470 476 data = utils.fixConsole(data);
471 477 data = utils.fixCarriageReturn(data);
472 478 data = utils.autoLinkUrls(data);
473 479 if (extra_class){
474 480 toinsert.addClass(extra_class);
475 481 }
476 482 toinsert.append($("<pre/>").html(data));
477 483 element.append(toinsert);
478 484 };
479 485
480 486
481 487 OutputArea.prototype.append_svg = function (svg, md, element) {
482 488 var toinsert = $("<div/>").addClass("output_subarea output_svg");
483 489 toinsert.append(svg);
484 490 element.append(toinsert);
485 491 };
486 492
487 493
488 494 OutputArea.prototype._dblclick_to_reset_size = function (img) {
489 495 // schedule wrapping image in resizable after a delay,
490 496 // so we don't end up calling resize on a zero-size object
491 497 var that = this;
492 498 setTimeout(function () {
493 499 var h0 = img.height();
494 500 var w0 = img.width();
495 501 if (!(h0 && w0)) {
496 502 // zero size, schedule another timeout
497 503 that._dblclick_to_reset_size(img);
498 504 return;
499 505 }
500 506 img.resizable({
501 507 aspectRatio: true,
502 508 autoHide: true
503 509 });
504 510 img.dblclick(function () {
505 511 // resize wrapper & image together for some reason:
506 512 img.parent().height(h0);
507 513 img.height(h0);
508 514 img.parent().width(w0);
509 515 img.width(w0);
510 516 });
511 517 }, 250);
512 518 };
513 519
514 520
515 521 OutputArea.prototype.append_png = function (png, md, element) {
516 522 var toinsert = $("<div/>").addClass("output_subarea output_png");
517 523 var img = $("<img/>").attr('src','data:image/png;base64,'+png);
518 524 if (md['height']) {
519 525 img.attr('height', md['height']);
520 526 }
521 527 if (md['width']) {
522 528 img.attr('width', md['width']);
523 529 }
524 530 this._dblclick_to_reset_size(img);
525 531 toinsert.append(img);
526 532 element.append(toinsert);
527 533 };
528 534
529 535
530 536 OutputArea.prototype.append_jpeg = function (jpeg, md, element) {
531 537 var toinsert = $("<div/>").addClass("output_subarea output_jpeg");
532 538 var img = $("<img/>").attr('src','data:image/jpeg;base64,'+jpeg);
533 539 if (md['height']) {
534 540 img.attr('height', md['height']);
535 541 }
536 542 if (md['width']) {
537 543 img.attr('width', md['width']);
538 544 }
539 545 this._dblclick_to_reset_size(img);
540 546 toinsert.append(img);
541 547 element.append(toinsert);
542 548 };
543 549
544 550
545 551 OutputArea.prototype.append_latex = function (latex, md, element) {
546 552 // This method cannot do the typesetting because the latex first has to
547 553 // be on the page.
548 554 var toinsert = $("<div/>").addClass("output_subarea output_latex");
549 555 toinsert.append(latex);
550 556 element.append(toinsert);
551 557 };
552 558
553 559 OutputArea.prototype.append_raw_input = function (content) {
554 560 var that = this;
555 561 this.expand();
556 562 var area = this.create_output_area();
557 563
558 564 // disable any other raw_inputs, if they are left around
559 565 $("div.output_subarea.raw_input").remove();
560 566
561 567 area.append(
562 568 $("<div/>")
563 569 .addClass("box-flex1 output_subarea raw_input")
564 570 .append(
565 571 $("<span/>")
566 572 .addClass("input_prompt")
567 573 .text(content.prompt)
568 574 )
569 575 .append(
570 576 $("<input/>")
571 577 .addClass("raw_input")
572 578 .attr('type', 'text')
573 579 .attr("size", 47)
574 580 .keydown(function (event, ui) {
575 581 // make sure we submit on enter,
576 582 // and don't re-execute the *cell* on shift-enter
577 583 if (event.which === utils.keycodes.ENTER) {
578 584 that._submit_raw_input();
579 585 return false;
580 586 }
581 587 })
582 588 )
583 589 );
584 590 this.element.append(area);
585 591 // weirdly need double-focus now,
586 592 // otherwise only the cell will be focused
587 593 area.find("input.raw_input").focus().focus();
588 594 }
589 595 OutputArea.prototype._submit_raw_input = function (evt) {
590 596 var container = this.element.find("div.raw_input");
591 597 var theprompt = container.find("span.input_prompt");
592 598 var theinput = container.find("input.raw_input");
593 599 var value = theinput.val();
594 600 var content = {
595 601 output_type : 'stream',
596 602 name : 'stdout',
597 603 text : theprompt.text() + value + '\n'
598 604 }
599 605 // remove form container
600 606 container.parent().remove();
601 607 // replace with plaintext version in stdout
602 608 this.append_output(content, false);
603 609 $([IPython.events]).trigger('send_input_reply.Kernel', value);
604 610 }
605 611
606 612
607 613 OutputArea.prototype.handle_clear_output = function (content) {
608 this.clear_output();
614 this.clear_output(content.wait);
609 615 };
610 616
611 617
612 OutputArea.prototype.clear_output = function() {
613
614 // Fix the output div's height
615 var height = this.element.height();
616 this.element.height(height);
618 OutputArea.prototype.clear_output = function(wait) {
619 if (wait) {
617 620
618 // clear all, no need for logic
619 this.element.html("");
620 this.outputs = [];
621 this.unscroll_area();
622 return;
621 // If a clear is queued, clear before adding another to the queue.
622 if (this.clear_queued) {
623 this.clear_output(false);
624 };
625
626 this.clear_queued = true;
627 } else {
628 this.clear_queued = false;
629
630 // Fix the output div's height
631 var height = this.element.height();
632 this.element.height(height);
633
634 // clear all, no need for logic
635 this.element.html("");
636 this.outputs = [];
637 this.unscroll_area();
638 return;
639 };
623 640 };
624 641
625 642
626 643 // JSON serialization
627 644
628 645 OutputArea.prototype.fromJSON = function (outputs) {
629 646 var len = outputs.length;
630 647 for (var i=0; i<len; i++) {
631 648 // append with dynamic=false.
632 649 this.append_output(outputs[i], false);
633 650 }
634 651 };
635 652
636 653
637 654 OutputArea.prototype.toJSON = function () {
638 655 var outputs = [];
639 656 var len = this.outputs.length;
640 657 for (var i=0; i<len; i++) {
641 658 outputs[i] = this.outputs[i];
642 659 }
643 660 return outputs;
644 661 };
645 662
646 663
647 664 IPython.OutputArea = OutputArea;
648 665
649 666 return IPython;
650 667
651 668 }(IPython));
@@ -1,599 +1,599 b''
1 1 """A ZMQ-based subclass of InteractiveShell.
2 2
3 3 This code is meant to ease the refactoring of the base InteractiveShell into
4 4 something with a cleaner architecture for 2-process use, without actually
5 5 breaking InteractiveShell itself. So we're doing something a bit ugly, where
6 6 we subclass and override what we want to fix. Once this is working well, we
7 7 can go back to the base class and refactor the code for a cleaner inheritance
8 8 implementation that doesn't rely on so much monkeypatching.
9 9
10 10 But this lets us maintain a fully working IPython as we develop the new
11 11 machinery. This should thus be thought of as scaffolding.
12 12 """
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from __future__ import print_function
17 17
18 18 # Stdlib
19 19 import os
20 20 import sys
21 21 import time
22 22
23 23 # System library imports
24 24 from zmq.eventloop import ioloop
25 25
26 26 # Our own
27 27 from IPython.core.interactiveshell import (
28 28 InteractiveShell, InteractiveShellABC
29 29 )
30 30 from IPython.core import page
31 31 from IPython.core.autocall import ZMQExitAutocall
32 32 from IPython.core.displaypub import DisplayPublisher
33 33 from IPython.core.error import UsageError
34 34 from IPython.core.magics import MacroToEdit, CodeMagics
35 35 from IPython.core.magic import magics_class, line_magic, Magics
36 36 from IPython.core.payloadpage import install_payload_page
37 37 from IPython.display import display, Javascript
38 38 from IPython.kernel.inprocess.socket import SocketABC
39 39 from IPython.kernel import (
40 40 get_connection_file, get_connection_info, connect_qtconsole
41 41 )
42 42 from IPython.testing.skipdoctest import skip_doctest
43 43 from IPython.utils import openpy
44 44 from IPython.utils.jsonutil import json_clean, encode_images
45 45 from IPython.utils.process import arg_split
46 46 from IPython.utils import py3compat
47 47 from IPython.utils.traitlets import Instance, Type, Dict, CBool, CBytes
48 48 from IPython.utils.warn import error
49 49 from IPython.kernel.zmq.displayhook import ZMQShellDisplayHook
50 50 from IPython.kernel.zmq.datapub import ZMQDataPublisher
51 51 from IPython.kernel.zmq.session import extract_header
52 52 from session import Session
53 53
54 54 #-----------------------------------------------------------------------------
55 55 # Functions and classes
56 56 #-----------------------------------------------------------------------------
57 57
58 58 class ZMQDisplayPublisher(DisplayPublisher):
59 59 """A display publisher that publishes data using a ZeroMQ PUB socket."""
60 60
61 61 session = Instance(Session)
62 62 pub_socket = Instance(SocketABC)
63 63 parent_header = Dict({})
64 64 topic = CBytes(b'display_data')
65 65
66 66 def set_parent(self, parent):
67 67 """Set the parent for outbound messages."""
68 68 self.parent_header = extract_header(parent)
69 69
70 70 def _flush_streams(self):
71 71 """flush IO Streams prior to display"""
72 72 sys.stdout.flush()
73 73 sys.stderr.flush()
74 74
75 75 def publish(self, source, data, metadata=None):
76 76 self._flush_streams()
77 77 if metadata is None:
78 78 metadata = {}
79 79 self._validate_data(source, data, metadata)
80 80 content = {}
81 81 content['source'] = source
82 82 content['data'] = encode_images(data)
83 83 content['metadata'] = metadata
84 84 self.session.send(
85 85 self.pub_socket, u'display_data', json_clean(content),
86 86 parent=self.parent_header, ident=self.topic,
87 87 )
88 88
89 def clear_output(self):
90 content = {}
89 def clear_output(self, wait=False):
90 content = dict(wait=wait)
91 91
92 92 print('\r', file=sys.stdout, end='')
93 93 print('\r', file=sys.stderr, end='')
94 94 self._flush_streams()
95 95
96 96 self.session.send(
97 97 self.pub_socket, u'clear_output', content,
98 98 parent=self.parent_header, ident=self.topic,
99 99 )
100 100
101 101 @magics_class
102 102 class KernelMagics(Magics):
103 103 #------------------------------------------------------------------------
104 104 # Magic overrides
105 105 #------------------------------------------------------------------------
106 106 # Once the base class stops inheriting from magic, this code needs to be
107 107 # moved into a separate machinery as well. For now, at least isolate here
108 108 # the magics which this class needs to implement differently from the base
109 109 # class, or that are unique to it.
110 110
111 111 @line_magic
112 112 def doctest_mode(self, parameter_s=''):
113 113 """Toggle doctest mode on and off.
114 114
115 115 This mode is intended to make IPython behave as much as possible like a
116 116 plain Python shell, from the perspective of how its prompts, exceptions
117 117 and output look. This makes it easy to copy and paste parts of a
118 118 session into doctests. It does so by:
119 119
120 120 - Changing the prompts to the classic ``>>>`` ones.
121 121 - Changing the exception reporting mode to 'Plain'.
122 122 - Disabling pretty-printing of output.
123 123
124 124 Note that IPython also supports the pasting of code snippets that have
125 125 leading '>>>' and '...' prompts in them. This means that you can paste
126 126 doctests from files or docstrings (even if they have leading
127 127 whitespace), and the code will execute correctly. You can then use
128 128 '%history -t' to see the translated history; this will give you the
129 129 input after removal of all the leading prompts and whitespace, which
130 130 can be pasted back into an editor.
131 131
132 132 With these features, you can switch into this mode easily whenever you
133 133 need to do testing and changes to doctests, without having to leave
134 134 your existing IPython session.
135 135 """
136 136
137 137 from IPython.utils.ipstruct import Struct
138 138
139 139 # Shorthands
140 140 shell = self.shell
141 141 disp_formatter = self.shell.display_formatter
142 142 ptformatter = disp_formatter.formatters['text/plain']
143 143 # dstore is a data store kept in the instance metadata bag to track any
144 144 # changes we make, so we can undo them later.
145 145 dstore = shell.meta.setdefault('doctest_mode', Struct())
146 146 save_dstore = dstore.setdefault
147 147
148 148 # save a few values we'll need to recover later
149 149 mode = save_dstore('mode', False)
150 150 save_dstore('rc_pprint', ptformatter.pprint)
151 151 save_dstore('rc_active_types',disp_formatter.active_types)
152 152 save_dstore('xmode', shell.InteractiveTB.mode)
153 153
154 154 if mode == False:
155 155 # turn on
156 156 ptformatter.pprint = False
157 157 disp_formatter.active_types = ['text/plain']
158 158 shell.magic('xmode Plain')
159 159 else:
160 160 # turn off
161 161 ptformatter.pprint = dstore.rc_pprint
162 162 disp_formatter.active_types = dstore.rc_active_types
163 163 shell.magic("xmode " + dstore.xmode)
164 164
165 165 # Store new mode and inform on console
166 166 dstore.mode = bool(1-int(mode))
167 167 mode_label = ['OFF','ON'][dstore.mode]
168 168 print('Doctest mode is:', mode_label)
169 169
170 170 # Send the payload back so that clients can modify their prompt display
171 171 payload = dict(
172 172 source='doctest_mode',
173 173 mode=dstore.mode)
174 174 shell.payload_manager.write_payload(payload)
175 175
176 176
177 177 _find_edit_target = CodeMagics._find_edit_target
178 178
179 179 @skip_doctest
180 180 @line_magic
181 181 def edit(self, parameter_s='', last_call=['','']):
182 182 """Bring up an editor and execute the resulting code.
183 183
184 184 Usage:
185 185 %edit [options] [args]
186 186
187 187 %edit runs an external text editor. You will need to set the command for
188 188 this editor via the ``TerminalInteractiveShell.editor`` option in your
189 189 configuration file before it will work.
190 190
191 191 This command allows you to conveniently edit multi-line code right in
192 192 your IPython session.
193 193
194 194 If called without arguments, %edit opens up an empty editor with a
195 195 temporary file and will execute the contents of this file when you
196 196 close it (don't forget to save it!).
197 197
198 198
199 199 Options:
200 200
201 201 -n <number>: open the editor at a specified line number. By default,
202 202 the IPython editor hook uses the unix syntax 'editor +N filename', but
203 203 you can configure this by providing your own modified hook if your
204 204 favorite editor supports line-number specifications with a different
205 205 syntax.
206 206
207 207 -p: this will call the editor with the same data as the previous time
208 208 it was used, regardless of how long ago (in your current session) it
209 209 was.
210 210
211 211 -r: use 'raw' input. This option only applies to input taken from the
212 212 user's history. By default, the 'processed' history is used, so that
213 213 magics are loaded in their transformed version to valid Python. If
214 214 this option is given, the raw input as typed as the command line is
215 215 used instead. When you exit the editor, it will be executed by
216 216 IPython's own processor.
217 217
218 218 -x: do not execute the edited code immediately upon exit. This is
219 219 mainly useful if you are editing programs which need to be called with
220 220 command line arguments, which you can then do using %run.
221 221
222 222
223 223 Arguments:
224 224
225 225 If arguments are given, the following possibilites exist:
226 226
227 227 - The arguments are numbers or pairs of colon-separated numbers (like
228 228 1 4:8 9). These are interpreted as lines of previous input to be
229 229 loaded into the editor. The syntax is the same of the %macro command.
230 230
231 231 - If the argument doesn't start with a number, it is evaluated as a
232 232 variable and its contents loaded into the editor. You can thus edit
233 233 any string which contains python code (including the result of
234 234 previous edits).
235 235
236 236 - If the argument is the name of an object (other than a string),
237 237 IPython will try to locate the file where it was defined and open the
238 238 editor at the point where it is defined. You can use `%edit function`
239 239 to load an editor exactly at the point where 'function' is defined,
240 240 edit it and have the file be executed automatically.
241 241
242 242 If the object is a macro (see %macro for details), this opens up your
243 243 specified editor with a temporary file containing the macro's data.
244 244 Upon exit, the macro is reloaded with the contents of the file.
245 245
246 246 Note: opening at an exact line is only supported under Unix, and some
247 247 editors (like kedit and gedit up to Gnome 2.8) do not understand the
248 248 '+NUMBER' parameter necessary for this feature. Good editors like
249 249 (X)Emacs, vi, jed, pico and joe all do.
250 250
251 251 - If the argument is not found as a variable, IPython will look for a
252 252 file with that name (adding .py if necessary) and load it into the
253 253 editor. It will execute its contents with execfile() when you exit,
254 254 loading any code in the file into your interactive namespace.
255 255
256 256 After executing your code, %edit will return as output the code you
257 257 typed in the editor (except when it was an existing file). This way
258 258 you can reload the code in further invocations of %edit as a variable,
259 259 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
260 260 the output.
261 261
262 262 Note that %edit is also available through the alias %ed.
263 263
264 264 This is an example of creating a simple function inside the editor and
265 265 then modifying it. First, start up the editor:
266 266
267 267 In [1]: ed
268 268 Editing... done. Executing edited code...
269 269 Out[1]: 'def foo():n print "foo() was defined in an editing session"n'
270 270
271 271 We can then call the function foo():
272 272
273 273 In [2]: foo()
274 274 foo() was defined in an editing session
275 275
276 276 Now we edit foo. IPython automatically loads the editor with the
277 277 (temporary) file where foo() was previously defined:
278 278
279 279 In [3]: ed foo
280 280 Editing... done. Executing edited code...
281 281
282 282 And if we call foo() again we get the modified version:
283 283
284 284 In [4]: foo()
285 285 foo() has now been changed!
286 286
287 287 Here is an example of how to edit a code snippet successive
288 288 times. First we call the editor:
289 289
290 290 In [5]: ed
291 291 Editing... done. Executing edited code...
292 292 hello
293 293 Out[5]: "print 'hello'n"
294 294
295 295 Now we call it again with the previous output (stored in _):
296 296
297 297 In [6]: ed _
298 298 Editing... done. Executing edited code...
299 299 hello world
300 300 Out[6]: "print 'hello world'n"
301 301
302 302 Now we call it with the output #8 (stored in _8, also as Out[8]):
303 303
304 304 In [7]: ed _8
305 305 Editing... done. Executing edited code...
306 306 hello again
307 307 Out[7]: "print 'hello again'n"
308 308 """
309 309
310 310 opts,args = self.parse_options(parameter_s,'prn:')
311 311
312 312 try:
313 313 filename, lineno, _ = CodeMagics._find_edit_target(self.shell, args, opts, last_call)
314 314 except MacroToEdit as e:
315 315 # TODO: Implement macro editing over 2 processes.
316 316 print("Macro editing not yet implemented in 2-process model.")
317 317 return
318 318
319 319 # Make sure we send to the client an absolute path, in case the working
320 320 # directory of client and kernel don't match
321 321 filename = os.path.abspath(filename)
322 322
323 323 payload = {
324 324 'source' : 'edit_magic',
325 325 'filename' : filename,
326 326 'line_number' : lineno
327 327 }
328 328 self.shell.payload_manager.write_payload(payload)
329 329
330 330 # A few magics that are adapted to the specifics of using pexpect and a
331 331 # remote terminal
332 332
333 333 @line_magic
334 334 def clear(self, arg_s):
335 335 """Clear the terminal."""
336 336 if os.name == 'posix':
337 337 self.shell.system("clear")
338 338 else:
339 339 self.shell.system("cls")
340 340
341 341 if os.name == 'nt':
342 342 # This is the usual name in windows
343 343 cls = line_magic('cls')(clear)
344 344
345 345 # Terminal pagers won't work over pexpect, but we do have our own pager
346 346
347 347 @line_magic
348 348 def less(self, arg_s):
349 349 """Show a file through the pager.
350 350
351 351 Files ending in .py are syntax-highlighted."""
352 352 if not arg_s:
353 353 raise UsageError('Missing filename.')
354 354
355 355 cont = open(arg_s).read()
356 356 if arg_s.endswith('.py'):
357 357 cont = self.shell.pycolorize(openpy.read_py_file(arg_s, skip_encoding_cookie=False))
358 358 else:
359 359 cont = open(arg_s).read()
360 360 page.page(cont)
361 361
362 362 more = line_magic('more')(less)
363 363
364 364 # Man calls a pager, so we also need to redefine it
365 365 if os.name == 'posix':
366 366 @line_magic
367 367 def man(self, arg_s):
368 368 """Find the man page for the given command and display in pager."""
369 369 page.page(self.shell.getoutput('man %s | col -b' % arg_s,
370 370 split=False))
371 371
372 372 @line_magic
373 373 def connect_info(self, arg_s):
374 374 """Print information for connecting other clients to this kernel
375 375
376 376 It will print the contents of this session's connection file, as well as
377 377 shortcuts for local clients.
378 378
379 379 In the simplest case, when called from the most recently launched kernel,
380 380 secondary clients can be connected, simply with:
381 381
382 382 $> ipython <app> --existing
383 383
384 384 """
385 385
386 386 from IPython.core.application import BaseIPythonApplication as BaseIPApp
387 387
388 388 if BaseIPApp.initialized():
389 389 app = BaseIPApp.instance()
390 390 security_dir = app.profile_dir.security_dir
391 391 profile = app.profile
392 392 else:
393 393 profile = 'default'
394 394 security_dir = ''
395 395
396 396 try:
397 397 connection_file = get_connection_file()
398 398 info = get_connection_info(unpack=False)
399 399 except Exception as e:
400 400 error("Could not get connection info: %r" % e)
401 401 return
402 402
403 403 # add profile flag for non-default profile
404 404 profile_flag = "--profile %s" % profile if profile != 'default' else ""
405 405
406 406 # if it's in the security dir, truncate to basename
407 407 if security_dir == os.path.dirname(connection_file):
408 408 connection_file = os.path.basename(connection_file)
409 409
410 410
411 411 print (info + '\n')
412 412 print ("Paste the above JSON into a file, and connect with:\n"
413 413 " $> ipython <app> --existing <file>\n"
414 414 "or, if you are local, you can connect with just:\n"
415 415 " $> ipython <app> --existing {0} {1}\n"
416 416 "or even just:\n"
417 417 " $> ipython <app> --existing {1}\n"
418 418 "if this is the most recent IPython session you have started.".format(
419 419 connection_file, profile_flag
420 420 )
421 421 )
422 422
423 423 @line_magic
424 424 def qtconsole(self, arg_s):
425 425 """Open a qtconsole connected to this kernel.
426 426
427 427 Useful for connecting a qtconsole to running notebooks, for better
428 428 debugging.
429 429 """
430 430
431 431 # %qtconsole should imply bind_kernel for engines:
432 432 try:
433 433 from IPython.parallel import bind_kernel
434 434 except ImportError:
435 435 # technically possible, because parallel has higher pyzmq min-version
436 436 pass
437 437 else:
438 438 bind_kernel()
439 439
440 440 try:
441 441 p = connect_qtconsole(argv=arg_split(arg_s, os.name=='posix'))
442 442 except Exception as e:
443 443 error("Could not start qtconsole: %r" % e)
444 444 return
445 445
446 446 @line_magic
447 447 def autosave(self, arg_s):
448 448 """Set the autosave interval in the notebook (in seconds).
449 449
450 450 The default value is 120, or two minutes.
451 451 ``%autosave 0`` will disable autosave.
452 452
453 453 This magic only has an effect when called from the notebook interface.
454 454 It has no effect when called in a startup file.
455 455 """
456 456
457 457 try:
458 458 interval = int(arg_s)
459 459 except ValueError:
460 460 raise UsageError("%%autosave requires an integer, got %r" % arg_s)
461 461
462 462 # javascript wants milliseconds
463 463 milliseconds = 1000 * interval
464 464 display(Javascript("IPython.notebook.set_autosave_interval(%i)" % milliseconds),
465 465 include=['application/javascript']
466 466 )
467 467 if interval:
468 468 print("Autosaving every %i seconds" % interval)
469 469 else:
470 470 print("Autosave disabled")
471 471
472 472
473 473 class ZMQInteractiveShell(InteractiveShell):
474 474 """A subclass of InteractiveShell for ZMQ."""
475 475
476 476 displayhook_class = Type(ZMQShellDisplayHook)
477 477 display_pub_class = Type(ZMQDisplayPublisher)
478 478 data_pub_class = Type(ZMQDataPublisher)
479 479
480 480 # Override the traitlet in the parent class, because there's no point using
481 481 # readline for the kernel. Can be removed when the readline code is moved
482 482 # to the terminal frontend.
483 483 colors_force = CBool(True)
484 484 readline_use = CBool(False)
485 485 # autoindent has no meaning in a zmqshell, and attempting to enable it
486 486 # will print a warning in the absence of readline.
487 487 autoindent = CBool(False)
488 488
489 489 exiter = Instance(ZMQExitAutocall)
490 490 def _exiter_default(self):
491 491 return ZMQExitAutocall(self)
492 492
493 493 def _exit_now_changed(self, name, old, new):
494 494 """stop eventloop when exit_now fires"""
495 495 if new:
496 496 loop = ioloop.IOLoop.instance()
497 497 loop.add_timeout(time.time()+0.1, loop.stop)
498 498
499 499 keepkernel_on_exit = None
500 500
501 501 # Over ZeroMQ, GUI control isn't done with PyOS_InputHook as there is no
502 502 # interactive input being read; we provide event loop support in ipkernel
503 503 @staticmethod
504 504 def enable_gui(gui):
505 505 from .eventloops import enable_gui as real_enable_gui
506 506 try:
507 507 real_enable_gui(gui)
508 508 except ValueError as e:
509 509 raise UsageError("%s" % e)
510 510
511 511 def init_environment(self):
512 512 """Configure the user's environment.
513 513
514 514 """
515 515 env = os.environ
516 516 # These two ensure 'ls' produces nice coloring on BSD-derived systems
517 517 env['TERM'] = 'xterm-color'
518 518 env['CLICOLOR'] = '1'
519 519 # Since normal pagers don't work at all (over pexpect we don't have
520 520 # single-key control of the subprocess), try to disable paging in
521 521 # subprocesses as much as possible.
522 522 env['PAGER'] = 'cat'
523 523 env['GIT_PAGER'] = 'cat'
524 524
525 525 # And install the payload version of page.
526 526 install_payload_page()
527 527
528 528 def auto_rewrite_input(self, cmd):
529 529 """Called to show the auto-rewritten input for autocall and friends.
530 530
531 531 FIXME: this payload is currently not correctly processed by the
532 532 frontend.
533 533 """
534 534 new = self.prompt_manager.render('rewrite') + cmd
535 535 payload = dict(
536 536 source='auto_rewrite_input',
537 537 transformed_input=new,
538 538 )
539 539 self.payload_manager.write_payload(payload)
540 540
541 541 def ask_exit(self):
542 542 """Engage the exit actions."""
543 543 self.exit_now = True
544 544 payload = dict(
545 545 source='ask_exit',
546 546 exit=True,
547 547 keepkernel=self.keepkernel_on_exit,
548 548 )
549 549 self.payload_manager.write_payload(payload)
550 550
551 551 def _showtraceback(self, etype, evalue, stb):
552 552
553 553 exc_content = {
554 554 u'traceback' : stb,
555 555 u'ename' : unicode(etype.__name__),
556 556 u'evalue' : py3compat.safe_unicode(evalue),
557 557 }
558 558
559 559 dh = self.displayhook
560 560 # Send exception info over pub socket for other clients than the caller
561 561 # to pick up
562 562 topic = None
563 563 if dh.topic:
564 564 topic = dh.topic.replace(b'pyout', b'pyerr')
565 565
566 566 exc_msg = dh.session.send(dh.pub_socket, u'pyerr', json_clean(exc_content), dh.parent_header, ident=topic)
567 567
568 568 # FIXME - Hack: store exception info in shell object. Right now, the
569 569 # caller is reading this info after the fact, we need to fix this logic
570 570 # to remove this hack. Even uglier, we need to store the error status
571 571 # here, because in the main loop, the logic that sets it is being
572 572 # skipped because runlines swallows the exceptions.
573 573 exc_content[u'status'] = u'error'
574 574 self._reply_content = exc_content
575 575 # /FIXME
576 576
577 577 return exc_content
578 578
579 579 def set_next_input(self, text):
580 580 """Send the specified text to the frontend to be presented at the next
581 581 input cell."""
582 582 payload = dict(
583 583 source='set_next_input',
584 584 text=text
585 585 )
586 586 self.payload_manager.write_payload(payload)
587 587
588 588 #-------------------------------------------------------------------------
589 589 # Things related to magics
590 590 #-------------------------------------------------------------------------
591 591
592 592 def init_magics(self):
593 593 super(ZMQInteractiveShell, self).init_magics()
594 594 self.register_magics(KernelMagics)
595 595 self.magics_manager.register_alias('ed', 'edit')
596 596
597 597
598 598
599 599 InteractiveShellABC.register(ZMQInteractiveShell)
@@ -1,1064 +1,1078 b''
1 1 .. _messaging:
2 2
3 3 ======================
4 4 Messaging in IPython
5 5 ======================
6 6
7 7
8 8 Introduction
9 9 ============
10 10
11 11 This document explains the basic communications design and messaging
12 12 specification for how the various IPython objects interact over a network
13 13 transport. The current implementation uses the ZeroMQ_ library for messaging
14 14 within and between hosts.
15 15
16 16 .. Note::
17 17
18 18 This document should be considered the authoritative description of the
19 19 IPython messaging protocol, and all developers are strongly encouraged to
20 20 keep it updated as the implementation evolves, so that we have a single
21 21 common reference for all protocol details.
22 22
23 23 The basic design is explained in the following diagram:
24 24
25 25 .. image:: figs/frontend-kernel.png
26 26 :width: 450px
27 27 :alt: IPython kernel/frontend messaging architecture.
28 28 :align: center
29 29 :target: ../_images/frontend-kernel.png
30 30
31 31 A single kernel can be simultaneously connected to one or more frontends. The
32 32 kernel has three sockets that serve the following functions:
33 33
34 34 1. stdin: this ROUTER socket is connected to all frontends, and it allows
35 35 the kernel to request input from the active frontend when :func:`raw_input` is called.
36 36 The frontend that executed the code has a DEALER socket that acts as a 'virtual keyboard'
37 37 for the kernel while this communication is happening (illustrated in the
38 38 figure by the black outline around the central keyboard). In practice,
39 39 frontends may display such kernel requests using a special input widget or
40 40 otherwise indicating that the user is to type input for the kernel instead
41 41 of normal commands in the frontend.
42 42
43 43 2. Shell: this single ROUTER socket allows multiple incoming connections from
44 44 frontends, and this is the socket where requests for code execution, object
45 45 information, prompts, etc. are made to the kernel by any frontend. The
46 46 communication on this socket is a sequence of request/reply actions from
47 47 each frontend and the kernel.
48 48
49 49 3. IOPub: this socket is the 'broadcast channel' where the kernel publishes all
50 50 side effects (stdout, stderr, etc.) as well as the requests coming from any
51 51 client over the shell socket and its own requests on the stdin socket. There
52 52 are a number of actions in Python which generate side effects: :func:`print`
53 53 writes to ``sys.stdout``, errors generate tracebacks, etc. Additionally, in
54 54 a multi-client scenario, we want all frontends to be able to know what each
55 55 other has sent to the kernel (this can be useful in collaborative scenarios,
56 56 for example). This socket allows both side effects and the information
57 57 about communications taking place with one client over the shell channel
58 58 to be made available to all clients in a uniform manner.
59 59
60 60 All messages are tagged with enough information (details below) for clients
61 61 to know which messages come from their own interaction with the kernel and
62 62 which ones are from other clients, so they can display each type
63 63 appropriately.
64 64
65 65 The actual format of the messages allowed on each of these channels is
66 66 specified below. Messages are dicts of dicts with string keys and values that
67 67 are reasonably representable in JSON. Our current implementation uses JSON
68 68 explicitly as its message format, but this shouldn't be considered a permanent
69 69 feature. As we've discovered that JSON has non-trivial performance issues due
70 70 to excessive copying, we may in the future move to a pure pickle-based raw
71 71 message format. However, it should be possible to easily convert from the raw
72 72 objects to JSON, since we may have non-python clients (e.g. a web frontend).
73 73 As long as it's easy to make a JSON version of the objects that is a faithful
74 74 representation of all the data, we can communicate with such clients.
75 75
76 76 .. Note::
77 77
78 78 Not all of these have yet been fully fleshed out, but the key ones are, see
79 79 kernel and frontend files for actual implementation details.
80 80
81 81 General Message Format
82 82 ======================
83 83
84 84 A message is defined by the following four-dictionary structure::
85 85
86 86 {
87 87 # The message header contains a pair of unique identifiers for the
88 88 # originating session and the actual message id, in addition to the
89 89 # username for the process that generated the message. This is useful in
90 90 # collaborative settings where multiple users may be interacting with the
91 91 # same kernel simultaneously, so that frontends can label the various
92 92 # messages in a meaningful way.
93 93 'header' : {
94 94 'msg_id' : uuid,
95 95 'username' : str,
96 96 'session' : uuid,
97 97 # All recognized message type strings are listed below.
98 98 'msg_type' : str,
99 99 },
100 100
101 101 # In a chain of messages, the header from the parent is copied so that
102 102 # clients can track where messages come from.
103 103 'parent_header' : dict,
104 104
105 105 # Any metadata associated with the message.
106 106 'metadata' : dict,
107 107
108 108 # The actual content of the message must be a dict, whose structure
109 109 # depends on the message type.
110 110 'content' : dict,
111 111 }
112 112
113 113 The Wire Protocol
114 114 =================
115 115
116 116
117 117 This message format exists at a high level,
118 118 but does not describe the actual *implementation* at the wire level in zeromq.
119 119 The canonical implementation of the message spec is our :class:`~IPython.kernel.zmq.session.Session` class.
120 120
121 121 .. note::
122 122
123 123 This section should only be relevant to non-Python consumers of the protocol.
124 124 Python consumers should simply import and use IPython's own implementation of the wire protocol
125 125 in the :class:`IPython.kernel.zmq.session.Session` object.
126 126
127 127 Every message is serialized to a sequence of at least six blobs of bytes:
128 128
129 129 .. sourcecode:: python
130 130
131 131 [
132 132 b'u-u-i-d', # zmq identity(ies)
133 133 b'<IDS|MSG>', # delimiter
134 134 b'baddad42', # HMAC signature
135 135 b'{header}', # serialized header dict
136 136 b'{parent_header}', # serialized parent header dict
137 137 b'{metadata}', # serialized metadata dict
138 138 b'{content}, # serialized content dict
139 139 b'blob', # extra raw data buffer(s)
140 140 ...
141 141 ]
142 142
143 143 The front of the message is the ZeroMQ routing prefix,
144 144 which can be zero or more socket identities.
145 145 This is every piece of the message prior to the delimiter key ``<IDS|MSG>``.
146 146 In the case of IOPub, there should be just one prefix component,
147 147 which is the topic for IOPub subscribers, e.g. ``pyout``, ``display_data``.
148 148
149 149 .. note::
150 150
151 151 In most cases, the IOPub topics are irrelevant and completely ignored,
152 152 because frontends just subscribe to all topics.
153 153 The convention used in the IPython kernel is to use the msg_type as the topic,
154 154 and possibly extra information about the message, e.g. ``pyout`` or ``stream.stdout``
155 155
156 156 After the delimiter is the `HMAC`_ signature of the message, used for authentication.
157 157 If authentication is disabled, this should be an empty string.
158 158 By default, the hashing function used for computing these signatures is sha256.
159 159
160 160 .. _HMAC: http://en.wikipedia.org/wiki/HMAC
161 161
162 162 .. note::
163 163
164 164 To disable authentication and signature checking,
165 165 set the `key` field of a connection file to an empty string.
166 166
167 167 The signature is the HMAC hex digest of the concatenation of:
168 168
169 169 - A shared key (typically the ``key`` field of a connection file)
170 170 - The serialized header dict
171 171 - The serialized parent header dict
172 172 - The serialized metadata dict
173 173 - The serialized content dict
174 174
175 175 In Python, this is implemented via:
176 176
177 177 .. sourcecode:: python
178 178
179 179 # once:
180 180 digester = HMAC(key, digestmod=hashlib.sha256)
181 181
182 182 # for each message
183 183 d = digester.copy()
184 184 for serialized_dict in (header, parent, metadata, content):
185 185 d.update(serialized_dict)
186 186 signature = d.hexdigest()
187 187
188 188 After the signature is the actual message, always in four frames of bytes.
189 189 The four dictionaries that compose a message are serialized separately,
190 190 in the order of header, parent header, metadata, and content.
191 191 These can be serialized by any function that turns a dict into bytes.
192 192 The default and most common serialization is JSON, but msgpack and pickle
193 193 are common alternatives.
194 194
195 195 After the serialized dicts are zero to many raw data buffers,
196 196 which can be used by message types that support binary data (mainly apply and data_pub).
197 197
198 198
199 199 Python functional API
200 200 =====================
201 201
202 202 As messages are dicts, they map naturally to a ``func(**kw)`` call form. We
203 203 should develop, at a few key points, functional forms of all the requests that
204 204 take arguments in this manner and automatically construct the necessary dict
205 205 for sending.
206 206
207 207 In addition, the Python implementation of the message specification extends
208 208 messages upon deserialization to the following form for convenience::
209 209
210 210 {
211 211 'header' : dict,
212 212 # The msg's unique identifier and type are always stored in the header,
213 213 # but the Python implementation copies them to the top level.
214 214 'msg_id' : uuid,
215 215 'msg_type' : str,
216 216 'parent_header' : dict,
217 217 'content' : dict,
218 218 'metadata' : dict,
219 219 }
220 220
221 221 All messages sent to or received by any IPython process should have this
222 222 extended structure.
223 223
224 224
225 225 Messages on the shell ROUTER/DEALER sockets
226 226 ===========================================
227 227
228 228 .. _execute:
229 229
230 230 Execute
231 231 -------
232 232
233 233 This message type is used by frontends to ask the kernel to execute code on
234 234 behalf of the user, in a namespace reserved to the user's variables (and thus
235 235 separate from the kernel's own internal code and variables).
236 236
237 237 Message type: ``execute_request``::
238 238
239 239 content = {
240 240 # Source code to be executed by the kernel, one or more lines.
241 241 'code' : str,
242 242
243 243 # A boolean flag which, if True, signals the kernel to execute
244 244 # this code as quietly as possible. This means that the kernel
245 245 # will compile the code with 'exec' instead of 'single' (so
246 246 # sys.displayhook will not fire), forces store_history to be False,
247 247 # and will *not*:
248 248 # - broadcast exceptions on the PUB socket
249 249 # - do any logging
250 250 #
251 251 # The default is False.
252 252 'silent' : bool,
253 253
254 254 # A boolean flag which, if True, signals the kernel to populate history
255 255 # The default is True if silent is False. If silent is True, store_history
256 256 # is forced to be False.
257 257 'store_history' : bool,
258 258
259 259 # A list of variable names from the user's namespace to be retrieved.
260 260 # What returns is a rich representation of each variable (dict keyed by name).
261 261 # See the display_data content for the structure of the representation data.
262 262 'user_variables' : list,
263 263
264 264 # Similarly, a dict mapping names to expressions to be evaluated in the
265 265 # user's dict.
266 266 'user_expressions' : dict,
267 267
268 268 # Some frontends (e.g. the Notebook) do not support stdin requests. If
269 269 # raw_input is called from code executed from such a frontend, a
270 270 # StdinNotImplementedError will be raised.
271 271 'allow_stdin' : True,
272 272
273 273 }
274 274
275 275 The ``code`` field contains a single string (possibly multiline). The kernel
276 276 is responsible for splitting this into one or more independent execution blocks
277 277 and deciding whether to compile these in 'single' or 'exec' mode (see below for
278 278 detailed execution semantics).
279 279
280 280 The ``user_`` fields deserve a detailed explanation. In the past, IPython had
281 281 the notion of a prompt string that allowed arbitrary code to be evaluated, and
282 282 this was put to good use by many in creating prompts that displayed system
283 283 status, path information, and even more esoteric uses like remote instrument
284 284 status acquired over the network. But now that IPython has a clean separation
285 285 between the kernel and the clients, the kernel has no prompt knowledge; prompts
286 286 are a frontend-side feature, and it should be even possible for different
287 287 frontends to display different prompts while interacting with the same kernel.
288 288
289 289 The kernel now provides the ability to retrieve data from the user's namespace
290 290 after the execution of the main ``code``, thanks to two fields in the
291 291 ``execute_request`` message:
292 292
293 293 - ``user_variables``: If only variables from the user's namespace are needed, a
294 294 list of variable names can be passed and a dict with these names as keys and
295 295 their :func:`repr()` as values will be returned.
296 296
297 297 - ``user_expressions``: For more complex expressions that require function
298 298 evaluations, a dict can be provided with string keys and arbitrary python
299 299 expressions as values. The return message will contain also a dict with the
300 300 same keys and the :func:`repr()` of the evaluated expressions as value.
301 301
302 302 With this information, frontends can display any status information they wish
303 303 in the form that best suits each frontend (a status line, a popup, inline for a
304 304 terminal, etc).
305 305
306 306 .. Note::
307 307
308 308 In order to obtain the current execution counter for the purposes of
309 309 displaying input prompts, frontends simply make an execution request with an
310 310 empty code string and ``silent=True``.
311 311
312 312 Execution semantics
313 313 ~~~~~~~~~~~~~~~~~~~
314 314
315 315 When the silent flag is false, the execution of use code consists of the
316 316 following phases (in silent mode, only the ``code`` field is executed):
317 317
318 318 1. Run the ``pre_runcode_hook``.
319 319
320 320 2. Execute the ``code`` field, see below for details.
321 321
322 322 3. If #2 succeeds, compute ``user_variables`` and ``user_expressions`` are
323 323 computed. This ensures that any error in the latter don't harm the main
324 324 code execution.
325 325
326 326 4. Call any method registered with :meth:`register_post_execute`.
327 327
328 328 .. warning::
329 329
330 330 The API for running code before/after the main code block is likely to
331 331 change soon. Both the ``pre_runcode_hook`` and the
332 332 :meth:`register_post_execute` are susceptible to modification, as we find a
333 333 consistent model for both.
334 334
335 335 To understand how the ``code`` field is executed, one must know that Python
336 336 code can be compiled in one of three modes (controlled by the ``mode`` argument
337 337 to the :func:`compile` builtin):
338 338
339 339 *single*
340 340 Valid for a single interactive statement (though the source can contain
341 341 multiple lines, such as a for loop). When compiled in this mode, the
342 342 generated bytecode contains special instructions that trigger the calling of
343 343 :func:`sys.displayhook` for any expression in the block that returns a value.
344 344 This means that a single statement can actually produce multiple calls to
345 345 :func:`sys.displayhook`, if for example it contains a loop where each
346 346 iteration computes an unassigned expression would generate 10 calls::
347 347
348 348 for i in range(10):
349 349 i**2
350 350
351 351 *exec*
352 352 An arbitrary amount of source code, this is how modules are compiled.
353 353 :func:`sys.displayhook` is *never* implicitly called.
354 354
355 355 *eval*
356 356 A single expression that returns a value. :func:`sys.displayhook` is *never*
357 357 implicitly called.
358 358
359 359
360 360 The ``code`` field is split into individual blocks each of which is valid for
361 361 execution in 'single' mode, and then:
362 362
363 363 - If there is only a single block: it is executed in 'single' mode.
364 364
365 365 - If there is more than one block:
366 366
367 367 * if the last one is a single line long, run all but the last in 'exec' mode
368 368 and the very last one in 'single' mode. This makes it easy to type simple
369 369 expressions at the end to see computed values.
370 370
371 371 * if the last one is no more than two lines long, run all but the last in
372 372 'exec' mode and the very last one in 'single' mode. This makes it easy to
373 373 type simple expressions at the end to see computed values. - otherwise
374 374 (last one is also multiline), run all in 'exec' mode
375 375
376 376 * otherwise (last one is also multiline), run all in 'exec' mode as a single
377 377 unit.
378 378
379 379 Any error in retrieving the ``user_variables`` or evaluating the
380 380 ``user_expressions`` will result in a simple error message in the return fields
381 381 of the form::
382 382
383 383 [ERROR] ExceptionType: Exception message
384 384
385 385 The user can simply send the same variable name or expression for evaluation to
386 386 see a regular traceback.
387 387
388 388 Errors in any registered post_execute functions are also reported similarly,
389 389 and the failing function is removed from the post_execution set so that it does
390 390 not continue triggering failures.
391 391
392 392 Upon completion of the execution request, the kernel *always* sends a reply,
393 393 with a status code indicating what happened and additional data depending on
394 394 the outcome. See :ref:`below <execution_results>` for the possible return
395 395 codes and associated data.
396 396
397 397
398 398 Execution counter (old prompt number)
399 399 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
400 400
401 401 The kernel has a single, monotonically increasing counter of all execution
402 402 requests that are made with ``store_history=True``. This counter is used to populate
403 403 the ``In[n]``, ``Out[n]`` and ``_n`` variables, so clients will likely want to
404 404 display it in some form to the user, which will typically (but not necessarily)
405 405 be done in the prompts. The value of this counter will be returned as the
406 406 ``execution_count`` field of all ``execute_reply`` messages.
407 407
408 408 .. _execution_results:
409 409
410 410 Execution results
411 411 ~~~~~~~~~~~~~~~~~
412 412
413 413 Message type: ``execute_reply``::
414 414
415 415 content = {
416 416 # One of: 'ok' OR 'error' OR 'abort'
417 417 'status' : str,
418 418
419 419 # The global kernel counter that increases by one with each request that
420 420 # stores history. This will typically be used by clients to display
421 421 # prompt numbers to the user. If the request did not store history, this will
422 422 # be the current value of the counter in the kernel.
423 423 'execution_count' : int,
424 424 }
425 425
426 426 When status is 'ok', the following extra fields are present::
427 427
428 428 {
429 429 # 'payload' will be a list of payload dicts.
430 430 # Each execution payload is a dict with string keys that may have been
431 431 # produced by the code being executed. It is retrieved by the kernel at
432 432 # the end of the execution and sent back to the front end, which can take
433 433 # action on it as needed. See main text for further details.
434 434 'payload' : list(dict),
435 435
436 436 # Results for the user_variables and user_expressions.
437 437 'user_variables' : dict,
438 438 'user_expressions' : dict,
439 439 }
440 440
441 441 .. admonition:: Execution payloads
442 442
443 443 The notion of an 'execution payload' is different from a return value of a
444 444 given set of code, which normally is just displayed on the pyout stream
445 445 through the PUB socket. The idea of a payload is to allow special types of
446 446 code, typically magics, to populate a data container in the IPython kernel
447 447 that will be shipped back to the caller via this channel. The kernel
448 448 has an API for this in the PayloadManager::
449 449
450 450 ip.payload_manager.write_payload(payload_dict)
451 451
452 452 which appends a dictionary to the list of payloads.
453 453
454 454 The payload API is not yet stabilized,
455 455 and should probably not be supported by non-Python kernels at this time.
456 456 In such cases, the payload list should always be empty.
457 457
458 458
459 459 When status is 'error', the following extra fields are present::
460 460
461 461 {
462 462 'ename' : str, # Exception name, as a string
463 463 'evalue' : str, # Exception value, as a string
464 464
465 465 # The traceback will contain a list of frames, represented each as a
466 466 # string. For now we'll stick to the existing design of ultraTB, which
467 467 # controls exception level of detail statefully. But eventually we'll
468 468 # want to grow into a model where more information is collected and
469 469 # packed into the traceback object, with clients deciding how little or
470 470 # how much of it to unpack. But for now, let's start with a simple list
471 471 # of strings, since that requires only minimal changes to ultratb as
472 472 # written.
473 473 'traceback' : list,
474 474 }
475 475
476 476
477 477 When status is 'abort', there are for now no additional data fields. This
478 478 happens when the kernel was interrupted by a signal.
479 479
480 480
481 481 Object information
482 482 ------------------
483 483
484 484 One of IPython's most used capabilities is the introspection of Python objects
485 485 in the user's namespace, typically invoked via the ``?`` and ``??`` characters
486 486 (which in reality are shorthands for the ``%pinfo`` magic). This is used often
487 487 enough that it warrants an explicit message type, especially because frontends
488 488 may want to get object information in response to user keystrokes (like Tab or
489 489 F1) besides from the user explicitly typing code like ``x??``.
490 490
491 491 Message type: ``object_info_request``::
492 492
493 493 content = {
494 494 # The (possibly dotted) name of the object to be searched in all
495 495 # relevant namespaces
496 496 'oname' : str,
497 497
498 498 # The level of detail desired. The default (0) is equivalent to typing
499 499 # 'x?' at the prompt, 1 is equivalent to 'x??'.
500 500 'detail_level' : int,
501 501 }
502 502
503 503 The returned information will be a dictionary with keys very similar to the
504 504 field names that IPython prints at the terminal.
505 505
506 506 Message type: ``object_info_reply``::
507 507
508 508 content = {
509 509 # The name the object was requested under
510 510 'name' : str,
511 511
512 512 # Boolean flag indicating whether the named object was found or not. If
513 513 # it's false, all other fields will be empty.
514 514 'found' : bool,
515 515
516 516 # Flags for magics and system aliases
517 517 'ismagic' : bool,
518 518 'isalias' : bool,
519 519
520 520 # The name of the namespace where the object was found ('builtin',
521 521 # 'magics', 'alias', 'interactive', etc.)
522 522 'namespace' : str,
523 523
524 524 # The type name will be type.__name__ for normal Python objects, but it
525 525 # can also be a string like 'Magic function' or 'System alias'
526 526 'type_name' : str,
527 527
528 528 # The string form of the object, possibly truncated for length if
529 529 # detail_level is 0
530 530 'string_form' : str,
531 531
532 532 # For objects with a __class__ attribute this will be set
533 533 'base_class' : str,
534 534
535 535 # For objects with a __len__ attribute this will be set
536 536 'length' : int,
537 537
538 538 # If the object is a function, class or method whose file we can find,
539 539 # we give its full path
540 540 'file' : str,
541 541
542 542 # For pure Python callable objects, we can reconstruct the object
543 543 # definition line which provides its call signature. For convenience this
544 544 # is returned as a single 'definition' field, but below the raw parts that
545 545 # compose it are also returned as the argspec field.
546 546 'definition' : str,
547 547
548 548 # The individual parts that together form the definition string. Clients
549 549 # with rich display capabilities may use this to provide a richer and more
550 550 # precise representation of the definition line (e.g. by highlighting
551 551 # arguments based on the user's cursor position). For non-callable
552 552 # objects, this field is empty.
553 553 'argspec' : { # The names of all the arguments
554 554 args : list,
555 555 # The name of the varargs (*args), if any
556 556 varargs : str,
557 557 # The name of the varkw (**kw), if any
558 558 varkw : str,
559 559 # The values (as strings) of all default arguments. Note
560 560 # that these must be matched *in reverse* with the 'args'
561 561 # list above, since the first positional args have no default
562 562 # value at all.
563 563 defaults : list,
564 564 },
565 565
566 566 # For instances, provide the constructor signature (the definition of
567 567 # the __init__ method):
568 568 'init_definition' : str,
569 569
570 570 # Docstrings: for any object (function, method, module, package) with a
571 571 # docstring, we show it. But in addition, we may provide additional
572 572 # docstrings. For example, for instances we will show the constructor
573 573 # and class docstrings as well, if available.
574 574 'docstring' : str,
575 575
576 576 # For instances, provide the constructor and class docstrings
577 577 'init_docstring' : str,
578 578 'class_docstring' : str,
579 579
580 580 # If it's a callable object whose call method has a separate docstring and
581 581 # definition line:
582 582 'call_def' : str,
583 583 'call_docstring' : str,
584 584
585 585 # If detail_level was 1, we also try to find the source code that
586 586 # defines the object, if possible. The string 'None' will indicate
587 587 # that no source was found.
588 588 'source' : str,
589 589 }
590 590
591 591
592 592 Complete
593 593 --------
594 594
595 595 Message type: ``complete_request``::
596 596
597 597 content = {
598 598 # The text to be completed, such as 'a.is'
599 599 # this may be an empty string if the frontend does not do any lexing,
600 600 # in which case the kernel must figure out the completion
601 601 # based on 'line' and 'cursor_pos'.
602 602 'text' : str,
603 603
604 604 # The full line, such as 'print a.is'. This allows completers to
605 605 # make decisions that may require information about more than just the
606 606 # current word.
607 607 'line' : str,
608 608
609 609 # The entire block of text where the line is. This may be useful in the
610 610 # case of multiline completions where more context may be needed. Note: if
611 611 # in practice this field proves unnecessary, remove it to lighten the
612 612 # messages.
613 613
614 614 'block' : str or null/None,
615 615
616 616 # The position of the cursor where the user hit 'TAB' on the line.
617 617 'cursor_pos' : int,
618 618 }
619 619
620 620 Message type: ``complete_reply``::
621 621
622 622 content = {
623 623 # The list of all matches to the completion request, such as
624 624 # ['a.isalnum', 'a.isalpha'] for the above example.
625 625 'matches' : list,
626 626
627 627 # the substring of the matched text
628 628 # this is typically the common prefix of the matches,
629 629 # and the text that is already in the block that would be replaced by the full completion.
630 630 # This would be 'a.is' in the above example.
631 631 'text' : str,
632 632
633 633 # status should be 'ok' unless an exception was raised during the request,
634 634 # in which case it should be 'error', along with the usual error message content
635 635 # in other messages.
636 636 'status' : 'ok'
637 637 }
638 638
639 639
640 640 History
641 641 -------
642 642
643 643 For clients to explicitly request history from a kernel. The kernel has all
644 644 the actual execution history stored in a single location, so clients can
645 645 request it from the kernel when needed.
646 646
647 647 Message type: ``history_request``::
648 648
649 649 content = {
650 650
651 651 # If True, also return output history in the resulting dict.
652 652 'output' : bool,
653 653
654 654 # If True, return the raw input history, else the transformed input.
655 655 'raw' : bool,
656 656
657 657 # So far, this can be 'range', 'tail' or 'search'.
658 658 'hist_access_type' : str,
659 659
660 660 # If hist_access_type is 'range', get a range of input cells. session can
661 661 # be a positive session number, or a negative number to count back from
662 662 # the current session.
663 663 'session' : int,
664 664 # start and stop are line numbers within that session.
665 665 'start' : int,
666 666 'stop' : int,
667 667
668 668 # If hist_access_type is 'tail' or 'search', get the last n cells.
669 669 'n' : int,
670 670
671 671 # If hist_access_type is 'search', get cells matching the specified glob
672 672 # pattern (with * and ? as wildcards).
673 673 'pattern' : str,
674 674
675 675 # If hist_access_type is 'search' and unique is true, do not
676 676 # include duplicated history. Default is false.
677 677 'unique' : bool,
678 678
679 679 }
680 680
681 681 .. versionadded:: 4.0
682 682 The key ``unique`` for ``history_request``.
683 683
684 684 Message type: ``history_reply``::
685 685
686 686 content = {
687 687 # A list of 3 tuples, either:
688 688 # (session, line_number, input) or
689 689 # (session, line_number, (input, output)),
690 690 # depending on whether output was False or True, respectively.
691 691 'history' : list,
692 692 }
693 693
694 694
695 695 Connect
696 696 -------
697 697
698 698 When a client connects to the request/reply socket of the kernel, it can issue
699 699 a connect request to get basic information about the kernel, such as the ports
700 700 the other ZeroMQ sockets are listening on. This allows clients to only have
701 701 to know about a single port (the shell channel) to connect to a kernel.
702 702
703 703 Message type: ``connect_request``::
704 704
705 705 content = {
706 706 }
707 707
708 708 Message type: ``connect_reply``::
709 709
710 710 content = {
711 711 'shell_port' : int, # The port the shell ROUTER socket is listening on.
712 712 'iopub_port' : int, # The port the PUB socket is listening on.
713 713 'stdin_port' : int, # The port the stdin ROUTER socket is listening on.
714 714 'hb_port' : int, # The port the heartbeat socket is listening on.
715 715 }
716 716
717 717
718 718 Kernel info
719 719 -----------
720 720
721 721 If a client needs to know information about the kernel, it can
722 722 make a request of the kernel's information.
723 723 This message can be used to fetch core information of the
724 724 kernel, including language (e.g., Python), language version number and
725 725 IPython version number, and the IPython message spec version number.
726 726
727 727 Message type: ``kernel_info_request``::
728 728
729 729 content = {
730 730 }
731 731
732 732 Message type: ``kernel_info_reply``::
733 733
734 734 content = {
735 735 # Version of messaging protocol (mandatory).
736 736 # The first integer indicates major version. It is incremented when
737 737 # there is any backward incompatible change.
738 738 # The second integer indicates minor version. It is incremented when
739 739 # there is any backward compatible change.
740 740 'protocol_version': [int, int],
741 741
742 742 # IPython version number (optional).
743 743 # Non-python kernel backend may not have this version number.
744 744 # The last component is an extra field, which may be 'dev' or
745 745 # 'rc1' in development version. It is an empty string for
746 746 # released version.
747 747 'ipython_version': [int, int, int, str],
748 748
749 749 # Language version number (mandatory).
750 750 # It is Python version number (e.g., [2, 7, 3]) for the kernel
751 751 # included in IPython.
752 752 'language_version': [int, ...],
753 753
754 754 # Programming language in which kernel is implemented (mandatory).
755 755 # Kernel included in IPython returns 'python'.
756 756 'language': str,
757 757 }
758 758
759 759
760 760 Kernel shutdown
761 761 ---------------
762 762
763 763 The clients can request the kernel to shut itself down; this is used in
764 764 multiple cases:
765 765
766 766 - when the user chooses to close the client application via a menu or window
767 767 control.
768 768 - when the user types 'exit' or 'quit' (or their uppercase magic equivalents).
769 769 - when the user chooses a GUI method (like the 'Ctrl-C' shortcut in the
770 770 IPythonQt client) to force a kernel restart to get a clean kernel without
771 771 losing client-side state like history or inlined figures.
772 772
773 773 The client sends a shutdown request to the kernel, and once it receives the
774 774 reply message (which is otherwise empty), it can assume that the kernel has
775 775 completed shutdown safely.
776 776
777 777 Upon their own shutdown, client applications will typically execute a last
778 778 minute sanity check and forcefully terminate any kernel that is still alive, to
779 779 avoid leaving stray processes in the user's machine.
780 780
781 781 Message type: ``shutdown_request``::
782 782
783 783 content = {
784 784 'restart' : bool # whether the shutdown is final, or precedes a restart
785 785 }
786 786
787 787 Message type: ``shutdown_reply``::
788 788
789 789 content = {
790 790 'restart' : bool # whether the shutdown is final, or precedes a restart
791 791 }
792 792
793 793 .. Note::
794 794
795 795 When the clients detect a dead kernel thanks to inactivity on the heartbeat
796 796 socket, they simply send a forceful process termination signal, since a dead
797 797 process is unlikely to respond in any useful way to messages.
798 798
799 799
800 800 Messages on the PUB/SUB socket
801 801 ==============================
802 802
803 803 Streams (stdout, stderr, etc)
804 804 ------------------------------
805 805
806 806 Message type: ``stream``::
807 807
808 808 content = {
809 809 # The name of the stream is one of 'stdout', 'stderr'
810 810 'name' : str,
811 811
812 812 # The data is an arbitrary string to be written to that stream
813 813 'data' : str,
814 814 }
815 815
816 816 Display Data
817 817 ------------
818 818
819 819 This type of message is used to bring back data that should be diplayed (text,
820 820 html, svg, etc.) in the frontends. This data is published to all frontends.
821 821 Each message can have multiple representations of the data; it is up to the
822 822 frontend to decide which to use and how. A single message should contain all
823 823 possible representations of the same information. Each representation should
824 824 be a JSON'able data structure, and should be a valid MIME type.
825 825
826 826 Some questions remain about this design:
827 827
828 828 * Do we use this message type for pyout/displayhook? Probably not, because
829 829 the displayhook also has to handle the Out prompt display. On the other hand
830 830 we could put that information into the metadata secion.
831 831
832 832 Message type: ``display_data``::
833 833
834 834 content = {
835 835
836 836 # Who create the data
837 837 'source' : str,
838 838
839 839 # The data dict contains key/value pairs, where the kids are MIME
840 840 # types and the values are the raw data of the representation in that
841 841 # format.
842 842 'data' : dict,
843 843
844 844 # Any metadata that describes the data
845 845 'metadata' : dict
846 846 }
847 847
848 848
849 849 The ``metadata`` contains any metadata that describes the output.
850 850 Global keys are assumed to apply to the output as a whole.
851 851 The ``metadata`` dict can also contain mime-type keys, which will be sub-dictionaries,
852 852 which are interpreted as applying only to output of that type.
853 853 Third parties should put any data they write into a single dict
854 854 with a reasonably unique name to avoid conflicts.
855 855
856 856 The only metadata keys currently defined in IPython are the width and height
857 857 of images::
858 858
859 859 'metadata' : {
860 860 'image/png' : {
861 861 'width': 640,
862 862 'height': 480
863 863 }
864 864 }
865 865
866 866
867 867 Raw Data Publication
868 868 --------------------
869 869
870 870 ``display_data`` lets you publish *representations* of data, such as images and html.
871 871 This ``data_pub`` message lets you publish *actual raw data*, sent via message buffers.
872 872
873 873 data_pub messages are constructed via the :func:`IPython.lib.datapub.publish_data` function:
874 874
875 875 .. sourcecode:: python
876 876
877 877 from IPython.kernel.zmq.datapub import publish_data
878 878 ns = dict(x=my_array)
879 879 publish_data(ns)
880 880
881 881
882 882 Message type: ``data_pub``::
883 883
884 884 content = {
885 885 # the keys of the data dict, after it has been unserialized
886 886 keys = ['a', 'b']
887 887 }
888 888 # the namespace dict will be serialized in the message buffers,
889 889 # which will have a length of at least one
890 890 buffers = ['pdict', ...]
891 891
892 892
893 893 The interpretation of a sequence of data_pub messages for a given parent request should be
894 894 to update a single namespace with subsequent results.
895 895
896 896 .. note::
897 897
898 898 No frontends directly handle data_pub messages at this time.
899 899 It is currently only used by the client/engines in :mod:`IPython.parallel`,
900 900 where engines may publish *data* to the Client,
901 901 of which the Client can then publish *representations* via ``display_data``
902 902 to various frontends.
903 903
904 904 Python inputs
905 905 -------------
906 906
907 907 These messages are the re-broadcast of the ``execute_request``.
908 908
909 909 Message type: ``pyin``::
910 910
911 911 content = {
912 912 'code' : str, # Source code to be executed, one or more lines
913 913
914 914 # The counter for this execution is also provided so that clients can
915 915 # display it, since IPython automatically creates variables called _iN
916 916 # (for input prompt In[N]).
917 917 'execution_count' : int
918 918 }
919 919
920 920 Python outputs
921 921 --------------
922 922
923 923 When Python produces output from code that has been compiled in with the
924 924 'single' flag to :func:`compile`, any expression that produces a value (such as
925 925 ``1+1``) is passed to ``sys.displayhook``, which is a callable that can do with
926 926 this value whatever it wants. The default behavior of ``sys.displayhook`` in
927 927 the Python interactive prompt is to print to ``sys.stdout`` the :func:`repr` of
928 928 the value as long as it is not ``None`` (which isn't printed at all). In our
929 929 case, the kernel instantiates as ``sys.displayhook`` an object which has
930 930 similar behavior, but which instead of printing to stdout, broadcasts these
931 931 values as ``pyout`` messages for clients to display appropriately.
932 932
933 933 IPython's displayhook can handle multiple simultaneous formats depending on its
934 934 configuration. The default pretty-printed repr text is always given with the
935 935 ``data`` entry in this message. Any other formats are provided in the
936 936 ``extra_formats`` list. Frontends are free to display any or all of these
937 937 according to its capabilities. ``extra_formats`` list contains 3-tuples of an ID
938 938 string, a type string, and the data. The ID is unique to the formatter
939 939 implementation that created the data. Frontends will typically ignore the ID
940 940 unless if it has requested a particular formatter. The type string tells the
941 941 frontend how to interpret the data. It is often, but not always a MIME type.
942 942 Frontends should ignore types that it does not understand. The data itself is
943 943 any JSON object and depends on the format. It is often, but not always a string.
944 944
945 945 Message type: ``pyout``::
946 946
947 947 content = {
948 948
949 949 # The counter for this execution is also provided so that clients can
950 950 # display it, since IPython automatically creates variables called _N
951 951 # (for prompt N).
952 952 'execution_count' : int,
953 953
954 954 # data and metadata are identical to a display_data message.
955 955 # the object being displayed is that passed to the display hook,
956 956 # i.e. the *result* of the execution.
957 957 'data' : dict,
958 958 'metadata' : dict,
959 959 }
960 960
961 961 Python errors
962 962 -------------
963 963
964 964 When an error occurs during code execution
965 965
966 966 Message type: ``pyerr``::
967 967
968 968 content = {
969 969 # Similar content to the execute_reply messages for the 'error' case,
970 970 # except the 'status' field is omitted.
971 971 }
972 972
973 973 Kernel status
974 974 -------------
975 975
976 976 This message type is used by frontends to monitor the status of the kernel.
977 977
978 978 Message type: ``status``::
979 979
980 980 content = {
981 981 # When the kernel starts to execute code, it will enter the 'busy'
982 982 # state and when it finishes, it will enter the 'idle' state.
983 983 # The kernel will publish state 'starting' exactly once at process startup.
984 984 execution_state : ('busy', 'idle', 'starting')
985 985 }
986 986
987 Clear output
988 ------------
989
990 This message type is used to clear the output that is visible on the frontend.
991
992 Message type: ``clear_output``::
993
994 content = {
995
996 # Wait to clear the output until new output is available. Clears the
997 # existing output immediately before the new output is displayed.
998 # Useful for creating simple animations with minimal flickering.
999 'wait' : bool,
1000 }
987 1001
988 1002 Messages on the stdin ROUTER/DEALER sockets
989 1003 ===========================================
990 1004
991 1005 This is a socket where the request/reply pattern goes in the opposite direction:
992 1006 from the kernel to a *single* frontend, and its purpose is to allow
993 1007 ``raw_input`` and similar operations that read from ``sys.stdin`` on the kernel
994 1008 to be fulfilled by the client. The request should be made to the frontend that
995 1009 made the execution request that prompted ``raw_input`` to be called. For now we
996 1010 will keep these messages as simple as possible, since they only mean to convey
997 1011 the ``raw_input(prompt)`` call.
998 1012
999 1013 Message type: ``input_request``::
1000 1014
1001 1015 content = { 'prompt' : str }
1002 1016
1003 1017 Message type: ``input_reply``::
1004 1018
1005 1019 content = { 'value' : str }
1006 1020
1007 1021 .. Note::
1008 1022
1009 1023 We do not explicitly try to forward the raw ``sys.stdin`` object, because in
1010 1024 practice the kernel should behave like an interactive program. When a
1011 1025 program is opened on the console, the keyboard effectively takes over the
1012 1026 ``stdin`` file descriptor, and it can't be used for raw reading anymore.
1013 1027 Since the IPython kernel effectively behaves like a console program (albeit
1014 1028 one whose "keyboard" is actually living in a separate process and
1015 1029 transported over the zmq connection), raw ``stdin`` isn't expected to be
1016 1030 available.
1017 1031
1018 1032
1019 1033 Heartbeat for kernels
1020 1034 =====================
1021 1035
1022 1036 Initially we had considered using messages like those above over ZMQ for a
1023 1037 kernel 'heartbeat' (a way to detect quickly and reliably whether a kernel is
1024 1038 alive at all, even if it may be busy executing user code). But this has the
1025 1039 problem that if the kernel is locked inside extension code, it wouldn't execute
1026 1040 the python heartbeat code. But it turns out that we can implement a basic
1027 1041 heartbeat with pure ZMQ, without using any Python messaging at all.
1028 1042
1029 1043 The monitor sends out a single zmq message (right now, it is a str of the
1030 1044 monitor's lifetime in seconds), and gets the same message right back, prefixed
1031 1045 with the zmq identity of the DEALER socket in the heartbeat process. This can be
1032 1046 a uuid, or even a full message, but there doesn't seem to be a need for packing
1033 1047 up a message when the sender and receiver are the exact same Python object.
1034 1048
1035 1049 The model is this::
1036 1050
1037 1051 monitor.send(str(self.lifetime)) # '1.2345678910'
1038 1052
1039 1053 and the monitor receives some number of messages of the form::
1040 1054
1041 1055 ['uuid-abcd-dead-beef', '1.2345678910']
1042 1056
1043 1057 where the first part is the zmq.IDENTITY of the heart's DEALER on the engine, and
1044 1058 the rest is the message sent by the monitor. No Python code ever has any
1045 1059 access to the message between the monitor's send, and the monitor's recv.
1046 1060
1047 1061
1048 1062 ToDo
1049 1063 ====
1050 1064
1051 1065 Missing things include:
1052 1066
1053 1067 * Important: finish thinking through the payload concept and API.
1054 1068
1055 1069 * Important: ensure that we have a good solution for magics like %edit. It's
1056 1070 likely that with the payload concept we can build a full solution, but not
1057 1071 100% clear yet.
1058 1072
1059 1073 * Finishing the details of the heartbeat protocol.
1060 1074
1061 1075 * Signal handling: specify what kind of information kernel should broadcast (or
1062 1076 not) when it receives signals.
1063 1077
1064 1078 .. include:: ../links.txt
General Comments 0
You need to be logged in to leave comments. Login now