Skip to content

Commit

Permalink
Merge pull request #10596 from Carreau/display-builtin
Browse files Browse the repository at this point in the history
Inject display into builtins
  • Loading branch information
rgbkrk committed May 30, 2017
2 parents c9ef6e3 + 1dc063d commit 4f93bf1
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 11 deletions.
130 changes: 119 additions & 11 deletions IPython/core/display.py
Expand Up @@ -145,18 +145,21 @@ def display(*objs, include=None, exclude=None, metadata=None, transient=None, di
By default all representations will be computed and sent to the frontends.
Frontends can decide which representation is used and how.
In terminal IPython this will be similar to using :func:`print`, for use in richer
frontends see Jupyter notebook examples with rich display logic.
Parameters
----------
objs : tuple of objects
The Python objects to display.
raw : bool, optional
Are the objects to be displayed already mimetype-keyed dicts of raw display data,
or Python objects that need to be formatted before display? [default: False]
include : list or tuple, optional
include : list, tuple or set, optional
A list of format type strings (MIME types) to include in the
format data dict. If this is set *only* the format types included
in this list will be computed.
exclude : list or tuple, optional
exclude : list, tuple or set, optional
A list of format type strings (MIME types) to exclude in the format
data dict. If this is set all format types will be computed,
except for those included in this argument.
Expand All @@ -167,25 +170,119 @@ def display(*objs, include=None, exclude=None, metadata=None, transient=None, di
transient : dict, optional
A dictionary of transient data to associate with the output.
Data in this dict should not be persisted to files (e.g. notebooks).
display_id : str, optional
display_id : str, bool optional
Set an id for the display.
This id can be used for updating this display area later via update_display.
If given as True, generate a new display_id
If given as `True`, generate a new `display_id`
kwargs: additional keyword-args, optional
Additional keyword-arguments are passed through to the display publisher.
Returns
-------
handle: DisplayHandle
Returns a handle on updatable displays, if display_id is given.
Returns None if no display_id is given (default).
Returns a handle on updatable displays for use with :func:`update_display`,
if `display_id` is given. Returns :any:`None` if no `display_id` is given
(default).
Examples
--------
>>> class Json(object):
... def __init__(self, json):
... self.json = json
... def _repr_pretty_(self, pp, cycle):
... import json
... pp.text(json.dumps(self.json, indent=2))
... def __repr__(self):
... return str(self.json)
...
>>> d = Json({1:2, 3: {4:5}})
>>> print(d)
{1: 2, 3: {4: 5}}
>>> display(d)
{
"1": 2,
"3": {
"4": 5
}
}
>>> def int_formatter(integer, pp, cycle):
... pp.text('I'*integer)
>>> plain = get_ipython().display_formatter.formatters['text/plain']
>>> plain.for_type(int, int_formatter)
<function _repr_pprint at 0x...>
>>> display(7-5)
II
>>> del plain.type_printers[int]
>>> display(7-5)
2
See Also
--------
:func:`update_display`
Notes
-----
In Python, objects can declare their textual representation using the
`__repr__` method. IPython expands on this idea and allows objects to declare
other, rich representations including:
- HTML
- JSON
- PNG
- JPEG
- SVG
- LaTeX
A single object can declare some or all of these representations; all are
handled by IPython's display system.
The main idea of the first approach is that you have to implement special
display methods when you define your class, one for each representation you
want to use. Here is a list of the names of the special methods and the
values they must return:
- `_repr_html_`: return raw HTML as a string
- `_repr_json_`: return a JSONable dict
- `_repr_jpeg_`: return raw JPEG data
- `_repr_png_`: return raw PNG data
- `_repr_svg_`: return raw SVG data as a string
- `_repr_latex_`: return LaTeX commands in a string surrounded by "$".
- `_repr_mimebundle_`: return a full mimebundle containing the mapping
from all mimetypes to data
When you are directly writing your own classes, you can adapt them for
display in IPython by following the above approach. But in practice, you
often need to work with existing classes that you can't easily modify.
You can refer to the documentation on IPython display formatters in order to
register custom formatters for already existing types.
.. versionadded:: 5.4 display available without import
.. versionadded:: 6.1 display available without import
Since IPython 5.4 and 6.1 :func:`display` is automatically made available to
the user without import. If you are using display in a document that might
be used in a pure python context or with older version of IPython, use the
following import at the top of your file::
from IPython.display import display
"""
raw = kwargs.pop('raw', False)
if transient is None:
transient = {}
if display_id:
if display_id == True:
if display_id is True:
display_id = _new_id()
transient['display_id'] = display_id
if kwargs.get('update') and 'display_id' not in transient:
Expand Down Expand Up @@ -225,6 +322,11 @@ def update_display(obj, *, display_id, **kwargs):
The object with which to update the display
display_id: keyword-only
The id of the display to update
See Also
--------
:func:`display`
"""
kwargs['update'] = True
display(obj, display_id=display_id, **kwargs)
Expand All @@ -233,10 +335,16 @@ def update_display(obj, *, display_id, **kwargs):
class DisplayHandle(object):
"""A handle on an updatable display
Call .update(obj) to display a new object.
Call `.update(obj)` to display a new object.
Call .display(obj) to add a new instance of this display,
Call `.display(obj`) to add a new instance of this display,
and update existing instances.
See Also
--------
:func:`display`, :func:`update_display`
"""

def __init__(self, display_id=None):
Expand Down
2 changes: 2 additions & 0 deletions IPython/core/interactiveshell.py
Expand Up @@ -55,6 +55,7 @@
from IPython.core.prefilter import PrefilterManager
from IPython.core.profiledir import ProfileDir
from IPython.core.usage import default_banner
from IPython.display import display
from IPython.testing.skipdoctest import skip_doctest
from IPython.utils import PyColorize
from IPython.utils import io
Expand Down Expand Up @@ -617,6 +618,7 @@ def init_builtins(self):
# removing on exit or representing the existence of more than one
# IPython at a time.
builtin_mod.__dict__['__IPYTHON__'] = True
builtin_mod.__dict__['display'] = display

self.builtin_trap = BuiltinTrap(shell=self)

Expand Down
20 changes: 20 additions & 0 deletions IPython/core/tests/test_display.py
Expand Up @@ -13,6 +13,7 @@
from IPython.core.getipython import get_ipython
from IPython.utils.tempdir import NamedFileInTemporaryDirectory
from IPython import paths as ipath
from IPython.testing.tools import AssertPrints, AssertNotPrints

import IPython.testing.decorators as dec

Expand Down Expand Up @@ -141,6 +142,25 @@ def test_set_matplotlib_formats_kwargs():
expected.update(cfg.print_figure_kwargs)
nt.assert_equal(cell, expected)

def test_display_available():
"""
Test that display is available without import
We don't really care if it's in builtin or anything else, but it should
always be available.
"""
ip = get_ipython()
with AssertNotPrints('NameError'):
ip.run_cell('display')
try:
ip.run_cell('del display')
except NameError:
pass # it's ok, it might be in builtins
# even if deleted it should be back
with AssertNotPrints('NameError'):
ip.run_cell('display')


def test_displayobject_repr():
h = display.HTML('<br />')
nt.assert_equal(repr(h), '<IPython.core.display.HTML object>')
Expand Down
25 changes: 25 additions & 0 deletions docs/source/interactive/plotting.rst
@@ -1,7 +1,32 @@
.. _plotting:

Rich Outputs
------------

One of the main feature of IPython when used as a kernel is its ability to
show rich output. This means that object that can be representing as image,
sounds, animation, (etc...) can be shown this way if the frontend support it.

In order for this to be possible, you need to use the ``display()`` function,
that should be available by default on IPython 5.4+ and 6.1+, or that you can
import with ``from IPython.display import display``. Then use ``display(<your
object>)`` instead of ``print()``, and if possible your object will be displayed
with a richer representation. In the terminal of course, there wont be much
difference as object are most of the time represented by text, but in notebook
and similar interface you will get richer outputs.


Plotting
--------

.. note::

Starting with IPython 5.0 and matplotlib 2.0 you can avoid the use of
IPython's specific magic and use
``matplotlib.pyplot.ion()``/``matplotlib.pyplot.ioff()`` which have the
advantages of working outside of IPython as well.


One major feature of the IPython kernel is the ability to display plots that
are the output of running code cells. The IPython kernel is designed to work
seamlessly with the matplotlib_ plotting library to provide this functionality.
Expand Down
13 changes: 13 additions & 0 deletions docs/source/whatsnew/version5.rst
Expand Up @@ -53,6 +53,19 @@ Implement display id and ability to update a given display. This should greatly
simplify a lot of code by removing the need for widgets and allow other frontend
to implement things like progress-bars. See :ghpull:`10048`

Display function
----------------

The :func:`display() <IPython.display.display>` function is now available by
default in an IPython session, meaning users can call it on any object to see
their rich representation. This should allow for better interactivity both at
the REPL and in notebook environment.

Scripts and library that rely on display and may be run outside of IPython still
need to import the display function using ``from IPython.display import
display``. See :ghpull:`10596`


Miscs
-----

Expand Down

0 comments on commit 4f93bf1

Please sign in to comment.