formatters.py
557 lines
| 18.8 KiB
| text/x-python
|
PythonLexer
Robert Kern
|
r3209 | # -*- coding: utf-8 -*- | ||
Brian Granger
|
r3278 | """Display formatters. | ||
Authors: | ||||
* Robert Kern | ||||
* Brian Granger | ||||
Robert Kern
|
r3209 | """ | ||
Robert Kern
|
r3225 | #----------------------------------------------------------------------------- | ||
# Copyright (c) 2010, IPython Development Team. | ||||
# | ||||
# Distributed under the terms of the Modified BSD License. | ||||
# | ||||
# The full license is in the file COPYING.txt, distributed with this software. | ||||
#----------------------------------------------------------------------------- | ||||
Robert Kern
|
r3209 | |||
Brian Granger
|
r3279 | #----------------------------------------------------------------------------- | ||
# Imports | ||||
#----------------------------------------------------------------------------- | ||||
Robert Kern
|
r3222 | # Stdlib imports | ||
Robert Kern
|
r3209 | import abc | ||
MinRK
|
r3352 | import sys | ||
Brian Granger
|
r3278 | # We must use StringIO, as cStringIO doesn't handle unicode properly. | ||
from StringIO import StringIO | ||||
Robert Kern
|
r3209 | |||
Robert Kern
|
r3222 | # Our own imports | ||
Robert Kern
|
r3209 | from IPython.config.configurable import Configurable | ||
Thomas Spura
|
r3413 | from IPython.lib import pretty | ||
MinRK
|
r3350 | from IPython.utils.traitlets import Bool, Dict, Int, Str, CStr | ||
Robert Kern
|
r3209 | |||
Robert Kern
|
r3222 | #----------------------------------------------------------------------------- | ||
Brian Granger
|
r3278 | # The main DisplayFormatter class | ||
Robert Kern
|
r3222 | #----------------------------------------------------------------------------- | ||
Brian Granger
|
r3278 | |||
class DisplayFormatter(Configurable): | ||||
Brian Granger
|
r3280 | # When set to true only the default plain text formatter will be used. | ||
plain_text_only = Bool(False, config=True) | ||||
Brian Granger
|
r3278 | # A dict of formatter whose keys are format types (MIME types) and whose | ||
# values are subclasses of BaseFormatter. | ||||
formatters = Dict(config=True) | ||||
def _formatters_default(self): | ||||
"""Activate the default formatters.""" | ||||
formatter_classes = [ | ||||
PlainTextFormatter, | ||||
HTMLFormatter, | ||||
SVGFormatter, | ||||
PNGFormatter, | ||||
LatexFormatter, | ||||
JSONFormatter | ||||
] | ||||
d = {} | ||||
for cls in formatter_classes: | ||||
f = cls(config=self.config) | ||||
d[f.format_type] = f | ||||
return d | ||||
def format(self, obj, include=None, exclude=None): | ||||
"""Return a format data dict for an object. | ||||
By default all format types will be computed. | ||||
The following MIME types are currently implemented: | ||||
* text/plain | ||||
* text/html | ||||
* text/latex | ||||
* application/json | ||||
* image/png | ||||
* immage/svg+xml | ||||
Parameters | ||||
---------- | ||||
obj : object | ||||
The Python object whose format data will be computed. | ||||
Brian Granger
|
r3282 | include : list or tuple, 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 | ||||
A list of format type string (MIME types) to exclue in the format | ||||
data dict. If this is set all format types will be computed, | ||||
except for those included in this argument. | ||||
Brian Granger
|
r3278 | |||
Returns | ||||
------- | ||||
format_dict : dict | ||||
A dictionary of key/value pairs, one or each format that was | ||||
generated for the object. The keys are the format types, which | ||||
will usually be MIME type strings and the values and JSON'able | ||||
data structure containing the raw data for the representation in | ||||
that format. | ||||
""" | ||||
format_dict = {} | ||||
Brian Granger
|
r3280 | |||
# If plain text only is active | ||||
if self.plain_text_only: | ||||
formatter = self.formatters['text/plain'] | ||||
try: | ||||
data = formatter(obj) | ||||
except: | ||||
# FIXME: log the exception | ||||
raise | ||||
if data is not None: | ||||
format_dict['text/plain'] = data | ||||
return format_dict | ||||
Brian Granger
|
r3278 | for format_type, formatter in self.formatters.items(): | ||
if include is not None: | ||||
if format_type not in include: | ||||
continue | ||||
if exclude is not None: | ||||
if format_type in exclude: | ||||
continue | ||||
try: | ||||
data = formatter(obj) | ||||
except: | ||||
# FIXME: log the exception | ||||
raise | ||||
if data is not None: | ||||
format_dict[format_type] = data | ||||
return format_dict | ||||
@property | ||||
def format_types(self): | ||||
"""Return the format types (MIME types) of the active formatters.""" | ||||
return self.formatters.keys() | ||||
#----------------------------------------------------------------------------- | ||||
# Formatters for specific format types (text, html, svg, etc.) | ||||
#----------------------------------------------------------------------------- | ||||
class FormatterABC(object): | ||||
""" Abstract base class for Formatters. | ||||
A formatter is a callable class that is responsible for computing the | ||||
raw format data for a particular format type (MIME type). For example, | ||||
an HTML formatter would have a format type of `text/html` and would return | ||||
the HTML representation of the object when called. | ||||
Robert Kern
|
r3209 | """ | ||
Brian Granger
|
r3278 | __metaclass__ = abc.ABCMeta | ||
Robert Kern
|
r3209 | |||
Brian Granger
|
r3278 | # The format type of the data returned, usually a MIME type. | ||
format_type = 'text/plain' | ||||
Robert Kern
|
r3209 | |||
Brian Granger
|
r3280 | # Is the formatter enabled... | ||
enabled = True | ||||
Brian Granger
|
r3278 | @abc.abstractmethod | ||
def __call__(self, obj): | ||||
"""Return a JSON'able representation of the object. | ||||
If the object cannot be formatted by this formatter, then return None | ||||
""" | ||||
try: | ||||
return repr(obj) | ||||
except TypeError: | ||||
return None | ||||
class BaseFormatter(Configurable): | ||||
"""A base formatter class that is configurable. | ||||
This formatter should usually be used as the base class of all formatters. | ||||
It is a traited :class:`Configurable` class and includes an extensible | ||||
API for users to determine how their objects are formatted. The following | ||||
logic is used to find a function to format an given object. | ||||
1. The object is introspected to see if it has a method with the name | ||||
:attr:`print_method`. If is does, that object is passed to that method | ||||
for formatting. | ||||
2. If no print method is found, three internal dictionaries are consulted | ||||
to find print method: :attr:`singleton_printers`, :attr:`type_printers` | ||||
and :attr:`deferred_printers`. | ||||
Brian Granger
|
r3288 | Users should use these dictionaries to register functions that will be | ||
used to compute the format data for their objects (if those objects don't | ||||
have the special print methods). The easiest way of using these | ||||
dictionaries is through the :meth:`for_type` and :meth:`for_type_by_name` | ||||
methods. | ||||
Brian Granger
|
r3278 | |||
If no function/callable is found to compute the format data, ``None`` is | ||||
returned and this format type is not used. | ||||
""" | ||||
format_type = Str('text/plain') | ||||
Brian Granger
|
r3280 | enabled = Bool(True, config=True) | ||
Brian Granger
|
r3278 | print_method = Str('__repr__') | ||
# The singleton printers. | ||||
# Maps the IDs of the builtin singleton objects to the format functions. | ||||
singleton_printers = Dict(config=True) | ||||
def _singleton_printers_default(self): | ||||
return {} | ||||
# The type-specific printers. | ||||
# Map type objects to the format functions. | ||||
type_printers = Dict(config=True) | ||||
def _type_printers_default(self): | ||||
return {} | ||||
# The deferred-import type-specific printers. | ||||
# Map (modulename, classname) pairs to the format functions. | ||||
deferred_printers = Dict(config=True) | ||||
def _deferred_printers_default(self): | ||||
return {} | ||||
def __call__(self, obj): | ||||
"""Compute the format for an object.""" | ||||
Brian Granger
|
r3280 | if self.enabled: | ||
obj_id = id(obj) | ||||
Brian Granger
|
r3278 | try: | ||
Brian Granger
|
r3280 | 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) | ||||
try: | ||||
printer = self.singleton_printers[obj_id] | ||||
except (TypeError, KeyError): | ||||
pass | ||||
Brian Granger
|
r3278 | else: | ||
Brian Granger
|
r3280 | return printer(obj) | ||
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) | ||||
return None | ||||
except Exception: | ||||
pass | ||||
else: | ||||
Brian Granger
|
r3278 | return None | ||
def for_type(self, typ, func): | ||||
"""Add a format function for a given type. | ||||
Brian Granger
|
r3288 | Parameters | ||
Brian Granger
|
r3278 | ----------- | ||
typ : class | ||||
The class of the object that will be formatted using `func`. | ||||
func : callable | ||||
The callable that will be called to compute the format data. The | ||||
call signature of this function is simple, it must take the | ||||
object to be formatted and return the raw data for the given | ||||
format. Subclasses may use a different call signature for the | ||||
`func` argument. | ||||
""" | ||||
oldfunc = self.type_printers.get(typ, None) | ||||
if func is not None: | ||||
# To support easy restoration of old printers, we need to ignore | ||||
# Nones. | ||||
self.type_printers[typ] = func | ||||
return oldfunc | ||||
def for_type_by_name(self, type_module, type_name, func): | ||||
"""Add a format function for a type specified by the full dotted | ||||
module and name of the type, rather than the type of the object. | ||||
Parameters | ||||
---------- | ||||
type_module : str | ||||
The full dotted name of the module the type is defined in, like | ||||
``numpy``. | ||||
type_name : str | ||||
The name of the type (the class name), like ``dtype`` | ||||
func : callable | ||||
The callable that will be called to compute the format data. The | ||||
call signature of this function is simple, it must take the | ||||
object to be formatted and return the raw data for the given | ||||
format. Subclasses may use a different call signature for the | ||||
`func` argument. | ||||
""" | ||||
key = (type_module, type_name) | ||||
oldfunc = self.deferred_printers.get(key, None) | ||||
if func is not None: | ||||
# To support easy restoration of old printers, we need to ignore | ||||
# Nones. | ||||
self.deferred_printers[key] = func | ||||
return oldfunc | ||||
Brian Granger
|
r3279 | def _in_deferred_types(self, cls): | ||
""" | ||||
Check if the given class is specified in the deferred type registry. | ||||
Returns the printer from the registry if it exists, and None if the | ||||
class is not in the registry. Successful matches will be moved to the | ||||
regular type registry for future use. | ||||
""" | ||||
mod = getattr(cls, '__module__', None) | ||||
name = getattr(cls, '__name__', None) | ||||
key = (mod, name) | ||||
printer = None | ||||
if key in self.deferred_printers: | ||||
# Move the printer over to the regular registry. | ||||
printer = self.deferred_printers.pop(key) | ||||
self.type_printers[cls] = printer | ||||
return printer | ||||
Brian Granger
|
r3278 | |||
Brian Granger
|
r3280 | |||
Brian Granger
|
r3278 | class PlainTextFormatter(BaseFormatter): | ||
"""The default pretty-printer. | ||||
This uses :mod:`IPython.external.pretty` to compute the format data of | ||||
the object. If the object cannot be pretty printed, :func:`repr` is used. | ||||
See the documentation of :mod:`IPython.external.pretty` for details on | ||||
how to write pretty printers. Here is a simple example:: | ||||
def dtype_pprinter(obj, p, cycle): | ||||
if cycle: | ||||
return p.text('dtype(...)') | ||||
if hasattr(obj, 'fields'): | ||||
if obj.fields is None: | ||||
p.text(repr(obj)) | ||||
else: | ||||
p.begin_group(7, 'dtype([') | ||||
for i, field in enumerate(obj.descr): | ||||
if i > 0: | ||||
p.text(',') | ||||
p.breakable() | ||||
p.pretty(field) | ||||
p.end_group(7, '])') | ||||
""" | ||||
# The format type of data returned. | ||||
format_type = Str('text/plain') | ||||
Brian Granger
|
r3280 | # This subclass ignores this attribute as it always need to return | ||
# something. | ||||
enabled = Bool(True, config=False) | ||||
Brian Granger
|
r3278 | # Look for a __pretty__ methods to use for pretty printing. | ||
print_method = Str('__pretty__') | ||||
Robert Kern
|
r3209 | |||
# Whether to pretty-print or not. | ||||
Robert Kern
|
r3210 | pprint = Bool(True, config=True) | ||
Robert Kern
|
r3209 | |||
# Whether to be verbose or not. | ||||
Robert Kern
|
r3210 | verbose = Bool(False, config=True) | ||
Robert Kern
|
r3209 | |||
# The maximum width. | ||||
Robert Kern
|
r3210 | max_width = Int(79, config=True) | ||
Robert Kern
|
r3209 | |||
# The newline character. | ||||
Robert Kern
|
r3210 | newline = Str('\n', config=True) | ||
MinRK
|
r3350 | |||
# format-string for pprinting floats | ||||
float_format = Str('%r') | ||||
# setter for float precision, either int or direct format-string | ||||
float_precision = CStr('', config=True) | ||||
def _float_precision_changed(self, name, old, new): | ||||
"""float_precision changed, set float_format accordingly. | ||||
float_precision can be set by int or str. | ||||
This will set float_format, after interpreting input. | ||||
If numpy has been imported, numpy print precision will also be set. | ||||
integer `n` sets format to '%.nf', otherwise, format set directly. | ||||
An empty string returns to defaults (repr for float, 8 for numpy). | ||||
This parameter can be set via the '%precision' magic. | ||||
""" | ||||
if '%' in new: | ||||
# got explicit format string | ||||
fmt = new | ||||
try: | ||||
fmt%3.14159 | ||||
except Exception: | ||||
raise ValueError("Precision must be int or format string, not %r"%new) | ||||
elif new: | ||||
# otherwise, should be an int | ||||
try: | ||||
i = int(new) | ||||
assert i >= 0 | ||||
except ValueError: | ||||
raise ValueError("Precision must be int or format string, not %r"%new) | ||||
except AssertionError: | ||||
raise ValueError("int precision must be non-negative, not %r"%i) | ||||
fmt = '%%.%if'%i | ||||
if 'numpy' in sys.modules: | ||||
# set numpy precision if it has been imported | ||||
import numpy | ||||
numpy.set_printoptions(precision=i) | ||||
else: | ||||
# default back to repr | ||||
fmt = '%r' | ||||
if 'numpy' in sys.modules: | ||||
import numpy | ||||
# numpy default is 8 | ||||
numpy.set_printoptions(precision=8) | ||||
self.float_format = fmt | ||||
Robert Kern
|
r3209 | |||
Brian Granger
|
r3278 | # Use the default pretty printers from IPython.external.pretty. | ||
def _singleton_printers_default(self): | ||||
Robert Kern
|
r3209 | return pretty._singleton_pprinters.copy() | ||
Brian Granger
|
r3278 | def _type_printers_default(self): | ||
MinRK
|
r3350 | d = pretty._type_pprinters.copy() | ||
d[float] = lambda obj,p,cycle: p.text(self.float_format%obj) | ||||
return d | ||||
Robert Kern
|
r3209 | |||
Brian Granger
|
r3278 | def _deferred_printers_default(self): | ||
Robert Kern
|
r3209 | return pretty._deferred_type_pprinters.copy() | ||
#### FormatterABC interface #### | ||||
def __call__(self, obj): | ||||
Brian Granger
|
r3278 | """Compute the pretty representation of the object.""" | ||
Robert Kern
|
r3209 | if not self.pprint: | ||
Robert Kern
|
r3210 | try: | ||
return repr(obj) | ||||
except TypeError: | ||||
return '' | ||||
Robert Kern
|
r3209 | else: | ||
Brian Granger
|
r3278 | # This uses use StringIO, as cStringIO doesn't handle unicode. | ||
Robert Kern
|
r3209 | stream = StringIO() | ||
printer = pretty.RepresentationPrinter(stream, self.verbose, | ||||
self.max_width, self.newline, | ||||
Brian Granger
|
r3278 | singleton_pprinters=self.singleton_printers, | ||
type_pprinters=self.type_printers, | ||||
deferred_pprinters=self.deferred_printers) | ||||
Robert Kern
|
r3209 | printer.pretty(obj) | ||
printer.flush() | ||||
return stream.getvalue() | ||||
Brian Granger
|
r3278 | class HTMLFormatter(BaseFormatter): | ||
"""An HTML formatter. | ||||
Robert Kern
|
r3209 | |||
Brian Granger
|
r3278 | To define the callables that compute the HTML representation of your | ||
objects, define a :meth:`__html__` method or use the :meth:`for_type` | ||||
or :meth:`for_type_by_name` methods to register functions that handle | ||||
this. | ||||
""" | ||||
format_type = Str('text/html') | ||||
Robert Kern
|
r3209 | |||
Brian Granger
|
r3278 | print_method = Str('__html__') | ||
Robert Kern
|
r3209 | |||
Brian Granger
|
r3278 | 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` | ||||
or :meth:`for_type_by_name` methods to register functions that handle | ||||
this. | ||||
Robert Kern
|
r3209 | """ | ||
Brian Granger
|
r3278 | format_type = Str('image/svg+xml') | ||
Robert Kern
|
r3209 | |||
Brian Granger
|
r3278 | print_method = Str('__svg__') | ||
Robert Kern
|
r3209 | |||
Brian Granger
|
r3278 | class PNGFormatter(BaseFormatter): | ||
"""A PNG formatter. | ||||
Robert Kern
|
r3214 | |||
Brian Granger
|
r3278 | To define the callables that compute the PNG representation of your | ||
Brian Granger
|
r3282 | objects, define a :meth:`__png__` method or use the :meth:`for_type` | ||
Brian Granger
|
r3278 | or :meth:`for_type_by_name` methods to register functions that handle | ||
this. The raw data should be the base64 encoded raw png data. | ||||
""" | ||||
format_type = Str('image/png') | ||||
print_method = Str('__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` | ||||
or :meth:`for_type_by_name` methods to register functions that handle | ||||
this. | ||||
""" | ||||
format_type = Str('text/latex') | ||||
print_method = Str('__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` | ||||
or :meth:`for_type_by_name` methods to register functions that handle | ||||
this. | ||||
""" | ||||
format_type = Str('application/json') | ||||
print_method = Str('__json__') | ||||
FormatterABC.register(BaseFormatter) | ||||
FormatterABC.register(PlainTextFormatter) | ||||
FormatterABC.register(HTMLFormatter) | ||||
FormatterABC.register(SVGFormatter) | ||||
FormatterABC.register(PNGFormatter) | ||||
FormatterABC.register(LatexFormatter) | ||||
FormatterABC.register(JSONFormatter) | ||||
def format_display_data(obj, include=None, exclude=None): | ||||
"""Return a format data dict for an object. | ||||
By default all format types will be computed. | ||||
The following MIME types are currently implemented: | ||||
* text/plain | ||||
* text/html | ||||
* text/latex | ||||
* application/json | ||||
* image/png | ||||
* immage/svg+xml | ||||
Parameters | ||||
---------- | ||||
obj : object | ||||
The Python object whose format data will be computed. | ||||
Returns | ||||
------- | ||||
format_dict : dict | ||||
A dictionary of key/value pairs, one or each format that was | ||||
generated for the object. The keys are the format types, which | ||||
will usually be MIME type strings and the values and JSON'able | ||||
data structure containing the raw data for the representation in | ||||
that format. | ||||
include : list or tuple, 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 | ||||
A list of format type string (MIME types) to exclue in the format | ||||
data dict. If this is set all format types will be computed, | ||||
except for those included in this argument. | ||||
""" | ||||
from IPython.core.interactiveshell import InteractiveShell | ||||
Robert Kern
|
r3209 | |||
Brian Granger
|
r3278 | InteractiveShell.instance().display_formatter.format( | ||
obj, | ||||
include, | ||||
exclude | ||||
) | ||||