##// END OF EJS Templates
comments about `*` for kw-only
Min RK -
Show More
@@ -1,1133 +1,1135 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Top-level display functions for displaying object in different formats."""
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7
8 8 try:
9 9 from base64 import encodebytes as base64_encode
10 10 except ImportError:
11 11 from base64 import encodestring as base64_encode
12 12
13 13 from binascii import b2a_hex
14 14 import json
15 15 import mimetypes
16 16 import os
17 17 import struct
18 18 import sys
19 19 import warnings
20 20
21 21 from IPython.utils.py3compat import (string_types, cast_bytes_py2, cast_unicode,
22 22 unicode_type)
23 23 from IPython.testing.skipdoctest import skip_doctest
24 24
25 25 __all__ = ['display', 'display_pretty', 'display_html', 'display_markdown',
26 26 'display_svg', 'display_png', 'display_jpeg', 'display_latex', 'display_json',
27 27 'display_javascript', 'display_pdf', 'DisplayObject', 'TextDisplayObject',
28 28 'Pretty', 'HTML', 'Markdown', 'Math', 'Latex', 'SVG', 'JSON', 'Javascript',
29 29 'Image', 'clear_output', 'set_matplotlib_formats', 'set_matplotlib_close',
30 30 'publish_display_data', 'update_display', 'DisplayHandle']
31 31
32 32 #-----------------------------------------------------------------------------
33 33 # utility functions
34 34 #-----------------------------------------------------------------------------
35 35
36 36 def _safe_exists(path):
37 37 """Check path, but don't let exceptions raise"""
38 38 try:
39 39 return os.path.exists(path)
40 40 except Exception:
41 41 return False
42 42
43 43 def _merge(d1, d2):
44 44 """Like update, but merges sub-dicts instead of clobbering at the top level.
45 45
46 46 Updates d1 in-place
47 47 """
48 48
49 49 if not isinstance(d2, dict) or not isinstance(d1, dict):
50 50 return d2
51 51 for key, value in d2.items():
52 52 d1[key] = _merge(d1.get(key), value)
53 53 return d1
54 54
55 55 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
56 56 """internal implementation of all display_foo methods
57 57
58 58 Parameters
59 59 ----------
60 60 mimetype : str
61 61 The mimetype to be published (e.g. 'image/png')
62 62 objs : tuple of objects
63 63 The Python objects to display, or if raw=True raw text data to
64 64 display.
65 65 raw : bool
66 66 Are the data objects raw data or Python objects that need to be
67 67 formatted before display? [default: False]
68 68 metadata : dict (optional)
69 69 Metadata to be associated with the specific mimetype output.
70 70 """
71 71 if metadata:
72 72 metadata = {mimetype: metadata}
73 73 if raw:
74 74 # turn list of pngdata into list of { 'image/png': pngdata }
75 75 objs = [ {mimetype: obj} for obj in objs ]
76 76 display(*objs, raw=raw, metadata=metadata, include=[mimetype])
77 77
78 78 #-----------------------------------------------------------------------------
79 79 # Main functions
80 80 #-----------------------------------------------------------------------------
81 81
82 # use * to indicate transient is keyword-only
82 83 def publish_display_data(data, metadata=None, source=None, *, transient=None, **kwargs):
83 84 """Publish data and metadata to all frontends.
84 85
85 86 See the ``display_data`` message in the messaging documentation for
86 87 more details about this message type.
87 88
88 89 The following MIME types are currently implemented:
89 90
90 91 * text/plain
91 92 * text/html
92 93 * text/markdown
93 94 * text/latex
94 95 * application/json
95 96 * application/javascript
96 97 * image/png
97 98 * image/jpeg
98 99 * image/svg+xml
99 100
100 101 Parameters
101 102 ----------
102 103 data : dict
103 104 A dictionary having keys that are valid MIME types (like
104 105 'text/plain' or 'image/svg+xml') and values that are the data for
105 106 that MIME type. The data itself must be a JSON'able data
106 107 structure. Minimally all data should have the 'text/plain' data,
107 108 which can be displayed by all frontends. If more than the plain
108 109 text is given, it is up to the frontend to decide which
109 110 representation to use.
110 111 metadata : dict
111 112 A dictionary for metadata related to the data. This can contain
112 113 arbitrary key, value pairs that frontends can use to interpret
113 114 the data. mime-type keys matching those in data can be used
114 115 to specify metadata about particular representations.
115 116 source : str, deprecated
116 117 Unused.
117 transient : dict
118 transient : dict, keyword-only
118 119 A dictionary of transient data, such as display_id.
119 120 """
120 121 from IPython.core.interactiveshell import InteractiveShell
121 122
122 123 display_pub = InteractiveShell.instance().display_pub
123 124
124 125 # only pass transient if supplied,
125 126 # to avoid errors with older ipykernel.
126 127 # TODO: We could check for ipykernel version and provide a detailed upgrade message.
127 128 if transient:
128 129 kwargs['transient'] = transient
129 130
130 131 display_pub.publish(
131 132 data=data,
132 133 metadata=metadata,
133 134 **kwargs
134 135 )
135 136
136 137
137 138 def _new_id():
138 139 """Generate a new random text id with urandom"""
139 140 return b2a_hex(os.urandom(16)).decode('ascii')
140 141
141 142
142 143 def display(*objs, include=None, exclude=None, metadata=None, transient=None, display_id=None, **kwargs):
143 144 """Display a Python object in all frontends.
144 145
145 146 By default all representations will be computed and sent to the frontends.
146 147 Frontends can decide which representation is used and how.
147 148
148 149 Parameters
149 150 ----------
150 151 objs : tuple of objects
151 152 The Python objects to display.
152 153 raw : bool, optional
153 154 Are the objects to be displayed already mimetype-keyed dicts of raw display data,
154 155 or Python objects that need to be formatted before display? [default: False]
155 156 include : list or tuple, optional
156 157 A list of format type strings (MIME types) to include in the
157 158 format data dict. If this is set *only* the format types included
158 159 in this list will be computed.
159 160 exclude : list or tuple, optional
160 161 A list of format type strings (MIME types) to exclude in the format
161 162 data dict. If this is set all format types will be computed,
162 163 except for those included in this argument.
163 164 metadata : dict, optional
164 165 A dictionary of metadata to associate with the output.
165 166 mime-type keys in this dictionary will be associated with the individual
166 167 representation formats, if they exist.
167 168 transient : dict, optional
168 169 A dictionary of transient data to associate with the output.
169 170 Data in this dict should not be persisted to files (e.g. notebooks).
170 171 display_id : str, optional
171 172 Set an id for the display.
172 173 This id can be used for updating this display area later via update_display.
173 174 If given as True, generate a new display_id
174 175 kwargs: additional keyword-args, optional
175 176 Additional keyword-arguments are passed through to the display publisher.
176 177
177 178 Returns
178 179 -------
179 180
180 181 handle: DisplayHandle
181 182 Returns a handle on updatable displays, if display_id is given.
182 183 Returns None if no display_id is given (default).
183 184 """
184 185 raw = kwargs.pop('raw', False)
185 186 if transient is None:
186 187 transient = {}
187 188 if display_id:
188 189 if display_id == True:
189 190 display_id = _new_id()
190 191 transient['display_id'] = display_id
191 192 if kwargs.get('update') and 'display_id' not in transient:
192 193 raise TypeError('display_id required for update_display')
193 194 if transient:
194 195 kwargs['transient'] = transient
195 196
196 197 from IPython.core.interactiveshell import InteractiveShell
197 198
198 199 if not raw:
199 200 format = InteractiveShell.instance().display_formatter.format
200 201
201 202 for obj in objs:
202 203 if raw:
203 204 publish_display_data(data=obj, metadata=metadata, **kwargs)
204 205 else:
205 206 format_dict, md_dict = format(obj, include=include, exclude=exclude)
206 207 if not format_dict:
207 208 # nothing to display (e.g. _ipython_display_ took over)
208 209 continue
209 210 if metadata:
210 211 # kwarg-specified metadata gets precedence
211 212 _merge(md_dict, metadata)
212 213 publish_display_data(data=format_dict, metadata=md_dict, **kwargs)
213 214 if display_id:
214 215 return DisplayHandle(display_id)
215 216
216 217
218 # use * for keyword-only display_id arg
217 219 def update_display(obj, *, display_id, **kwargs):
218 220 """Update an existing display by id
219 221
220 222 Parameters
221 223 ----------
222 224
223 225 obj:
224 226 The object with which to update the display
225 227 display_id: keyword-only
226 228 The id of the display to update
227 229 """
228 230 kwargs['update'] = True
229 231 display(obj, display_id=display_id, **kwargs)
230 232
231 233
232 234 class DisplayHandle(object):
233 235 """A handle on an updatable display
234 236
235 237 Call .update(obj) to display a new object.
236 238
237 239 Call .display(obj) to add a new instance of this display,
238 240 and update existing instances.
239 241 """
240 242
241 243 def __init__(self, display_id=None):
242 244 if display_id is None:
243 245 display_id = _new_id()
244 246 self.display_id = display_id
245 247
246 248 def __repr__(self):
247 249 return "<%s display_id=%s>" % (self.__class__.__name__, self.display_id)
248 250
249 251 def display(self, obj, **kwargs):
250 252 """Make a new display with my id, updating existing instances.
251 253
252 254 Parameters
253 255 ----------
254 256
255 257 obj:
256 258 object to display
257 259 **kwargs:
258 260 additional keyword arguments passed to display
259 261 """
260 262 display(obj, display_id=self.display_id, **kwargs)
261 263
262 264 def update(self, obj, **kwargs):
263 265 """Update existing displays with my id
264 266
265 267 Parameters
266 268 ----------
267 269
268 270 obj:
269 271 object to display
270 272 **kwargs:
271 273 additional keyword arguments passed to update_display
272 274 """
273 275 update_display(obj, display_id=self.display_id, **kwargs)
274 276
275 277
276 278 def display_pretty(*objs, **kwargs):
277 279 """Display the pretty (default) representation of an object.
278 280
279 281 Parameters
280 282 ----------
281 283 objs : tuple of objects
282 284 The Python objects to display, or if raw=True raw text data to
283 285 display.
284 286 raw : bool
285 287 Are the data objects raw data or Python objects that need to be
286 288 formatted before display? [default: False]
287 289 metadata : dict (optional)
288 290 Metadata to be associated with the specific mimetype output.
289 291 """
290 292 _display_mimetype('text/plain', objs, **kwargs)
291 293
292 294
293 295 def display_html(*objs, **kwargs):
294 296 """Display the HTML representation of an object.
295 297
296 298 Note: If raw=False and the object does not have a HTML
297 299 representation, no HTML will be shown.
298 300
299 301 Parameters
300 302 ----------
301 303 objs : tuple of objects
302 304 The Python objects to display, or if raw=True raw HTML data to
303 305 display.
304 306 raw : bool
305 307 Are the data objects raw data or Python objects that need to be
306 308 formatted before display? [default: False]
307 309 metadata : dict (optional)
308 310 Metadata to be associated with the specific mimetype output.
309 311 """
310 312 _display_mimetype('text/html', objs, **kwargs)
311 313
312 314
313 315 def display_markdown(*objs, **kwargs):
314 316 """Displays the Markdown representation of an object.
315 317
316 318 Parameters
317 319 ----------
318 320 objs : tuple of objects
319 321 The Python objects to display, or if raw=True raw markdown data to
320 322 display.
321 323 raw : bool
322 324 Are the data objects raw data or Python objects that need to be
323 325 formatted before display? [default: False]
324 326 metadata : dict (optional)
325 327 Metadata to be associated with the specific mimetype output.
326 328 """
327 329
328 330 _display_mimetype('text/markdown', objs, **kwargs)
329 331
330 332
331 333 def display_svg(*objs, **kwargs):
332 334 """Display the SVG representation of an object.
333 335
334 336 Parameters
335 337 ----------
336 338 objs : tuple of objects
337 339 The Python objects to display, or if raw=True raw svg data to
338 340 display.
339 341 raw : bool
340 342 Are the data objects raw data or Python objects that need to be
341 343 formatted before display? [default: False]
342 344 metadata : dict (optional)
343 345 Metadata to be associated with the specific mimetype output.
344 346 """
345 347 _display_mimetype('image/svg+xml', objs, **kwargs)
346 348
347 349
348 350 def display_png(*objs, **kwargs):
349 351 """Display the PNG representation of an object.
350 352
351 353 Parameters
352 354 ----------
353 355 objs : tuple of objects
354 356 The Python objects to display, or if raw=True raw png data to
355 357 display.
356 358 raw : bool
357 359 Are the data objects raw data or Python objects that need to be
358 360 formatted before display? [default: False]
359 361 metadata : dict (optional)
360 362 Metadata to be associated with the specific mimetype output.
361 363 """
362 364 _display_mimetype('image/png', objs, **kwargs)
363 365
364 366
365 367 def display_jpeg(*objs, **kwargs):
366 368 """Display the JPEG representation of an object.
367 369
368 370 Parameters
369 371 ----------
370 372 objs : tuple of objects
371 373 The Python objects to display, or if raw=True raw JPEG data to
372 374 display.
373 375 raw : bool
374 376 Are the data objects raw data or Python objects that need to be
375 377 formatted before display? [default: False]
376 378 metadata : dict (optional)
377 379 Metadata to be associated with the specific mimetype output.
378 380 """
379 381 _display_mimetype('image/jpeg', objs, **kwargs)
380 382
381 383
382 384 def display_latex(*objs, **kwargs):
383 385 """Display the LaTeX representation of an object.
384 386
385 387 Parameters
386 388 ----------
387 389 objs : tuple of objects
388 390 The Python objects to display, or if raw=True raw latex data to
389 391 display.
390 392 raw : bool
391 393 Are the data objects raw data or Python objects that need to be
392 394 formatted before display? [default: False]
393 395 metadata : dict (optional)
394 396 Metadata to be associated with the specific mimetype output.
395 397 """
396 398 _display_mimetype('text/latex', objs, **kwargs)
397 399
398 400
399 401 def display_json(*objs, **kwargs):
400 402 """Display the JSON representation of an object.
401 403
402 404 Note that not many frontends support displaying JSON.
403 405
404 406 Parameters
405 407 ----------
406 408 objs : tuple of objects
407 409 The Python objects to display, or if raw=True raw json data to
408 410 display.
409 411 raw : bool
410 412 Are the data objects raw data or Python objects that need to be
411 413 formatted before display? [default: False]
412 414 metadata : dict (optional)
413 415 Metadata to be associated with the specific mimetype output.
414 416 """
415 417 _display_mimetype('application/json', objs, **kwargs)
416 418
417 419
418 420 def display_javascript(*objs, **kwargs):
419 421 """Display the Javascript representation of an object.
420 422
421 423 Parameters
422 424 ----------
423 425 objs : tuple of objects
424 426 The Python objects to display, or if raw=True raw javascript data to
425 427 display.
426 428 raw : bool
427 429 Are the data objects raw data or Python objects that need to be
428 430 formatted before display? [default: False]
429 431 metadata : dict (optional)
430 432 Metadata to be associated with the specific mimetype output.
431 433 """
432 434 _display_mimetype('application/javascript', objs, **kwargs)
433 435
434 436
435 437 def display_pdf(*objs, **kwargs):
436 438 """Display the PDF representation of an object.
437 439
438 440 Parameters
439 441 ----------
440 442 objs : tuple of objects
441 443 The Python objects to display, or if raw=True raw javascript data to
442 444 display.
443 445 raw : bool
444 446 Are the data objects raw data or Python objects that need to be
445 447 formatted before display? [default: False]
446 448 metadata : dict (optional)
447 449 Metadata to be associated with the specific mimetype output.
448 450 """
449 451 _display_mimetype('application/pdf', objs, **kwargs)
450 452
451 453
452 454 #-----------------------------------------------------------------------------
453 455 # Smart classes
454 456 #-----------------------------------------------------------------------------
455 457
456 458
457 459 class DisplayObject(object):
458 460 """An object that wraps data to be displayed."""
459 461
460 462 _read_flags = 'r'
461 463 _show_mem_addr = False
462 464
463 465 def __init__(self, data=None, url=None, filename=None):
464 466 """Create a display object given raw data.
465 467
466 468 When this object is returned by an expression or passed to the
467 469 display function, it will result in the data being displayed
468 470 in the frontend. The MIME type of the data should match the
469 471 subclasses used, so the Png subclass should be used for 'image/png'
470 472 data. If the data is a URL, the data will first be downloaded
471 473 and then displayed. If
472 474
473 475 Parameters
474 476 ----------
475 477 data : unicode, str or bytes
476 478 The raw data or a URL or file to load the data from
477 479 url : unicode
478 480 A URL to download the data from.
479 481 filename : unicode
480 482 Path to a local file to load the data from.
481 483 """
482 484 if data is not None and isinstance(data, string_types):
483 485 if data.startswith('http') and url is None:
484 486 url = data
485 487 filename = None
486 488 data = None
487 489 elif _safe_exists(data) and filename is None:
488 490 url = None
489 491 filename = data
490 492 data = None
491 493
492 494 self.data = data
493 495 self.url = url
494 496 self.filename = None if filename is None else unicode_type(filename)
495 497
496 498 self.reload()
497 499 self._check_data()
498 500
499 501 def __repr__(self):
500 502 if not self._show_mem_addr:
501 503 cls = self.__class__
502 504 r = "<%s.%s object>" % (cls.__module__, cls.__name__)
503 505 else:
504 506 r = super(DisplayObject, self).__repr__()
505 507 return r
506 508
507 509 def _check_data(self):
508 510 """Override in subclasses if there's something to check."""
509 511 pass
510 512
511 513 def reload(self):
512 514 """Reload the raw data from file or URL."""
513 515 if self.filename is not None:
514 516 with open(self.filename, self._read_flags) as f:
515 517 self.data = f.read()
516 518 elif self.url is not None:
517 519 try:
518 520 try:
519 521 from urllib.request import urlopen # Py3
520 522 except ImportError:
521 523 from urllib2 import urlopen
522 524 response = urlopen(self.url)
523 525 self.data = response.read()
524 526 # extract encoding from header, if there is one:
525 527 encoding = None
526 528 for sub in response.headers['content-type'].split(';'):
527 529 sub = sub.strip()
528 530 if sub.startswith('charset'):
529 531 encoding = sub.split('=')[-1].strip()
530 532 break
531 533 # decode data, if an encoding was specified
532 534 if encoding:
533 535 self.data = self.data.decode(encoding, 'replace')
534 536 except:
535 537 self.data = None
536 538
537 539 class TextDisplayObject(DisplayObject):
538 540 """Validate that display data is text"""
539 541 def _check_data(self):
540 542 if self.data is not None and not isinstance(self.data, string_types):
541 543 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
542 544
543 545 class Pretty(TextDisplayObject):
544 546
545 547 def _repr_pretty_(self):
546 548 return self.data
547 549
548 550
549 551 class HTML(TextDisplayObject):
550 552
551 553 def _repr_html_(self):
552 554 return self.data
553 555
554 556 def __html__(self):
555 557 """
556 558 This method exists to inform other HTML-using modules (e.g. Markupsafe,
557 559 htmltag, etc) that this object is HTML and does not need things like
558 560 special characters (<>&) escaped.
559 561 """
560 562 return self._repr_html_()
561 563
562 564
563 565 class Markdown(TextDisplayObject):
564 566
565 567 def _repr_markdown_(self):
566 568 return self.data
567 569
568 570
569 571 class Math(TextDisplayObject):
570 572
571 573 def _repr_latex_(self):
572 574 s = self.data.strip('$')
573 575 return "$$%s$$" % s
574 576
575 577
576 578 class Latex(TextDisplayObject):
577 579
578 580 def _repr_latex_(self):
579 581 return self.data
580 582
581 583
582 584 class SVG(DisplayObject):
583 585
584 586 # wrap data in a property, which extracts the <svg> tag, discarding
585 587 # document headers
586 588 _data = None
587 589
588 590 @property
589 591 def data(self):
590 592 return self._data
591 593
592 594 @data.setter
593 595 def data(self, svg):
594 596 if svg is None:
595 597 self._data = None
596 598 return
597 599 # parse into dom object
598 600 from xml.dom import minidom
599 601 svg = cast_bytes_py2(svg)
600 602 x = minidom.parseString(svg)
601 603 # get svg tag (should be 1)
602 604 found_svg = x.getElementsByTagName('svg')
603 605 if found_svg:
604 606 svg = found_svg[0].toxml()
605 607 else:
606 608 # fallback on the input, trust the user
607 609 # but this is probably an error.
608 610 pass
609 611 svg = cast_unicode(svg)
610 612 self._data = svg
611 613
612 614 def _repr_svg_(self):
613 615 return self.data
614 616
615 617
616 618 class JSON(DisplayObject):
617 619 """JSON expects a JSON-able dict or list
618 620
619 621 not an already-serialized JSON string.
620 622
621 623 Scalar types (None, number, string) are not allowed, only dict or list containers.
622 624 """
623 625 # wrap data in a property, which warns about passing already-serialized JSON
624 626 _data = None
625 627 def __init__(self, data=None, url=None, filename=None, expanded=False, metadata=None):
626 628 """Create a JSON display object given raw data.
627 629
628 630 Parameters
629 631 ----------
630 632 data : dict or list
631 633 JSON data to display. Not an already-serialized JSON string.
632 634 Scalar types (None, number, string) are not allowed, only dict
633 635 or list containers.
634 636 url : unicode
635 637 A URL to download the data from.
636 638 filename : unicode
637 639 Path to a local file to load the data from.
638 640 expanded : boolean
639 641 Metadata to control whether a JSON display component is expanded.
640 642 metadata: dict
641 643 Specify extra metadata to attach to the json display object.
642 644 """
643 645 self.expanded = expanded
644 646 self.metadata = metadata
645 647 super(JSON, self).__init__(data=data, url=url, filename=filename)
646 648
647 649 def _check_data(self):
648 650 if self.data is not None and not isinstance(self.data, (dict, list)):
649 651 raise TypeError("%s expects JSONable dict or list, not %r" % (self.__class__.__name__, self.data))
650 652
651 653 @property
652 654 def data(self):
653 655 return self._data
654 656
655 657 @data.setter
656 658 def data(self, data):
657 659 if isinstance(data, string_types):
658 660 warnings.warn("JSON expects JSONable dict or list, not JSON strings")
659 661 data = json.loads(data)
660 662 self._data = data
661 663
662 664 def _data_and_metadata(self):
663 665 md = {'expanded': self.expanded}
664 666 if self.metadata:
665 667 md.update(self.metadata)
666 668 return self.data, md
667 669
668 670 def _repr_json_(self):
669 671 return self._data_and_metadata()
670 672
671 673 css_t = """$("head").append($("<link/>").attr({
672 674 rel: "stylesheet",
673 675 type: "text/css",
674 676 href: "%s"
675 677 }));
676 678 """
677 679
678 680 lib_t1 = """$.getScript("%s", function () {
679 681 """
680 682 lib_t2 = """});
681 683 """
682 684
683 685 class Javascript(TextDisplayObject):
684 686
685 687 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
686 688 """Create a Javascript display object given raw data.
687 689
688 690 When this object is returned by an expression or passed to the
689 691 display function, it will result in the data being displayed
690 692 in the frontend. If the data is a URL, the data will first be
691 693 downloaded and then displayed.
692 694
693 695 In the Notebook, the containing element will be available as `element`,
694 696 and jQuery will be available. Content appended to `element` will be
695 697 visible in the output area.
696 698
697 699 Parameters
698 700 ----------
699 701 data : unicode, str or bytes
700 702 The Javascript source code or a URL to download it from.
701 703 url : unicode
702 704 A URL to download the data from.
703 705 filename : unicode
704 706 Path to a local file to load the data from.
705 707 lib : list or str
706 708 A sequence of Javascript library URLs to load asynchronously before
707 709 running the source code. The full URLs of the libraries should
708 710 be given. A single Javascript library URL can also be given as a
709 711 string.
710 712 css: : list or str
711 713 A sequence of css files to load before running the source code.
712 714 The full URLs of the css files should be given. A single css URL
713 715 can also be given as a string.
714 716 """
715 717 if isinstance(lib, string_types):
716 718 lib = [lib]
717 719 elif lib is None:
718 720 lib = []
719 721 if isinstance(css, string_types):
720 722 css = [css]
721 723 elif css is None:
722 724 css = []
723 725 if not isinstance(lib, (list,tuple)):
724 726 raise TypeError('expected sequence, got: %r' % lib)
725 727 if not isinstance(css, (list,tuple)):
726 728 raise TypeError('expected sequence, got: %r' % css)
727 729 self.lib = lib
728 730 self.css = css
729 731 super(Javascript, self).__init__(data=data, url=url, filename=filename)
730 732
731 733 def _repr_javascript_(self):
732 734 r = ''
733 735 for c in self.css:
734 736 r += css_t % c
735 737 for l in self.lib:
736 738 r += lib_t1 % l
737 739 r += self.data
738 740 r += lib_t2*len(self.lib)
739 741 return r
740 742
741 743 # constants for identifying png/jpeg data
742 744 _PNG = b'\x89PNG\r\n\x1a\n'
743 745 _JPEG = b'\xff\xd8'
744 746
745 747 def _pngxy(data):
746 748 """read the (width, height) from a PNG header"""
747 749 ihdr = data.index(b'IHDR')
748 750 # next 8 bytes are width/height
749 751 w4h4 = data[ihdr+4:ihdr+12]
750 752 return struct.unpack('>ii', w4h4)
751 753
752 754 def _jpegxy(data):
753 755 """read the (width, height) from a JPEG header"""
754 756 # adapted from http://www.64lines.com/jpeg-width-height
755 757
756 758 idx = 4
757 759 while True:
758 760 block_size = struct.unpack('>H', data[idx:idx+2])[0]
759 761 idx = idx + block_size
760 762 if data[idx:idx+2] == b'\xFF\xC0':
761 763 # found Start of Frame
762 764 iSOF = idx
763 765 break
764 766 else:
765 767 # read another block
766 768 idx += 2
767 769
768 770 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
769 771 return w, h
770 772
771 773 class Image(DisplayObject):
772 774
773 775 _read_flags = 'rb'
774 776 _FMT_JPEG = u'jpeg'
775 777 _FMT_PNG = u'png'
776 778 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG]
777 779
778 780 def __init__(self, data=None, url=None, filename=None, format=None,
779 781 embed=None, width=None, height=None, retina=False,
780 782 unconfined=False, metadata=None):
781 783 """Create a PNG/JPEG image object given raw data.
782 784
783 785 When this object is returned by an input cell or passed to the
784 786 display function, it will result in the image being displayed
785 787 in the frontend.
786 788
787 789 Parameters
788 790 ----------
789 791 data : unicode, str or bytes
790 792 The raw image data or a URL or filename to load the data from.
791 793 This always results in embedded image data.
792 794 url : unicode
793 795 A URL to download the data from. If you specify `url=`,
794 796 the image data will not be embedded unless you also specify `embed=True`.
795 797 filename : unicode
796 798 Path to a local file to load the data from.
797 799 Images from a file are always embedded.
798 800 format : unicode
799 801 The format of the image data (png/jpeg/jpg). If a filename or URL is given
800 802 for format will be inferred from the filename extension.
801 803 embed : bool
802 804 Should the image data be embedded using a data URI (True) or be
803 805 loaded using an <img> tag. Set this to True if you want the image
804 806 to be viewable later with no internet connection in the notebook.
805 807
806 808 Default is `True`, unless the keyword argument `url` is set, then
807 809 default value is `False`.
808 810
809 811 Note that QtConsole is not able to display images if `embed` is set to `False`
810 812 width : int
811 813 Width in pixels to which to constrain the image in html
812 814 height : int
813 815 Height in pixels to which to constrain the image in html
814 816 retina : bool
815 817 Automatically set the width and height to half of the measured
816 818 width and height.
817 819 This only works for embedded images because it reads the width/height
818 820 from image data.
819 821 For non-embedded images, you can just set the desired display width
820 822 and height directly.
821 823 unconfined: bool
822 824 Set unconfined=True to disable max-width confinement of the image.
823 825 metadata: dict
824 826 Specify extra metadata to attach to the image.
825 827
826 828 Examples
827 829 --------
828 830 # embedded image data, works in qtconsole and notebook
829 831 # when passed positionally, the first arg can be any of raw image data,
830 832 # a URL, or a filename from which to load image data.
831 833 # The result is always embedding image data for inline images.
832 834 Image('http://www.google.fr/images/srpr/logo3w.png')
833 835 Image('/path/to/image.jpg')
834 836 Image(b'RAW_PNG_DATA...')
835 837
836 838 # Specifying Image(url=...) does not embed the image data,
837 839 # it only generates `<img>` tag with a link to the source.
838 840 # This will not work in the qtconsole or offline.
839 841 Image(url='http://www.google.fr/images/srpr/logo3w.png')
840 842
841 843 """
842 844 if filename is not None:
843 845 ext = self._find_ext(filename)
844 846 elif url is not None:
845 847 ext = self._find_ext(url)
846 848 elif data is None:
847 849 raise ValueError("No image data found. Expecting filename, url, or data.")
848 850 elif isinstance(data, string_types) and (
849 851 data.startswith('http') or _safe_exists(data)
850 852 ):
851 853 ext = self._find_ext(data)
852 854 else:
853 855 ext = None
854 856
855 857 if format is None:
856 858 if ext is not None:
857 859 if ext == u'jpg' or ext == u'jpeg':
858 860 format = self._FMT_JPEG
859 861 if ext == u'png':
860 862 format = self._FMT_PNG
861 863 else:
862 864 format = ext.lower()
863 865 elif isinstance(data, bytes):
864 866 # infer image type from image data header,
865 867 # only if format has not been specified.
866 868 if data[:2] == _JPEG:
867 869 format = self._FMT_JPEG
868 870
869 871 # failed to detect format, default png
870 872 if format is None:
871 873 format = 'png'
872 874
873 875 if format.lower() == 'jpg':
874 876 # jpg->jpeg
875 877 format = self._FMT_JPEG
876 878
877 879 self.format = unicode_type(format).lower()
878 880 self.embed = embed if embed is not None else (url is None)
879 881
880 882 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
881 883 raise ValueError("Cannot embed the '%s' image format" % (self.format))
882 884 self.width = width
883 885 self.height = height
884 886 self.retina = retina
885 887 self.unconfined = unconfined
886 888 self.metadata = metadata
887 889 super(Image, self).__init__(data=data, url=url, filename=filename)
888 890
889 891 if retina:
890 892 self._retina_shape()
891 893
892 894 def _retina_shape(self):
893 895 """load pixel-doubled width and height from image data"""
894 896 if not self.embed:
895 897 return
896 898 if self.format == 'png':
897 899 w, h = _pngxy(self.data)
898 900 elif self.format == 'jpeg':
899 901 w, h = _jpegxy(self.data)
900 902 else:
901 903 # retina only supports png
902 904 return
903 905 self.width = w // 2
904 906 self.height = h // 2
905 907
906 908 def reload(self):
907 909 """Reload the raw data from file or URL."""
908 910 if self.embed:
909 911 super(Image,self).reload()
910 912 if self.retina:
911 913 self._retina_shape()
912 914
913 915 def _repr_html_(self):
914 916 if not self.embed:
915 917 width = height = klass = ''
916 918 if self.width:
917 919 width = ' width="%d"' % self.width
918 920 if self.height:
919 921 height = ' height="%d"' % self.height
920 922 if self.unconfined:
921 923 klass = ' class="unconfined"'
922 924 return u'<img src="{url}"{width}{height}{klass}/>'.format(
923 925 url=self.url,
924 926 width=width,
925 927 height=height,
926 928 klass=klass,
927 929 )
928 930
929 931 def _data_and_metadata(self):
930 932 """shortcut for returning metadata with shape information, if defined"""
931 933 md = {}
932 934 if self.width:
933 935 md['width'] = self.width
934 936 if self.height:
935 937 md['height'] = self.height
936 938 if self.unconfined:
937 939 md['unconfined'] = self.unconfined
938 940 if self.metadata:
939 941 md.update(self.metadata)
940 942 if md:
941 943 return self.data, md
942 944 else:
943 945 return self.data
944 946
945 947 def _repr_png_(self):
946 948 if self.embed and self.format == u'png':
947 949 return self._data_and_metadata()
948 950
949 951 def _repr_jpeg_(self):
950 952 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
951 953 return self._data_and_metadata()
952 954
953 955 def _find_ext(self, s):
954 956 return unicode_type(s.split('.')[-1].lower())
955 957
956 958 class Video(DisplayObject):
957 959
958 960 def __init__(self, data=None, url=None, filename=None, embed=False, mimetype=None):
959 961 """Create a video object given raw data or an URL.
960 962
961 963 When this object is returned by an input cell or passed to the
962 964 display function, it will result in the video being displayed
963 965 in the frontend.
964 966
965 967 Parameters
966 968 ----------
967 969 data : unicode, str or bytes
968 970 The raw video data or a URL or filename to load the data from.
969 971 Raw data will require passing `embed=True`.
970 972 url : unicode
971 973 A URL for the video. If you specify `url=`,
972 974 the image data will not be embedded.
973 975 filename : unicode
974 976 Path to a local file containing the video.
975 977 Will be interpreted as a local URL unless `embed=True`.
976 978 embed : bool
977 979 Should the video be embedded using a data URI (True) or be
978 980 loaded using a <video> tag (False).
979 981
980 982 Since videos are large, embedding them should be avoided, if possible.
981 983 You must confirm embedding as your intention by passing `embed=True`.
982 984
983 985 Local files can be displayed with URLs without embedding the content, via::
984 986
985 987 Video('./video.mp4')
986 988
987 989 mimetype: unicode
988 990 Specify the mimetype for embedded videos.
989 991 Default will be guessed from file extension, if available.
990 992
991 993 Examples
992 994 --------
993 995
994 996 Video('https://archive.org/download/Sita_Sings_the_Blues/Sita_Sings_the_Blues_small.mp4')
995 997 Video('path/to/video.mp4')
996 998 Video('path/to/video.mp4', embed=True)
997 999 Video(b'raw-videodata', embed=True)
998 1000 """
999 1001 if url is None and isinstance(data, string_types) and data.startswith(('http:', 'https:')):
1000 1002 url = data
1001 1003 data = None
1002 1004 elif os.path.exists(data):
1003 1005 filename = data
1004 1006 data = None
1005 1007
1006 1008 if data and not embed:
1007 1009 msg = ''.join([
1008 1010 "To embed videos, you must pass embed=True ",
1009 1011 "(this may make your notebook files huge)\n",
1010 1012 "Consider passing Video(url='...')",
1011 1013 ])
1012 1014 raise ValueError(msg)
1013 1015
1014 1016 self.mimetype = mimetype
1015 1017 self.embed = embed
1016 1018 super(Video, self).__init__(data=data, url=url, filename=filename)
1017 1019
1018 1020 def _repr_html_(self):
1019 1021 # External URLs and potentially local files are not embedded into the
1020 1022 # notebook output.
1021 1023 if not self.embed:
1022 1024 url = self.url if self.url is not None else self.filename
1023 1025 output = """<video src="{0}" controls>
1024 1026 Your browser does not support the <code>video</code> element.
1025 1027 </video>""".format(url)
1026 1028 return output
1027 1029
1028 1030 # Embedded videos are base64-encoded.
1029 1031 mimetype = self.mimetype
1030 1032 if self.filename is not None:
1031 1033 if not mimetype:
1032 1034 mimetype, _ = mimetypes.guess_type(self.filename)
1033 1035
1034 1036 with open(self.filename, 'rb') as f:
1035 1037 video = f.read()
1036 1038 else:
1037 1039 video = self.data
1038 1040 if isinstance(video, unicode_type):
1039 1041 # unicode input is already b64-encoded
1040 1042 b64_video = video
1041 1043 else:
1042 1044 b64_video = base64_encode(video).decode('ascii').rstrip()
1043 1045
1044 1046 output = """<video controls>
1045 1047 <source src="data:{0};base64,{1}" type="{0}">
1046 1048 Your browser does not support the video tag.
1047 1049 </video>""".format(mimetype, b64_video)
1048 1050 return output
1049 1051
1050 1052 def reload(self):
1051 1053 # TODO
1052 1054 pass
1053 1055
1054 1056 def _repr_png_(self):
1055 1057 # TODO
1056 1058 pass
1057 1059 def _repr_jpeg_(self):
1058 1060 # TODO
1059 1061 pass
1060 1062
1061 1063 def clear_output(wait=False):
1062 1064 """Clear the output of the current cell receiving output.
1063 1065
1064 1066 Parameters
1065 1067 ----------
1066 1068 wait : bool [default: false]
1067 1069 Wait to clear the output until new output is available to replace it."""
1068 1070 from IPython.core.interactiveshell import InteractiveShell
1069 1071 if InteractiveShell.initialized():
1070 1072 InteractiveShell.instance().display_pub.clear_output(wait)
1071 1073 else:
1072 1074 print('\033[2K\r', end='')
1073 1075 sys.stdout.flush()
1074 1076 print('\033[2K\r', end='')
1075 1077 sys.stderr.flush()
1076 1078
1077 1079
1078 1080 @skip_doctest
1079 1081 def set_matplotlib_formats(*formats, **kwargs):
1080 1082 """Select figure formats for the inline backend. Optionally pass quality for JPEG.
1081 1083
1082 1084 For example, this enables PNG and JPEG output with a JPEG quality of 90%::
1083 1085
1084 1086 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90)
1085 1087
1086 1088 To set this in your config files use the following::
1087 1089
1088 1090 c.InlineBackend.figure_formats = {'png', 'jpeg'}
1089 1091 c.InlineBackend.print_figure_kwargs.update({'quality' : 90})
1090 1092
1091 1093 Parameters
1092 1094 ----------
1093 1095 *formats : strs
1094 1096 One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
1095 1097 **kwargs :
1096 1098 Keyword args will be relayed to ``figure.canvas.print_figure``.
1097 1099 """
1098 1100 from IPython.core.interactiveshell import InteractiveShell
1099 1101 from IPython.core.pylabtools import select_figure_formats
1100 1102 # build kwargs, starting with InlineBackend config
1101 1103 kw = {}
1102 1104 from ipykernel.pylab.config import InlineBackend
1103 1105 cfg = InlineBackend.instance()
1104 1106 kw.update(cfg.print_figure_kwargs)
1105 1107 kw.update(**kwargs)
1106 1108 shell = InteractiveShell.instance()
1107 1109 select_figure_formats(shell, formats, **kw)
1108 1110
1109 1111 @skip_doctest
1110 1112 def set_matplotlib_close(close=True):
1111 1113 """Set whether the inline backend closes all figures automatically or not.
1112 1114
1113 1115 By default, the inline backend used in the IPython Notebook will close all
1114 1116 matplotlib figures automatically after each cell is run. This means that
1115 1117 plots in different cells won't interfere. Sometimes, you may want to make
1116 1118 a plot in one cell and then refine it in later cells. This can be accomplished
1117 1119 by::
1118 1120
1119 1121 In [1]: set_matplotlib_close(False)
1120 1122
1121 1123 To set this in your config files use the following::
1122 1124
1123 1125 c.InlineBackend.close_figures = False
1124 1126
1125 1127 Parameters
1126 1128 ----------
1127 1129 close : bool
1128 1130 Should all matplotlib figures be automatically closed after each cell is
1129 1131 run?
1130 1132 """
1131 1133 from ipykernel.pylab.config import InlineBackend
1132 1134 cfg = InlineBackend.instance()
1133 1135 cfg.close_figures = close
@@ -1,116 +1,124 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
15 15 # Copyright (c) IPython Development Team.
16 16 # Distributed under the terms of the Modified BSD License.
17 17
18 18
19 19 import sys
20 20
21 21 from traitlets.config.configurable import Configurable
22 22 from traitlets import List
23 23
24 24 # This used to be defined here - it is imported for backwards compatibility
25 25 from .display import publish_display_data
26 26
27 27 #-----------------------------------------------------------------------------
28 28 # Main payload class
29 29 #-----------------------------------------------------------------------------
30 30
31 31 class DisplayPublisher(Configurable):
32 32 """A traited class that publishes display data to frontends.
33 33
34 34 Instances of this class are created by the main IPython object and should
35 35 be accessed there.
36 36 """
37 37
38 38 def _validate_data(self, data, metadata=None):
39 39 """Validate the display data.
40 40
41 41 Parameters
42 42 ----------
43 43 data : dict
44 44 The formata data dictionary.
45 45 metadata : dict
46 46 Any metadata for the data.
47 47 """
48 48
49 49 if not isinstance(data, dict):
50 50 raise TypeError('data must be a dict, got: %r' % data)
51 51 if metadata is not None:
52 52 if not isinstance(metadata, dict):
53 53 raise TypeError('metadata must be a dict, got: %r' % data)
54 54
55 def publish(self, data, metadata=None, source=None, **kwargs):
55 # use * to indicate transient, update are keyword-only
56 def publish(self, data, metadata=None, source=None, *, transient=None, update=False, **kwargs):
56 57 """Publish data and metadata to all frontends.
57 58
58 59 See the ``display_data`` message in the messaging documentation for
59 60 more details about this message type.
60 61
61 62 The following MIME types are currently implemented:
62 63
63 64 * text/plain
64 65 * text/html
65 66 * text/markdown
66 67 * text/latex
67 68 * application/json
68 69 * application/javascript
69 70 * image/png
70 71 * image/jpeg
71 72 * image/svg+xml
72 73
73 74 Parameters
74 75 ----------
75 76 data : dict
76 77 A dictionary having keys that are valid MIME types (like
77 78 'text/plain' or 'image/svg+xml') and values that are the data for
78 79 that MIME type. The data itself must be a JSON'able data
79 80 structure. Minimally all data should have the 'text/plain' data,
80 81 which can be displayed by all frontends. If more than the plain
81 82 text is given, it is up to the frontend to decide which
82 83 representation to use.
83 84 metadata : dict
84 85 A dictionary for metadata related to the data. This can contain
85 86 arbitrary key, value pairs that frontends can use to interpret
86 87 the data. Metadata specific to each mime-type can be specified
87 88 in the metadata dict with the same mime-type keys as
88 89 the data itself.
89 90 source : str, deprecated
90 91 Unused.
92 transient: dict, keyword-only
93 A dictionary for transient data.
94 Data in this dictionary should not be persisted as part of saving this output.
95 Examples include 'display_id'.
96 update: bool, keyword-only, default: False
97 If True, only update existing outputs with the same display_id,
98 rather than creating a new output.
91 99 """
92 100
93 101 # The default is to simply write the plain text data using sys.stdout.
94 102 if 'text/plain' in data:
95 103 print(data['text/plain'])
96 104
97 105 def clear_output(self, wait=False):
98 106 """Clear the output of the cell receiving output."""
99 107 print('\033[2K\r', end='')
100 108 sys.stdout.flush()
101 109 print('\033[2K\r', end='')
102 110 sys.stderr.flush()
103 111
104 112
105 113 class CapturingDisplayPublisher(DisplayPublisher):
106 114 """A DisplayPublisher that stores"""
107 115 outputs = List()
108 116
109 117 def publish(self, data, metadata=None, source=None):
110 118 self.outputs.append((data, metadata))
111 119
112 120 def clear_output(self, wait=False):
113 121 super(CapturingDisplayPublisher, self).clear_output(wait)
114 122
115 123 # empty the list, *do not* reassign a new list
116 124 del self.outputs[:]
General Comments 0
You need to be logged in to leave comments. Login now