"""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'] def dict_pprinter(obj, p, cycle): return p.text("") c.PrettyResultDisplay.verbose = True c.PrettyResultDisplay.defaults_for_type = [ (dict, dict_pprinter) ] c.PrettyResultDisplay.defaults_for_type_by_name = [ ('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.io import Term from IPython.utils.autoattr import auto_attr from IPython.utils.importstring import import_item #----------------------------------------------------------------------------- # Code #----------------------------------------------------------------------------- _loaded = False class PrettyResultDisplay(Component): """A component for pretty printing on steroids.""" verbose = Bool(False, config=True) # 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) # A list of (module_name, type_name, func_name), like # [('numpy', 'dtype', 'IPython.extensions.pretty.dtype_pprinter')] # The final argument can also be a callable defaults_for_type_by_name = List(default_value=[], config=True) def __init__(self, parent, name=None, config=None): super(PrettyResultDisplay, self).__init__(parent, name=name, config=config) self._setup_defaults() def _setup_defaults(self): """Initialize the default pretty printers.""" 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) self.for_type_by_name(type_module, type_name, func) 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. @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.""" return pretty.for_type_by_name(type_module, type_name, func) #----------------------------------------------------------------------------- # Initialization code for the extension #----------------------------------------------------------------------------- def load_ipython_extension(ip): """Load the extension in IPython as a hook.""" global _loaded if not _loaded: prd = PrettyResultDisplay(ip, name='pretty_result_display') ip.set_hook('result_display', prd, priority=99) _loaded = True return prd def unload_ipython_extension(ip): """Unload the extension.""" # 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(...)') 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, '])')