| """An interface for publishing rich data to frontends. |
| |
| There are two components of the display system: |
| |
| * Display formatters, which take a Python object and compute the |
| representation of the object in various formats (text, HTML, SVG, etc.). |
| * The display publisher that is used to send the representation data to the |
| various frontends. |
| |
| This module defines the logic display publishing. The display publisher uses |
| the ``display_data`` message type that is defined in the IPython messaging |
| spec. |
| """ |
|
|
| |
| |
|
|
| import sys |
|
|
| from traitlets.config.configurable import Configurable |
| from traitlets import List |
|
|
| |
| from .display_functions import publish_display_data |
| from .history import HistoryOutput |
|
|
| import typing as t |
|
|
| |
| |
| |
|
|
| _sentinel = object() |
|
|
|
|
| class DisplayPublisher(Configurable): |
| """A traited class that publishes display data to frontends. |
| |
| Instances of this class are created by the main IPython object and should |
| be accessed there. |
| """ |
|
|
| def __init__(self, shell=None, *args, **kwargs): |
| self.shell = shell |
| self._is_publishing = False |
| self._in_post_execute = False |
| if self.shell: |
| self._setup_execution_tracking() |
| super().__init__(*args, **kwargs) |
|
|
| def _validate_data(self, data, metadata=None): |
| """Validate the display data. |
| |
| Parameters |
| ---------- |
| data : dict |
| The formata data dictionary. |
| metadata : dict |
| Any metadata for the data. |
| """ |
|
|
| if not isinstance(data, dict): |
| raise TypeError("data must be a dict, got: %r" % data) |
| if metadata is not None: |
| if not isinstance(metadata, dict): |
| raise TypeError("metadata must be a dict, got: %r" % data) |
|
|
| def _setup_execution_tracking(self): |
| """Set up hooks to track execution state""" |
| self.shell.events.register("post_execute", self._on_post_execute) |
| self.shell.events.register("pre_execute", self._on_pre_execute) |
|
|
| def _on_post_execute(self): |
| """Called at start of post_execute phase""" |
| self._in_post_execute = True |
|
|
| def _on_pre_execute(self): |
| """Called at start of pre_execute phase""" |
| self._in_post_execute = False |
|
|
| |
| def publish( |
| self, |
| data, |
| metadata=None, |
| source=_sentinel, |
| *, |
| transient=None, |
| update=False, |
| **kwargs, |
| ) -> None: |
| """Publish data and metadata to all frontends. |
| |
| See the ``display_data`` message in the messaging documentation for |
| more details about this message type. |
| |
| The following MIME types are currently implemented: |
| |
| * text/plain |
| * text/html |
| * text/markdown |
| * text/latex |
| * application/json |
| * application/javascript |
| * image/png |
| * image/jpeg |
| * image/svg+xml |
| |
| Parameters |
| ---------- |
| data : dict |
| A dictionary having keys that are valid MIME types (like |
| 'text/plain' or 'image/svg+xml') and values that are the data for |
| that MIME type. The data itself must be a JSON'able data |
| structure. Minimally all data should have the 'text/plain' data, |
| which can be displayed by all frontends. If more than the plain |
| text is given, it is up to the frontend to decide which |
| representation to use. |
| metadata : dict |
| A dictionary for metadata related to the data. This can contain |
| arbitrary key, value pairs that frontends can use to interpret |
| the data. Metadata specific to each mime-type can be specified |
| in the metadata dict with the same mime-type keys as |
| the data itself. |
| source : str, deprecated |
| Unused. |
| transient : dict, keyword-only |
| A dictionary for transient data. |
| Data in this dictionary should not be persisted as part of saving this output. |
| Examples include 'display_id'. |
| update : bool, keyword-only, default: False |
| If True, only update existing outputs with the same display_id, |
| rather than creating a new output. |
| """ |
|
|
| if source is not _sentinel: |
| import warnings |
|
|
| warnings.warn( |
| "The 'source' parameter is deprecated since IPython 3.0 and will be ignored " |
| "(this warning is present since 9.0). `source` parameter will be removed in the future.", |
| DeprecationWarning, |
| stacklevel=2, |
| ) |
|
|
| handlers: t.Dict = {} |
| if self.shell is not None: |
| handlers = getattr(self.shell, "mime_renderers", {}) |
|
|
| outputs = self.shell.history_manager.outputs |
|
|
| target_execution_count = self.shell.execution_count - 1 |
| if self._in_post_execute: |
| |
| |
| target_execution_count = self.shell.execution_count - 1 |
|
|
| outputs[target_execution_count].append( |
| HistoryOutput(output_type="display_data", bundle=data) |
| ) |
|
|
| for mime, handler in handlers.items(): |
| if mime in data: |
| handler(data[mime], metadata.get(mime, None)) |
| return |
|
|
| self._is_publishing = True |
| if "text/plain" in data: |
| print(data["text/plain"]) |
| self._is_publishing = False |
|
|
| @property |
| def is_publishing(self): |
| return self._is_publishing |
|
|
| def clear_output(self, wait=False): |
| """Clear the output of the cell receiving output.""" |
| print("\033[2K\r", end="") |
| sys.stdout.flush() |
| print("\033[2K\r", end="") |
| sys.stderr.flush() |
|
|
|
|
| class CapturingDisplayPublisher(DisplayPublisher): |
| """A DisplayPublisher that stores""" |
|
|
| outputs: List = List() |
|
|
| def publish( |
| self, data, metadata=None, source=None, *, transient=None, update=False |
| ): |
| self.outputs.append( |
| { |
| "data": data, |
| "metadata": metadata, |
| "transient": transient, |
| "update": update, |
| } |
| ) |
|
|
| def clear_output(self, wait=False): |
| super(CapturingDisplayPublisher, self).clear_output(wait) |
|
|
| |
| self.outputs.clear() |
|
|