Format: text/x-python
⬇ Download File (12.6 KB)
   1  # -*- coding: utf-8 -*-
   2  """Top-level display functions for displaying object in different formats."""
   3  
   4  # Copyright (c) IPython Development Team.
   5  # Distributed under the terms of the Modified BSD License.
   6  
   7  
   8  from binascii import b2a_hex
   9  import os
  10  import sys
  11  import warnings
  12  
  13  __all__ = ['display', 'clear_output', 'publish_display_data', 'update_display', 'DisplayHandle']
  14  
  15  #-----------------------------------------------------------------------------
  16  # utility functions
  17  #-----------------------------------------------------------------------------
  18  
  19  
  20  def _merge(d1, d2):
  21      """Like update, but merges sub-dicts instead of clobbering at the top level.
  22  
  23      Updates d1 in-place
  24      """
  25  
  26      if not isinstance(d2, dict) or not isinstance(d1, dict):
  27          return d2
  28      for key, value in d2.items():
  29          d1[key] = _merge(d1.get(key), value)
  30      return d1
  31  
  32  
  33  #-----------------------------------------------------------------------------
  34  # Main functions
  35  #-----------------------------------------------------------------------------
  36  
  37  class _Sentinel:
  38      def __repr__(self):
  39          return "<deprecated>"
  40  
  41  
  42  _sentinel = _Sentinel()
  43  
  44  # use * to indicate transient is keyword-only
  45  def publish_display_data(
  46      data, metadata=None, source=_sentinel, *, transient=None, **kwargs
  47  ):
  48      """Publish data and metadata to all frontends.
  49  
  50      See the ``display_data`` message in the messaging documentation for
  51      more details about this message type.
  52  
  53      Keys of data and metadata can be any mime-type.
  54  
  55      Parameters
  56      ----------
  57      data : dict
  58          A dictionary having keys that are valid MIME types (like
  59          'text/plain' or 'image/svg+xml') and values that are the data for
  60          that MIME type. The data itself must be a JSON'able data
  61          structure. Minimally all data should have the 'text/plain' data,
  62          which can be displayed by all frontends. If more than the plain
  63          text is given, it is up to the frontend to decide which
  64          representation to use.
  65      metadata : dict
  66          A dictionary for metadata related to the data. This can contain
  67          arbitrary key, value pairs that frontends can use to interpret
  68          the data. mime-type keys matching those in data can be used
  69          to specify metadata about particular representations.
  70      source : str, deprecated
  71          Unused.
  72      transient : dict, keyword-only
  73          A dictionary of transient data, such as display_id.
  74      """
  75      from IPython.core.interactiveshell import InteractiveShell
  76  
  77      if source is not _sentinel:
  78          warnings.warn(
  79              "The `source` parameter emit a  deprecation warning since"
  80              " IPython 8.0, it had no effects for a long time and will "
  81              " be removed in future versions.",
  82              DeprecationWarning,
  83              stacklevel=2,
  84          )
  85      display_pub = InteractiveShell.instance().display_pub
  86  
  87      # only pass transient if supplied,
  88      # to avoid errors with older ipykernel.
  89      # TODO: We could check for ipykernel version and provide a detailed upgrade message.
  90      if transient:
  91          kwargs['transient'] = transient
  92  
  93      display_pub.publish(
  94          data=data,
  95          metadata=metadata,
  96          **kwargs
  97      )
  98  
  99  
 100  def _new_id():
 101      """Generate a new random text id with urandom"""
 102      return b2a_hex(os.urandom(16)).decode('ascii')
 103  
 104  
 105  def display(
 106      *objs,
 107      include=None,
 108      exclude=None,
 109      metadata=None,
 110      transient=None,
 111      display_id=None,
 112      raw=False,
 113      clear=False,
 114      **kwargs,
 115  ):
 116      """Display a Python object in all frontends.
 117  
 118      By default all representations will be computed and sent to the frontends.
 119      Frontends can decide which representation is used and how.
 120  
 121      In terminal IPython this will be similar to using :func:`print`, for use in richer
 122      frontends see Jupyter notebook examples with rich display logic.
 123  
 124      Parameters
 125      ----------
 126      *objs : object
 127          The Python objects to display.
 128      raw : bool, optional
 129          Are the objects to be displayed already mimetype-keyed dicts of raw display data,
 130          or Python objects that need to be formatted before display? [default: False]
 131      include : list, tuple or set, optional
 132          A list of format type strings (MIME types) to include in the
 133          format data dict. If this is set *only* the format types included
 134          in this list will be computed.
 135      exclude : list, tuple or set, optional
 136          A list of format type strings (MIME types) to exclude in the format
 137          data dict. If this is set all format types will be computed,
 138          except for those included in this argument.
 139      metadata : dict, optional
 140          A dictionary of metadata to associate with the output.
 141          mime-type keys in this dictionary will be associated with the individual
 142          representation formats, if they exist.
 143      transient : dict, optional
 144          A dictionary of transient data to associate with the output.
 145          Data in this dict should not be persisted to files (e.g. notebooks).
 146      display_id : str, bool optional
 147          Set an id for the display.
 148          This id can be used for updating this display area later via update_display.
 149          If given as `True`, generate a new `display_id`
 150      clear : bool, optional
 151          Should the output area be cleared before displaying anything? If True,
 152          this will wait for additional output before clearing. [default: False]
 153      **kwargs : additional keyword-args, optional
 154          Additional keyword-arguments are passed through to the display publisher.
 155  
 156      Returns
 157      -------
 158      handle: DisplayHandle
 159          Returns a handle on updatable displays for use with :func:`update_display`,
 160          if `display_id` is given. Returns :any:`None` if no `display_id` is given
 161          (default).
 162  
 163      Examples
 164      --------
 165      >>> class Json(object):
 166      ...     def __init__(self, json):
 167      ...         self.json = json
 168      ...     def _repr_pretty_(self, pp, cycle):
 169      ...         import json
 170      ...         pp.text(json.dumps(self.json, indent=2))
 171      ...     def __repr__(self):
 172      ...         return str(self.json)
 173      ...
 174  
 175      >>> d = Json({1:2, 3: {4:5}})
 176  
 177      >>> print(d)
 178      {1: 2, 3: {4: 5}}
 179  
 180      >>> display(d)
 181      {
 182        "1": 2,
 183        "3": {
 184          "4": 5
 185        }
 186      }
 187  
 188      >>> def int_formatter(integer, pp, cycle):
 189      ...     pp.text('I'*integer)
 190  
 191      >>> plain = get_ipython().display_formatter.formatters['text/plain']
 192      >>> plain.for_type(int, int_formatter)
 193      <function _repr_pprint at 0x...>
 194      >>> display(7-5)
 195      II
 196  
 197      >>> del plain.type_printers[int]
 198      >>> display(7-5)
 199      2
 200  
 201      See Also
 202      --------
 203      :func:`update_display`
 204  
 205      Notes
 206      -----
 207      In Python, objects can declare their textual representation using the
 208      `__repr__` method. IPython expands on this idea and allows objects to declare
 209      other, rich representations including:
 210  
 211        - HTML
 212        - JSON
 213        - PNG
 214        - JPEG
 215        - SVG
 216        - LaTeX
 217  
 218      A single object can declare some or all of these representations; all are
 219      handled by IPython's display system.
 220  
 221      The main idea of the first approach is that you have to implement special
 222      display methods when you define your class, one for each representation you
 223      want to use. Here is a list of the names of the special methods and the
 224      values they must return:
 225  
 226        - `_repr_html_`: return raw HTML as a string, or a tuple (see below).
 227        - `_repr_json_`: return a JSONable dict, or a tuple (see below).
 228        - `_repr_jpeg_`: return raw JPEG data, or a tuple (see below).
 229        - `_repr_png_`: return raw PNG data, or a tuple (see below).
 230        - `_repr_svg_`: return raw SVG data as a string, or a tuple (see below).
 231        - `_repr_latex_`: return LaTeX commands in a string surrounded by "$",
 232                          or a tuple (see below).
 233        - `_repr_mimebundle_`: return a full mimebundle containing the mapping
 234                               from all mimetypes to data.
 235                               Use this for any mime-type not listed above.
 236  
 237      The above functions may also return the object's metadata alonside the
 238      data.  If the metadata is available, the functions will return a tuple
 239      containing the data and metadata, in that order.  If there is no metadata
 240      available, then the functions will return the data only.
 241  
 242      When you are directly writing your own classes, you can adapt them for
 243      display in IPython by following the above approach. But in practice, you
 244      often need to work with existing classes that you can't easily modify.
 245  
 246      You can refer to the documentation on integrating with the display system in
 247      order to register custom formatters for already existing types
 248      (:ref:`integrating_rich_display`).
 249  
 250      .. versionadded:: 5.4 display available without import
 251      .. versionadded:: 6.1 display available without import
 252  
 253      Since IPython 5.4 and 6.1 :func:`display` is automatically made available to
 254      the user without import. If you are using display in a document that might
 255      be used in a pure python context or with older version of IPython, use the
 256      following import at the top of your file::
 257  
 258          from IPython.display import display
 259  
 260      """
 261      from IPython.core.interactiveshell import InteractiveShell
 262  
 263      if not InteractiveShell.initialized():
 264          # Directly print objects.
 265          print(*objs)
 266          return
 267  
 268      if transient is None:
 269          transient = {}
 270      if metadata is None:
 271          metadata={}
 272      if display_id:
 273          if display_id is True:
 274              display_id = _new_id()
 275          transient['display_id'] = display_id
 276      if kwargs.get('update') and 'display_id' not in transient:
 277          raise TypeError('display_id required for update_display')
 278      if transient:
 279          kwargs['transient'] = transient
 280  
 281      if not objs and display_id:
 282          # if given no objects, but still a request for a display_id,
 283          # we assume the user wants to insert an empty output that
 284          # can be updated later
 285          objs = [{}]
 286          raw = True
 287  
 288      if not raw:
 289          format = InteractiveShell.instance().display_formatter.format
 290  
 291      if clear:
 292          clear_output(wait=True)
 293  
 294      for obj in objs:
 295          if raw:
 296              publish_display_data(data=obj, metadata=metadata, **kwargs)
 297          else:
 298              format_dict, md_dict = format(obj, include=include, exclude=exclude)
 299              if not format_dict:
 300                  # nothing to display (e.g. _ipython_display_ took over)
 301                  continue
 302              if metadata:
 303                  # kwarg-specified metadata gets precedence
 304                  _merge(md_dict, metadata)
 305              publish_display_data(data=format_dict, metadata=md_dict, **kwargs)
 306      if display_id:
 307          return DisplayHandle(display_id)
 308  
 309  
 310  # use * for keyword-only display_id arg
 311  def update_display(obj, *, display_id, **kwargs):
 312      """Update an existing display by id
 313  
 314      Parameters
 315      ----------
 316      obj
 317          The object with which to update the display
 318      display_id : keyword-only
 319          The id of the display to update
 320  
 321      See Also
 322      --------
 323      :func:`display`
 324      """
 325      kwargs['update'] = True
 326      display(obj, display_id=display_id, **kwargs)
 327  
 328  
 329  class DisplayHandle(object):
 330      """A handle on an updatable display
 331  
 332      Call `.update(obj)` to display a new object.
 333  
 334      Call `.display(obj`) to add a new instance of this display,
 335      and update existing instances.
 336  
 337      See Also
 338      --------
 339  
 340          :func:`display`, :func:`update_display`
 341  
 342      """
 343  
 344      def __init__(self, display_id=None):
 345          if display_id is None:
 346              display_id = _new_id()
 347          self.display_id = display_id
 348  
 349      def __repr__(self):
 350          return "<%s display_id=%s>" % (self.__class__.__name__, self.display_id)
 351  
 352      def display(self, obj, **kwargs):
 353          """Make a new display with my id, updating existing instances.
 354  
 355          Parameters
 356          ----------
 357          obj
 358              object to display
 359          **kwargs
 360              additional keyword arguments passed to display
 361          """
 362          display(obj, display_id=self.display_id, **kwargs)
 363  
 364      def update(self, obj, **kwargs):
 365          """Update existing displays with my id
 366  
 367          Parameters
 368          ----------
 369          obj
 370              object to display
 371          **kwargs
 372              additional keyword arguments passed to update_display
 373          """
 374          update_display(obj, display_id=self.display_id, **kwargs)
 375  
 376  
 377  def clear_output(wait=False):
 378      """Clear the output of the current cell receiving output.
 379  
 380      Parameters
 381      ----------
 382      wait : bool [default: false]
 383          Wait to clear the output until new output is available to replace it."""
 384      from IPython.core.interactiveshell import InteractiveShell
 385      if InteractiveShell.initialized():
 386          InteractiveShell.instance().display_pub.clear_output(wait)
 387      else:
 388          print('\033[2K\r', end='')
 389          sys.stdout.flush()
 390          print('\033[2K\r', end='')
 391          sys.stderr.flush()
 392