##// END OF EJS Templates
CLN: core/display: reformat per darker
Wes Turner -
Show More
@@ -1,1347 +1,1349
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 from binascii import b2a_base64, hexlify
9 9 import html
10 10 import json
11 11 import mimetypes
12 12 import os
13 13 import struct
14 14 import warnings
15 15 from copy import deepcopy
16 16 from os.path import splitext
17 17 from pathlib import Path, PurePath
18 18
19 19 from IPython.utils.py3compat import cast_unicode
20 20 from IPython.testing.skipdoctest import skip_doctest
21 21 from . import display_functions
22 22
23 23
24 24 __all__ = ['display_pretty', 'display_html', 'display_markdown',
25 25 'display_svg', 'display_png', 'display_jpeg', 'display_webp',
26 26 'display_latex', 'display_json',
27 27 'display_javascript', 'display_pdf', 'DisplayObject', 'TextDisplayObject',
28 28 'Pretty', 'HTML', 'Markdown', 'Math', 'Latex', 'SVG', 'ProgressBar', 'JSON',
29 29 'GeoJSON', 'Javascript', 'Image', 'set_matplotlib_formats',
30 30 'set_matplotlib_close',
31 31 'Video']
32 32
33 33 _deprecated_names = ["display", "clear_output", "publish_display_data", "update_display", "DisplayHandle"]
34 34
35 35 __all__ = __all__ + _deprecated_names
36 36
37 37
38 38 # ----- warn to import from IPython.display -----
39 39
40 40 from warnings import warn
41 41
42 42
43 43 def __getattr__(name):
44 44 if name in _deprecated_names:
45 45 warn(
46 46 f"Importing {name} from IPython.core.display is deprecated since IPython 7.14, please import from IPython.display",
47 47 DeprecationWarning,
48 48 stacklevel=2,
49 49 )
50 50 return getattr(display_functions, name)
51 51
52 52 if name in globals().keys():
53 53 return globals()[name]
54 54 else:
55 55 raise AttributeError(f"module {__name__} has no attribute {name}")
56 56
57 57
58 58 #-----------------------------------------------------------------------------
59 59 # utility functions
60 60 #-----------------------------------------------------------------------------
61 61
62 62 def _safe_exists(path):
63 63 """Check path, but don't let exceptions raise"""
64 64 try:
65 65 return os.path.exists(path)
66 66 except Exception:
67 67 return False
68 68
69 69
70 70 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
71 71 """internal implementation of all display_foo methods
72 72
73 73 Parameters
74 74 ----------
75 75 mimetype : str
76 76 The mimetype to be published (e.g. 'image/png')
77 77 *objs : object
78 78 The Python objects to display, or if raw=True raw text data to
79 79 display.
80 80 raw : bool
81 81 Are the data objects raw data or Python objects that need to be
82 82 formatted before display? [default: False]
83 83 metadata : dict (optional)
84 84 Metadata to be associated with the specific mimetype output.
85 85 """
86 86 if metadata:
87 87 metadata = {mimetype: metadata}
88 88 if raw:
89 89 # turn list of pngdata into list of { 'image/png': pngdata }
90 90 objs = [ {mimetype: obj} for obj in objs ]
91 91 display_functions.display(*objs, raw=raw, metadata=metadata, include=[mimetype])
92 92
93 93 #-----------------------------------------------------------------------------
94 94 # Main functions
95 95 #-----------------------------------------------------------------------------
96 96
97 97
98 98 def display_pretty(*objs, **kwargs):
99 99 """Display the pretty (default) representation of an object.
100 100
101 101 Parameters
102 102 ----------
103 103 *objs : object
104 104 The Python objects to display, or if raw=True raw text data to
105 105 display.
106 106 raw : bool
107 107 Are the data objects raw data or Python objects that need to be
108 108 formatted before display? [default: False]
109 109 metadata : dict (optional)
110 110 Metadata to be associated with the specific mimetype output.
111 111 """
112 112 _display_mimetype('text/plain', objs, **kwargs)
113 113
114 114
115 115 def display_html(*objs, **kwargs):
116 116 """Display the HTML representation of an object.
117 117
118 118 Note: If raw=False and the object does not have a HTML
119 119 representation, no HTML will be shown.
120 120
121 121 Parameters
122 122 ----------
123 123 *objs : object
124 124 The Python objects to display, or if raw=True raw HTML data to
125 125 display.
126 126 raw : bool
127 127 Are the data objects raw data or Python objects that need to be
128 128 formatted before display? [default: False]
129 129 metadata : dict (optional)
130 130 Metadata to be associated with the specific mimetype output.
131 131 """
132 132 _display_mimetype('text/html', objs, **kwargs)
133 133
134 134
135 135 def display_markdown(*objs, **kwargs):
136 136 """Displays the Markdown representation of an object.
137 137
138 138 Parameters
139 139 ----------
140 140 *objs : object
141 141 The Python objects to display, or if raw=True raw markdown data to
142 142 display.
143 143 raw : bool
144 144 Are the data objects raw data or Python objects that need to be
145 145 formatted before display? [default: False]
146 146 metadata : dict (optional)
147 147 Metadata to be associated with the specific mimetype output.
148 148 """
149 149
150 150 _display_mimetype('text/markdown', objs, **kwargs)
151 151
152 152
153 153 def display_svg(*objs, **kwargs):
154 154 """Display the SVG representation of an object.
155 155
156 156 Parameters
157 157 ----------
158 158 *objs : object
159 159 The Python objects to display, or if raw=True raw svg data to
160 160 display.
161 161 raw : bool
162 162 Are the data objects raw data or Python objects that need to be
163 163 formatted before display? [default: False]
164 164 metadata : dict (optional)
165 165 Metadata to be associated with the specific mimetype output.
166 166 """
167 167 _display_mimetype('image/svg+xml', objs, **kwargs)
168 168
169 169
170 170 def display_png(*objs, **kwargs):
171 171 """Display the PNG representation of an object.
172 172
173 173 Parameters
174 174 ----------
175 175 *objs : object
176 176 The Python objects to display, or if raw=True raw png data to
177 177 display.
178 178 raw : bool
179 179 Are the data objects raw data or Python objects that need to be
180 180 formatted before display? [default: False]
181 181 metadata : dict (optional)
182 182 Metadata to be associated with the specific mimetype output.
183 183 """
184 184 _display_mimetype('image/png', objs, **kwargs)
185 185
186 186
187 187 def display_jpeg(*objs, **kwargs):
188 188 """Display the JPEG representation of an object.
189 189
190 190 Parameters
191 191 ----------
192 192 *objs : object
193 193 The Python objects to display, or if raw=True raw JPEG data to
194 194 display.
195 195 raw : bool
196 196 Are the data objects raw data or Python objects that need to be
197 197 formatted before display? [default: False]
198 198 metadata : dict (optional)
199 199 Metadata to be associated with the specific mimetype output.
200 200 """
201 201 _display_mimetype('image/jpeg', objs, **kwargs)
202 202
203 203
204 204 def display_webp(*objs, **kwargs):
205 205 """Display the WEBP representation of an object.
206 206
207 207 Parameters
208 208 ----------
209 209 *objs : object
210 210 The Python objects to display, or if raw=True raw JPEG data to
211 211 display.
212 212 raw : bool
213 213 Are the data objects raw data or Python objects that need to be
214 214 formatted before display? [default: False]
215 215 metadata : dict (optional)
216 216 Metadata to be associated with the specific mimetype output.
217 217 """
218 218 _display_mimetype('image/webp', objs, **kwargs)
219 219
220 220
221 221 def display_latex(*objs, **kwargs):
222 222 """Display the LaTeX representation of an object.
223 223
224 224 Parameters
225 225 ----------
226 226 *objs : object
227 227 The Python objects to display, or if raw=True raw latex data to
228 228 display.
229 229 raw : bool
230 230 Are the data objects raw data or Python objects that need to be
231 231 formatted before display? [default: False]
232 232 metadata : dict (optional)
233 233 Metadata to be associated with the specific mimetype output.
234 234 """
235 235 _display_mimetype('text/latex', objs, **kwargs)
236 236
237 237
238 238 def display_json(*objs, **kwargs):
239 239 """Display the JSON representation of an object.
240 240
241 241 Note that not many frontends support displaying JSON.
242 242
243 243 Parameters
244 244 ----------
245 245 *objs : object
246 246 The Python objects to display, or if raw=True raw json data to
247 247 display.
248 248 raw : bool
249 249 Are the data objects raw data or Python objects that need to be
250 250 formatted before display? [default: False]
251 251 metadata : dict (optional)
252 252 Metadata to be associated with the specific mimetype output.
253 253 """
254 254 _display_mimetype('application/json', objs, **kwargs)
255 255
256 256
257 257 def display_javascript(*objs, **kwargs):
258 258 """Display the Javascript representation of an object.
259 259
260 260 Parameters
261 261 ----------
262 262 *objs : object
263 263 The Python objects to display, or if raw=True raw javascript data to
264 264 display.
265 265 raw : bool
266 266 Are the data objects raw data or Python objects that need to be
267 267 formatted before display? [default: False]
268 268 metadata : dict (optional)
269 269 Metadata to be associated with the specific mimetype output.
270 270 """
271 271 _display_mimetype('application/javascript', objs, **kwargs)
272 272
273 273
274 274 def display_pdf(*objs, **kwargs):
275 275 """Display the PDF representation of an object.
276 276
277 277 Parameters
278 278 ----------
279 279 *objs : object
280 280 The Python objects to display, or if raw=True raw javascript data to
281 281 display.
282 282 raw : bool
283 283 Are the data objects raw data or Python objects that need to be
284 284 formatted before display? [default: False]
285 285 metadata : dict (optional)
286 286 Metadata to be associated with the specific mimetype output.
287 287 """
288 288 _display_mimetype('application/pdf', objs, **kwargs)
289 289
290 290
291 291 #-----------------------------------------------------------------------------
292 292 # Smart classes
293 293 #-----------------------------------------------------------------------------
294 294
295 295
296 296 class DisplayObject(object):
297 297 """An object that wraps data to be displayed."""
298 298
299 299 _read_flags = 'r'
300 300 _show_mem_addr = False
301 301 metadata = None
302 302
303 303 def __init__(self, data=None, url=None, filename=None, metadata=None):
304 304 """Create a display object given raw data.
305 305
306 306 When this object is returned by an expression or passed to the
307 307 display function, it will result in the data being displayed
308 308 in the frontend. The MIME type of the data should match the
309 309 subclasses used, so the Png subclass should be used for 'image/png'
310 310 data. If the data is a URL, the data will first be downloaded
311 311 and then displayed.
312 312
313 313 Parameters
314 314 ----------
315 315 data : unicode, str or bytes
316 316 The raw data or a URL or file to load the data from
317 317 url : unicode
318 318 A URL to download the data from.
319 319 filename : unicode
320 320 Path to a local file to load the data from.
321 321 metadata : dict
322 322 Dict of metadata associated to be the object when displayed
323 323 """
324 324 if isinstance(data, (Path, PurePath)):
325 325 data = str(data)
326 326
327 327 if data is not None and isinstance(data, str):
328 328 if data.startswith('http') and url is None:
329 329 url = data
330 330 filename = None
331 331 data = None
332 332 elif _safe_exists(data) and filename is None:
333 333 url = None
334 334 filename = data
335 335 data = None
336 336
337 337 self.url = url
338 338 self.filename = filename
339 339 # because of @data.setter methods in
340 340 # subclasses ensure url and filename are set
341 341 # before assigning to self.data
342 342 self.data = data
343 343
344 344 if metadata is not None:
345 345 self.metadata = metadata
346 346 elif self.metadata is None:
347 347 self.metadata = {}
348 348
349 349 self.reload()
350 350 self._check_data()
351 351
352 352 def __repr__(self):
353 353 if not self._show_mem_addr:
354 354 cls = self.__class__
355 355 r = "<%s.%s object>" % (cls.__module__, cls.__name__)
356 356 else:
357 357 r = super(DisplayObject, self).__repr__()
358 358 return r
359 359
360 360 def _check_data(self):
361 361 """Override in subclasses if there's something to check."""
362 362 pass
363 363
364 364 def _data_and_metadata(self):
365 365 """shortcut for returning metadata with shape information, if defined"""
366 366 if self.metadata:
367 367 return self.data, deepcopy(self.metadata)
368 368 else:
369 369 return self.data
370 370
371 371 def reload(self):
372 372 """Reload the raw data from file or URL."""
373 373 if self.filename is not None:
374 374 encoding = None if "b" in self._read_flags else "utf-8"
375 375 with open(self.filename, self._read_flags, encoding=encoding) as f:
376 376 self.data = f.read()
377 377 elif self.url is not None:
378 378 # Deferred import
379 379 from urllib.request import urlopen
380 380 response = urlopen(self.url)
381 381 data = response.read()
382 382 # extract encoding from header, if there is one:
383 383 encoding = None
384 384 if 'content-type' in response.headers:
385 385 for sub in response.headers['content-type'].split(';'):
386 386 sub = sub.strip()
387 387 if sub.startswith('charset'):
388 388 encoding = sub.split('=')[-1].strip()
389 389 break
390 390 if 'content-encoding' in response.headers:
391 391 # TODO: do deflate?
392 392 if 'gzip' in response.headers['content-encoding']:
393 393 import gzip
394 394 from io import BytesIO
395 395
396 396 # assume utf-8 if encoding is not specified
397 397 with gzip.open(
398 398 BytesIO(data), "rt", encoding=encoding or "utf-8"
399 399 ) as fp:
400 400 encoding = None
401 401 data = fp.read()
402 402
403 403 # decode data, if an encoding was specified
404 404 # We only touch self.data once since
405 405 # subclasses such as SVG have @data.setter methods
406 406 # that transform self.data into ... well svg.
407 407 if encoding:
408 408 self.data = data.decode(encoding, 'replace')
409 409 else:
410 410 self.data = data
411 411
412 412
413 413 class TextDisplayObject(DisplayObject):
414 414 """Create a text display object given raw data.
415 415
416 416 Parameters
417 417 ----------
418 418 data : str or unicode
419 419 The raw data or a URL or file to load the data from.
420 420 url : unicode
421 421 A URL to download the data from.
422 422 filename : unicode
423 423 Path to a local file to load the data from.
424 424 metadata : dict
425 425 Dict of metadata associated to be the object when displayed
426 426 """
427 427 def _check_data(self):
428 428 if self.data is not None and not isinstance(self.data, str):
429 429 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
430 430
431 431 class Pretty(TextDisplayObject):
432 432
433 433 def _repr_pretty_(self, pp, cycle):
434 434 return pp.text(self.data)
435 435
436 436
437 437 class HTML(TextDisplayObject):
438 438
439 439 def __init__(self, data=None, url=None, filename=None, metadata=None):
440 440 def warn():
441 441 if not data:
442 442 return False
443 443
444 444 #
445 445 # Avoid calling lower() on the entire data, because it could be a
446 446 # long string and we're only interested in its beginning and end.
447 447 #
448 448 prefix = data[:10].lower()
449 449 suffix = data[-10:].lower()
450 450 return prefix.startswith("<iframe ") and suffix.endswith("</iframe>")
451 451
452 452 if warn():
453 453 warnings.warn("Consider using IPython.display.IFrame instead")
454 454 super(HTML, self).__init__(data=data, url=url, filename=filename, metadata=metadata)
455 455
456 456 def _repr_html_(self):
457 457 return self._data_and_metadata()
458 458
459 459 def __html__(self):
460 460 """
461 461 This method exists to inform other HTML-using modules (e.g. Markupsafe,
462 462 htmltag, etc) that this object is HTML and does not need things like
463 463 special characters (<>&) escaped.
464 464 """
465 465 return self._repr_html_()
466 466
467 467
468 468 class Markdown(TextDisplayObject):
469 469
470 470 def _repr_markdown_(self):
471 471 return self._data_and_metadata()
472 472
473 473
474 474 class Math(TextDisplayObject):
475 475
476 476 def _repr_latex_(self):
477 477 s = r"$\displaystyle %s$" % self.data.strip('$')
478 478 if self.metadata:
479 479 return s, deepcopy(self.metadata)
480 480 else:
481 481 return s
482 482
483 483
484 484 class Latex(TextDisplayObject):
485 485
486 486 def _repr_latex_(self):
487 487 return self._data_and_metadata()
488 488
489 489
490 490 class SVG(DisplayObject):
491 491 """Embed an SVG into the display.
492 492
493 493 Note if you just want to view a svg image via a URL use `:class:Image` with
494 494 a url=URL keyword argument.
495 495 """
496 496
497 497 _read_flags = 'rb'
498 498 # wrap data in a property, which extracts the <svg> tag, discarding
499 499 # document headers
500 500 _data = None
501 501
502 502 @property
503 503 def data(self):
504 504 return self._data
505 505
506 506 @data.setter
507 507 def data(self, svg):
508 508 if svg is None:
509 509 self._data = None
510 510 return
511 511 # parse into dom object
512 512 from xml.dom import minidom
513 513 x = minidom.parseString(svg)
514 514 # get svg tag (should be 1)
515 515 found_svg = x.getElementsByTagName('svg')
516 516 if found_svg:
517 517 svg = found_svg[0].toxml()
518 518 else:
519 519 # fallback on the input, trust the user
520 520 # but this is probably an error.
521 521 pass
522 522 svg = cast_unicode(svg)
523 523 self._data = svg
524 524
525 525 def _repr_svg_(self):
526 526 return self._data_and_metadata()
527 527
528 528 class ProgressBar(DisplayObject):
529 529 """Progressbar supports displaying a progressbar like element
530 530 """
531 531 def __init__(self, total):
532 532 """Creates a new progressbar
533 533
534 534 Parameters
535 535 ----------
536 536 total : int
537 537 maximum size of the progressbar
538 538 """
539 539 self.total = total
540 540 self._progress = 0
541 541 self.html_width = '60ex'
542 542 self.text_width = 60
543 543 self._display_id = hexlify(os.urandom(8)).decode('ascii')
544 544
545 545 def __repr__(self):
546 546 fraction = self.progress / self.total
547 547 filled = '=' * int(fraction * self.text_width)
548 548 rest = ' ' * (self.text_width - len(filled))
549 549 return '[{}{}] {}/{}'.format(
550 550 filled, rest,
551 551 self.progress, self.total,
552 552 )
553 553
554 554 def _repr_html_(self):
555 555 return "<progress style='width:{}' max='{}' value='{}'></progress>".format(
556 556 self.html_width, self.total, self.progress)
557 557
558 558 def display(self):
559 559 display_functions.display(self, display_id=self._display_id)
560 560
561 561 def update(self):
562 562 display_functions.display(self, display_id=self._display_id, update=True)
563 563
564 564 @property
565 565 def progress(self):
566 566 return self._progress
567 567
568 568 @progress.setter
569 569 def progress(self, value):
570 570 self._progress = value
571 571 self.update()
572 572
573 573 def __iter__(self):
574 574 self.display()
575 575 self._progress = -1 # First iteration is 0
576 576 return self
577 577
578 578 def __next__(self):
579 579 """Returns current value and increments display by one."""
580 580 self.progress += 1
581 581 if self.progress < self.total:
582 582 return self.progress
583 583 else:
584 584 raise StopIteration()
585 585
586 586 class JSON(DisplayObject):
587 587 """JSON expects a JSON-able dict or list
588 588
589 589 not an already-serialized JSON string.
590 590
591 591 Scalar types (None, number, string) are not allowed, only dict or list containers.
592 592 """
593 593 # wrap data in a property, which warns about passing already-serialized JSON
594 594 _data = None
595 595 def __init__(self, data=None, url=None, filename=None, expanded=False, metadata=None, root='root', **kwargs):
596 596 """Create a JSON display object given raw data.
597 597
598 598 Parameters
599 599 ----------
600 600 data : dict or list
601 601 JSON data to display. Not an already-serialized JSON string.
602 602 Scalar types (None, number, string) are not allowed, only dict
603 603 or list containers.
604 604 url : unicode
605 605 A URL to download the data from.
606 606 filename : unicode
607 607 Path to a local file to load the data from.
608 608 expanded : boolean
609 609 Metadata to control whether a JSON display component is expanded.
610 610 metadata : dict
611 611 Specify extra metadata to attach to the json display object.
612 612 root : str
613 613 The name of the root element of the JSON tree
614 614 """
615 615 self.metadata = {
616 616 'expanded': expanded,
617 617 'root': root,
618 618 }
619 619 if metadata:
620 620 self.metadata.update(metadata)
621 621 if kwargs:
622 622 self.metadata.update(kwargs)
623 623 super(JSON, self).__init__(data=data, url=url, filename=filename)
624 624
625 625 def _check_data(self):
626 626 if self.data is not None and not isinstance(self.data, (dict, list)):
627 627 raise TypeError("%s expects JSONable dict or list, not %r" % (self.__class__.__name__, self.data))
628 628
629 629 @property
630 630 def data(self):
631 631 return self._data
632 632
633 633 @data.setter
634 634 def data(self, data):
635 635 if isinstance(data, (Path, PurePath)):
636 636 data = str(data)
637 637
638 638 if isinstance(data, str):
639 639 if self.filename is None and self.url is None:
640 640 warnings.warn("JSON expects JSONable dict or list, not JSON strings")
641 641 data = json.loads(data)
642 642 self._data = data
643 643
644 644 def _data_and_metadata(self):
645 645 return self.data, self.metadata
646 646
647 647 def _repr_json_(self):
648 648 return self._data_and_metadata()
649 649
650 650
651 651 _css_t = """var link = document.createElement("link");
652 652 link.rel = "stylesheet";
653 653 link.type = "text/css";
654 654 link.href = "%s";
655 655 document.head.appendChild(link);
656 656 """
657 657
658 658 _lib_t1 = """new Promise(function(resolve, reject) {
659 659 var script = document.createElement("script");
660 660 script.onload = resolve;
661 661 script.onerror = reject;
662 662 script.src = "%s";
663 663 document.head.appendChild(script);
664 664 }).then(() => {
665 665 """
666 666
667 667 _lib_t2 = """
668 668 });"""
669 669
670 670 class GeoJSON(JSON):
671 671 """GeoJSON expects JSON-able dict
672 672
673 673 not an already-serialized JSON string.
674 674
675 675 Scalar types (None, number, string) are not allowed, only dict containers.
676 676 """
677 677
678 678 def __init__(self, *args, **kwargs):
679 679 """Create a GeoJSON display object given raw data.
680 680
681 681 Parameters
682 682 ----------
683 683 data : dict or list
684 684 VegaLite data. Not an already-serialized JSON string.
685 685 Scalar types (None, number, string) are not allowed, only dict
686 686 or list containers.
687 687 url_template : string
688 688 Leaflet TileLayer URL template: http://leafletjs.com/reference.html#url-template
689 689 layer_options : dict
690 690 Leaflet TileLayer options: http://leafletjs.com/reference.html#tilelayer-options
691 691 url : unicode
692 692 A URL to download the data from.
693 693 filename : unicode
694 694 Path to a local file to load the data from.
695 695 metadata : dict
696 696 Specify extra metadata to attach to the json display object.
697 697
698 698 Examples
699 699 --------
700 700 The following will display an interactive map of Mars with a point of
701 701 interest on frontend that do support GeoJSON display.
702 702
703 703 >>> from IPython.display import GeoJSON
704 704
705 705 >>> GeoJSON(data={
706 706 ... "type": "Feature",
707 707 ... "geometry": {
708 708 ... "type": "Point",
709 709 ... "coordinates": [-81.327, 296.038]
710 710 ... }
711 711 ... },
712 712 ... url_template="http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png",
713 713 ... layer_options={
714 714 ... "basemap_id": "celestia_mars-shaded-16k_global",
715 715 ... "attribution" : "Celestia/praesepe",
716 716 ... "minZoom" : 0,
717 717 ... "maxZoom" : 18,
718 718 ... })
719 719 <IPython.core.display.GeoJSON object>
720 720
721 721 In the terminal IPython, you will only see the text representation of
722 722 the GeoJSON object.
723 723
724 724 """
725 725
726 726 super(GeoJSON, self).__init__(*args, **kwargs)
727 727
728 728
729 729 def _ipython_display_(self):
730 730 bundle = {
731 731 'application/geo+json': self.data,
732 732 'text/plain': '<IPython.display.GeoJSON object>'
733 733 }
734 734 metadata = {
735 735 'application/geo+json': self.metadata
736 736 }
737 737 display_functions.display(bundle, metadata=metadata, raw=True)
738 738
739 739 class Javascript(TextDisplayObject):
740 740
741 741 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
742 742 """Create a Javascript display object given raw data.
743 743
744 744 When this object is returned by an expression or passed to the
745 745 display function, it will result in the data being displayed
746 746 in the frontend. If the data is a URL, the data will first be
747 747 downloaded and then displayed.
748 748
749 749 In the Notebook, the containing element will be available as `element`,
750 750 and jQuery will be available. Content appended to `element` will be
751 751 visible in the output area.
752 752
753 753 Parameters
754 754 ----------
755 755 data : unicode, str or bytes
756 756 The Javascript source code or a URL to download it from.
757 757 url : unicode
758 758 A URL to download the data from.
759 759 filename : unicode
760 760 Path to a local file to load the data from.
761 761 lib : list or str
762 762 A sequence of Javascript library URLs to load asynchronously before
763 763 running the source code. The full URLs of the libraries should
764 764 be given. A single Javascript library URL can also be given as a
765 765 string.
766 766 css : list or str
767 767 A sequence of css files to load before running the source code.
768 768 The full URLs of the css files should be given. A single css URL
769 769 can also be given as a string.
770 770 """
771 771 if isinstance(lib, str):
772 772 lib = [lib]
773 773 elif lib is None:
774 774 lib = []
775 775 if isinstance(css, str):
776 776 css = [css]
777 777 elif css is None:
778 778 css = []
779 779 if not isinstance(lib, (list,tuple)):
780 780 raise TypeError('expected sequence, got: %r' % lib)
781 781 if not isinstance(css, (list,tuple)):
782 782 raise TypeError('expected sequence, got: %r' % css)
783 783 self.lib = lib
784 784 self.css = css
785 785 super(Javascript, self).__init__(data=data, url=url, filename=filename)
786 786
787 787 def _repr_javascript_(self):
788 788 r = ''
789 789 for c in self.css:
790 790 r += _css_t % c
791 791 for l in self.lib:
792 792 r += _lib_t1 % l
793 793 r += self.data
794 794 r += _lib_t2*len(self.lib)
795 795 return r
796 796
797 797
798 798 # constants for identifying png/jpeg/gif/webp data
799 799 _PNG = b'\x89PNG\r\n\x1a\n'
800 800 _JPEG = b'\xff\xd8'
801 _GIF1 = b"GIF87a"
802 _GIF2 = b"GIF89a"
801 _GIF1 = b'GIF87a'
802 _GIF2 = b'GIF89a'
803 803 _WEBP = b'WEBP'
804 804
805 805
806 806 def _pngxy(data):
807 807 """read the (width, height) from a PNG header"""
808 808 ihdr = data.index(b'IHDR')
809 809 # next 8 bytes are width/height
810 810 return struct.unpack('>ii', data[ihdr+4:ihdr+12])
811 811
812 812
813 813 def _jpegxy(data):
814 814 """read the (width, height) from a JPEG header"""
815 815 # adapted from http://www.64lines.com/jpeg-width-height
816 816
817 817 idx = 4
818 818 while True:
819 819 block_size = struct.unpack('>H', data[idx:idx+2])[0]
820 820 idx = idx + block_size
821 821 if data[idx:idx+2] == b'\xFF\xC0':
822 822 # found Start of Frame
823 823 iSOF = idx
824 824 break
825 825 else:
826 826 # read another block
827 827 idx += 2
828 828
829 829 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
830 830 return w, h
831 831
832 832
833 833 def _gifxy(data):
834 834 """read the (width, height) from a GIF header"""
835 835 return struct.unpack('<HH', data[6:10])
836 836
837 837
838 838 def _webpxy(data):
839 839 """read the (width, height) from a WEBP header"""
840 840 if data[12:16] == b"VP8 ":
841 width, height = struct.unpack('<HH', data[24:30])
842 width = (width & 0x3fff)
843 height = (height & 0x3fff)
841 width, height = struct.unpack("<HH", data[24:30])
842 width = width & 0x3FFF
843 height = height & 0x3FFF
844 844 return (width, height)
845 845 elif data[12:16] == b"VP8L":
846 size_info = struct.unpack('<I', data[21:25])[0]
846 size_info = struct.unpack("<I", data[21:25])[0]
847 847 width = 1 + ((size_info & 0x3F) << 8) | (size_info >> 24)
848 height = 1 + ((((size_info >> 8) & 0xF) << 10) |
849 (((size_info >> 14) & 0x3FC) << 2) |
850 ((size_info >> 22) & 0x3))
848 height = 1 + (
849 (((size_info >> 8) & 0xF) << 10)
850 | (((size_info >> 14) & 0x3FC) << 2)
851 | ((size_info >> 22) & 0x3)
852 )
851 853 return (width, height)
852 854 else:
853 855 raise ValueError("Not a valid WEBP header")
854 856
855 857
856 858 class Image(DisplayObject):
857 859
858 _read_flags = 'rb'
859 _FMT_JPEG = u'jpeg'
860 _FMT_PNG = u'png'
861 _FMT_GIF = u'gif'
862 _FMT_WEBP = u'webp'
860 _read_flags = "rb"
861 _FMT_JPEG = "jpeg"
862 _FMT_PNG = "png"
863 _FMT_GIF = "gif"
864 _FMT_WEBP = "webp"
863 865 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG, _FMT_GIF, _FMT_WEBP]
864 866 _MIMETYPES = {
865 _FMT_PNG: 'image/png',
866 _FMT_JPEG: 'image/jpeg',
867 _FMT_GIF: 'image/gif',
868 _FMT_WEBP: 'image/webp',
867 _FMT_PNG: "image/png",
868 _FMT_JPEG: "image/jpeg",
869 _FMT_GIF: "image/gif",
870 _FMT_WEBP: "image/webp",
869 871 }
870 872
871 873 def __init__(
872 874 self,
873 875 data=None,
874 876 url=None,
875 877 filename=None,
876 878 format=None,
877 879 embed=None,
878 880 width=None,
879 881 height=None,
880 882 retina=False,
881 883 unconfined=False,
882 884 metadata=None,
883 885 alt=None,
884 886 ):
885 887 """Create a PNG/JPEG/GIF image object given raw data.
886 888
887 889 When this object is returned by an input cell or passed to the
888 890 display function, it will result in the image being displayed
889 891 in the frontend.
890 892
891 893 Parameters
892 894 ----------
893 895 data : unicode, str or bytes
894 896 The raw image data or a URL or filename to load the data from.
895 897 This always results in embedded image data.
896 898
897 899 url : unicode
898 900 A URL to download the data from. If you specify `url=`,
899 901 the image data will not be embedded unless you also specify `embed=True`.
900 902
901 903 filename : unicode
902 904 Path to a local file to load the data from.
903 905 Images from a file are always embedded.
904 906
905 907 format : unicode
906 908 The format of the image data (png/jpeg/jpg/gif/webp). If a filename or URL is given
907 909 for format will be inferred from the filename extension.
908 910
909 911 embed : bool
910 912 Should the image data be embedded using a data URI (True) or be
911 913 loaded using an <img> tag. Set this to True if you want the image
912 914 to be viewable later with no internet connection in the notebook.
913 915
914 916 Default is `True`, unless the keyword argument `url` is set, then
915 917 default value is `False`.
916 918
917 919 Note that QtConsole is not able to display images if `embed` is set to `False`
918 920
919 921 width : int
920 922 Width in pixels to which to constrain the image in html
921 923
922 924 height : int
923 925 Height in pixels to which to constrain the image in html
924 926
925 927 retina : bool
926 928 Automatically set the width and height to half of the measured
927 929 width and height.
928 930 This only works for embedded images because it reads the width/height
929 931 from image data.
930 932 For non-embedded images, you can just set the desired display width
931 933 and height directly.
932 934
933 935 unconfined : bool
934 936 Set unconfined=True to disable max-width confinement of the image.
935 937
936 938 metadata : dict
937 939 Specify extra metadata to attach to the image.
938 940
939 941 alt : unicode
940 942 Alternative text for the image, for use by screen readers.
941 943
942 944 Examples
943 945 --------
944 946 embedded image data, works in qtconsole and notebook
945 947 when passed positionally, the first arg can be any of raw image data,
946 948 a URL, or a filename from which to load image data.
947 949 The result is always embedding image data for inline images.
948 950
949 951 >>> Image('https://www.google.fr/images/srpr/logo3w.png') # doctest: +SKIP
950 952 <IPython.core.display.Image object>
951 953
952 954 >>> Image('/path/to/image.jpg')
953 955 <IPython.core.display.Image object>
954 956
955 957 >>> Image(b'RAW_PNG_DATA...')
956 958 <IPython.core.display.Image object>
957 959
958 960 Specifying Image(url=...) does not embed the image data,
959 961 it only generates ``<img>`` tag with a link to the source.
960 962 This will not work in the qtconsole or offline.
961 963
962 964 >>> Image(url='https://www.google.fr/images/srpr/logo3w.png')
963 965 <IPython.core.display.Image object>
964 966
965 967 """
966 968 if isinstance(data, (Path, PurePath)):
967 969 data = str(data)
968 970
969 971 if filename is not None:
970 972 ext = self._find_ext(filename)
971 973 elif url is not None:
972 974 ext = self._find_ext(url)
973 975 elif data is None:
974 976 raise ValueError("No image data found. Expecting filename, url, or data.")
975 977 elif isinstance(data, str) and (
976 978 data.startswith('http') or _safe_exists(data)
977 979 ):
978 980 ext = self._find_ext(data)
979 981 else:
980 982 ext = None
981 983
982 984 if format is None:
983 985 if ext is not None:
984 986 if ext == u'jpg' or ext == u'jpeg':
985 987 format = self._FMT_JPEG
986 988 elif ext == u'png':
987 989 format = self._FMT_PNG
988 990 elif ext == u'gif':
989 991 format = self._FMT_GIF
990 elif ext == u'webp':
992 elif ext == "webp":
991 993 format = self._FMT_WEBP
992 994 else:
993 995 format = ext.lower()
994 996 elif isinstance(data, bytes):
995 997 # infer image type from image data header,
996 998 # only if format has not been specified.
997 999 if data[:2] == _JPEG:
998 1000 format = self._FMT_JPEG
999 1001 elif data[:8] == _PNG:
1000 1002 format = self._FMT_PNG
1001 1003 elif data[8:12] == _WEBP:
1002 1004 format = self._FMT_WEBP
1003 1005 elif data[:6] == self._GIF1 or data[:6] == self._GIF2:
1004 1006 format = self._FMT_GIF
1005 1007
1006 1008 # failed to detect format, default png
1007 1009 if format is None:
1008 1010 format = self._FMT_PNG
1009 1011
1010 1012 if format.lower() == 'jpg':
1011 1013 # jpg->jpeg
1012 1014 format = self._FMT_JPEG
1013 1015
1014 1016 self.format = format.lower()
1015 1017 self.embed = embed if embed is not None else (url is None)
1016 1018
1017 1019 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
1018 1020 raise ValueError("Cannot embed the '%s' image format" % (self.format))
1019 1021 if self.embed:
1020 1022 self._mimetype = self._MIMETYPES.get(self.format)
1021 1023
1022 1024 self.width = width
1023 1025 self.height = height
1024 1026 self.retina = retina
1025 1027 self.unconfined = unconfined
1026 1028 self.alt = alt
1027 1029 super(Image, self).__init__(data=data, url=url, filename=filename,
1028 1030 metadata=metadata)
1029 1031
1030 1032 if self.width is None and self.metadata.get('width', {}):
1031 1033 self.width = metadata['width']
1032 1034
1033 1035 if self.height is None and self.metadata.get('height', {}):
1034 1036 self.height = metadata['height']
1035 1037
1036 1038 if self.alt is None and self.metadata.get("alt", {}):
1037 1039 self.alt = metadata["alt"]
1038 1040
1039 1041 if retina:
1040 1042 self._retina_shape()
1041 1043
1042 1044
1043 1045 def _retina_shape(self):
1044 1046 """load pixel-doubled width and height from image data"""
1045 1047 if not self.embed:
1046 1048 return
1047 1049 if self.format == self._FMT_PNG:
1048 1050 w, h = _pngxy(self.data)
1049 1051 elif self.format == self._FMT_JPEG:
1050 1052 w, h = _jpegxy(self.data)
1051 1053 elif self.format == self._FMT_GIF:
1052 1054 w, h = _gifxy(self.data)
1053 1055 else:
1054 1056 # retina only supports png
1055 1057 return
1056 1058 self.width = w // 2
1057 1059 self.height = h // 2
1058 1060
1059 1061 def reload(self):
1060 1062 """Reload the raw data from file or URL."""
1061 1063 if self.embed:
1062 1064 super(Image,self).reload()
1063 1065 if self.retina:
1064 1066 self._retina_shape()
1065 1067
1066 1068 def _repr_html_(self):
1067 1069 if not self.embed:
1068 1070 width = height = klass = alt = ""
1069 1071 if self.width:
1070 1072 width = ' width="%d"' % self.width
1071 1073 if self.height:
1072 1074 height = ' height="%d"' % self.height
1073 1075 if self.unconfined:
1074 1076 klass = ' class="unconfined"'
1075 1077 if self.alt:
1076 1078 alt = ' alt="%s"' % html.escape(self.alt)
1077 1079 return '<img src="{url}"{width}{height}{klass}{alt}/>'.format(
1078 1080 url=self.url,
1079 1081 width=width,
1080 1082 height=height,
1081 1083 klass=klass,
1082 1084 alt=alt,
1083 1085 )
1084 1086
1085 1087 def _repr_mimebundle_(self, include=None, exclude=None):
1086 1088 """Return the image as a mimebundle
1087 1089
1088 1090 Any new mimetype support should be implemented here.
1089 1091 """
1090 1092 if self.embed:
1091 1093 mimetype = self._mimetype
1092 1094 data, metadata = self._data_and_metadata(always_both=True)
1093 1095 if metadata:
1094 1096 metadata = {mimetype: metadata}
1095 1097 return {mimetype: data}, metadata
1096 1098 else:
1097 1099 return {'text/html': self._repr_html_()}
1098 1100
1099 1101 def _data_and_metadata(self, always_both=False):
1100 1102 """shortcut for returning metadata with shape information, if defined"""
1101 1103 try:
1102 1104 b64_data = b2a_base64(self.data, newline=False).decode("ascii")
1103 1105 except TypeError as e:
1104 1106 raise FileNotFoundError(
1105 1107 "No such file or directory: '%s'" % (self.data)) from e
1106 1108 md = {}
1107 1109 if self.metadata:
1108 1110 md.update(self.metadata)
1109 1111 if self.width:
1110 1112 md['width'] = self.width
1111 1113 if self.height:
1112 1114 md['height'] = self.height
1113 1115 if self.unconfined:
1114 1116 md['unconfined'] = self.unconfined
1115 1117 if self.alt:
1116 1118 md["alt"] = self.alt
1117 1119 if md or always_both:
1118 1120 return b64_data, md
1119 1121 else:
1120 1122 return b64_data
1121 1123
1122 1124 def _repr_png_(self):
1123 1125 if self.embed and self.format == self._FMT_PNG:
1124 1126 return self._data_and_metadata()
1125 1127
1126 1128 def _repr_jpeg_(self):
1127 1129 if self.embed and self.format == self._FMT_JPEG:
1128 1130 return self._data_and_metadata()
1129 1131
1130 1132 def _find_ext(self, s):
1131 1133 base, ext = splitext(s)
1132 1134
1133 1135 if not ext:
1134 1136 return base
1135 1137
1136 1138 # `splitext` includes leading period, so we skip it
1137 1139 return ext[1:].lower()
1138 1140
1139 1141
1140 1142 class Video(DisplayObject):
1141 1143
1142 1144 def __init__(self, data=None, url=None, filename=None, embed=False,
1143 1145 mimetype=None, width=None, height=None, html_attributes="controls"):
1144 1146 """Create a video object given raw data or an URL.
1145 1147
1146 1148 When this object is returned by an input cell or passed to the
1147 1149 display function, it will result in the video being displayed
1148 1150 in the frontend.
1149 1151
1150 1152 Parameters
1151 1153 ----------
1152 1154 data : unicode, str or bytes
1153 1155 The raw video data or a URL or filename to load the data from.
1154 1156 Raw data will require passing ``embed=True``.
1155 1157
1156 1158 url : unicode
1157 1159 A URL for the video. If you specify ``url=``,
1158 1160 the image data will not be embedded.
1159 1161
1160 1162 filename : unicode
1161 1163 Path to a local file containing the video.
1162 1164 Will be interpreted as a local URL unless ``embed=True``.
1163 1165
1164 1166 embed : bool
1165 1167 Should the video be embedded using a data URI (True) or be
1166 1168 loaded using a <video> tag (False).
1167 1169
1168 1170 Since videos are large, embedding them should be avoided, if possible.
1169 1171 You must confirm embedding as your intention by passing ``embed=True``.
1170 1172
1171 1173 Local files can be displayed with URLs without embedding the content, via::
1172 1174
1173 1175 Video('./video.mp4')
1174 1176
1175 1177 mimetype : unicode
1176 1178 Specify the mimetype for embedded videos.
1177 1179 Default will be guessed from file extension, if available.
1178 1180
1179 1181 width : int
1180 1182 Width in pixels to which to constrain the video in HTML.
1181 1183 If not supplied, defaults to the width of the video.
1182 1184
1183 1185 height : int
1184 1186 Height in pixels to which to constrain the video in html.
1185 1187 If not supplied, defaults to the height of the video.
1186 1188
1187 1189 html_attributes : str
1188 1190 Attributes for the HTML ``<video>`` block.
1189 1191 Default: ``"controls"`` to get video controls.
1190 1192 Other examples: ``"controls muted"`` for muted video with controls,
1191 1193 ``"loop autoplay"`` for looping autoplaying video without controls.
1192 1194
1193 1195 Examples
1194 1196 --------
1195 1197 ::
1196 1198
1197 1199 Video('https://archive.org/download/Sita_Sings_the_Blues/Sita_Sings_the_Blues_small.mp4')
1198 1200 Video('path/to/video.mp4')
1199 1201 Video('path/to/video.mp4', embed=True)
1200 1202 Video('path/to/video.mp4', embed=True, html_attributes="controls muted autoplay")
1201 1203 Video(b'raw-videodata', embed=True)
1202 1204 """
1203 1205 if isinstance(data, (Path, PurePath)):
1204 1206 data = str(data)
1205 1207
1206 1208 if url is None and isinstance(data, str) and data.startswith(('http:', 'https:')):
1207 1209 url = data
1208 1210 data = None
1209 1211 elif data is not None and os.path.exists(data):
1210 1212 filename = data
1211 1213 data = None
1212 1214
1213 1215 if data and not embed:
1214 1216 msg = ''.join([
1215 1217 "To embed videos, you must pass embed=True ",
1216 1218 "(this may make your notebook files huge)\n",
1217 1219 "Consider passing Video(url='...')",
1218 1220 ])
1219 1221 raise ValueError(msg)
1220 1222
1221 1223 self.mimetype = mimetype
1222 1224 self.embed = embed
1223 1225 self.width = width
1224 1226 self.height = height
1225 1227 self.html_attributes = html_attributes
1226 1228 super(Video, self).__init__(data=data, url=url, filename=filename)
1227 1229
1228 1230 def _repr_html_(self):
1229 1231 width = height = ''
1230 1232 if self.width:
1231 1233 width = ' width="%d"' % self.width
1232 1234 if self.height:
1233 1235 height = ' height="%d"' % self.height
1234 1236
1235 1237 # External URLs and potentially local files are not embedded into the
1236 1238 # notebook output.
1237 1239 if not self.embed:
1238 1240 url = self.url if self.url is not None else self.filename
1239 1241 output = """<video src="{0}" {1} {2} {3}>
1240 1242 Your browser does not support the <code>video</code> element.
1241 1243 </video>""".format(url, self.html_attributes, width, height)
1242 1244 return output
1243 1245
1244 1246 # Embedded videos are base64-encoded.
1245 1247 mimetype = self.mimetype
1246 1248 if self.filename is not None:
1247 1249 if not mimetype:
1248 1250 mimetype, _ = mimetypes.guess_type(self.filename)
1249 1251
1250 1252 with open(self.filename, 'rb') as f:
1251 1253 video = f.read()
1252 1254 else:
1253 1255 video = self.data
1254 1256 if isinstance(video, str):
1255 1257 # unicode input is already b64-encoded
1256 1258 b64_video = video
1257 1259 else:
1258 1260 b64_video = b2a_base64(video, newline=False).decode("ascii").rstrip()
1259 1261
1260 1262 output = """<video {0} {1} {2}>
1261 1263 <source src="data:{3};base64,{4}" type="{3}">
1262 1264 Your browser does not support the video tag.
1263 1265 </video>""".format(self.html_attributes, width, height, mimetype, b64_video)
1264 1266 return output
1265 1267
1266 1268 def reload(self):
1267 1269 # TODO
1268 1270 pass
1269 1271
1270 1272
1271 1273 @skip_doctest
1272 1274 def set_matplotlib_formats(*formats, **kwargs):
1273 1275 """
1274 1276 .. deprecated:: 7.23
1275 1277
1276 1278 use `matplotlib_inline.backend_inline.set_matplotlib_formats()`
1277 1279
1278 1280 Select figure formats for the inline backend. Optionally pass quality for JPEG.
1279 1281
1280 1282 For example, this enables PNG and JPEG output with a JPEG quality of 90%::
1281 1283
1282 1284 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90)
1283 1285
1284 1286 To set this in your config files use the following::
1285 1287
1286 1288 c.InlineBackend.figure_formats = {'png', 'jpeg'}
1287 1289 c.InlineBackend.print_figure_kwargs.update({'quality' : 90})
1288 1290
1289 1291 Parameters
1290 1292 ----------
1291 1293 *formats : strs
1292 1294 One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
1293 1295 **kwargs
1294 1296 Keyword args will be relayed to ``figure.canvas.print_figure``.
1295 1297 """
1296 1298 warnings.warn(
1297 1299 "`set_matplotlib_formats` is deprecated since IPython 7.23, directly "
1298 1300 "use `matplotlib_inline.backend_inline.set_matplotlib_formats()`",
1299 1301 DeprecationWarning,
1300 1302 stacklevel=2,
1301 1303 )
1302 1304
1303 1305 from matplotlib_inline.backend_inline import (
1304 1306 set_matplotlib_formats as set_matplotlib_formats_orig,
1305 1307 )
1306 1308
1307 1309 set_matplotlib_formats_orig(*formats, **kwargs)
1308 1310
1309 1311 @skip_doctest
1310 1312 def set_matplotlib_close(close=True):
1311 1313 """
1312 1314 .. deprecated:: 7.23
1313 1315
1314 1316 use `matplotlib_inline.backend_inline.set_matplotlib_close()`
1315 1317
1316 1318 Set whether the inline backend closes all figures automatically or not.
1317 1319
1318 1320 By default, the inline backend used in the IPython Notebook will close all
1319 1321 matplotlib figures automatically after each cell is run. This means that
1320 1322 plots in different cells won't interfere. Sometimes, you may want to make
1321 1323 a plot in one cell and then refine it in later cells. This can be accomplished
1322 1324 by::
1323 1325
1324 1326 In [1]: set_matplotlib_close(False)
1325 1327
1326 1328 To set this in your config files use the following::
1327 1329
1328 1330 c.InlineBackend.close_figures = False
1329 1331
1330 1332 Parameters
1331 1333 ----------
1332 1334 close : bool
1333 1335 Should all matplotlib figures be automatically closed after each cell is
1334 1336 run?
1335 1337 """
1336 1338 warnings.warn(
1337 1339 "`set_matplotlib_close` is deprecated since IPython 7.23, directly "
1338 1340 "use `matplotlib_inline.backend_inline.set_matplotlib_close()`",
1339 1341 DeprecationWarning,
1340 1342 stacklevel=2,
1341 1343 )
1342 1344
1343 1345 from matplotlib_inline.backend_inline import (
1344 1346 set_matplotlib_close as set_matplotlib_close_orig,
1345 1347 )
1346 1348
1347 1349 set_matplotlib_close_orig(close)
General Comments 0
You need to be logged in to leave comments. Login now