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