Fixes to the display system #370

Merged
merged 4 commits into from May 17, 2011
@@ -119,4 +119,12 @@ def display_json(*objs):
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'])
@@ -52,7 +52,8 @@ def _formatters_default(self):
SVGFormatter,
PNGFormatter,
LatexFormatter,
- JSONFormatter
+ JSONFormatter,
+ JavascriptFormatter
]
d = {}
for cls in formatter_classes:
@@ -220,22 +221,25 @@ def __call__(self, obj):
obj_id = id(obj)
try:
obj_class = getattr(obj, '__class__', None) or type(obj)
- if hasattr(obj_class, self.print_method):
- printer = getattr(obj_class, self.print_method)
- return printer(obj)
+ # First try to find registered singleton printers for the type.
try:
printer = self.singleton_printers[obj_id]
except (TypeError, KeyError):
pass
else:
return printer(obj)
+ # Next look for type_printers.
for cls in pretty._get_mro(obj_class):
if cls in self.type_printers:
return self.type_printers[cls](obj)
else:
printer = self._in_deferred_types(cls)
if printer is not None:
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
except Exception:
pass
@@ -339,8 +343,8 @@ def dtype_pprinter(obj, p, cycle):
# something.
enabled = Bool(True, config=False)
- # Look for a __pretty__ methods to use for pretty printing.
- print_method = Str('__pretty__')
+ # Look for a _repr_pretty_ methods to use for pretty printing.
+ print_method = Str('_repr_pretty_')
# Whether to pretty-print or not.
pprint = Bool(True, config=True)
@@ -442,66 +446,97 @@ class HTMLFormatter(BaseFormatter):
"""An HTML formatter.
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
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')
- print_method = Str('__html__')
+ print_method = Str('_repr_html_')
class SVGFormatter(BaseFormatter):
"""An SVG formatter.
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
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')
- print_method = Str('__svg__')
+ print_method = Str('_repr_svg_')
class PNGFormatter(BaseFormatter):
"""A PNG formatter.
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
- 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')
- print_method = Str('__png__')
+ print_method = Str('_repr_png_')
class LatexFormatter(BaseFormatter):
"""A LaTeX formatter.
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
this.
+
+ The return value of this formatter should be a valid LaTeX equation,
+ enclosed in either ```$``` or ```$$```.
"""
format_type = Str('text/latex')
- print_method = Str('__latex__')
+ print_method = Str('_repr_latex_')
class JSONFormatter(BaseFormatter):
"""A JSON string formatter.
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
this.
+
+ The return value of this formatter should be a valid JSON string.
"""
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(PlainTextFormatter)
@@ -510,6 +545,7 @@ class JSONFormatter(BaseFormatter):
FormatterABC.register(PNGFormatter)
FormatterABC.register(LatexFormatter)
FormatterABC.register(JSONFormatter)
+FormatterABC.register(JavascriptFormatter)
def format_display_data(obj, include=None, exclude=None):
@@ -458,7 +458,7 @@
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
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::
In [6]: ip = get_ipython()
@@ -39,13 +39,13 @@ def print_basic_unicode(o, p, cycle):
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')
# mathtext does not understand certain latex flags, so we try to replace
# them with suitable subs.
s = s.replace('\\operatorname','')
s = s.replace('\\overline', '\\bar')
- png = latex_to_png(s, encode=True)
+ png = latex_to_png(s)
return png
_loaded = False
@@ -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.
Parameters
@@ -45,6 +45,7 @@ def latex_to_png(s, encode=True):
bin_data = encodestring(bin_data)
return bin_data
+
_data_uri_template_png = """<img src="data:image/png;base64,%s" alt=%s />"""
def latex_to_html(s, alt='image'):
@@ -60,3 +61,50 @@ def latex_to_html(s, alt='image'):
base64_data = latex_to_png(s, encode=True)
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
+
View
@@ -29,26 +29,26 @@
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
- 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::
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
possibilities. The following list shows the python 2.5 version and the
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
__future__ import)::
class MyList(list):
- def __pretty__(self, p, cycle):
+ def _repr_pretty_(self, p, cycle):
if cycle:
p.text('MyList(...)')
else:
@@ -75,7 +75,7 @@ def __pretty__(self, p, cycle):
class MyList(list):
- def __pretty__(self, p, cycle):
+ def _repr_pretty_(self, p, cycle):
if cycle:
p.text('MyList(...)')
else:
@@ -164,7 +164,7 @@ class PrettyPrinter(_PrettyPrinterBase):
"""
Baseclass for the `RepresentationPrinter` prettyprinter that is used to
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.
"""
@@ -330,21 +330,24 @@ def pretty(self, obj):
self.begin_group()
try:
obj_class = getattr(obj, '__class__', None) or type(obj)
- if hasattr(obj_class, '__pretty__'):
- return obj_class.__pretty__(obj, self, cycle)
+ # First try to find registered singleton printers for the type.
try:
printer = self.singleton_pprinters[obj_id]
except (TypeError, KeyError):
pass
else:
return printer(obj, self, cycle)
+ # Next look for type_printers.
for cls in _get_mro(obj_class):
if cls in self.type_pprinters:
return self.type_pprinters[cls](obj, self, cycle)
else:
printer = self._in_deferred_types(cls)
if printer is not None:
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)
finally:
self.end_group()
@@ -60,7 +60,8 @@ def getfigs(*fig_nums):
f = Gcf.figs.get(num)
if f is None:
print('Warning: figure %s not available.' % num)
- figs.append(f.canvas.figure)
+ else:
+ figs.append(f.canvas.figure)
return figs
@@ -16,6 +16,7 @@
from __future__ import print_function
# Stdlib
+from base64 import encodestring
import inspect
import os
@@ -67,6 +68,9 @@ def write_output_prompt(self):
self.msg['content']['execution_count'] = self.prompt_count
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
def finish_displayhook(self):
Oops, something went wrong.