pretty.py
222 lines
| 6.9 KiB
| text/x-python
|
PythonLexer
Brian Granger
|
r2281 | """Use pretty.py for configurable pretty-printing. | ||
To enable this extension in your configuration | ||||
file, add the following to :file:`ipython_config.py`:: | ||||
c.Global.extensions = ['IPython.extensions.pretty'] | ||||
Brian Granger
|
r2282 | def dict_pprinter(obj, p, cycle): | ||
return p.text("<dict>") | ||||
Brian Granger
|
r2281 | c.PrettyResultDisplay.verbose = True | ||
Brian Granger
|
r2282 | c.PrettyResultDisplay.defaults_for_type = [ | ||
(dict, dict_pprinter) | ||||
] | ||||
c.PrettyResultDisplay.defaults_for_type_by_name = [ | ||||
Brian Granger
|
r2281 | ('numpy', 'dtype', 'IPython.extensions.pretty.dtype_pprinter') | ||
] | ||||
This extension can also be loaded by using the ``%load_ext`` magic:: | ||||
%load_ext IPython.extensions.pretty | ||||
If this extension is enabled, you can always add additional pretty printers | ||||
by doing:: | ||||
ip = get_ipython() | ||||
prd = ip.get_component('pretty_result_display') | ||||
import numpy | ||||
from IPython.extensions.pretty import dtype_pprinter | ||||
prd.for_type(numpy.dtype, dtype_pprinter) | ||||
# If you don't want to have numpy imported until it needs to be: | ||||
prd.for_type_by_name('numpy', 'dtype', dtype_pprinter) | ||||
""" | ||||
#----------------------------------------------------------------------------- | ||||
# Imports | ||||
#----------------------------------------------------------------------------- | ||||
from IPython.core.error import TryNext | ||||
from IPython.external import pretty | ||||
from IPython.core.component import Component | ||||
from IPython.utils.traitlets import Bool, List | ||||
from IPython.utils.genutils import Term | ||||
from IPython.utils.autoattr import auto_attr | ||||
from IPython.utils.importstring import import_item | ||||
#----------------------------------------------------------------------------- | ||||
# Code | ||||
#----------------------------------------------------------------------------- | ||||
Brian Granger
|
r2282 | |||
Brian Granger
|
r2281 | _loaded = False | ||
class PrettyResultDisplay(Component): | ||||
Brian Granger
|
r2282 | """A component for pretty printing on steroids.""" | ||
Brian Granger
|
r2281 | |||
verbose = Bool(False, config=True) | ||||
Brian Granger
|
r2282 | |||
# A list of (type, func_name), like | ||||
# [(dict, 'my_dict_printer')] | ||||
# The final argument can also be a callable | ||||
defaults_for_type = List(default_value=[], config=True) | ||||
Brian Granger
|
r2281 | # A list of (module_name, type_name, func_name), like | ||
# [('numpy', 'dtype', 'IPython.extensions.pretty.dtype_pprinter')] | ||||
Brian Granger
|
r2282 | # The final argument can also be a callable | ||
defaults_for_type_by_name = List(default_value=[], config=True) | ||||
Brian Granger
|
r2281 | |||
def __init__(self, parent, name=None, config=None): | ||||
super(PrettyResultDisplay, self).__init__(parent, name=name, config=config) | ||||
Brian Granger
|
r2282 | self._setup_defaults() | ||
Brian Granger
|
r2281 | |||
Brian Granger
|
r2282 | def _setup_defaults(self): | ||
Brian Granger
|
r2281 | """Initialize the default pretty printers.""" | ||
Brian Granger
|
r2282 | for typ, func_name in self.defaults_for_type: | ||
func = self._resolve_func_name(func_name) | ||||
self.for_type(typ, func) | ||||
for type_module, type_name, func_name in self.defaults_for_type_by_name: | ||||
func = self._resolve_func_name(func_name) | ||||
Brian Granger
|
r2281 | self.for_type_by_name(type_module, type_name, func) | ||
Brian Granger
|
r2282 | def _resolve_func_name(self, func_name): | ||
if callable(func_name): | ||||
return func_name | ||||
elif isinstance(func_name, basestring): | ||||
return import_item(func_name) | ||||
else: | ||||
raise TypeError('func_name must be a str or callable, got: %r' % func_name) | ||||
# Access other components like this rather than by a regular attribute. | ||||
# This won't lookup the InteractiveShell object until it is used and | ||||
# then it is cached. This is both efficient and couples this class | ||||
# more loosely to InteractiveShell. | ||||
Brian Granger
|
r2281 | @auto_attr | ||
def shell(self): | ||||
return Component.get_instances( | ||||
root=self.root, | ||||
klass='IPython.core.iplib.InteractiveShell')[0] | ||||
def __call__(self, otherself, arg): | ||||
"""Uber-pretty-printing display hook. | ||||
Called for displaying the result to the user. | ||||
""" | ||||
if self.shell.pprint: | ||||
out = pretty.pretty(arg, verbose=self.verbose) | ||||
if '\n' in out: | ||||
# So that multi-line strings line up with the left column of | ||||
# the screen, instead of having the output prompt mess up | ||||
# their first line. | ||||
Term.cout.write('\n') | ||||
print >>Term.cout, out | ||||
else: | ||||
raise TryNext | ||||
def for_type(self, typ, func): | ||||
"""Add a pretty printer for a type.""" | ||||
return pretty.for_type(typ, func) | ||||
def for_type_by_name(self, type_module, type_name, func): | ||||
"""Add a pretty printer for a type by its name and module name.""" | ||||
Brian Granger
|
r2282 | return pretty.for_type_by_name(type_module, type_name, func) | ||
Brian Granger
|
r2281 | |||
#----------------------------------------------------------------------------- | ||||
# Initialization code for the extension | ||||
#----------------------------------------------------------------------------- | ||||
def load_ipython_extension(ip): | ||||
Brian Granger
|
r2282 | """Load the extension in IPython as a hook.""" | ||
Brian Granger
|
r2281 | global _loaded | ||
if not _loaded: | ||||
prd = PrettyResultDisplay(ip, name='pretty_result_display') | ||||
ip.set_hook('result_display', prd, priority=99) | ||||
_loaded = True | ||||
def unload_ipython_extension(ip): | ||||
Brian Granger
|
r2282 | """Unload the extension.""" | ||
Brian Granger
|
r2281 | # The hook system does not have a way to remove a hook so this is a pass | ||
pass | ||||
#----------------------------------------------------------------------------- | ||||
# Example pretty printers | ||||
#----------------------------------------------------------------------------- | ||||
def dtype_pprinter(obj, p, cycle): | ||||
""" A pretty-printer for numpy dtype objects. | ||||
""" | ||||
if cycle: | ||||
return p.text('dtype(...)') | ||||
Brian Granger
|
r2282 | 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, '])') | ||||
Brian Granger
|
r2281 | |||
#----------------------------------------------------------------------------- | ||||
# Tests | ||||
#----------------------------------------------------------------------------- | ||||
def test_pretty(): | ||||
""" | ||||
In [1]: from IPython.extensions import ipy_pretty | ||||
In [2]: ipy_pretty.activate() | ||||
In [3]: class A(object): | ||||
...: def __repr__(self): | ||||
...: return 'A()' | ||||
...: | ||||
...: | ||||
In [4]: a = A() | ||||
In [5]: a | ||||
Out[5]: A() | ||||
In [6]: def a_pretty_printer(obj, p, cycle): | ||||
...: p.text('<A>') | ||||
...: | ||||
...: | ||||
In [7]: ipy_pretty.for_type(A, a_pretty_printer) | ||||
In [8]: a | ||||
Out[8]: <A> | ||||
In [9]: class B(object): | ||||
...: def __repr__(self): | ||||
...: return 'B()' | ||||
...: | ||||
...: | ||||
In [10]: B.__module__, B.__name__ | ||||
Out[10]: ('__main__', 'B') | ||||
In [11]: def b_pretty_printer(obj, p, cycle): | ||||
....: p.text('<B>') | ||||
....: | ||||
....: | ||||
In [12]: ipy_pretty.for_type_by_name('__main__', 'B', b_pretty_printer) | ||||
In [13]: b = B() | ||||
In [14]: b | ||||
Out[14]: <B> | ||||
""" | ||||
assert False, "This should only be doctested, not run." | ||||