##// END OF EJS Templates
Merge pull request #5648 from andrewjesaitis/ticket4756...
Min RK -
r16416:b257cbb2 merge
parent child Browse files
Show More
@@ -1,779 +1,803 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.core.formatters import _safe_get_formatter_method
26 26 from IPython.utils.py3compat import (string_types, cast_bytes_py2, cast_unicode,
27 27 unicode_type)
28 28 from IPython.testing.skipdoctest import skip_doctest
29 29 from .displaypub import publish_display_data
30 30
31 31 #-----------------------------------------------------------------------------
32 32 # utility functions
33 33 #-----------------------------------------------------------------------------
34 34
35 35 def _safe_exists(path):
36 36 """Check path, but don't let exceptions raise"""
37 37 try:
38 38 return os.path.exists(path)
39 39 except Exception:
40 40 return False
41 41
42 42 def _merge(d1, d2):
43 43 """Like update, but merges sub-dicts instead of clobbering at the top level.
44 44
45 45 Updates d1 in-place
46 46 """
47 47
48 48 if not isinstance(d2, dict) or not isinstance(d1, dict):
49 49 return d2
50 50 for key, value in d2.items():
51 51 d1[key] = _merge(d1.get(key), value)
52 52 return d1
53 53
54 54 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
55 55 """internal implementation of all display_foo methods
56 56
57 57 Parameters
58 58 ----------
59 59 mimetype : str
60 60 The mimetype to be published (e.g. 'image/png')
61 61 objs : tuple of objects
62 62 The Python objects to display, or if raw=True raw text data to
63 63 display.
64 64 raw : bool
65 65 Are the data objects raw data or Python objects that need to be
66 66 formatted before display? [default: False]
67 67 metadata : dict (optional)
68 68 Metadata to be associated with the specific mimetype output.
69 69 """
70 70 if metadata:
71 71 metadata = {mimetype: metadata}
72 72 if raw:
73 73 # turn list of pngdata into list of { 'image/png': pngdata }
74 74 objs = [ {mimetype: obj} for obj in objs ]
75 75 display(*objs, raw=raw, metadata=metadata, include=[mimetype])
76 76
77 77 #-----------------------------------------------------------------------------
78 78 # Main functions
79 79 #-----------------------------------------------------------------------------
80 80
81 81 def display(*objs, **kwargs):
82 82 """Display a Python object in all frontends.
83 83
84 84 By default all representations will be computed and sent to the frontends.
85 85 Frontends can decide which representation is used and how.
86 86
87 87 Parameters
88 88 ----------
89 89 objs : tuple of objects
90 90 The Python objects to display.
91 91 raw : bool, optional
92 92 Are the objects to be displayed already mimetype-keyed dicts of raw display data,
93 93 or Python objects that need to be formatted before display? [default: False]
94 94 include : list or tuple, optional
95 95 A list of format type strings (MIME types) to include in the
96 96 format data dict. If this is set *only* the format types included
97 97 in this list will be computed.
98 98 exclude : list or tuple, optional
99 99 A list of format type strings (MIME types) to exclude in the format
100 100 data dict. If this is set all format types will be computed,
101 101 except for those included in this argument.
102 102 metadata : dict, optional
103 103 A dictionary of metadata to associate with the output.
104 104 mime-type keys in this dictionary will be associated with the individual
105 105 representation formats, if they exist.
106 106 """
107 107 raw = kwargs.get('raw', False)
108 108 include = kwargs.get('include')
109 109 exclude = kwargs.get('exclude')
110 110 metadata = kwargs.get('metadata')
111 111
112 112 from IPython.core.interactiveshell import InteractiveShell
113 113
114 114 if not raw:
115 115 format = InteractiveShell.instance().display_formatter.format
116 116
117 117 for obj in objs:
118 118
119 119 # If _ipython_display_ is defined, use that to display this object.
120 120 display_method = _safe_get_formatter_method(obj, '_ipython_display_')
121 121 if display_method is not None:
122 122 try:
123 123 display_method(**kwargs)
124 124 except NotImplementedError:
125 125 pass
126 126 else:
127 127 continue
128 128 if raw:
129 129 publish_display_data('display', obj, metadata)
130 130 else:
131 131 format_dict, md_dict = format(obj, include=include, exclude=exclude)
132 132 if metadata:
133 133 # kwarg-specified metadata gets precedence
134 134 _merge(md_dict, metadata)
135 135 publish_display_data('display', format_dict, md_dict)
136 136
137 137
138 138 def display_pretty(*objs, **kwargs):
139 139 """Display the pretty (default) representation of an object.
140 140
141 141 Parameters
142 142 ----------
143 143 objs : tuple of objects
144 144 The Python objects to display, or if raw=True raw text data to
145 145 display.
146 146 raw : bool
147 147 Are the data objects raw data or Python objects that need to be
148 148 formatted before display? [default: False]
149 149 metadata : dict (optional)
150 150 Metadata to be associated with the specific mimetype output.
151 151 """
152 152 _display_mimetype('text/plain', objs, **kwargs)
153 153
154 154
155 155 def display_html(*objs, **kwargs):
156 156 """Display the HTML representation of an object.
157 157
158 158 Parameters
159 159 ----------
160 160 objs : tuple of objects
161 161 The Python objects to display, or if raw=True raw HTML data to
162 162 display.
163 163 raw : bool
164 164 Are the data objects raw data or Python objects that need to be
165 165 formatted before display? [default: False]
166 166 metadata : dict (optional)
167 167 Metadata to be associated with the specific mimetype output.
168 168 """
169 169 _display_mimetype('text/html', objs, **kwargs)
170 170
171 171
172 def display_markdown(*objs, **kwargs):
173 """Displays the Markdown representation of an object.
174
175 Parameters
176 ----------
177 objs : tuple of objects
178 The Python objects to display, or if raw=True raw markdown data to
179 display.
180 raw : bool
181 Are the data objects raw data or Python objects that need to be
182 formatted before display? [default: False]
183 metadata : dict (optional)
184 Metadata to be associated with the specific mimetype output.
185 """
186
187 _display_mimetype('text/markdown', objs, **kwargs)
188
189
172 190 def display_svg(*objs, **kwargs):
173 191 """Display the SVG representation of an object.
174 192
175 193 Parameters
176 194 ----------
177 195 objs : tuple of objects
178 196 The Python objects to display, or if raw=True raw svg data to
179 197 display.
180 198 raw : bool
181 199 Are the data objects raw data or Python objects that need to be
182 200 formatted before display? [default: False]
183 201 metadata : dict (optional)
184 202 Metadata to be associated with the specific mimetype output.
185 203 """
186 204 _display_mimetype('image/svg+xml', objs, **kwargs)
187 205
188 206
189 207 def display_png(*objs, **kwargs):
190 208 """Display the PNG representation of an object.
191 209
192 210 Parameters
193 211 ----------
194 212 objs : tuple of objects
195 213 The Python objects to display, or if raw=True raw png data to
196 214 display.
197 215 raw : bool
198 216 Are the data objects raw data or Python objects that need to be
199 217 formatted before display? [default: False]
200 218 metadata : dict (optional)
201 219 Metadata to be associated with the specific mimetype output.
202 220 """
203 221 _display_mimetype('image/png', objs, **kwargs)
204 222
205 223
206 224 def display_jpeg(*objs, **kwargs):
207 225 """Display the JPEG representation of an object.
208 226
209 227 Parameters
210 228 ----------
211 229 objs : tuple of objects
212 230 The Python objects to display, or if raw=True raw JPEG data to
213 231 display.
214 232 raw : bool
215 233 Are the data objects raw data or Python objects that need to be
216 234 formatted before display? [default: False]
217 235 metadata : dict (optional)
218 236 Metadata to be associated with the specific mimetype output.
219 237 """
220 238 _display_mimetype('image/jpeg', objs, **kwargs)
221 239
222 240
223 241 def display_latex(*objs, **kwargs):
224 242 """Display the LaTeX representation of an object.
225 243
226 244 Parameters
227 245 ----------
228 246 objs : tuple of objects
229 247 The Python objects to display, or if raw=True raw latex data to
230 248 display.
231 249 raw : bool
232 250 Are the data objects raw data or Python objects that need to be
233 251 formatted before display? [default: False]
234 252 metadata : dict (optional)
235 253 Metadata to be associated with the specific mimetype output.
236 254 """
237 255 _display_mimetype('text/latex', objs, **kwargs)
238 256
239 257
240 258 def display_json(*objs, **kwargs):
241 259 """Display the JSON representation of an object.
242 260
243 261 Note that not many frontends support displaying JSON.
244 262
245 263 Parameters
246 264 ----------
247 265 objs : tuple of objects
248 266 The Python objects to display, or if raw=True raw json data to
249 267 display.
250 268 raw : bool
251 269 Are the data objects raw data or Python objects that need to be
252 270 formatted before display? [default: False]
253 271 metadata : dict (optional)
254 272 Metadata to be associated with the specific mimetype output.
255 273 """
256 274 _display_mimetype('application/json', objs, **kwargs)
257 275
258 276
259 277 def display_javascript(*objs, **kwargs):
260 278 """Display the Javascript representation of an object.
261 279
262 280 Parameters
263 281 ----------
264 282 objs : tuple of objects
265 283 The Python objects to display, or if raw=True raw javascript data to
266 284 display.
267 285 raw : bool
268 286 Are the data objects raw data or Python objects that need to be
269 287 formatted before display? [default: False]
270 288 metadata : dict (optional)
271 289 Metadata to be associated with the specific mimetype output.
272 290 """
273 291 _display_mimetype('application/javascript', objs, **kwargs)
274 292
275 293
276 294 def display_pdf(*objs, **kwargs):
277 295 """Display the PDF representation of an object.
278 296
279 297 Parameters
280 298 ----------
281 299 objs : tuple of objects
282 300 The Python objects to display, or if raw=True raw javascript data to
283 301 display.
284 302 raw : bool
285 303 Are the data objects raw data or Python objects that need to be
286 304 formatted before display? [default: False]
287 305 metadata : dict (optional)
288 306 Metadata to be associated with the specific mimetype output.
289 307 """
290 308 _display_mimetype('application/pdf', objs, **kwargs)
291 309
292 310
293 311 #-----------------------------------------------------------------------------
294 312 # Smart classes
295 313 #-----------------------------------------------------------------------------
296 314
297 315
298 316 class DisplayObject(object):
299 317 """An object that wraps data to be displayed."""
300 318
301 319 _read_flags = 'r'
302 320
303 321 def __init__(self, data=None, url=None, filename=None):
304 322 """Create a display object given raw data.
305 323
306 324 When this object is returned by an expression or passed to the
307 325 display function, it will result in the data being displayed
308 326 in the frontend. The MIME type of the data should match the
309 327 subclasses used, so the Png subclass should be used for 'image/png'
310 328 data. If the data is a URL, the data will first be downloaded
311 329 and then displayed. If
312 330
313 331 Parameters
314 332 ----------
315 333 data : unicode, str or bytes
316 334 The raw data or a URL or file to load the data from
317 335 url : unicode
318 336 A URL to download the data from.
319 337 filename : unicode
320 338 Path to a local file to load the data from.
321 339 """
322 340 if data is not None and isinstance(data, string_types):
323 341 if data.startswith('http') and url is None:
324 342 url = data
325 343 filename = None
326 344 data = None
327 345 elif _safe_exists(data) and filename is None:
328 346 url = None
329 347 filename = data
330 348 data = None
331 349
332 350 self.data = data
333 351 self.url = url
334 352 self.filename = None if filename is None else unicode_type(filename)
335 353
336 354 self.reload()
337 355 self._check_data()
338 356
339 357 def _check_data(self):
340 358 """Override in subclasses if there's something to check."""
341 359 pass
342 360
343 361 def reload(self):
344 362 """Reload the raw data from file or URL."""
345 363 if self.filename is not None:
346 364 with open(self.filename, self._read_flags) as f:
347 365 self.data = f.read()
348 366 elif self.url is not None:
349 367 try:
350 368 try:
351 369 from urllib.request import urlopen # Py3
352 370 except ImportError:
353 371 from urllib2 import urlopen
354 372 response = urlopen(self.url)
355 373 self.data = response.read()
356 374 # extract encoding from header, if there is one:
357 375 encoding = None
358 376 for sub in response.headers['content-type'].split(';'):
359 377 sub = sub.strip()
360 378 if sub.startswith('charset'):
361 379 encoding = sub.split('=')[-1].strip()
362 380 break
363 381 # decode data, if an encoding was specified
364 382 if encoding:
365 383 self.data = self.data.decode(encoding, 'replace')
366 384 except:
367 385 self.data = None
368 386
369 387 class TextDisplayObject(DisplayObject):
370 388 """Validate that display data is text"""
371 389 def _check_data(self):
372 390 if self.data is not None and not isinstance(self.data, string_types):
373 391 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
374 392
375 393 class Pretty(TextDisplayObject):
376 394
377 395 def _repr_pretty_(self):
378 396 return self.data
379 397
380 398
381 399 class HTML(TextDisplayObject):
382 400
383 401 def _repr_html_(self):
384 402 return self.data
385 403
386 404 def __html__(self):
387 405 """
388 406 This method exists to inform other HTML-using modules (e.g. Markupsafe,
389 407 htmltag, etc) that this object is HTML and does not need things like
390 408 special characters (<>&) escaped.
391 409 """
392 410 return self._repr_html_()
393 411
394 412
413 class Markdown(TextDisplayObject):
414
415 def _repr_markdown_(self):
416 return self.data
417
418
395 419 class Math(TextDisplayObject):
396 420
397 421 def _repr_latex_(self):
398 422 s = self.data.strip('$')
399 423 return "$$%s$$" % s
400 424
401 425
402 426 class Latex(TextDisplayObject):
403 427
404 428 def _repr_latex_(self):
405 429 return self.data
406 430
407 431
408 432 class SVG(DisplayObject):
409 433
410 434 # wrap data in a property, which extracts the <svg> tag, discarding
411 435 # document headers
412 436 _data = None
413 437
414 438 @property
415 439 def data(self):
416 440 return self._data
417 441
418 442 @data.setter
419 443 def data(self, svg):
420 444 if svg is None:
421 445 self._data = None
422 446 return
423 447 # parse into dom object
424 448 from xml.dom import minidom
425 449 svg = cast_bytes_py2(svg)
426 450 x = minidom.parseString(svg)
427 451 # get svg tag (should be 1)
428 452 found_svg = x.getElementsByTagName('svg')
429 453 if found_svg:
430 454 svg = found_svg[0].toxml()
431 455 else:
432 456 # fallback on the input, trust the user
433 457 # but this is probably an error.
434 458 pass
435 459 svg = cast_unicode(svg)
436 460 self._data = svg
437 461
438 462 def _repr_svg_(self):
439 463 return self.data
440 464
441 465
442 466 class JSON(TextDisplayObject):
443 467
444 468 def _repr_json_(self):
445 469 return self.data
446 470
447 471 css_t = """$("head").append($("<link/>").attr({
448 472 rel: "stylesheet",
449 473 type: "text/css",
450 474 href: "%s"
451 475 }));
452 476 """
453 477
454 478 lib_t1 = """$.getScript("%s", function () {
455 479 """
456 480 lib_t2 = """});
457 481 """
458 482
459 483 class Javascript(TextDisplayObject):
460 484
461 485 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
462 486 """Create a Javascript display object given raw data.
463 487
464 488 When this object is returned by an expression or passed to the
465 489 display function, it will result in the data being displayed
466 490 in the frontend. If the data is a URL, the data will first be
467 491 downloaded and then displayed.
468 492
469 493 In the Notebook, the containing element will be available as `element`,
470 494 and jQuery will be available. The output area starts hidden, so if
471 495 the js appends content to `element` that should be visible, then
472 496 it must call `container.show()` to unhide the area.
473 497
474 498 Parameters
475 499 ----------
476 500 data : unicode, str or bytes
477 501 The Javascript source code or a URL to download it from.
478 502 url : unicode
479 503 A URL to download the data from.
480 504 filename : unicode
481 505 Path to a local file to load the data from.
482 506 lib : list or str
483 507 A sequence of Javascript library URLs to load asynchronously before
484 508 running the source code. The full URLs of the libraries should
485 509 be given. A single Javascript library URL can also be given as a
486 510 string.
487 511 css: : list or str
488 512 A sequence of css files to load before running the source code.
489 513 The full URLs of the css files should be given. A single css URL
490 514 can also be given as a string.
491 515 """
492 516 if isinstance(lib, string_types):
493 517 lib = [lib]
494 518 elif lib is None:
495 519 lib = []
496 520 if isinstance(css, string_types):
497 521 css = [css]
498 522 elif css is None:
499 523 css = []
500 524 if not isinstance(lib, (list,tuple)):
501 525 raise TypeError('expected sequence, got: %r' % lib)
502 526 if not isinstance(css, (list,tuple)):
503 527 raise TypeError('expected sequence, got: %r' % css)
504 528 self.lib = lib
505 529 self.css = css
506 530 super(Javascript, self).__init__(data=data, url=url, filename=filename)
507 531
508 532 def _repr_javascript_(self):
509 533 r = ''
510 534 for c in self.css:
511 535 r += css_t % c
512 536 for l in self.lib:
513 537 r += lib_t1 % l
514 538 r += self.data
515 539 r += lib_t2*len(self.lib)
516 540 return r
517 541
518 542 # constants for identifying png/jpeg data
519 543 _PNG = b'\x89PNG\r\n\x1a\n'
520 544 _JPEG = b'\xff\xd8'
521 545
522 546 def _pngxy(data):
523 547 """read the (width, height) from a PNG header"""
524 548 ihdr = data.index(b'IHDR')
525 549 # next 8 bytes are width/height
526 550 w4h4 = data[ihdr+4:ihdr+12]
527 551 return struct.unpack('>ii', w4h4)
528 552
529 553 def _jpegxy(data):
530 554 """read the (width, height) from a JPEG header"""
531 555 # adapted from http://www.64lines.com/jpeg-width-height
532 556
533 557 idx = 4
534 558 while True:
535 559 block_size = struct.unpack('>H', data[idx:idx+2])[0]
536 560 idx = idx + block_size
537 561 if data[idx:idx+2] == b'\xFF\xC0':
538 562 # found Start of Frame
539 563 iSOF = idx
540 564 break
541 565 else:
542 566 # read another block
543 567 idx += 2
544 568
545 569 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
546 570 return w, h
547 571
548 572 class Image(DisplayObject):
549 573
550 574 _read_flags = 'rb'
551 575 _FMT_JPEG = u'jpeg'
552 576 _FMT_PNG = u'png'
553 577 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG]
554 578
555 579 def __init__(self, data=None, url=None, filename=None, format=u'png', embed=None, width=None, height=None, retina=False):
556 580 """Create a PNG/JPEG image object given raw data.
557 581
558 582 When this object is returned by an input cell or passed to the
559 583 display function, it will result in the image being displayed
560 584 in the frontend.
561 585
562 586 Parameters
563 587 ----------
564 588 data : unicode, str or bytes
565 589 The raw image data or a URL or filename to load the data from.
566 590 This always results in embedded image data.
567 591 url : unicode
568 592 A URL to download the data from. If you specify `url=`,
569 593 the image data will not be embedded unless you also specify `embed=True`.
570 594 filename : unicode
571 595 Path to a local file to load the data from.
572 596 Images from a file are always embedded.
573 597 format : unicode
574 598 The format of the image data (png/jpeg/jpg). If a filename or URL is given
575 599 for format will be inferred from the filename extension.
576 600 embed : bool
577 601 Should the image data be embedded using a data URI (True) or be
578 602 loaded using an <img> tag. Set this to True if you want the image
579 603 to be viewable later with no internet connection in the notebook.
580 604
581 605 Default is `True`, unless the keyword argument `url` is set, then
582 606 default value is `False`.
583 607
584 608 Note that QtConsole is not able to display images if `embed` is set to `False`
585 609 width : int
586 610 Width to which to constrain the image in html
587 611 height : int
588 612 Height to which to constrain the image in html
589 613 retina : bool
590 614 Automatically set the width and height to half of the measured
591 615 width and height.
592 616 This only works for embedded images because it reads the width/height
593 617 from image data.
594 618 For non-embedded images, you can just set the desired display width
595 619 and height directly.
596 620
597 621 Examples
598 622 --------
599 623 # embedded image data, works in qtconsole and notebook
600 624 # when passed positionally, the first arg can be any of raw image data,
601 625 # a URL, or a filename from which to load image data.
602 626 # The result is always embedding image data for inline images.
603 627 Image('http://www.google.fr/images/srpr/logo3w.png')
604 628 Image('/path/to/image.jpg')
605 629 Image(b'RAW_PNG_DATA...')
606 630
607 631 # Specifying Image(url=...) does not embed the image data,
608 632 # it only generates `<img>` tag with a link to the source.
609 633 # This will not work in the qtconsole or offline.
610 634 Image(url='http://www.google.fr/images/srpr/logo3w.png')
611 635
612 636 """
613 637 if filename is not None:
614 638 ext = self._find_ext(filename)
615 639 elif url is not None:
616 640 ext = self._find_ext(url)
617 641 elif data is None:
618 642 raise ValueError("No image data found. Expecting filename, url, or data.")
619 643 elif isinstance(data, string_types) and (
620 644 data.startswith('http') or _safe_exists(data)
621 645 ):
622 646 ext = self._find_ext(data)
623 647 else:
624 648 ext = None
625 649
626 650 if ext is not None:
627 651 format = ext.lower()
628 652 if ext == u'jpg' or ext == u'jpeg':
629 653 format = self._FMT_JPEG
630 654 if ext == u'png':
631 655 format = self._FMT_PNG
632 656 elif isinstance(data, bytes) and format == 'png':
633 657 # infer image type from image data header,
634 658 # only if format might not have been specified.
635 659 if data[:2] == _JPEG:
636 660 format = 'jpeg'
637 661
638 662 self.format = unicode_type(format).lower()
639 663 self.embed = embed if embed is not None else (url is None)
640 664
641 665 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
642 666 raise ValueError("Cannot embed the '%s' image format" % (self.format))
643 667 self.width = width
644 668 self.height = height
645 669 self.retina = retina
646 670 super(Image, self).__init__(data=data, url=url, filename=filename)
647 671
648 672 if retina:
649 673 self._retina_shape()
650 674
651 675 def _retina_shape(self):
652 676 """load pixel-doubled width and height from image data"""
653 677 if not self.embed:
654 678 return
655 679 if self.format == 'png':
656 680 w, h = _pngxy(self.data)
657 681 elif self.format == 'jpeg':
658 682 w, h = _jpegxy(self.data)
659 683 else:
660 684 # retina only supports png
661 685 return
662 686 self.width = w // 2
663 687 self.height = h // 2
664 688
665 689 def reload(self):
666 690 """Reload the raw data from file or URL."""
667 691 if self.embed:
668 692 super(Image,self).reload()
669 693 if self.retina:
670 694 self._retina_shape()
671 695
672 696 def _repr_html_(self):
673 697 if not self.embed:
674 698 width = height = ''
675 699 if self.width:
676 700 width = ' width="%d"' % self.width
677 701 if self.height:
678 702 height = ' height="%d"' % self.height
679 703 return u'<img src="%s"%s%s/>' % (self.url, width, height)
680 704
681 705 def _data_and_metadata(self):
682 706 """shortcut for returning metadata with shape information, if defined"""
683 707 md = {}
684 708 if self.width:
685 709 md['width'] = self.width
686 710 if self.height:
687 711 md['height'] = self.height
688 712 if md:
689 713 return self.data, md
690 714 else:
691 715 return self.data
692 716
693 717 def _repr_png_(self):
694 718 if self.embed and self.format == u'png':
695 719 return self._data_and_metadata()
696 720
697 721 def _repr_jpeg_(self):
698 722 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
699 723 return self._data_and_metadata()
700 724
701 725 def _find_ext(self, s):
702 726 return unicode_type(s.split('.')[-1].lower())
703 727
704 728
705 729 def clear_output(wait=False):
706 730 """Clear the output of the current cell receiving output.
707 731
708 732 Parameters
709 733 ----------
710 734 wait : bool [default: false]
711 735 Wait to clear the output until new output is available to replace it."""
712 736 from IPython.core.interactiveshell import InteractiveShell
713 737 if InteractiveShell.initialized():
714 738 InteractiveShell.instance().display_pub.clear_output(wait)
715 739 else:
716 740 from IPython.utils import io
717 741 print('\033[2K\r', file=io.stdout, end='')
718 742 io.stdout.flush()
719 743 print('\033[2K\r', file=io.stderr, end='')
720 744 io.stderr.flush()
721 745
722 746
723 747 @skip_doctest
724 748 def set_matplotlib_formats(*formats, **kwargs):
725 749 """Select figure formats for the inline backend. Optionally pass quality for JPEG.
726 750
727 751 For example, this enables PNG and JPEG output with a JPEG quality of 90%::
728 752
729 753 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90)
730 754
731 755 To set this in your config files use the following::
732 756
733 757 c.InlineBackend.figure_formats = {'png', 'jpeg'}
734 758 c.InlineBackend.print_figure_kwargs.update({'quality' : 90})
735 759
736 760 Parameters
737 761 ----------
738 762 *formats : strs
739 763 One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
740 764 **kwargs :
741 765 Keyword args will be relayed to ``figure.canvas.print_figure``.
742 766 """
743 767 from IPython.core.interactiveshell import InteractiveShell
744 768 from IPython.core.pylabtools import select_figure_formats
745 769 from IPython.kernel.zmq.pylab.config import InlineBackend
746 770 # build kwargs, starting with InlineBackend config
747 771 kw = {}
748 772 cfg = InlineBackend.instance()
749 773 kw.update(cfg.print_figure_kwargs)
750 774 kw.update(**kwargs)
751 775 shell = InteractiveShell.instance()
752 776 select_figure_formats(shell, formats, **kw)
753 777
754 778 @skip_doctest
755 779 def set_matplotlib_close(close=True):
756 780 """Set whether the inline backend closes all figures automatically or not.
757 781
758 782 By default, the inline backend used in the IPython Notebook will close all
759 783 matplotlib figures automatically after each cell is run. This means that
760 784 plots in different cells won't interfere. Sometimes, you may want to make
761 785 a plot in one cell and then refine it in later cells. This can be accomplished
762 786 by::
763 787
764 788 In [1]: set_matplotlib_close(False)
765 789
766 790 To set this in your config files use the following::
767 791
768 792 c.InlineBackend.close_figures = False
769 793
770 794 Parameters
771 795 ----------
772 796 close : bool
773 797 Should all matplotlib figures be automatically closed after each cell is
774 798 run?
775 799 """
776 800 from IPython.kernel.zmq.pylab.config import InlineBackend
777 801 cfg = InlineBackend.instance()
778 802 cfg.close_figures = close
779 803
@@ -1,177 +1,179 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.py3compat import string_types
35 35 from IPython.utils.traitlets import List
36 36
37 37 #-----------------------------------------------------------------------------
38 38 # Main payload class
39 39 #-----------------------------------------------------------------------------
40 40
41 41 class DisplayPublisher(Configurable):
42 42 """A traited class that publishes display data to frontends.
43 43
44 44 Instances of this class are created by the main IPython object and should
45 45 be accessed there.
46 46 """
47 47
48 48 def _validate_data(self, source, data, metadata=None):
49 49 """Validate the display data.
50 50
51 51 Parameters
52 52 ----------
53 53 source : str
54 54 The fully dotted name of the callable that created the data, like
55 55 :func:`foo.bar.my_formatter`.
56 56 data : dict
57 57 The formata data dictionary.
58 58 metadata : dict
59 59 Any metadata for the data.
60 60 """
61 61
62 62 if not isinstance(source, string_types):
63 63 raise TypeError('source must be a str, got: %r' % source)
64 64 if not isinstance(data, dict):
65 65 raise TypeError('data must be a dict, got: %r' % data)
66 66 if metadata is not None:
67 67 if not isinstance(metadata, dict):
68 68 raise TypeError('metadata must be a dict, got: %r' % data)
69 69
70 70 def publish(self, source, data, metadata=None):
71 71 """Publish data and metadata to all frontends.
72 72
73 73 See the ``display_data`` message in the messaging documentation for
74 74 more details about this message type.
75 75
76 76 The following MIME types are currently implemented:
77 77
78 78 * text/plain
79 79 * text/html
80 * text/markdown
80 81 * text/latex
81 82 * application/json
82 83 * application/javascript
83 84 * image/png
84 85 * image/jpeg
85 86 * image/svg+xml
86 87
87 88 Parameters
88 89 ----------
89 90 source : str
90 91 A string that give the function or method that created the data,
91 92 such as 'IPython.core.page'.
92 93 data : dict
93 94 A dictionary having keys that are valid MIME types (like
94 95 'text/plain' or 'image/svg+xml') and values that are the data for
95 96 that MIME type. The data itself must be a JSON'able data
96 97 structure. Minimally all data should have the 'text/plain' data,
97 98 which can be displayed by all frontends. If more than the plain
98 99 text is given, it is up to the frontend to decide which
99 100 representation to use.
100 101 metadata : dict
101 102 A dictionary for metadata related to the data. This can contain
102 103 arbitrary key, value pairs that frontends can use to interpret
103 104 the data. Metadata specific to each mime-type can be specified
104 105 in the metadata dict with the same mime-type keys as
105 106 the data itself.
106 107 """
107 108
108 109 # The default is to simply write the plain text data using io.stdout.
109 110 if 'text/plain' in data:
110 111 print(data['text/plain'], file=io.stdout)
111 112
112 113 def clear_output(self, wait=False):
113 114 """Clear the output of the cell receiving output."""
114 115 print('\033[2K\r', file=io.stdout, end='')
115 116 io.stdout.flush()
116 117 print('\033[2K\r', file=io.stderr, end='')
117 118 io.stderr.flush()
118 119
119 120
120 121 class CapturingDisplayPublisher(DisplayPublisher):
121 122 """A DisplayPublisher that stores"""
122 123 outputs = List()
123 124
124 125 def publish(self, source, data, metadata=None):
125 126 self.outputs.append((source, data, metadata))
126 127
127 128 def clear_output(self, wait=False):
128 129 super(CapturingDisplayPublisher, self).clear_output(wait)
129 130
130 131 # empty the list, *do not* reassign a new list
131 132 del self.outputs[:]
132 133
133 134
134 135 def publish_display_data(source, data, metadata=None):
135 136 """Publish data and metadata to all frontends.
136 137
137 138 See the ``display_data`` message in the messaging documentation for
138 139 more details about this message type.
139 140
140 141 The following MIME types are currently implemented:
141 142
142 143 * text/plain
143 144 * text/html
145 * text/markdown
144 146 * text/latex
145 147 * application/json
146 148 * application/javascript
147 149 * image/png
148 150 * image/jpeg
149 151 * image/svg+xml
150 152
151 153 Parameters
152 154 ----------
153 155 source : str
154 156 A string that give the function or method that created the data,
155 157 such as 'IPython.core.page'.
156 158 data : dict
157 159 A dictionary having keys that are valid MIME types (like
158 160 'text/plain' or 'image/svg+xml') and values that are the data for
159 161 that MIME type. The data itself must be a JSON'able data
160 162 structure. Minimally all data should have the 'text/plain' data,
161 163 which can be displayed by all frontends. If more than the plain
162 164 text is given, it is up to the frontend to decide which
163 165 representation to use.
164 166 metadata : dict
165 167 A dictionary for metadata related to the data. This can contain
166 168 arbitrary key, value pairs that frontends can use to interpret
167 169 the data. mime-type keys matching those in data can be used
168 170 to specify metadata about particular representations.
169 171 """
170 172 from IPython.core.interactiveshell import InteractiveShell
171 173 InteractiveShell.instance().display_pub.publish(
172 174 source,
173 175 data,
174 176 metadata
175 177 )
176 178
177 179
@@ -1,884 +1,902 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Display formatters.
3 3
4 4 Inheritance diagram:
5 5
6 6 .. inheritance-diagram:: IPython.core.formatters
7 7 :parts: 3
8 8
9 9 Authors:
10 10
11 11 * Robert Kern
12 12 * Brian Granger
13 13 """
14 14 #-----------------------------------------------------------------------------
15 15 # Copyright (C) 2010-2011, IPython Development Team.
16 16 #
17 17 # Distributed under the terms of the Modified BSD License.
18 18 #
19 19 # The full license is in the file COPYING.txt, distributed with this software.
20 20 #-----------------------------------------------------------------------------
21 21
22 22 #-----------------------------------------------------------------------------
23 23 # Imports
24 24 #-----------------------------------------------------------------------------
25 25
26 26 # Stdlib imports
27 27 import abc
28 28 import inspect
29 29 import sys
30 30 import types
31 31 import warnings
32 32
33 33 from IPython.external.decorator import decorator
34 34
35 35 # Our own imports
36 36 from IPython.config.configurable import Configurable
37 37 from IPython.lib import pretty
38 38 from IPython.utils import io
39 39 from IPython.utils.traitlets import (
40 40 Bool, Dict, Integer, Unicode, CUnicode, ObjectName, List,
41 41 )
42 42 from IPython.utils.warn import warn
43 43 from IPython.utils.py3compat import (
44 44 unicode_to_str, with_metaclass, PY3, string_types, unicode_type,
45 45 )
46 46
47 47 if PY3:
48 48 from io import StringIO
49 49 else:
50 50 from StringIO import StringIO
51 51
52 52
53 53 #-----------------------------------------------------------------------------
54 54 # The main DisplayFormatter class
55 55 #-----------------------------------------------------------------------------
56 56
57 57
58 58 def _valid_formatter(f):
59 59 """Return whether an object is a valid formatter
60 60
61 61 Cases checked:
62 62
63 63 - bound methods OK
64 64 - unbound methods NO
65 65 - callable with zero args OK
66 66 """
67 67 if f is None:
68 68 return False
69 69 elif isinstance(f, type(str.find)):
70 70 # unbound methods on compiled classes have type method_descriptor
71 71 return False
72 72 elif isinstance(f, types.BuiltinFunctionType):
73 73 # bound methods on compiled classes have type builtin_function
74 74 return True
75 75 elif callable(f):
76 76 # anything that works with zero args should be okay
77 77 try:
78 78 inspect.getcallargs(f)
79 79 except TypeError:
80 80 return False
81 81 else:
82 82 return True
83 83 return False
84 84
85 85 def _safe_get_formatter_method(obj, name):
86 86 """Safely get a formatter method"""
87 87 method = pretty._safe_getattr(obj, name, None)
88 88 # formatter methods must be bound
89 89 if _valid_formatter(method):
90 90 return method
91 91
92 92
93 93 class DisplayFormatter(Configurable):
94 94
95 95 # When set to true only the default plain text formatter will be used.
96 96 plain_text_only = Bool(False, config=True)
97 97 def _plain_text_only_changed(self, name, old, new):
98 98 warnings.warn("""DisplayFormatter.plain_text_only is deprecated.
99 99
100 100 Use DisplayFormatter.active_types = ['text/plain']
101 101 for the same effect.
102 102 """, DeprecationWarning)
103 103 if new:
104 104 self.active_types = ['text/plain']
105 105 else:
106 106 self.active_types = self.format_types
107 107
108 108 active_types = List(Unicode, config=True,
109 109 help="""List of currently active mime-types to display.
110 110 You can use this to set a white-list for formats to display.
111 111
112 112 Most users will not need to change this value.
113 113 """)
114 114 def _active_types_default(self):
115 115 return self.format_types
116 116
117 117 def _active_types_changed(self, name, old, new):
118 118 for key, formatter in self.formatters.items():
119 119 if key in new:
120 120 formatter.enabled = True
121 121 else:
122 122 formatter.enabled = False
123 123
124 124 # A dict of formatter whose keys are format types (MIME types) and whose
125 125 # values are subclasses of BaseFormatter.
126 126 formatters = Dict()
127 127 def _formatters_default(self):
128 128 """Activate the default formatters."""
129 129 formatter_classes = [
130 130 PlainTextFormatter,
131 131 HTMLFormatter,
132 MarkdownFormatter,
132 133 SVGFormatter,
133 134 PNGFormatter,
134 135 PDFFormatter,
135 136 JPEGFormatter,
136 137 LatexFormatter,
137 138 JSONFormatter,
138 139 JavascriptFormatter
139 140 ]
140 141 d = {}
141 142 for cls in formatter_classes:
142 143 f = cls(parent=self)
143 144 d[f.format_type] = f
144 145 return d
145 146
146 147 def format(self, obj, include=None, exclude=None):
147 148 """Return a format data dict for an object.
148 149
149 150 By default all format types will be computed.
150 151
151 152 The following MIME types are currently implemented:
152 153
153 154 * text/plain
154 155 * text/html
156 * text/markdown
155 157 * text/latex
156 158 * application/json
157 159 * application/javascript
158 160 * application/pdf
159 161 * image/png
160 162 * image/jpeg
161 163 * image/svg+xml
162 164
163 165 Parameters
164 166 ----------
165 167 obj : object
166 168 The Python object whose format data will be computed.
167 169 include : list or tuple, optional
168 170 A list of format type strings (MIME types) to include in the
169 171 format data dict. If this is set *only* the format types included
170 172 in this list will be computed.
171 173 exclude : list or tuple, optional
172 174 A list of format type string (MIME types) to exclude in the format
173 175 data dict. If this is set all format types will be computed,
174 176 except for those included in this argument.
175 177
176 178 Returns
177 179 -------
178 180 (format_dict, metadata_dict) : tuple of two dicts
179 181
180 182 format_dict is a dictionary of key/value pairs, one of each format that was
181 183 generated for the object. The keys are the format types, which
182 184 will usually be MIME type strings and the values and JSON'able
183 185 data structure containing the raw data for the representation in
184 186 that format.
185 187
186 188 metadata_dict is a dictionary of metadata about each mime-type output.
187 189 Its keys will be a strict subset of the keys in format_dict.
188 190 """
189 191 format_dict = {}
190 192 md_dict = {}
191 193
192 194 for format_type, formatter in self.formatters.items():
193 195 if include and format_type not in include:
194 196 continue
195 197 if exclude and format_type in exclude:
196 198 continue
197 199
198 200 md = None
199 201 try:
200 202 data = formatter(obj)
201 203 except:
202 204 # FIXME: log the exception
203 205 raise
204 206
205 207 # formatters can return raw data or (data, metadata)
206 208 if isinstance(data, tuple) and len(data) == 2:
207 209 data, md = data
208 210
209 211 if data is not None:
210 212 format_dict[format_type] = data
211 213 if md is not None:
212 214 md_dict[format_type] = md
213 215
214 216 return format_dict, md_dict
215 217
216 218 @property
217 219 def format_types(self):
218 220 """Return the format types (MIME types) of the active formatters."""
219 221 return list(self.formatters.keys())
220 222
221 223
222 224 #-----------------------------------------------------------------------------
223 225 # Formatters for specific format types (text, html, svg, etc.)
224 226 #-----------------------------------------------------------------------------
225 227
226 228 class FormatterWarning(UserWarning):
227 229 """Warning class for errors in formatters"""
228 230
229 231 @decorator
230 232 def warn_format_error(method, self, *args, **kwargs):
231 233 """decorator for warning on failed format call"""
232 234 try:
233 235 r = method(self, *args, **kwargs)
234 236 except NotImplementedError as e:
235 237 # don't warn on NotImplementedErrors
236 238 return None
237 239 except Exception as e:
238 240 warnings.warn("Exception in %s formatter: %s" % (self.format_type, e),
239 241 FormatterWarning,
240 242 )
241 243 return None
242 244 if r is None or isinstance(r, self._return_type) or \
243 245 (isinstance(r, tuple) and r and isinstance(r[0], self._return_type)):
244 246 return r
245 247 else:
246 248 warnings.warn(
247 249 "%s formatter returned invalid type %s (expected %s) for object: %s" % \
248 250 (self.format_type, type(r), self._return_type, pretty._safe_repr(args[0])),
249 251 FormatterWarning
250 252 )
251 253
252 254
253 255 class FormatterABC(with_metaclass(abc.ABCMeta, object)):
254 256 """ Abstract base class for Formatters.
255 257
256 258 A formatter is a callable class that is responsible for computing the
257 259 raw format data for a particular format type (MIME type). For example,
258 260 an HTML formatter would have a format type of `text/html` and would return
259 261 the HTML representation of the object when called.
260 262 """
261 263
262 264 # The format type of the data returned, usually a MIME type.
263 265 format_type = 'text/plain'
264 266
265 267 # Is the formatter enabled...
266 268 enabled = True
267 269
268 270 @abc.abstractmethod
269 271 @warn_format_error
270 272 def __call__(self, obj):
271 273 """Return a JSON'able representation of the object.
272 274
273 275 If the object cannot be formatted by this formatter,
274 276 warn and return None.
275 277 """
276 278 return repr(obj)
277 279
278 280
279 281 def _mod_name_key(typ):
280 282 """Return a (__module__, __name__) tuple for a type.
281 283
282 284 Used as key in Formatter.deferred_printers.
283 285 """
284 286 module = getattr(typ, '__module__', None)
285 287 name = getattr(typ, '__name__', None)
286 288 return (module, name)
287 289
288 290
289 291 def _get_type(obj):
290 292 """Return the type of an instance (old and new-style)"""
291 293 return getattr(obj, '__class__', None) or type(obj)
292 294
293 295 _raise_key_error = object()
294 296
295 297
296 298 class BaseFormatter(Configurable):
297 299 """A base formatter class that is configurable.
298 300
299 301 This formatter should usually be used as the base class of all formatters.
300 302 It is a traited :class:`Configurable` class and includes an extensible
301 303 API for users to determine how their objects are formatted. The following
302 304 logic is used to find a function to format an given object.
303 305
304 306 1. The object is introspected to see if it has a method with the name
305 307 :attr:`print_method`. If is does, that object is passed to that method
306 308 for formatting.
307 309 2. If no print method is found, three internal dictionaries are consulted
308 310 to find print method: :attr:`singleton_printers`, :attr:`type_printers`
309 311 and :attr:`deferred_printers`.
310 312
311 313 Users should use these dictionaries to register functions that will be
312 314 used to compute the format data for their objects (if those objects don't
313 315 have the special print methods). The easiest way of using these
314 316 dictionaries is through the :meth:`for_type` and :meth:`for_type_by_name`
315 317 methods.
316 318
317 319 If no function/callable is found to compute the format data, ``None`` is
318 320 returned and this format type is not used.
319 321 """
320 322
321 323 format_type = Unicode('text/plain')
322 324 _return_type = string_types
323 325
324 326 enabled = Bool(True, config=True)
325 327
326 328 print_method = ObjectName('__repr__')
327 329
328 330 # The singleton printers.
329 331 # Maps the IDs of the builtin singleton objects to the format functions.
330 332 singleton_printers = Dict(config=True)
331 333
332 334 # The type-specific printers.
333 335 # Map type objects to the format functions.
334 336 type_printers = Dict(config=True)
335 337
336 338 # The deferred-import type-specific printers.
337 339 # Map (modulename, classname) pairs to the format functions.
338 340 deferred_printers = Dict(config=True)
339 341
340 342 @warn_format_error
341 343 def __call__(self, obj):
342 344 """Compute the format for an object."""
343 345 if self.enabled:
344 346 # lookup registered printer
345 347 try:
346 348 printer = self.lookup(obj)
347 349 except KeyError:
348 350 pass
349 351 else:
350 352 return printer(obj)
351 353 # Finally look for special method names
352 354 method = _safe_get_formatter_method(obj, self.print_method)
353 355 if method is not None:
354 356 return method()
355 357 return None
356 358 else:
357 359 return None
358 360
359 361 def __contains__(self, typ):
360 362 """map in to lookup_by_type"""
361 363 try:
362 364 self.lookup_by_type(typ)
363 365 except KeyError:
364 366 return False
365 367 else:
366 368 return True
367 369
368 370 def lookup(self, obj):
369 371 """Look up the formatter for a given instance.
370 372
371 373 Parameters
372 374 ----------
373 375 obj : object instance
374 376
375 377 Returns
376 378 -------
377 379 f : callable
378 380 The registered formatting callable for the type.
379 381
380 382 Raises
381 383 ------
382 384 KeyError if the type has not been registered.
383 385 """
384 386 # look for singleton first
385 387 obj_id = id(obj)
386 388 if obj_id in self.singleton_printers:
387 389 return self.singleton_printers[obj_id]
388 390 # then lookup by type
389 391 return self.lookup_by_type(_get_type(obj))
390 392
391 393 def lookup_by_type(self, typ):
392 394 """Look up the registered formatter for a type.
393 395
394 396 Parameters
395 397 ----------
396 398 typ : type or '__module__.__name__' string for a type
397 399
398 400 Returns
399 401 -------
400 402 f : callable
401 403 The registered formatting callable for the type.
402 404
403 405 Raises
404 406 ------
405 407 KeyError if the type has not been registered.
406 408 """
407 409 if isinstance(typ, string_types):
408 410 typ_key = tuple(typ.rsplit('.',1))
409 411 if typ_key not in self.deferred_printers:
410 412 # We may have it cached in the type map. We will have to
411 413 # iterate over all of the types to check.
412 414 for cls in self.type_printers:
413 415 if _mod_name_key(cls) == typ_key:
414 416 return self.type_printers[cls]
415 417 else:
416 418 return self.deferred_printers[typ_key]
417 419 else:
418 420 for cls in pretty._get_mro(typ):
419 421 if cls in self.type_printers or self._in_deferred_types(cls):
420 422 return self.type_printers[cls]
421 423
422 424 # If we have reached here, the lookup failed.
423 425 raise KeyError("No registered printer for {0!r}".format(typ))
424 426
425 427 def for_type(self, typ, func=None):
426 428 """Add a format function for a given type.
427 429
428 430 Parameters
429 431 -----------
430 432 typ : type or '__module__.__name__' string for a type
431 433 The class of the object that will be formatted using `func`.
432 434 func : callable
433 435 A callable for computing the format data.
434 436 `func` will be called with the object to be formatted,
435 437 and will return the raw data in this formatter's format.
436 438 Subclasses may use a different call signature for the
437 439 `func` argument.
438 440
439 441 If `func` is None or not specified, there will be no change,
440 442 only returning the current value.
441 443
442 444 Returns
443 445 -------
444 446 oldfunc : callable
445 447 The currently registered callable.
446 448 If you are registering a new formatter,
447 449 this will be the previous value (to enable restoring later).
448 450 """
449 451 # if string given, interpret as 'pkg.module.class_name'
450 452 if isinstance(typ, string_types):
451 453 type_module, type_name = typ.rsplit('.', 1)
452 454 return self.for_type_by_name(type_module, type_name, func)
453 455
454 456 try:
455 457 oldfunc = self.lookup_by_type(typ)
456 458 except KeyError:
457 459 oldfunc = None
458 460
459 461 if func is not None:
460 462 self.type_printers[typ] = func
461 463
462 464 return oldfunc
463 465
464 466 def for_type_by_name(self, type_module, type_name, func=None):
465 467 """Add a format function for a type specified by the full dotted
466 468 module and name of the type, rather than the type of the object.
467 469
468 470 Parameters
469 471 ----------
470 472 type_module : str
471 473 The full dotted name of the module the type is defined in, like
472 474 ``numpy``.
473 475 type_name : str
474 476 The name of the type (the class name), like ``dtype``
475 477 func : callable
476 478 A callable for computing the format data.
477 479 `func` will be called with the object to be formatted,
478 480 and will return the raw data in this formatter's format.
479 481 Subclasses may use a different call signature for the
480 482 `func` argument.
481 483
482 484 If `func` is None or unspecified, there will be no change,
483 485 only returning the current value.
484 486
485 487 Returns
486 488 -------
487 489 oldfunc : callable
488 490 The currently registered callable.
489 491 If you are registering a new formatter,
490 492 this will be the previous value (to enable restoring later).
491 493 """
492 494 key = (type_module, type_name)
493 495
494 496 try:
495 497 oldfunc = self.lookup_by_type("%s.%s" % key)
496 498 except KeyError:
497 499 oldfunc = None
498 500
499 501 if func is not None:
500 502 self.deferred_printers[key] = func
501 503 return oldfunc
502 504
503 505 def pop(self, typ, default=_raise_key_error):
504 506 """Pop a formatter for the given type.
505 507
506 508 Parameters
507 509 ----------
508 510 typ : type or '__module__.__name__' string for a type
509 511 default : object
510 512 value to be returned if no formatter is registered for typ.
511 513
512 514 Returns
513 515 -------
514 516 obj : object
515 517 The last registered object for the type.
516 518
517 519 Raises
518 520 ------
519 521 KeyError if the type is not registered and default is not specified.
520 522 """
521 523
522 524 if isinstance(typ, string_types):
523 525 typ_key = tuple(typ.rsplit('.',1))
524 526 if typ_key not in self.deferred_printers:
525 527 # We may have it cached in the type map. We will have to
526 528 # iterate over all of the types to check.
527 529 for cls in self.type_printers:
528 530 if _mod_name_key(cls) == typ_key:
529 531 old = self.type_printers.pop(cls)
530 532 break
531 533 else:
532 534 old = default
533 535 else:
534 536 old = self.deferred_printers.pop(typ_key)
535 537 else:
536 538 if typ in self.type_printers:
537 539 old = self.type_printers.pop(typ)
538 540 else:
539 541 old = self.deferred_printers.pop(_mod_name_key(typ), default)
540 542 if old is _raise_key_error:
541 543 raise KeyError("No registered value for {0!r}".format(typ))
542 544 return old
543 545
544 546 def _in_deferred_types(self, cls):
545 547 """
546 548 Check if the given class is specified in the deferred type registry.
547 549
548 550 Successful matches will be moved to the regular type registry for future use.
549 551 """
550 552 mod = getattr(cls, '__module__', None)
551 553 name = getattr(cls, '__name__', None)
552 554 key = (mod, name)
553 555 if key in self.deferred_printers:
554 556 # Move the printer over to the regular registry.
555 557 printer = self.deferred_printers.pop(key)
556 558 self.type_printers[cls] = printer
557 559 return True
558 560 return False
559 561
560 562
561 563 class PlainTextFormatter(BaseFormatter):
562 564 """The default pretty-printer.
563 565
564 566 This uses :mod:`IPython.lib.pretty` to compute the format data of
565 567 the object. If the object cannot be pretty printed, :func:`repr` is used.
566 568 See the documentation of :mod:`IPython.lib.pretty` for details on
567 569 how to write pretty printers. Here is a simple example::
568 570
569 571 def dtype_pprinter(obj, p, cycle):
570 572 if cycle:
571 573 return p.text('dtype(...)')
572 574 if hasattr(obj, 'fields'):
573 575 if obj.fields is None:
574 576 p.text(repr(obj))
575 577 else:
576 578 p.begin_group(7, 'dtype([')
577 579 for i, field in enumerate(obj.descr):
578 580 if i > 0:
579 581 p.text(',')
580 582 p.breakable()
581 583 p.pretty(field)
582 584 p.end_group(7, '])')
583 585 """
584 586
585 587 # The format type of data returned.
586 588 format_type = Unicode('text/plain')
587 589
588 590 # This subclass ignores this attribute as it always need to return
589 591 # something.
590 592 enabled = Bool(True, config=False)
591 593
592 594 # Look for a _repr_pretty_ methods to use for pretty printing.
593 595 print_method = ObjectName('_repr_pretty_')
594 596
595 597 # Whether to pretty-print or not.
596 598 pprint = Bool(True, config=True)
597 599
598 600 # Whether to be verbose or not.
599 601 verbose = Bool(False, config=True)
600 602
601 603 # The maximum width.
602 604 max_width = Integer(79, config=True)
603 605
604 606 # The newline character.
605 607 newline = Unicode('\n', config=True)
606 608
607 609 # format-string for pprinting floats
608 610 float_format = Unicode('%r')
609 611 # setter for float precision, either int or direct format-string
610 612 float_precision = CUnicode('', config=True)
611 613
612 614 def _float_precision_changed(self, name, old, new):
613 615 """float_precision changed, set float_format accordingly.
614 616
615 617 float_precision can be set by int or str.
616 618 This will set float_format, after interpreting input.
617 619 If numpy has been imported, numpy print precision will also be set.
618 620
619 621 integer `n` sets format to '%.nf', otherwise, format set directly.
620 622
621 623 An empty string returns to defaults (repr for float, 8 for numpy).
622 624
623 625 This parameter can be set via the '%precision' magic.
624 626 """
625 627
626 628 if '%' in new:
627 629 # got explicit format string
628 630 fmt = new
629 631 try:
630 632 fmt%3.14159
631 633 except Exception:
632 634 raise ValueError("Precision must be int or format string, not %r"%new)
633 635 elif new:
634 636 # otherwise, should be an int
635 637 try:
636 638 i = int(new)
637 639 assert i >= 0
638 640 except ValueError:
639 641 raise ValueError("Precision must be int or format string, not %r"%new)
640 642 except AssertionError:
641 643 raise ValueError("int precision must be non-negative, not %r"%i)
642 644
643 645 fmt = '%%.%if'%i
644 646 if 'numpy' in sys.modules:
645 647 # set numpy precision if it has been imported
646 648 import numpy
647 649 numpy.set_printoptions(precision=i)
648 650 else:
649 651 # default back to repr
650 652 fmt = '%r'
651 653 if 'numpy' in sys.modules:
652 654 import numpy
653 655 # numpy default is 8
654 656 numpy.set_printoptions(precision=8)
655 657 self.float_format = fmt
656 658
657 659 # Use the default pretty printers from IPython.lib.pretty.
658 660 def _singleton_printers_default(self):
659 661 return pretty._singleton_pprinters.copy()
660 662
661 663 def _type_printers_default(self):
662 664 d = pretty._type_pprinters.copy()
663 665 d[float] = lambda obj,p,cycle: p.text(self.float_format%obj)
664 666 return d
665 667
666 668 def _deferred_printers_default(self):
667 669 return pretty._deferred_type_pprinters.copy()
668 670
669 671 #### FormatterABC interface ####
670 672
671 673 @warn_format_error
672 674 def __call__(self, obj):
673 675 """Compute the pretty representation of the object."""
674 676 if not self.pprint:
675 677 return pretty._safe_repr(obj)
676 678 else:
677 679 # This uses use StringIO, as cStringIO doesn't handle unicode.
678 680 stream = StringIO()
679 681 # self.newline.encode() is a quick fix for issue gh-597. We need to
680 682 # ensure that stream does not get a mix of unicode and bytestrings,
681 683 # or it will cause trouble.
682 684 printer = pretty.RepresentationPrinter(stream, self.verbose,
683 685 self.max_width, unicode_to_str(self.newline),
684 686 singleton_pprinters=self.singleton_printers,
685 687 type_pprinters=self.type_printers,
686 688 deferred_pprinters=self.deferred_printers)
687 689 printer.pretty(obj)
688 690 printer.flush()
689 691 return stream.getvalue()
690 692
691 693
692 694 class HTMLFormatter(BaseFormatter):
693 695 """An HTML formatter.
694 696
695 697 To define the callables that compute the HTML representation of your
696 698 objects, define a :meth:`_repr_html_` method or use the :meth:`for_type`
697 699 or :meth:`for_type_by_name` methods to register functions that handle
698 700 this.
699 701
700 702 The return value of this formatter should be a valid HTML snippet that
701 703 could be injected into an existing DOM. It should *not* include the
702 704 ```<html>`` or ```<body>`` tags.
703 705 """
704 706 format_type = Unicode('text/html')
705 707
706 708 print_method = ObjectName('_repr_html_')
707 709
708 710
711 class MarkdownFormatter(BaseFormatter):
712 """A Markdown formatter.
713
714 To define the callables that compute the Markdown representation of your
715 objects, define a :meth:`_repr_markdown_` method or use the :meth:`for_type`
716 or :meth:`for_type_by_name` methods to register functions that handle
717 this.
718
719 The return value of this formatter should be a valid Markdown.
720 """
721 format_type = Unicode('text/markdown')
722
723 print_method = ObjectName('_repr_markdown_')
724
709 725 class SVGFormatter(BaseFormatter):
710 726 """An SVG formatter.
711 727
712 728 To define the callables that compute the SVG representation of your
713 729 objects, define a :meth:`_repr_svg_` method or use the :meth:`for_type`
714 730 or :meth:`for_type_by_name` methods to register functions that handle
715 731 this.
716 732
717 733 The return value of this formatter should be valid SVG enclosed in
718 734 ```<svg>``` tags, that could be injected into an existing DOM. It should
719 735 *not* include the ```<html>`` or ```<body>`` tags.
720 736 """
721 737 format_type = Unicode('image/svg+xml')
722 738
723 739 print_method = ObjectName('_repr_svg_')
724 740
725 741
726 742 class PNGFormatter(BaseFormatter):
727 743 """A PNG formatter.
728 744
729 745 To define the callables that compute the PNG representation of your
730 746 objects, define a :meth:`_repr_png_` method or use the :meth:`for_type`
731 747 or :meth:`for_type_by_name` methods to register functions that handle
732 748 this.
733 749
734 750 The return value of this formatter should be raw PNG data, *not*
735 751 base64 encoded.
736 752 """
737 753 format_type = Unicode('image/png')
738 754
739 755 print_method = ObjectName('_repr_png_')
740 756
741 757 _return_type = (bytes, unicode_type)
742 758
743 759
744 760 class JPEGFormatter(BaseFormatter):
745 761 """A JPEG formatter.
746 762
747 763 To define the callables that compute the JPEG representation of your
748 764 objects, define a :meth:`_repr_jpeg_` method or use the :meth:`for_type`
749 765 or :meth:`for_type_by_name` methods to register functions that handle
750 766 this.
751 767
752 768 The return value of this formatter should be raw JPEG data, *not*
753 769 base64 encoded.
754 770 """
755 771 format_type = Unicode('image/jpeg')
756 772
757 773 print_method = ObjectName('_repr_jpeg_')
758 774
759 775 _return_type = (bytes, unicode_type)
760 776
761 777
762 778 class LatexFormatter(BaseFormatter):
763 779 """A LaTeX formatter.
764 780
765 781 To define the callables that compute the LaTeX representation of your
766 782 objects, define a :meth:`_repr_latex_` method or use the :meth:`for_type`
767 783 or :meth:`for_type_by_name` methods to register functions that handle
768 784 this.
769 785
770 786 The return value of this formatter should be a valid LaTeX equation,
771 787 enclosed in either ```$```, ```$$``` or another LaTeX equation
772 788 environment.
773 789 """
774 790 format_type = Unicode('text/latex')
775 791
776 792 print_method = ObjectName('_repr_latex_')
777 793
778 794
779 795 class JSONFormatter(BaseFormatter):
780 796 """A JSON string formatter.
781 797
782 798 To define the callables that compute the JSON string representation of
783 799 your objects, define a :meth:`_repr_json_` method or use the :meth:`for_type`
784 800 or :meth:`for_type_by_name` methods to register functions that handle
785 801 this.
786 802
787 803 The return value of this formatter should be a valid JSON string.
788 804 """
789 805 format_type = Unicode('application/json')
790 806
791 807 print_method = ObjectName('_repr_json_')
792 808
793 809
794 810 class JavascriptFormatter(BaseFormatter):
795 811 """A Javascript formatter.
796 812
797 813 To define the callables that compute the Javascript representation of
798 814 your objects, define a :meth:`_repr_javascript_` method or use the
799 815 :meth:`for_type` or :meth:`for_type_by_name` methods to register functions
800 816 that handle this.
801 817
802 818 The return value of this formatter should be valid Javascript code and
803 819 should *not* be enclosed in ```<script>``` tags.
804 820 """
805 821 format_type = Unicode('application/javascript')
806 822
807 823 print_method = ObjectName('_repr_javascript_')
808 824
809 825
810 826 class PDFFormatter(BaseFormatter):
811 827 """A PDF formatter.
812 828
813 829 To define the callables that compute the PDF representation of your
814 830 objects, define a :meth:`_repr_pdf_` method or use the :meth:`for_type`
815 831 or :meth:`for_type_by_name` methods to register functions that handle
816 832 this.
817 833
818 834 The return value of this formatter should be raw PDF data, *not*
819 835 base64 encoded.
820 836 """
821 837 format_type = Unicode('application/pdf')
822 838
823 839 print_method = ObjectName('_repr_pdf_')
824 840
825 841
826 842 FormatterABC.register(BaseFormatter)
827 843 FormatterABC.register(PlainTextFormatter)
828 844 FormatterABC.register(HTMLFormatter)
845 FormatterABC.register(MarkdownFormatter)
829 846 FormatterABC.register(SVGFormatter)
830 847 FormatterABC.register(PNGFormatter)
831 848 FormatterABC.register(PDFFormatter)
832 849 FormatterABC.register(JPEGFormatter)
833 850 FormatterABC.register(LatexFormatter)
834 851 FormatterABC.register(JSONFormatter)
835 852 FormatterABC.register(JavascriptFormatter)
836 853
837 854
838 855 def format_display_data(obj, include=None, exclude=None):
839 856 """Return a format data dict for an object.
840 857
841 858 By default all format types will be computed.
842 859
843 860 The following MIME types are currently implemented:
844 861
845 862 * text/plain
846 863 * text/html
864 * text/markdown
847 865 * text/latex
848 866 * application/json
849 867 * application/javascript
850 868 * application/pdf
851 869 * image/png
852 870 * image/jpeg
853 871 * image/svg+xml
854 872
855 873 Parameters
856 874 ----------
857 875 obj : object
858 876 The Python object whose format data will be computed.
859 877
860 878 Returns
861 879 -------
862 880 format_dict : dict
863 881 A dictionary of key/value pairs, one or each format that was
864 882 generated for the object. The keys are the format types, which
865 883 will usually be MIME type strings and the values and JSON'able
866 884 data structure containing the raw data for the representation in
867 885 that format.
868 886 include : list or tuple, optional
869 887 A list of format type strings (MIME types) to include in the
870 888 format data dict. If this is set *only* the format types included
871 889 in this list will be computed.
872 890 exclude : list or tuple, optional
873 891 A list of format type string (MIME types) to exclue in the format
874 892 data dict. If this is set all format types will be computed,
875 893 except for those included in this argument.
876 894 """
877 895 from IPython.core.interactiveshell import InteractiveShell
878 896
879 897 InteractiveShell.instance().display_formatter.format(
880 898 obj,
881 899 include,
882 900 exclude
883 901 )
884 902
@@ -1,942 +1,963 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 34 this.trusted = true;
35 35 this.clear_queued = null;
36 36 if (prompt_area === undefined) {
37 37 this.prompt_area = true;
38 38 } else {
39 39 this.prompt_area = prompt_area;
40 40 }
41 41 this.create_elements();
42 42 this.style();
43 43 this.bind_events();
44 44 };
45 45
46 46
47 47 /**
48 48 * Class prototypes
49 49 **/
50 50
51 51 OutputArea.prototype.create_elements = function () {
52 52 this.element = $("<div/>");
53 53 this.collapse_button = $("<div/>");
54 54 this.prompt_overlay = $("<div/>");
55 55 this.wrapper.append(this.prompt_overlay);
56 56 this.wrapper.append(this.element);
57 57 this.wrapper.append(this.collapse_button);
58 58 };
59 59
60 60
61 61 OutputArea.prototype.style = function () {
62 62 this.collapse_button.hide();
63 63 this.prompt_overlay.hide();
64 64
65 65 this.wrapper.addClass('output_wrapper');
66 66 this.element.addClass('output');
67 67
68 68 this.collapse_button.addClass("btn output_collapsed");
69 69 this.collapse_button.attr('title', 'click to expand output');
70 70 this.collapse_button.text('. . .');
71 71
72 72 this.prompt_overlay.addClass('out_prompt_overlay prompt');
73 73 this.prompt_overlay.attr('title', 'click to expand output; double click to hide output');
74 74
75 75 this.collapse();
76 76 };
77 77
78 78 /**
79 79 * Should the OutputArea scroll?
80 80 * Returns whether the height (in lines) exceeds a threshold.
81 81 *
82 82 * @private
83 83 * @method _should_scroll
84 84 * @param [lines=100]{Integer}
85 85 * @return {Bool}
86 86 *
87 87 */
88 88 OutputArea.prototype._should_scroll = function (lines) {
89 89 if (lines <=0 ){ return }
90 90 if (!lines) {
91 91 lines = 100;
92 92 }
93 93 // line-height from http://stackoverflow.com/questions/1185151
94 94 var fontSize = this.element.css('font-size');
95 95 var lineHeight = Math.floor(parseInt(fontSize.replace('px','')) * 1.5);
96 96
97 97 return (this.element.height() > lines * lineHeight);
98 98 };
99 99
100 100
101 101 OutputArea.prototype.bind_events = function () {
102 102 var that = this;
103 103 this.prompt_overlay.dblclick(function () { that.toggle_output(); });
104 104 this.prompt_overlay.click(function () { that.toggle_scroll(); });
105 105
106 106 this.element.resize(function () {
107 107 // FIXME: Firefox on Linux misbehaves, so automatic scrolling is disabled
108 108 if ( IPython.utils.browser[0] === "Firefox" ) {
109 109 return;
110 110 }
111 111 // maybe scroll output,
112 112 // if it's grown large enough and hasn't already been scrolled.
113 113 if ( !that.scrolled && that._should_scroll(OutputArea.auto_scroll_threshold)) {
114 114 that.scroll_area();
115 115 }
116 116 });
117 117 this.collapse_button.click(function () {
118 118 that.expand();
119 119 });
120 120 };
121 121
122 122
123 123 OutputArea.prototype.collapse = function () {
124 124 if (!this.collapsed) {
125 125 this.element.hide();
126 126 this.prompt_overlay.hide();
127 127 if (this.element.html()){
128 128 this.collapse_button.show();
129 129 }
130 130 this.collapsed = true;
131 131 }
132 132 };
133 133
134 134
135 135 OutputArea.prototype.expand = function () {
136 136 if (this.collapsed) {
137 137 this.collapse_button.hide();
138 138 this.element.show();
139 139 this.prompt_overlay.show();
140 140 this.collapsed = false;
141 141 }
142 142 };
143 143
144 144
145 145 OutputArea.prototype.toggle_output = function () {
146 146 if (this.collapsed) {
147 147 this.expand();
148 148 } else {
149 149 this.collapse();
150 150 }
151 151 };
152 152
153 153
154 154 OutputArea.prototype.scroll_area = function () {
155 155 this.element.addClass('output_scroll');
156 156 this.prompt_overlay.attr('title', 'click to unscroll output; double click to hide');
157 157 this.scrolled = true;
158 158 };
159 159
160 160
161 161 OutputArea.prototype.unscroll_area = function () {
162 162 this.element.removeClass('output_scroll');
163 163 this.prompt_overlay.attr('title', 'click to scroll output; double click to hide');
164 164 this.scrolled = false;
165 165 };
166 166
167 167 /**
168 168 *
169 169 * Scroll OutputArea if height supperior than a threshold (in lines).
170 170 *
171 171 * Threshold is a maximum number of lines. If unspecified, defaults to
172 172 * OutputArea.minimum_scroll_threshold.
173 173 *
174 174 * Negative threshold will prevent the OutputArea from ever scrolling.
175 175 *
176 176 * @method scroll_if_long
177 177 *
178 178 * @param [lines=20]{Number} Default to 20 if not set,
179 179 * behavior undefined for value of `0`.
180 180 *
181 181 **/
182 182 OutputArea.prototype.scroll_if_long = function (lines) {
183 183 var n = lines | OutputArea.minimum_scroll_threshold;
184 184 if(n <= 0){
185 185 return
186 186 }
187 187
188 188 if (this._should_scroll(n)) {
189 189 // only allow scrolling long-enough output
190 190 this.scroll_area();
191 191 }
192 192 };
193 193
194 194
195 195 OutputArea.prototype.toggle_scroll = function () {
196 196 if (this.scrolled) {
197 197 this.unscroll_area();
198 198 } else {
199 199 // only allow scrolling long-enough output
200 200 this.scroll_if_long();
201 201 }
202 202 };
203 203
204 204
205 205 // typeset with MathJax if MathJax is available
206 206 OutputArea.prototype.typeset = function () {
207 207 if (window.MathJax){
208 208 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
209 209 }
210 210 };
211 211
212 212
213 213 OutputArea.prototype.handle_output = function (msg) {
214 214 var json = {};
215 215 var msg_type = json.output_type = msg.header.msg_type;
216 216 var content = msg.content;
217 217 if (msg_type === "stream") {
218 218 json.text = content.data;
219 219 json.stream = content.name;
220 220 } else if (msg_type === "display_data") {
221 221 json = content.data;
222 222 json.output_type = msg_type;
223 223 json.metadata = content.metadata;
224 224 } else if (msg_type === "pyout") {
225 225 json = content.data;
226 226 json.output_type = msg_type;
227 227 json.metadata = content.metadata;
228 228 json.prompt_number = content.execution_count;
229 229 } else if (msg_type === "pyerr") {
230 230 json.ename = content.ename;
231 231 json.evalue = content.evalue;
232 232 json.traceback = content.traceback;
233 233 }
234 234 this.append_output(json);
235 235 };
236 236
237 237
238 238 OutputArea.prototype.rename_keys = function (data, key_map) {
239 239 var remapped = {};
240 240 for (var key in data) {
241 241 var new_key = key_map[key] || key;
242 242 remapped[new_key] = data[key];
243 243 }
244 244 return remapped;
245 245 };
246 246
247 247
248 248 OutputArea.output_types = [
249 249 'application/javascript',
250 250 'text/html',
251 'text/markdown',
251 252 'text/latex',
252 253 'image/svg+xml',
253 254 'image/png',
254 255 'image/jpeg',
255 256 'application/pdf',
256 257 'text/plain'
257 258 ];
258 259
259 260 OutputArea.prototype.validate_output = function (json) {
260 261 // scrub invalid outputs
261 262 // TODO: right now everything is a string, but JSON really shouldn't be.
262 263 // nbformat 4 will fix that.
263 264 $.map(OutputArea.output_types, function(key){
264 265 if (json[key] !== undefined && typeof json[key] !== 'string') {
265 266 console.log("Invalid type for " + key, json[key]);
266 267 delete json[key];
267 268 }
268 269 });
269 270 return json;
270 271 };
271 272
272 273 OutputArea.prototype.append_output = function (json) {
273 274 this.expand();
274 275
275 276 // validate output data types
276 277 json = this.validate_output(json);
277 278
278 279 // Clear the output if clear is queued.
279 280 var needs_height_reset = false;
280 281 if (this.clear_queued) {
281 282 this.clear_output(false);
282 283 needs_height_reset = true;
283 284 }
284 285
285 286 if (json.output_type === 'pyout') {
286 287 this.append_pyout(json);
287 288 } else if (json.output_type === 'pyerr') {
288 289 this.append_pyerr(json);
289 290 } else if (json.output_type === 'stream') {
290 291 this.append_stream(json);
291 292 }
292 293
293 294 // We must release the animation fixed height in a callback since Gecko
294 295 // (FireFox) doesn't render the image immediately as the data is
295 296 // available.
296 297 var that = this;
297 298 var handle_appended = function ($el) {
298 299 // Only reset the height to automatic if the height is currently
299 300 // fixed (done by wait=True flag on clear_output).
300 301 if (needs_height_reset) {
301 302 that.element.height('');
302 303 }
303 304 that.element.trigger('resize');
304 305 };
305 306 if (json.output_type === 'display_data') {
306 307 this.append_display_data(json, handle_appended);
307 308 } else {
308 309 handle_appended();
309 310 }
310 311
311 312 this.outputs.push(json);
312 313 };
313 314
314 315
315 316 OutputArea.prototype.create_output_area = function () {
316 317 var oa = $("<div/>").addClass("output_area");
317 318 if (this.prompt_area) {
318 319 oa.append($('<div/>').addClass('prompt'));
319 320 }
320 321 return oa;
321 322 };
322 323
323 324
324 325 function _get_metadata_key(metadata, key, mime) {
325 326 var mime_md = metadata[mime];
326 327 // mime-specific higher priority
327 328 if (mime_md && mime_md[key] !== undefined) {
328 329 return mime_md[key];
329 330 }
330 331 // fallback on global
331 332 return metadata[key];
332 333 }
333 334
334 335 OutputArea.prototype.create_output_subarea = function(md, classes, mime) {
335 336 var subarea = $('<div/>').addClass('output_subarea').addClass(classes);
336 337 if (_get_metadata_key(md, 'isolated', mime)) {
337 338 // Create an iframe to isolate the subarea from the rest of the
338 339 // document
339 340 var iframe = $('<iframe/>').addClass('box-flex1');
340 341 iframe.css({'height':1, 'width':'100%', 'display':'block'});
341 342 iframe.attr('frameborder', 0);
342 343 iframe.attr('scrolling', 'auto');
343 344
344 345 // Once the iframe is loaded, the subarea is dynamically inserted
345 346 iframe.on('load', function() {
346 347 // Workaround needed by Firefox, to properly render svg inside
347 348 // iframes, see http://stackoverflow.com/questions/10177190/
348 349 // svg-dynamically-added-to-iframe-does-not-render-correctly
349 350 this.contentDocument.open();
350 351
351 352 // Insert the subarea into the iframe
352 353 // We must directly write the html. When using Jquery's append
353 354 // method, javascript is evaluated in the parent document and
354 355 // not in the iframe document. At this point, subarea doesn't
355 356 // contain any user content.
356 357 this.contentDocument.write(subarea.html());
357 358
358 359 this.contentDocument.close();
359 360
360 361 var body = this.contentDocument.body;
361 362 // Adjust the iframe height automatically
362 363 iframe.height(body.scrollHeight + 'px');
363 364 });
364 365
365 366 // Elements should be appended to the inner subarea and not to the
366 367 // iframe
367 368 iframe.append = function(that) {
368 369 subarea.append(that);
369 370 };
370 371
371 372 return iframe;
372 373 } else {
373 374 return subarea;
374 375 }
375 376 }
376 377
377 378
378 379 OutputArea.prototype._append_javascript_error = function (err, element) {
379 380 // display a message when a javascript error occurs in display output
380 381 var msg = "Javascript error adding output!"
381 382 if ( element === undefined ) return;
382 383 element
383 384 .append($('<div/>').text(msg).addClass('js-error'))
384 385 .append($('<div/>').text(err.toString()).addClass('js-error'))
385 386 .append($('<div/>').text('See your browser Javascript console for more details.').addClass('js-error'));
386 387 };
387 388
388 389 OutputArea.prototype._safe_append = function (toinsert) {
389 390 // safely append an item to the document
390 391 // this is an object created by user code,
391 392 // and may have errors, which should not be raised
392 393 // under any circumstances.
393 394 try {
394 395 this.element.append(toinsert);
395 396 } catch(err) {
396 397 console.log(err);
397 398 // Create an actual output_area and output_subarea, which creates
398 399 // the prompt area and the proper indentation.
399 400 var toinsert = this.create_output_area();
400 401 var subarea = $('<div/>').addClass('output_subarea');
401 402 toinsert.append(subarea);
402 403 this._append_javascript_error(err, subarea);
403 404 this.element.append(toinsert);
404 405 }
405 406 };
406 407
407 408
408 409 OutputArea.prototype.append_pyout = function (json) {
409 410 var n = json.prompt_number || ' ';
410 411 var toinsert = this.create_output_area();
411 412 if (this.prompt_area) {
412 413 toinsert.find('div.prompt').addClass('output_prompt').text('Out[' + n + ']:');
413 414 }
414 415 var inserted = this.append_mime_type(json, toinsert);
415 416 if (inserted) {
416 417 inserted.addClass('output_pyout');
417 418 }
418 419 this._safe_append(toinsert);
419 420 // If we just output latex, typeset it.
420 if ((json['text/latex'] !== undefined) || (json['text/html'] !== undefined)) {
421 if ((json['text/latex'] !== undefined) ||
422 (json['text/html'] !== undefined) ||
423 (json['text/markdown'] !== undefined)) {
421 424 this.typeset();
422 425 }
423 426 };
424 427
425 428
426 429 OutputArea.prototype.append_pyerr = function (json) {
427 430 var tb = json.traceback;
428 431 if (tb !== undefined && tb.length > 0) {
429 432 var s = '';
430 433 var len = tb.length;
431 434 for (var i=0; i<len; i++) {
432 435 s = s + tb[i] + '\n';
433 436 }
434 437 s = s + '\n';
435 438 var toinsert = this.create_output_area();
436 439 var append_text = OutputArea.append_map['text/plain'];
437 440 if (append_text) {
438 441 append_text.apply(this, [s, {}, toinsert]).addClass('output_pyerr');
439 442 }
440 443 this._safe_append(toinsert);
441 444 }
442 445 };
443 446
444 447
445 448 OutputArea.prototype.append_stream = function (json) {
446 449 // temporary fix: if stream undefined (json file written prior to this patch),
447 450 // default to most likely stdout:
448 451 if (json.stream === undefined){
449 452 json.stream = 'stdout';
450 453 }
451 454 var text = json.text;
452 455 var subclass = "output_"+json.stream;
453 456 if (this.outputs.length > 0){
454 457 // have at least one output to consider
455 458 var last = this.outputs[this.outputs.length-1];
456 459 if (last.output_type == 'stream' && json.stream == last.stream){
457 460 // latest output was in the same stream,
458 461 // so append directly into its pre tag
459 462 // escape ANSI & HTML specials:
460 463 var pre = this.element.find('div.'+subclass).last().find('pre');
461 464 var html = utils.fixCarriageReturn(
462 465 pre.html() + utils.fixConsole(text));
463 466 // The only user content injected with this HTML call is
464 467 // escaped by the fixConsole() method.
465 468 pre.html(html);
466 469 return;
467 470 }
468 471 }
469 472
470 473 if (!text.replace("\r", "")) {
471 474 // text is nothing (empty string, \r, etc.)
472 475 // so don't append any elements, which might add undesirable space
473 476 return;
474 477 }
475 478
476 479 // If we got here, attach a new div
477 480 var toinsert = this.create_output_area();
478 481 var append_text = OutputArea.append_map['text/plain'];
479 482 if (append_text) {
480 483 append_text.apply(this, [text, {}, toinsert]).addClass("output_stream " + subclass);
481 484 }
482 485 this._safe_append(toinsert);
483 486 };
484 487
485 488
486 489 OutputArea.prototype.append_display_data = function (json, handle_inserted) {
487 490 var toinsert = this.create_output_area();
488 491 if (this.append_mime_type(json, toinsert, handle_inserted)) {
489 492 this._safe_append(toinsert);
490 493 // If we just output latex, typeset it.
491 if ((json['text/latex'] !== undefined) || (json['text/html'] !== undefined)) {
494 if ((json['text/latex'] !== undefined) ||
495 (json['text/html'] !== undefined) ||
496 (json['text/markdown'] !== undefined)) {
492 497 this.typeset();
493 498 }
494 499 }
495 500 };
496 501
497 502
498 503 OutputArea.safe_outputs = {
499 504 'text/plain' : true,
500 505 'text/latex' : true,
501 506 'image/png' : true,
502 507 'image/jpeg' : true
503 508 };
504 509
505 510 OutputArea.prototype.append_mime_type = function (json, element, handle_inserted) {
506 511 for (var i=0; i < OutputArea.display_order.length; i++) {
507 512 var type = OutputArea.display_order[i];
508 513 var append = OutputArea.append_map[type];
509 514 if ((json[type] !== undefined) && append) {
510 515 var value = json[type];
511 516 if (!this.trusted && !OutputArea.safe_outputs[type]) {
512 517 // not trusted, sanitize HTML
513 518 if (type==='text/html' || type==='text/svg') {
514 519 value = IPython.security.sanitize_html(value);
515 520 } else {
516 521 // don't display if we don't know how to sanitize it
517 522 console.log("Ignoring untrusted " + type + " output.");
518 523 continue;
519 524 }
520 525 }
521 526 var md = json.metadata || {};
522 527 var toinsert = append.apply(this, [value, md, element, handle_inserted]);
523 528 // Since only the png and jpeg mime types call the inserted
524 529 // callback, if the mime type is something other we must call the
525 530 // inserted callback only when the element is actually inserted
526 531 // into the DOM. Use a timeout of 0 to do this.
527 532 if (['image/png', 'image/jpeg'].indexOf(type) < 0 && handle_inserted !== undefined) {
528 533 setTimeout(handle_inserted, 0);
529 534 }
530 535 $([IPython.events]).trigger('output_appended.OutputArea', [type, value, md, toinsert]);
531 536 return toinsert;
532 537 }
533 538 }
534 539 return null;
535 540 };
536 541
537 542
538 543 var append_html = function (html, md, element) {
539 544 var type = 'text/html';
540 545 var toinsert = this.create_output_subarea(md, "output_html rendered_html", type);
541 546 IPython.keyboard_manager.register_events(toinsert);
542 547 toinsert.append(html);
543 548 element.append(toinsert);
544 549 return toinsert;
545 550 };
546 551
547 552
553 var append_markdown = function(markdown, md, element) {
554 var type = 'text/markdown';
555 var toinsert = this.create_output_subarea(md, "output_markdown", type);
556 var text_and_math = IPython.mathjaxutils.remove_math(markdown);
557 var text = text_and_math[0];
558 var math = text_and_math[1];
559 var html = marked.parser(marked.lexer(text));
560 html = IPython.mathjaxutils.replace_math(html, math);
561 toinsert.append(html);
562 element.append(toinsert);
563 return toinsert;
564 };
565
566
548 567 var append_javascript = function (js, md, element) {
549 568 // We just eval the JS code, element appears in the local scope.
550 569 var type = 'application/javascript';
551 570 var toinsert = this.create_output_subarea(md, "output_javascript", type);
552 571 IPython.keyboard_manager.register_events(toinsert);
553 572 element.append(toinsert);
554 573 // FIXME TODO : remove `container element for 3.0`
555 574 //backward compat, js should be eval'ed in a context where `container` is defined.
556 575 var container = element;
557 576 container.show = function(){console.log('Warning "container.show()" is deprecated.')};
558 577 // end backward compat
559 578
560 579 // Fix for ipython/issues/5293, make sure `element` is the area which
561 580 // output can be inserted into at the time of JS execution.
562 581 element = toinsert;
563 582 try {
564 583 eval(js);
565 584 } catch(err) {
566 585 console.log(err);
567 586 this._append_javascript_error(err, toinsert);
568 587 }
569 588 return toinsert;
570 589 };
571 590
572 591
573 592 var append_text = function (data, md, element) {
574 593 var type = 'text/plain';
575 594 var toinsert = this.create_output_subarea(md, "output_text", type);
576 595 // escape ANSI & HTML specials in plaintext:
577 596 data = utils.fixConsole(data);
578 597 data = utils.fixCarriageReturn(data);
579 598 data = utils.autoLinkUrls(data);
580 599 // The only user content injected with this HTML call is
581 600 // escaped by the fixConsole() method.
582 601 toinsert.append($("<pre/>").html(data));
583 602 element.append(toinsert);
584 603 return toinsert;
585 604 };
586 605
587 606
588 607 var append_svg = function (svg_html, md, element) {
589 608 var type = 'image/svg+xml';
590 609 var toinsert = this.create_output_subarea(md, "output_svg", type);
591 610
592 611 // Get the svg element from within the HTML.
593 612 var svg = $('<div />').html(svg_html).find('svg');
594 613 var svg_area = $('<div />');
595 614 var width = svg.attr('width');
596 615 var height = svg.attr('height');
597 616 svg
598 617 .width('100%')
599 618 .height('100%');
600 619 svg_area
601 620 .width(width)
602 621 .height(height);
603 622
604 623 // The jQuery resize handlers don't seem to work on the svg element.
605 624 // When the svg renders completely, measure it's size and set the parent
606 625 // div to that size. Then set the svg to 100% the size of the parent
607 626 // div and make the parent div resizable.
608 627 this._dblclick_to_reset_size(svg_area, true, false);
609 628
610 629 svg_area.append(svg);
611 630 toinsert.append(svg_area);
612 631 element.append(toinsert);
613 632
614 633 return toinsert;
615 634 };
616 635
617 636 OutputArea.prototype._dblclick_to_reset_size = function (img, immediately, resize_parent) {
618 637 // Add a resize handler to an element
619 638 //
620 639 // img: jQuery element
621 640 // immediately: bool=False
622 641 // Wait for the element to load before creating the handle.
623 642 // resize_parent: bool=True
624 643 // Should the parent of the element be resized when the element is
625 644 // reset (by double click).
626 645 var callback = function (){
627 646 var h0 = img.height();
628 647 var w0 = img.width();
629 648 if (!(h0 && w0)) {
630 649 // zero size, don't make it resizable
631 650 return;
632 651 }
633 652 img.resizable({
634 653 aspectRatio: true,
635 654 autoHide: true
636 655 });
637 656 img.dblclick(function () {
638 657 // resize wrapper & image together for some reason:
639 658 img.height(h0);
640 659 img.width(w0);
641 660 if (resize_parent === undefined || resize_parent) {
642 661 img.parent().height(h0);
643 662 img.parent().width(w0);
644 663 }
645 664 });
646 665 };
647 666
648 667 if (immediately) {
649 668 callback();
650 669 } else {
651 670 img.on("load", callback);
652 671 }
653 672 };
654 673
655 674 var set_width_height = function (img, md, mime) {
656 675 // set width and height of an img element from metadata
657 676 var height = _get_metadata_key(md, 'height', mime);
658 677 if (height !== undefined) img.attr('height', height);
659 678 var width = _get_metadata_key(md, 'width', mime);
660 679 if (width !== undefined) img.attr('width', width);
661 680 };
662 681
663 682 var append_png = function (png, md, element, handle_inserted) {
664 683 var type = 'image/png';
665 684 var toinsert = this.create_output_subarea(md, "output_png", type);
666 685 var img = $("<img/>");
667 686 if (handle_inserted !== undefined) {
668 687 img.on('load', function(){
669 688 handle_inserted(img);
670 689 });
671 690 }
672 691 img[0].src = 'data:image/png;base64,'+ png;
673 692 set_width_height(img, md, 'image/png');
674 693 this._dblclick_to_reset_size(img);
675 694 toinsert.append(img);
676 695 element.append(toinsert);
677 696 return toinsert;
678 697 };
679 698
680 699
681 700 var append_jpeg = function (jpeg, md, element, handle_inserted) {
682 701 var type = 'image/jpeg';
683 702 var toinsert = this.create_output_subarea(md, "output_jpeg", type);
684 703 var img = $("<img/>");
685 704 if (handle_inserted !== undefined) {
686 705 img.on('load', function(){
687 706 handle_inserted(img);
688 707 });
689 708 }
690 709 img[0].src = 'data:image/jpeg;base64,'+ jpeg;
691 710 set_width_height(img, md, 'image/jpeg');
692 711 this._dblclick_to_reset_size(img);
693 712 toinsert.append(img);
694 713 element.append(toinsert);
695 714 return toinsert;
696 715 };
697 716
698 717
699 718 var append_pdf = function (pdf, md, element) {
700 719 var type = 'application/pdf';
701 720 var toinsert = this.create_output_subarea(md, "output_pdf", type);
702 721 var a = $('<a/>').attr('href', 'data:application/pdf;base64,'+pdf);
703 722 a.attr('target', '_blank');
704 723 a.text('View PDF')
705 724 toinsert.append(a);
706 725 element.append(toinsert);
707 726 return toinsert;
708 727 }
709 728
710 729 var append_latex = function (latex, md, element) {
711 730 // This method cannot do the typesetting because the latex first has to
712 731 // be on the page.
713 732 var type = 'text/latex';
714 733 var toinsert = this.create_output_subarea(md, "output_latex", type);
715 734 toinsert.append(latex);
716 735 element.append(toinsert);
717 736 return toinsert;
718 737 };
719 738
720 739
721 740 OutputArea.prototype.append_raw_input = function (msg) {
722 741 var that = this;
723 742 this.expand();
724 743 var content = msg.content;
725 744 var area = this.create_output_area();
726 745
727 746 // disable any other raw_inputs, if they are left around
728 747 $("div.output_subarea.raw_input_container").remove();
729 748
730 749 area.append(
731 750 $("<div/>")
732 751 .addClass("box-flex1 output_subarea raw_input_container")
733 752 .append(
734 753 $("<span/>")
735 754 .addClass("raw_input_prompt")
736 755 .text(content.prompt)
737 756 )
738 757 .append(
739 758 $("<input/>")
740 759 .addClass("raw_input")
741 760 .attr('type', 'text')
742 761 .attr("size", 47)
743 762 .keydown(function (event, ui) {
744 763 // make sure we submit on enter,
745 764 // and don't re-execute the *cell* on shift-enter
746 765 if (event.which === IPython.keyboard.keycodes.enter) {
747 766 that._submit_raw_input();
748 767 return false;
749 768 }
750 769 })
751 770 )
752 771 );
753 772
754 773 this.element.append(area);
755 774 var raw_input = area.find('input.raw_input');
756 775 // Register events that enable/disable the keyboard manager while raw
757 776 // input is focused.
758 777 IPython.keyboard_manager.register_events(raw_input);
759 778 // Note, the following line used to read raw_input.focus().focus().
760 779 // This seemed to be needed otherwise only the cell would be focused.
761 780 // But with the modal UI, this seems to work fine with one call to focus().
762 781 raw_input.focus();
763 782 }
764 783
765 784 OutputArea.prototype._submit_raw_input = function (evt) {
766 785 var container = this.element.find("div.raw_input_container");
767 786 var theprompt = container.find("span.raw_input_prompt");
768 787 var theinput = container.find("input.raw_input");
769 788 var value = theinput.val();
770 789 var content = {
771 790 output_type : 'stream',
772 791 name : 'stdout',
773 792 text : theprompt.text() + value + '\n'
774 793 }
775 794 // remove form container
776 795 container.parent().remove();
777 796 // replace with plaintext version in stdout
778 797 this.append_output(content, false);
779 798 $([IPython.events]).trigger('send_input_reply.Kernel', value);
780 799 }
781 800
782 801
783 802 OutputArea.prototype.handle_clear_output = function (msg) {
784 803 // msg spec v4 had stdout, stderr, display keys
785 804 // v4.1 replaced these with just wait
786 805 // The default behavior is the same (stdout=stderr=display=True, wait=False),
787 806 // so v4 messages will still be properly handled,
788 807 // except for the rarely used clearing less than all output.
789 808 this.clear_output(msg.content.wait || false);
790 809 };
791 810
792 811
793 812 OutputArea.prototype.clear_output = function(wait) {
794 813 if (wait) {
795 814
796 815 // If a clear is queued, clear before adding another to the queue.
797 816 if (this.clear_queued) {
798 817 this.clear_output(false);
799 818 };
800 819
801 820 this.clear_queued = true;
802 821 } else {
803 822
804 823 // Fix the output div's height if the clear_output is waiting for
805 824 // new output (it is being used in an animation).
806 825 if (this.clear_queued) {
807 826 var height = this.element.height();
808 827 this.element.height(height);
809 828 this.clear_queued = false;
810 829 }
811 830
812 831 // Clear all
813 832 // Remove load event handlers from img tags because we don't want
814 833 // them to fire if the image is never added to the page.
815 834 this.element.find('img').off('load');
816 835 this.element.html("");
817 836 this.outputs = [];
818 837 this.trusted = true;
819 838 this.unscroll_area();
820 839 return;
821 840 };
822 841 };
823 842
824 843
825 844 // JSON serialization
826 845
827 846 OutputArea.prototype.fromJSON = function (outputs) {
828 847 var len = outputs.length;
829 848 var data;
830 849
831 850 for (var i=0; i<len; i++) {
832 851 data = outputs[i];
833 852 var msg_type = data.output_type;
834 853 if (msg_type === "display_data" || msg_type === "pyout") {
835 854 // convert short keys to mime keys
836 855 // TODO: remove mapping of short keys when we update to nbformat 4
837 856 data = this.rename_keys(data, OutputArea.mime_map_r);
838 857 data.metadata = this.rename_keys(data.metadata, OutputArea.mime_map_r);
839 858 }
840 859
841 860 this.append_output(data);
842 861 }
843 862 };
844 863
845 864
846 865 OutputArea.prototype.toJSON = function () {
847 866 var outputs = [];
848 867 var len = this.outputs.length;
849 868 var data;
850 869 for (var i=0; i<len; i++) {
851 870 data = this.outputs[i];
852 871 var msg_type = data.output_type;
853 872 if (msg_type === "display_data" || msg_type === "pyout") {
854 873 // convert mime keys to short keys
855 874 data = this.rename_keys(data, OutputArea.mime_map);
856 875 data.metadata = this.rename_keys(data.metadata, OutputArea.mime_map);
857 876 }
858 877 outputs[i] = data;
859 878 }
860 879 return outputs;
861 880 };
862 881
863 882 /**
864 883 * Class properties
865 884 **/
866 885
867 886 /**
868 887 * Threshold to trigger autoscroll when the OutputArea is resized,
869 888 * typically when new outputs are added.
870 889 *
871 890 * Behavior is undefined if autoscroll is lower than minimum_scroll_threshold,
872 891 * unless it is < 0, in which case autoscroll will never be triggered
873 892 *
874 893 * @property auto_scroll_threshold
875 894 * @type Number
876 895 * @default 100
877 896 *
878 897 **/
879 898 OutputArea.auto_scroll_threshold = 100;
880 899
881 900 /**
882 901 * Lower limit (in lines) for OutputArea to be made scrollable. OutputAreas
883 902 * shorter than this are never scrolled.
884 903 *
885 904 * @property minimum_scroll_threshold
886 905 * @type Number
887 906 * @default 20
888 907 *
889 908 **/
890 909 OutputArea.minimum_scroll_threshold = 20;
891 910
892 911
893 912
894 913 OutputArea.mime_map = {
895 914 "text/plain" : "text",
896 915 "text/html" : "html",
897 916 "image/svg+xml" : "svg",
898 917 "image/png" : "png",
899 918 "image/jpeg" : "jpeg",
900 919 "text/latex" : "latex",
901 920 "application/json" : "json",
902 921 "application/javascript" : "javascript",
903 922 };
904 923
905 924 OutputArea.mime_map_r = {
906 925 "text" : "text/plain",
907 926 "html" : "text/html",
908 927 "svg" : "image/svg+xml",
909 928 "png" : "image/png",
910 929 "jpeg" : "image/jpeg",
911 930 "latex" : "text/latex",
912 931 "json" : "application/json",
913 932 "javascript" : "application/javascript",
914 933 };
915 934
916 935 OutputArea.display_order = [
917 936 'application/javascript',
918 937 'text/html',
938 'text/markdown',
919 939 'text/latex',
920 940 'image/svg+xml',
921 941 'image/png',
922 942 'image/jpeg',
923 943 'application/pdf',
924 944 'text/plain'
925 945 ];
926 946
927 947 OutputArea.append_map = {
928 948 "text/plain" : append_text,
929 949 "text/html" : append_html,
950 "text/markdown": append_markdown,
930 951 "image/svg+xml" : append_svg,
931 952 "image/png" : append_png,
932 953 "image/jpeg" : append_jpeg,
933 954 "text/latex" : append_latex,
934 955 "application/javascript" : append_javascript,
935 956 "application/pdf" : append_pdf
936 957 };
937 958
938 959 IPython.OutputArea = OutputArea;
939 960
940 961 return IPython;
941 962
942 963 }(IPython));
General Comments 0
You need to be logged in to leave comments. Login now