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