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