##// END OF EJS Templates
Improve typing and MIME hook API for inspector (#14342)...
Improve typing and MIME hook API for inspector (#14342) Fixes https://github.com/ipython/ipython/issues/14339 ### Additions Adds `InfoDict` type to improve the typing of `info()` result. Adds missing `"subclasses"` to `info_fields` list (these were added to the field list in https://github.com/ipython/ipython/pull/11486 but we forgot to update `info_fields` variable at the time) - the newly added `InfoDict` type will ensure that this won't happen again. Adds `InspectorHookData` dataclass which is passed to the MIME hooks which now should expect a single argument. Having a single dataclass argument enables us to deprecate individual fields, or add new fields without breaking the existing hooks. The old hooks will still work (if any are out there since this mechanism got just added in the previous point version). ### Deletions A comment over `info_fields` gets deleted: - Contrarily to the comment (which is getting deleted in this PR), `info_fields` were not defining the order of display since at least 2015 (https://github.com/ipython/ipython/pull/7903 - I did not feel the need to go further in the history to find when exactly it happened). - Also contrarily to this comment, current Jupyter messaging spec does not define the contents of `info_fields` (I guess this was lost during IPython/Jupyter split), but the newly added `InfoDict` at least properly annotates their type (if you know where I can find the old IPython messaging spec with the descriptions I can add these as doc comments). Unused `cast_unicode` import gets deleted. If someone imported it from here... well they really should not have. ### Deprecations - mime hooks taking two arguments (`obj, info`)

File last commit:

r23969:d73e5f74
r28661:2084e7f3 merge
Show More
capture.py
170 lines | 5.0 KiB | text/x-python | PythonLexer
# encoding: utf-8
"""IO capturing utilities."""
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
import sys
from io import StringIO
#-----------------------------------------------------------------------------
# Classes and functions
#-----------------------------------------------------------------------------
class RichOutput(object):
def __init__(self, data=None, metadata=None, transient=None, update=False):
self.data = data or {}
self.metadata = metadata or {}
self.transient = transient or {}
self.update = update
def display(self):
from IPython.display import publish_display_data
publish_display_data(data=self.data, metadata=self.metadata,
transient=self.transient, update=self.update)
def _repr_mime_(self, mime):
if mime not in self.data:
return
data = self.data[mime]
if mime in self.metadata:
return data, self.metadata[mime]
else:
return data
def _repr_mimebundle_(self, include=None, exclude=None):
return self.data, self.metadata
def _repr_html_(self):
return self._repr_mime_("text/html")
def _repr_latex_(self):
return self._repr_mime_("text/latex")
def _repr_json_(self):
return self._repr_mime_("application/json")
def _repr_javascript_(self):
return self._repr_mime_("application/javascript")
def _repr_png_(self):
return self._repr_mime_("image/png")
def _repr_jpeg_(self):
return self._repr_mime_("image/jpeg")
def _repr_svg_(self):
return self._repr_mime_("image/svg+xml")
class CapturedIO(object):
"""Simple object for containing captured stdout/err and rich display StringIO objects
Each instance `c` has three attributes:
- ``c.stdout`` : standard output as a string
- ``c.stderr`` : standard error as a string
- ``c.outputs``: a list of rich display outputs
Additionally, there's a ``c.show()`` method which will print all of the
above in the same order, and can be invoked simply via ``c()``.
"""
def __init__(self, stdout, stderr, outputs=None):
self._stdout = stdout
self._stderr = stderr
if outputs is None:
outputs = []
self._outputs = outputs
def __str__(self):
return self.stdout
@property
def stdout(self):
"Captured standard output"
if not self._stdout:
return ''
return self._stdout.getvalue()
@property
def stderr(self):
"Captured standard error"
if not self._stderr:
return ''
return self._stderr.getvalue()
@property
def outputs(self):
"""A list of the captured rich display outputs, if any.
If you have a CapturedIO object ``c``, these can be displayed in IPython
using::
from IPython.display import display
for o in c.outputs:
display(o)
"""
return [ RichOutput(**kargs) for kargs in self._outputs ]
def show(self):
"""write my output to sys.stdout/err as appropriate"""
sys.stdout.write(self.stdout)
sys.stderr.write(self.stderr)
sys.stdout.flush()
sys.stderr.flush()
for kargs in self._outputs:
RichOutput(**kargs).display()
__call__ = show
class capture_output(object):
"""context manager for capturing stdout/err"""
stdout = True
stderr = True
display = True
def __init__(self, stdout=True, stderr=True, display=True):
self.stdout = stdout
self.stderr = stderr
self.display = display
self.shell = None
def __enter__(self):
from IPython.core.getipython import get_ipython
from IPython.core.displaypub import CapturingDisplayPublisher
from IPython.core.displayhook import CapturingDisplayHook
self.sys_stdout = sys.stdout
self.sys_stderr = sys.stderr
if self.display:
self.shell = get_ipython()
if self.shell is None:
self.save_display_pub = None
self.display = False
stdout = stderr = outputs = None
if self.stdout:
stdout = sys.stdout = StringIO()
if self.stderr:
stderr = sys.stderr = StringIO()
if self.display:
self.save_display_pub = self.shell.display_pub
self.shell.display_pub = CapturingDisplayPublisher()
outputs = self.shell.display_pub.outputs
self.save_display_hook = sys.displayhook
sys.displayhook = CapturingDisplayHook(shell=self.shell,
outputs=outputs)
return CapturedIO(stdout, stderr, outputs)
def __exit__(self, exc_type, exc_value, traceback):
sys.stdout = self.sys_stdout
sys.stderr = self.sys_stderr
if self.display and self.shell:
self.shell.display_pub = self.save_display_pub
sys.displayhook = self.save_display_hook