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