# -*- coding: utf-8 -*- """Top-level display functions for displaying object in different formats.""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. from binascii import b2a_base64, hexlify import html import json import mimetypes import os import struct import warnings from copy import deepcopy from os.path import splitext from pathlib import Path, PurePath from IPython.utils.py3compat import cast_unicode from IPython.testing.skipdoctest import skip_doctest from . import display_functions __all__ = ['display_pretty', 'display_html', 'display_markdown', 'display_svg', 'display_png', 'display_jpeg', 'display_latex', 'display_json', 'display_javascript', 'display_pdf', 'DisplayObject', 'TextDisplayObject', 'Pretty', 'HTML', 'Markdown', 'Math', 'Latex', 'SVG', 'ProgressBar', 'JSON', 'GeoJSON', 'Javascript', 'Image', 'set_matplotlib_formats', 'set_matplotlib_close', 'Video'] _deprecated_names = ["display", "clear_output", "publish_display_data", "update_display", "DisplayHandle"] __all__ = __all__ + _deprecated_names # ----- warn to import from IPython.display ----- from warnings import warn def __getattr__(name): if name in _deprecated_names: warn(f"Importing {name} from IPython.core.display is deprecated since IPython 7.14, please import from IPython display", DeprecationWarning, stacklevel=2) return getattr(display_functions, name) if name in globals().keys(): return globals()[name] else: raise AttributeError(f"module {__name__} has no attribute {name}") #----------------------------------------------------------------------------- # utility functions #----------------------------------------------------------------------------- def _safe_exists(path): """Check path, but don't let exceptions raise""" try: return os.path.exists(path) except Exception: return False def _display_mimetype(mimetype, objs, raw=False, metadata=None): """internal implementation of all display_foo methods Parameters ---------- mimetype : str The mimetype to be published (e.g. 'image/png') *objs : object The Python objects to display, or if raw=True raw text data to display. raw : bool Are the data objects raw data or Python objects that need to be formatted before display? [default: False] metadata : dict (optional) Metadata to be associated with the specific mimetype output. """ if metadata: metadata = {mimetype: metadata} if raw: # turn list of pngdata into list of { 'image/png': pngdata } objs = [ {mimetype: obj} for obj in objs ] display(*objs, raw=raw, metadata=metadata, include=[mimetype]) #----------------------------------------------------------------------------- # Main functions #----------------------------------------------------------------------------- def display_pretty(*objs, **kwargs): """Display the pretty (default) representation of an object. Parameters ---------- *objs : object The Python objects to display, or if raw=True raw text data to display. raw : bool Are the data objects raw data or Python objects that need to be formatted before display? [default: False] metadata : dict (optional) Metadata to be associated with the specific mimetype output. """ _display_mimetype('text/plain', objs, **kwargs) def display_html(*objs, **kwargs): """Display the HTML representation of an object. Note: If raw=False and the object does not have a HTML representation, no HTML will be shown. Parameters ---------- *objs : object The Python objects to display, or if raw=True raw HTML data to display. raw : bool Are the data objects raw data or Python objects that need to be formatted before display? [default: False] metadata : dict (optional) Metadata to be associated with the specific mimetype output. """ _display_mimetype('text/html', objs, **kwargs) def display_markdown(*objs, **kwargs): """Displays the Markdown representation of an object. Parameters ---------- *objs : object The Python objects to display, or if raw=True raw markdown data to display. raw : bool Are the data objects raw data or Python objects that need to be formatted before display? [default: False] metadata : dict (optional) Metadata to be associated with the specific mimetype output. """ _display_mimetype('text/markdown', objs, **kwargs) def display_svg(*objs, **kwargs): """Display the SVG representation of an object. Parameters ---------- *objs : object The Python objects to display, or if raw=True raw svg data to display. raw : bool Are the data objects raw data or Python objects that need to be formatted before display? [default: False] metadata : dict (optional) Metadata to be associated with the specific mimetype output. """ _display_mimetype('image/svg+xml', objs, **kwargs) def display_png(*objs, **kwargs): """Display the PNG representation of an object. Parameters ---------- *objs : object The Python objects to display, or if raw=True raw png data to display. raw : bool Are the data objects raw data or Python objects that need to be formatted before display? [default: False] metadata : dict (optional) Metadata to be associated with the specific mimetype output. """ _display_mimetype('image/png', objs, **kwargs) def display_jpeg(*objs, **kwargs): """Display the JPEG representation of an object. Parameters ---------- *objs : object The Python objects to display, or if raw=True raw JPEG data to display. raw : bool Are the data objects raw data or Python objects that need to be formatted before display? [default: False] metadata : dict (optional) Metadata to be associated with the specific mimetype output. """ _display_mimetype('image/jpeg', objs, **kwargs) def display_latex(*objs, **kwargs): """Display the LaTeX representation of an object. Parameters ---------- *objs : object The Python objects to display, or if raw=True raw latex data to display. raw : bool Are the data objects raw data or Python objects that need to be formatted before display? [default: False] metadata : dict (optional) Metadata to be associated with the specific mimetype output. """ _display_mimetype('text/latex', objs, **kwargs) def display_json(*objs, **kwargs): """Display the JSON representation of an object. Note that not many frontends support displaying JSON. Parameters ---------- *objs : object The Python objects to display, or if raw=True raw json data to display. raw : bool Are the data objects raw data or Python objects that need to be formatted before display? [default: False] metadata : dict (optional) Metadata to be associated with the specific mimetype output. """ _display_mimetype('application/json', objs, **kwargs) def display_javascript(*objs, **kwargs): """Display the Javascript representation of an object. Parameters ---------- *objs : object The Python objects to display, or if raw=True raw javascript data to display. raw : bool Are the data objects raw data or Python objects that need to be formatted before display? [default: False] metadata : dict (optional) Metadata to be associated with the specific mimetype output. """ _display_mimetype('application/javascript', objs, **kwargs) def display_pdf(*objs, **kwargs): """Display the PDF representation of an object. Parameters ---------- *objs : object The Python objects to display, or if raw=True raw javascript data to display. raw : bool Are the data objects raw data or Python objects that need to be formatted before display? [default: False] metadata : dict (optional) Metadata to be associated with the specific mimetype output. """ _display_mimetype('application/pdf', objs, **kwargs) #----------------------------------------------------------------------------- # Smart classes #----------------------------------------------------------------------------- class DisplayObject(object): """An object that wraps data to be displayed.""" _read_flags = 'r' _show_mem_addr = False metadata = None def __init__(self, data=None, url=None, filename=None, metadata=None): """Create a display object given raw data. When this object is returned by an expression or passed to the display function, it will result in the data being displayed in the frontend. The MIME type of the data should match the subclasses used, so the Png subclass should be used for 'image/png' data. If the data is a URL, the data will first be downloaded and then displayed. If Parameters ---------- data : unicode, str or bytes The raw data or a URL or file to load the data from url : unicode A URL to download the data from. filename : unicode Path to a local file to load the data from. metadata : dict Dict of metadata associated to be the object when displayed """ if isinstance(data, (Path, PurePath)): data = str(data) if data is not None and isinstance(data, str): if data.startswith('http') and url is None: url = data filename = None data = None elif _safe_exists(data) and filename is None: url = None filename = data data = None self.url = url self.filename = filename # because of @data.setter methods in # subclasses ensure url and filename are set # before assigning to self.data self.data = data if metadata is not None: self.metadata = metadata elif self.metadata is None: self.metadata = {} self.reload() self._check_data() def __repr__(self): if not self._show_mem_addr: cls = self.__class__ r = "<%s.%s object>" % (cls.__module__, cls.__name__) else: r = super(DisplayObject, self).__repr__() return r def _check_data(self): """Override in subclasses if there's something to check.""" pass def _data_and_metadata(self): """shortcut for returning metadata with shape information, if defined""" if self.metadata: return self.data, deepcopy(self.metadata) else: return self.data def reload(self): """Reload the raw data from file or URL.""" if self.filename is not None: with open(self.filename, self._read_flags) as f: self.data = f.read() elif self.url is not None: # Deferred import from urllib.request import urlopen response = urlopen(self.url) data = response.read() # extract encoding from header, if there is one: encoding = None if 'content-type' in response.headers: for sub in response.headers['content-type'].split(';'): sub = sub.strip() if sub.startswith('charset'): encoding = sub.split('=')[-1].strip() break if 'content-encoding' in response.headers: # TODO: do deflate? if 'gzip' in response.headers['content-encoding']: import gzip from io import BytesIO with gzip.open(BytesIO(data), 'rt', encoding=encoding) as fp: encoding = None data = fp.read() # decode data, if an encoding was specified # We only touch self.data once since # subclasses such as SVG have @data.setter methods # that transform self.data into ... well svg. if encoding: self.data = data.decode(encoding, 'replace') else: self.data = data class TextDisplayObject(DisplayObject): """Validate that display data is text""" def _check_data(self): if self.data is not None and not isinstance(self.data, str): raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data)) class Pretty(TextDisplayObject): def _repr_pretty_(self, pp, cycle): return pp.text(self.data) class HTML(TextDisplayObject): def __init__(self, data=None, url=None, filename=None, metadata=None): def warn(): if not data: return False # # Avoid calling lower() on the entire data, because it could be a # long string and we're only interested in its beginning and end. # prefix = data[:10].lower() suffix = data[-10:].lower() return prefix.startswith("