Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes to the display system #370

Merged
merged 4 commits into from May 17, 2011
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 8 additions & 0 deletions IPython/core/display.py
Expand Up @@ -119,4 +119,12 @@ def display_json(*objs):
display(*objs, include=['text/plain','application/json']) display(*objs, include=['text/plain','application/json'])




def display_javascript(*objs):
"""Display the Javascript representation of an object.


Parameters
----------
objs : tuple of objects
The Python objects to display.
"""
display(*objs, include=['text/plain','application/javascript'])
70 changes: 53 additions & 17 deletions IPython/core/formatters.py
Expand Up @@ -52,7 +52,8 @@ def _formatters_default(self):
SVGFormatter, SVGFormatter,
PNGFormatter, PNGFormatter,
LatexFormatter, LatexFormatter,
JSONFormatter JSONFormatter,
JavascriptFormatter
] ]
d = {} d = {}
for cls in formatter_classes: for cls in formatter_classes:
Expand Down Expand Up @@ -220,22 +221,25 @@ def __call__(self, obj):
obj_id = id(obj) obj_id = id(obj)
try: try:
obj_class = getattr(obj, '__class__', None) or type(obj) obj_class = getattr(obj, '__class__', None) or type(obj)
if hasattr(obj_class, self.print_method): # First try to find registered singleton printers for the type.
printer = getattr(obj_class, self.print_method)
return printer(obj)
try: try:
printer = self.singleton_printers[obj_id] printer = self.singleton_printers[obj_id]
except (TypeError, KeyError): except (TypeError, KeyError):
pass pass
else: else:
return printer(obj) return printer(obj)
# Next look for type_printers.
for cls in pretty._get_mro(obj_class): for cls in pretty._get_mro(obj_class):
if cls in self.type_printers: if cls in self.type_printers:
return self.type_printers[cls](obj) return self.type_printers[cls](obj)
else: else:
printer = self._in_deferred_types(cls) printer = self._in_deferred_types(cls)
if printer is not None: if printer is not None:
return printer(obj) return printer(obj)
# Finally look for special method names.
if hasattr(obj_class, self.print_method):
printer = getattr(obj_class, self.print_method)
return printer(obj)
return None return None
except Exception: except Exception:
pass pass
Expand Down Expand Up @@ -339,8 +343,8 @@ def dtype_pprinter(obj, p, cycle):
# something. # something.
enabled = Bool(True, config=False) enabled = Bool(True, config=False)


# Look for a __pretty__ methods to use for pretty printing. # Look for a _repr_pretty_ methods to use for pretty printing.
print_method = Str('__pretty__') print_method = Str('_repr_pretty_')


# Whether to pretty-print or not. # Whether to pretty-print or not.
pprint = Bool(True, config=True) pprint = Bool(True, config=True)
Expand Down Expand Up @@ -442,66 +446,97 @@ class HTMLFormatter(BaseFormatter):
"""An HTML formatter. """An HTML formatter.


To define the callables that compute the HTML representation of your To define the callables that compute the HTML representation of your
objects, define a :meth:`__html__` method or use the :meth:`for_type` objects, define a :meth:`_repr_html_` method or use the :meth:`for_type`
or :meth:`for_type_by_name` methods to register functions that handle or :meth:`for_type_by_name` methods to register functions that handle
this. this.

The return value of this formatter should be a valid HTML snippet that
could be injected into an existing DOM. It should *not* include the
```<html>`` or ```<body>`` tags.
""" """
format_type = Str('text/html') format_type = Str('text/html')


print_method = Str('__html__') print_method = Str('_repr_html_')




class SVGFormatter(BaseFormatter): class SVGFormatter(BaseFormatter):
"""An SVG formatter. """An SVG formatter.


To define the callables that compute the SVG representation of your To define the callables that compute the SVG representation of your
objects, define a :meth:`__svg__` method or use the :meth:`for_type` objects, define a :meth:`_repr_svg_` method or use the :meth:`for_type`
or :meth:`for_type_by_name` methods to register functions that handle or :meth:`for_type_by_name` methods to register functions that handle
this. this.

The return value of this formatter should be valid SVG enclosed in
```<svg>``` tags, that could be injected into an existing DOM. It should
*not* include the ```<html>`` or ```<body>`` tags.
""" """
format_type = Str('image/svg+xml') format_type = Str('image/svg+xml')


print_method = Str('__svg__') print_method = Str('_repr_svg_')




class PNGFormatter(BaseFormatter): class PNGFormatter(BaseFormatter):
"""A PNG formatter. """A PNG formatter.


To define the callables that compute the PNG representation of your To define the callables that compute the PNG representation of your
objects, define a :meth:`__png__` method or use the :meth:`for_type` objects, define a :meth:`_repr_png_` method or use the :meth:`for_type`
or :meth:`for_type_by_name` methods to register functions that handle or :meth:`for_type_by_name` methods to register functions that handle
this. The raw data should be the base64 encoded raw png data. this.

The return value of this formatter should be raw PNG data, *not*
base64 encoded.
""" """
format_type = Str('image/png') format_type = Str('image/png')


print_method = Str('__png__') print_method = Str('_repr_png_')




class LatexFormatter(BaseFormatter): class LatexFormatter(BaseFormatter):
"""A LaTeX formatter. """A LaTeX formatter.


To define the callables that compute the LaTeX representation of your To define the callables that compute the LaTeX representation of your
objects, define a :meth:`__latex__` method or use the :meth:`for_type` objects, define a :meth:`_repr_latex_` method or use the :meth:`for_type`
or :meth:`for_type_by_name` methods to register functions that handle or :meth:`for_type_by_name` methods to register functions that handle
this. this.

The return value of this formatter should be a valid LaTeX equation,
enclosed in either ```$``` or ```$$```.
""" """
format_type = Str('text/latex') format_type = Str('text/latex')


print_method = Str('__latex__') print_method = Str('_repr_latex_')




class JSONFormatter(BaseFormatter): class JSONFormatter(BaseFormatter):
"""A JSON string formatter. """A JSON string formatter.


To define the callables that compute the JSON string representation of To define the callables that compute the JSON string representation of
your objects, define a :meth:`__json__` method or use the :meth:`for_type` your objects, define a :meth:`_repr_json_` method or use the :meth:`for_type`
or :meth:`for_type_by_name` methods to register functions that handle or :meth:`for_type_by_name` methods to register functions that handle
this. this.

The return value of this formatter should be a valid JSON string.
""" """
format_type = Str('application/json') format_type = Str('application/json')


print_method = Str('__json__') print_method = Str('_repr_json_')


class JavascriptFormatter(BaseFormatter):
"""A Javascript formatter.

To define the callables that compute the Javascript representation of
your objects, define a :meth:`_repr_javascript_` method or use the
:meth:`for_type` or :meth:`for_type_by_name` methods to register functions
that handle this.

The return value of this formatter should be valid Javascript code and
should *not* be enclosed in ```<script>``` tags.
"""
format_type = Str('application/javascript')


print_method = Str('_repr_javascript_')


FormatterABC.register(BaseFormatter) FormatterABC.register(BaseFormatter)
FormatterABC.register(PlainTextFormatter) FormatterABC.register(PlainTextFormatter)
Expand All @@ -510,6 +545,7 @@ class JSONFormatter(BaseFormatter):
FormatterABC.register(PNGFormatter) FormatterABC.register(PNGFormatter)
FormatterABC.register(LatexFormatter) FormatterABC.register(LatexFormatter)
FormatterABC.register(JSONFormatter) FormatterABC.register(JSONFormatter)
FormatterABC.register(JavascriptFormatter)




def format_display_data(obj, include=None, exclude=None): def format_display_data(obj, include=None, exclude=None):
Expand Down
2 changes: 1 addition & 1 deletion IPython/core/usage.py
Expand Up @@ -458,7 +458,7 @@
representations will be displayed in the console as long as the objects know representations will be displayed in the console as long as the objects know
how to compute those representations. The easiest way of teaching objects how how to compute those representations. The easiest way of teaching objects how
to format themselves in various representations is to define special methods to format themselves in various representations is to define special methods
such as: ``__html``, ``__svg__`` and ``__png__``. IPython's display formatters such as: ``_repr_html_``, ``_repr_svg_`` and ``_repr_png_``. IPython's display formatters
can also be given custom formatter functions for various types:: can also be given custom formatter functions for various types::


In [6]: ip = get_ipython() In [6]: ip = get_ipython()
Expand Down
Expand Up @@ -39,13 +39,13 @@ def print_basic_unicode(o, p, cycle):




def print_png(o): def print_png(o):
"""A funciton to display sympy expression using LaTex -> PNG.""" """A function to display sympy expression using LaTex -> PNG."""
s = latex(o, mode='inline') s = latex(o, mode='inline')
# mathtext does not understand certain latex flags, so we try to replace # mathtext does not understand certain latex flags, so we try to replace
# them with suitable subs. # them with suitable subs.
s = s.replace('\\operatorname','') s = s.replace('\\operatorname','')
s = s.replace('\\overline', '\\bar') s = s.replace('\\overline', '\\bar')
png = latex_to_png(s, encode=True) png = latex_to_png(s)
return png return png


_loaded = False _loaded = False
Expand Down
50 changes: 49 additions & 1 deletion IPython/lib/latextools.py
Expand Up @@ -25,7 +25,7 @@
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------




def latex_to_png(s, encode=True): def latex_to_png(s, encode=False):
"""Render a LaTeX string to PNG using matplotlib.mathtext. """Render a LaTeX string to PNG using matplotlib.mathtext.


Parameters Parameters
Expand All @@ -45,6 +45,7 @@ def latex_to_png(s, encode=True):
bin_data = encodestring(bin_data) bin_data = encodestring(bin_data)
return bin_data return bin_data



_data_uri_template_png = """<img src="data:image/png;base64,%s" alt=%s />""" _data_uri_template_png = """<img src="data:image/png;base64,%s" alt=%s />"""


def latex_to_html(s, alt='image'): def latex_to_html(s, alt='image'):
Expand All @@ -60,3 +61,50 @@ def latex_to_html(s, alt='image'):
base64_data = latex_to_png(s, encode=True) base64_data = latex_to_png(s, encode=True)
return _data_uri_template_png % (base64_data, alt) return _data_uri_template_png % (base64_data, alt)



# From matplotlib, thanks to mdboom. Once this is in matplotlib releases, we
# will remove.
def math_to_image(s, filename_or_obj, prop=None, dpi=None, format=None):
"""
Given a math expression, renders it in a closely-clipped bounding
box to an image file.

*s*
A math expression. The math portion should be enclosed in
dollar signs.

*filename_or_obj*
A filepath or writable file-like object to write the image data
to.

*prop*
If provided, a FontProperties() object describing the size and
style of the text.

*dpi*
Override the output dpi, otherwise use the default associated
with the output format.

*format*
The output format, eg. 'svg', 'pdf', 'ps' or 'png'. If not
provided, will be deduced from the filename.
"""
from matplotlib import figure
# backend_agg supports all of the core output formats
from matplotlib.backends import backend_agg
from matplotlib.font_manager import FontProperties
from matplotlib.mathtext import MathTextParser

if prop is None:
prop = FontProperties()

parser = MathTextParser('path')
width, height, depth, _, _ = parser.parse(s, dpi=72, prop=prop)

fig = figure.Figure(figsize=(width / 72.0, height / 72.0))
fig.text(0, depth/height, s, fontproperties=prop)
backend_agg.FigureCanvasAgg(fig)
fig.savefig(filename_or_obj, dpi=dpi, format=format)

return depth

19 changes: 11 additions & 8 deletions IPython/lib/pretty.py
Expand Up @@ -29,26 +29,26 @@


The pretty library allows developers to add pretty printing rules for their The pretty library allows developers to add pretty printing rules for their
own objects. This process is straightforward. All you have to do is to own objects. This process is straightforward. All you have to do is to
add a `__pretty__` method to your object and call the methods on the add a `_repr_pretty_` method to your object and call the methods on the
pretty printer passed:: pretty printer passed::


class MyObject(object): class MyObject(object):


def __pretty__(self, p, cycle): def _repr_pretty_(self, p, cycle):
... ...


Depending on the python version you want to support you have two Depending on the python version you want to support you have two
possibilities. The following list shows the python 2.5 version and the possibilities. The following list shows the python 2.5 version and the
compatibility one. compatibility one.




Here the example implementation of a `__pretty__` method for a list Here the example implementation of a `_repr_pretty_` method for a list
subclass for python 2.5 and higher (python 2.5 requires the with statement subclass for python 2.5 and higher (python 2.5 requires the with statement
__future__ import):: __future__ import)::


class MyList(list): class MyList(list):


def __pretty__(self, p, cycle): def _repr_pretty_(self, p, cycle):
if cycle: if cycle:
p.text('MyList(...)') p.text('MyList(...)')
else: else:
Expand All @@ -75,7 +75,7 @@ def __pretty__(self, p, cycle):


class MyList(list): class MyList(list):


def __pretty__(self, p, cycle): def _repr_pretty_(self, p, cycle):
if cycle: if cycle:
p.text('MyList(...)') p.text('MyList(...)')
else: else:
Expand Down Expand Up @@ -164,7 +164,7 @@ class PrettyPrinter(_PrettyPrinterBase):
""" """
Baseclass for the `RepresentationPrinter` prettyprinter that is used to Baseclass for the `RepresentationPrinter` prettyprinter that is used to
generate pretty reprs of objects. Contrary to the `RepresentationPrinter` generate pretty reprs of objects. Contrary to the `RepresentationPrinter`
this printer knows nothing about the default pprinters or the `__pretty__` this printer knows nothing about the default pprinters or the `_repr_pretty_`
callback method. callback method.
""" """


Expand Down Expand Up @@ -330,21 +330,24 @@ def pretty(self, obj):
self.begin_group() self.begin_group()
try: try:
obj_class = getattr(obj, '__class__', None) or type(obj) obj_class = getattr(obj, '__class__', None) or type(obj)
if hasattr(obj_class, '__pretty__'): # First try to find registered singleton printers for the type.
return obj_class.__pretty__(obj, self, cycle)
try: try:
printer = self.singleton_pprinters[obj_id] printer = self.singleton_pprinters[obj_id]
except (TypeError, KeyError): except (TypeError, KeyError):
pass pass
else: else:
return printer(obj, self, cycle) return printer(obj, self, cycle)
# Next look for type_printers.
for cls in _get_mro(obj_class): for cls in _get_mro(obj_class):
if cls in self.type_pprinters: if cls in self.type_pprinters:
return self.type_pprinters[cls](obj, self, cycle) return self.type_pprinters[cls](obj, self, cycle)
else: else:
printer = self._in_deferred_types(cls) printer = self._in_deferred_types(cls)
if printer is not None: if printer is not None:
return printer(obj, self, cycle) return printer(obj, self, cycle)
# Finally look for special method names.
if hasattr(obj_class, '_repr_pretty_'):
return obj_class._repr_pretty_(obj, self, cycle)
return _default_pprint(obj, self, cycle) return _default_pprint(obj, self, cycle)
finally: finally:
self.end_group() self.end_group()
Expand Down
3 changes: 2 additions & 1 deletion IPython/lib/pylabtools.py
Expand Up @@ -60,7 +60,8 @@ def getfigs(*fig_nums):
f = Gcf.figs.get(num) f = Gcf.figs.get(num)
if f is None: if f is None:
print('Warning: figure %s not available.' % num) print('Warning: figure %s not available.' % num)
figs.append(f.canvas.figure) else:
figs.append(f.canvas.figure)
return figs return figs




Expand Down
4 changes: 4 additions & 0 deletions IPython/zmq/zmqshell.py
Expand Up @@ -16,6 +16,7 @@
from __future__ import print_function from __future__ import print_function


# Stdlib # Stdlib
from base64 import encodestring
import inspect import inspect
import os import os


Expand Down Expand Up @@ -67,6 +68,9 @@ def write_output_prompt(self):
self.msg['content']['execution_count'] = self.prompt_count self.msg['content']['execution_count'] = self.prompt_count


def write_format_data(self, format_dict): def write_format_data(self, format_dict):
pngdata = format_dict.get('image/png')
if pngdata is not None:
format_dict['image/png'] = encodestring(pngdata)
self.msg['content']['data'] = format_dict self.msg['content']['data'] = format_dict


def finish_displayhook(self): def finish_displayhook(self):
Expand Down