diff --git a/IPython/config/profile/ipython_config_sympy.py b/IPython/config/profile/ipython_config_sympy.py index a32b522..b687b45 100644 --- a/IPython/config/profile/ipython_config_sympy.py +++ b/IPython/config/profile/ipython_config_sympy.py @@ -15,7 +15,14 @@ f, g, h = map(Function, 'fgh') # You have to make sure that attributes that are containers already # exist before using them. Simple assigning a new list will override # all previous values. + if hasattr(c.Global, 'exec_lines'): c.Global.exec_lines.append(lines) else: c.Global.exec_lines = [lines] + +if hasattr(c.Global, 'extensions'): + c.Global.extensions.append('IPython.extensions.sympy_printing') +else: + c.Global.extensions = ['IPython.extensions.sympy_printing'] + diff --git a/IPython/core/display.py b/IPython/core/display.py new file mode 100644 index 0000000..f4195ed --- /dev/null +++ b/IPython/core/display.py @@ -0,0 +1,106 @@ +# -*- coding: utf-8 -*- +"""Top-level display functions for displaying object in different formats. + +Authors: + +* Brian Granger +""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2008-2010 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Main functions +#----------------------------------------------------------------------------- + +def display(obj, include=None, exclude=None): + """Display a Python object in all frontends. + + By default all representations will be computed and sent to the frontends. + Frontends can decide which representation is used and how. + + Parameters + ---------- + obj : object + The Python object to display. + 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 + format = InteractiveShell.instance().display_formatter.format + publish = InteractiveShell.instance().display_pub.publish + + format_dict = format(obj, include=include, exclude=exclude) + publish('IPython.core.display.display', format_dict) + + +def display_html(obj): + """Display the HTML representation of an object. + + Parameters + ---------- + obj : object + The Python object to display. + """ + display(obj, include=['text/plain','text/html']) + + +def display_svg(obj): + """Display the SVG representation of an object. + + Parameters + ---------- + obj : object + The Python object to display. + """ + display(obj, include=['text/plain','image/svg+xml']) + + +def display_png(obj): + """Display the PNG representation of an object. + + Parameters + ---------- + obj : object + The Python object to display. + """ + display(obj, include=['text/plain','image/png']) + + +def display_latex(obj): + """Display the LaTeX representation of an object. + + Parameters + ---------- + obj : object + The Python object to display. + """ + display(obj, include=['text/plain','text/latex']) + + +def display_json(obj): + """Display the JSON representation of an object. + + Parameters + ---------- + obj : object + The Python object to display. + """ + display(obj, include=['text/plain','application/json']) + + + diff --git a/IPython/core/displayhook.py b/IPython/core/displayhook.py index 9324ce6..d73f865 100644 --- a/IPython/core/displayhook.py +++ b/IPython/core/displayhook.py @@ -1,10 +1,13 @@ # -*- coding: utf-8 -*- """Displayhook for IPython. +This defines a callable class that IPython uses for `sys.displayhook`. + Authors: * Fernando Perez * Brian Granger +* Robert Kern """ #----------------------------------------------------------------------------- @@ -27,7 +30,6 @@ import IPython.utils.generics import IPython.utils.io from IPython.utils.traitlets import Instance, List from IPython.utils.warn import warn -from IPython.core.formatters import DefaultFormatter #----------------------------------------------------------------------------- # Main displayhook class @@ -54,19 +56,6 @@ class DisplayHook(Configurable): shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') - # The default formatter. - default_formatter = Instance('IPython.core.formatters.FormatterABC') - def _default_formatter_default(self): - # FIXME: backwards compatibility for the InteractiveShell.pprint option? - return DefaultFormatter(config=self.config) - - # Any additional FormatterABC instances we use. - # FIXME: currently unused. - extra_formatters = List(config=True) - - # Each call to the In[] prompt raises it by 1, even the first. - #prompt_count = Int(0) - def __init__(self, shell=None, cache_size=1000, colors='NoColor', input_sep='\n', output_sep='\n', output_sep2='', @@ -185,36 +174,64 @@ class DisplayHook(Configurable): pass def write_output_prompt(self): - """Write the output prompt.""" + """Write the output prompt. + + The default implementation simply writes the prompt to + ``io.Term.cout``. + """ # Use write, not print which adds an extra space. IPython.utils.io.Term.cout.write(self.output_sep) outprompt = str(self.prompt_out) if self.do_full_cache: IPython.utils.io.Term.cout.write(outprompt) - def compute_result_repr(self, result): - """Compute and return the repr of the object to be displayed. + def compute_format_data(self, result): + """Compute format data of the object to be displayed. - This method only compute the string form of the repr and should NOT + The format data is a generalization of the :func:`repr` of an object. + In the default implementation the format data is a :class:`dict` of + key value pair where the keys are valid MIME types and the values + are JSON'able data structure containing the raw data for that MIME + type. It is up to frontends to determine pick a MIME to to use and + display that data in an appropriate manner. + + This method only compute the format data for the object and should NOT actually print or write that to a stream. + + Parameters + ---------- + result : object + The Python object passed to the display hook, whose forat will be + computed. + + Returns + ------- + format_data : dict + A :class:`dict` whose keys are valid MIME types and values are + JSON'able raw data for that MIME type. It is recommended that + all return values of this should always include the "text/plain" + MIME type representation of the object. """ - result_repr = self.default_formatter(result) - extra_formats = [] - for f in self.extra_formatters: - try: - data = f(result) - except Exception: - # FIXME: log the exception. - continue - if data is not None: - extra_formats.append((f.id, f.format, data)) + format_dict = self.shell.display_formatter.format(result) + return format_dict - return result_repr, extra_formats + def write_format_data(self, format_dict): + """Write the format data dict to the frontend. - def write_result_repr(self, result_repr, extra_formats): + This default version of this method simply writes the plain text + representation of the object to ``io.Term.cout``. Subclasses should + override this method to send the entire `format_dict` to the + frontends. + + Parameters + ---------- + format_dict : dict + The format dict for the object passed to `sys.displayhook`. + """ # We want to print because we want to always make sure we have a # newline, even if all the prompt separators are ''. This is the # standard IPython behavior. + result_repr = format_dict['text/plain'] if '\n' in result_repr: # So that multi-line strings line up with the left column of # the screen, instead of having the output prompt mess up @@ -278,8 +295,8 @@ class DisplayHook(Configurable): if result is not None and not self.quiet(): self.start_displayhook() self.write_output_prompt() - result_repr, extra_formats = self.compute_result_repr(result) - self.write_result_repr(result_repr, extra_formats) + format_dict = self.compute_format_data(result) + self.write_format_data(format_dict) self.update_user_ns(result) self.log_output(result) self.finish_displayhook() @@ -300,5 +317,6 @@ class DisplayHook(Configurable): if '_' not in __builtin__.__dict__: self.shell.user_ns.update({'_':None,'__':None, '___':None}) import gc - gc.collect() # xxx needed? + # TODO: Is this really needed? + gc.collect() diff --git a/IPython/core/displaypub.py b/IPython/core/displaypub.py index f3bcf39..208143a 100644 --- a/IPython/core/displaypub.py +++ b/IPython/core/displaypub.py @@ -1,6 +1,16 @@ -# -*- coding: utf-8 -*- """An interface for publishing rich data to frontends. +There are two components of the display system: + +* Display formatters, which take a Python object and compute the + representation of the object in various formats (text, HTML, SVg, etc.). +* The display publisher that is used to send the representation data to the + various frontends. + +This module defines the logic display publishing. The display publisher uses +the ``display_data`` message type that is defined in the IPython messaging +spec. + Authors: * Brian Granger @@ -24,8 +34,26 @@ from IPython.config.configurable import Configurable #----------------------------------------------------------------------------- class DisplayPublisher(Configurable): + """A traited class that publishes display data to frontends. + + Instances of this class are created by the main IPython object and should + be accessed there. + """ def _validate_data(self, source, data, metadata=None): + """Validate the display data. + + Parameters + ---------- + source : str + The fully dotted name of the callable that created the data, like + :func:`foo.bar.my_formatter`. + data : dict + The formata data dictionary. + metadata : dict + Any metadata for the data. + """ + if not isinstance(source, str): raise TypeError('source must be a str, got: %r' % source) if not isinstance(data, dict): @@ -40,6 +68,15 @@ class DisplayPublisher(Configurable): See the ``display_data`` message in the messaging documentation for more details about this message type. + The following MIME types are currently implemented: + + * text/plain + * text/html + * text/latex + * application/json + * image/png + * immage/svg+xml + Parameters ---------- source : str @@ -64,40 +101,43 @@ class DisplayPublisher(Configurable): print >>io.Term.cout, data['text/plain'] -def publish_display_data(source, text, svg=None, png=None, - html=None, metadata=None): - """Publish a display data to the frontends. +def publish_display_data(self, source, data, metadata=None): + """Publish data and metadata to all frontends. - This function is a high level helper for the publishing of display data. - It handle a number of common MIME types in a clean API. For other MIME - types, use ``get_ipython().display_pub.publish`` directly. + See the ``display_data`` message in the messaging documentation for + more details about this message type. - Parameters - ---------- - text : str/unicode - The string representation of the plot. + The following MIME types are currently implemented: - svn : str/unicode - The raw svg data of the plot. + * text/plain + * text/html + * text/latex + * application/json + * image/png + * immage/svg+xml - png : ??? - The raw png data of the plot. - - metadata : dict, optional [default empty] - Allows for specification of additional information about the plot data. + Parameters + ---------- + source : str + A string that give the function or method that created the data, + such as 'IPython.core.page'. + data : dict + A dictionary having keys that are valid MIME types (like + 'text/plain' or 'image/svg+xml') and values that are the data for + that MIME type. The data itself must be a JSON'able data + structure. Minimally all data should have the 'text/plain' data, + which can be displayed by all frontends. If more than the plain + text is given, it is up to the frontend to decide which + representation to use. + metadata : dict + A dictionary for metadata related to the data. This can contain + arbitrary key, value pairs that frontends can use to interpret + the data. """ from IPython.core.interactiveshell import InteractiveShell - - data_dict = {} - data_dict['text/plain'] = text - if svg is not None: - data_dict['image/svg+xml'] = svg - if png is not None: - data_dict['image/png'] = png - if html is not None: - data_dict['text/html'] = html InteractiveShell.instance().display_pub.publish( source, - data_dict, + data, metadata ) + diff --git a/IPython/core/formatters.py b/IPython/core/formatters.py index be13192..ff2e15e 100644 --- a/IPython/core/formatters.py +++ b/IPython/core/formatters.py @@ -1,36 +1,11 @@ # -*- coding: utf-8 -*- -"""Displayhook formatters. - -The DefaultFormatter is always present and may be configured from the -ipython_config.py file. For example, to add a pretty-printer for a numpy.dtype -object:: - - 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, '])') - - c.DefaultFormatter.deferred_pprinters = { - ('numpy', 'dtype'): dtype_pprinter, - } - -The deferred_pprinters dictionary is the preferred way to configure these -pretty-printers. This allows you to define the pretty-printer without needing to -import the type itself. The dictionary maps (modulename, typename) pairs to -a function. - -See the `IPython.external.pretty` documentation for how to write -pretty-printer functions. +"""Display formatters. + + +Authors: + +* Robert Kern +* Brian Granger """ #----------------------------------------------------------------------------- # Copyright (c) 2010, IPython Development Team. @@ -42,7 +17,8 @@ pretty-printer functions. # Stdlib imports import abc -from cStringIO import StringIO +# We must use StringIO, as cStringIO doesn't handle unicode properly. +from StringIO import StringIO # Our own imports from IPython.config.configurable import Configurable @@ -51,19 +27,268 @@ from IPython.utils.traitlets import Bool, Dict, Int, Str #----------------------------------------------------------------------------- -# Classes and functions +# The main DisplayFormatter class #----------------------------------------------------------------------------- -class DefaultFormatter(Configurable): - """ The default pretty-printer. + +class DisplayFormatter(Configurable): + + # 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. + + 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. + """ + format_dict = {} + 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. """ + __metaclass__ = abc.ABCMeta - # The ID of the formatter. - id = Str('default') + # The format type of the data returned, usually a MIME type. + format_type = 'text/plain' - # The kind of data returned. - # This is often, but not always a MIME type. - format = Str('text/plain') + @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`. + + Users should use these dictionarie 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. + + 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') + + 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.""" + 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) + try: + printer = self.singleton_printers[obj_id] + except (TypeError, KeyError): + pass + else: + 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 + + def for_type(self, typ, func): + """Add a format function for a given type. + + Parameteres + ----------- + 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 + + +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') + + # Look for a __pretty__ methods to use for pretty printing. + print_method = Str('__pretty__') # Whether to pretty-print or not. pprint = Bool(True, config=True) @@ -77,93 +302,152 @@ class DefaultFormatter(Configurable): # The newline character. newline = Str('\n', config=True) - # The singleton prettyprinters. - # Maps the IDs of the builtin singleton objects to the format functions. - singleton_pprinters = Dict(config=True) - def _singleton_pprinters_default(self): + # Use the default pretty printers from IPython.external.pretty. + def _singleton_printers_default(self): return pretty._singleton_pprinters.copy() - # The type-specific prettyprinters. - # Map type objects to the format functions. - type_pprinters = Dict(config=True) - def _type_pprinters_default(self): + def _type_printers_default(self): return pretty._type_pprinters.copy() - # The deferred-import type-specific prettyprinters. - # Map (modulename, classname) pairs to the format functions. - deferred_pprinters = Dict(config=True) - def _deferred_pprinters_default(self): + def _deferred_printers_default(self): return pretty._deferred_type_pprinters.copy() #### FormatterABC interface #### def __call__(self, obj): - """ Format the object. - """ + """Compute the pretty representation of the object.""" if not self.pprint: try: return repr(obj) except TypeError: return '' else: + # This uses use StringIO, as cStringIO doesn't handle unicode. stream = StringIO() printer = pretty.RepresentationPrinter(stream, self.verbose, self.max_width, self.newline, - singleton_pprinters=self.singleton_pprinters, - type_pprinters=self.type_pprinters, - deferred_pprinters=self.deferred_pprinters) + singleton_pprinters=self.singleton_printers, + type_pprinters=self.type_printers, + deferred_pprinters=self.deferred_printers) printer.pretty(obj) printer.flush() return stream.getvalue() - #### DefaultFormatter interface #### +class HTMLFormatter(BaseFormatter): + """An HTML formatter. - def for_type(self, typ, func): - """ - Add a pretty printer for a given type. - """ - oldfunc = self.type_pprinters.get(typ, None) - if func is not None: - # To support easy restoration of old pprinters, we need to ignore - # Nones. - self.type_pprinters[typ] = func - return oldfunc + 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') - def for_type_by_name(self, type_module, type_name, func): - """ - Add a pretty printer for a type specified by the module and name of - a type rather than the type object itself. - """ - key = (type_module, type_name) - oldfunc = self.deferred_pprinters.get(key, None) - if func is not None: - # To support easy restoration of old pprinters, we need to ignore - # Nones. - self.deferred_pprinters[key] = func - return oldfunc + print_method = Str('__html__') -class FormatterABC(object): - """ Abstract base class for Formatters. +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. """ - __metaclass__ = abc.ABCMeta + format_type = Str('image/svg+xml') - # The ID of the formatter. - id = 'abstract' + print_method = Str('__svg__') - # The kind of data returned. - format = 'text/plain' - @abc.abstractmethod - def __call__(self, obj): - """ Return a JSONable representation of the object. +class PNGFormatter(BaseFormatter): + """A PNG formatter. - If the object cannot be formatted by this formatter, then return None - """ - try: - return repr(obj) - except TypeError: - return None + To define the callables that compute the PNG 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. 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 -FormatterABC.register(DefaultFormatter) + InteractiveShell.instance().display_formatter.format( + obj, + include, + exclude + ) diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index a2e646f..42e417c 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -45,6 +45,7 @@ from IPython.core.displaypub import DisplayPublisher from IPython.core.error import TryNext, UsageError from IPython.core.extensions import ExtensionManager from IPython.core.fakemodule import FakeModule, init_fakemod_dict +from IPython.core.formatters import DisplayFormatter from IPython.core.history import HistoryManager from IPython.core.inputsplitter import IPythonInputSplitter from IPython.core.logger import Logger @@ -150,6 +151,7 @@ class InteractiveShell(Configurable, Magic): default_value=get_default_colors(), config=True) debug = CBool(False, config=True) deep_reload = CBool(False, config=True) + display_formatter = Instance(DisplayFormatter) displayhook_class = Type(DisplayHook) display_pub_class = Type(DisplayPublisher) @@ -287,6 +289,7 @@ class InteractiveShell(Configurable, Magic): self.init_io() self.init_traceback_handlers(custom_exceptions) self.init_prompts() + self.init_display_formatter() self.init_display_pub() self.init_displayhook() self.init_reload_doctest() @@ -485,6 +488,9 @@ class InteractiveShell(Configurable, Magic): # will initialize that object and all prompt related information. pass + def init_display_formatter(self): + self.display_formatter = DisplayFormatter(config=self.config) + def init_display_pub(self): self.display_pub = self.display_pub_class(config=self.config) diff --git a/IPython/extensions/sympy_printing.py b/IPython/extensions/sympy_printing.py new file mode 100644 index 0000000..0bdafa6 --- /dev/null +++ b/IPython/extensions/sympy_printing.py @@ -0,0 +1,45 @@ +"""A print function that pretty prints sympy Basic objects. + +Authors: +* Brian Granger +""" +#----------------------------------------------------------------------------- +# Copyright (C) 2008-2011 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +from sympy import pretty + +#----------------------------------------------------------------------------- +# Definitions of magic functions for use with IPython +#----------------------------------------------------------------------------- + +def print_basic_unicode(o, p, cycle): + """A function to pretty print sympy Basic objects.""" + if cycle: + return p.text('Basic(...)') + out = pretty(o, use_unicode=True) + if '\n' in out: + p.text(u'\n') + p.text(out) + + +_loaded = False + + +def load_ipython_extension(ip): + """Load the extension in IPython.""" + global _loaded + if not _loaded: + plaintext_formatter = ip.display_formatter.formatters['text/plain'] + plaintext_formatter.for_type_by_name( + 'sympy.core.basic', 'Basic', print_basic_unicode + ) + _loaded = True + diff --git a/IPython/frontend/qt/console/frontend_widget.py b/IPython/frontend/qt/console/frontend_widget.py index f170639..f0ae16d 100644 --- a/IPython/frontend/qt/console/frontend_widget.py +++ b/IPython/frontend/qt/console/frontend_widget.py @@ -352,7 +352,7 @@ class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin): """ Handle display hook output. """ if not self._hidden and self._is_from_this_session(msg): - self._append_plain_text(msg['content']['data'] + '\n') + self._append_plain_text(msg['content']['data']['text/plain'] + '\n') def _handle_stream(self, msg): """ Handle stdout, stderr, and stdin. diff --git a/IPython/frontend/qt/console/ipython_widget.py b/IPython/frontend/qt/console/ipython_widget.py index bc7db31..082ba79 100644 --- a/IPython/frontend/qt/console/ipython_widget.py +++ b/IPython/frontend/qt/console/ipython_widget.py @@ -179,9 +179,18 @@ class IPythonWidget(FrontendWidget): if not self._hidden and self._is_from_this_session(msg): content = msg['content'] prompt_number = content['execution_count'] - self._append_plain_text(self.output_sep) - self._append_html(self._make_out_prompt(prompt_number)) - self._append_plain_text(content['data']+self.output_sep2) + data = content['data'] + if data.has_key('text/html'): + self._append_plain_text(self.output_sep) + self._append_html(self._make_out_prompt(prompt_number)) + html = data['text/html'] + self._append_plain_text('\n') + self._append_html(html + self.output_sep2) + elif data.has_key('text/plain'): + self._append_plain_text(self.output_sep) + self._append_html(self._make_out_prompt(prompt_number)) + text = data['text/plain'] + self._append_plain_text(text + self.output_sep2) def _handle_display_data(self, msg): """ The base handler for the ``display_data`` message. @@ -195,8 +204,12 @@ class IPythonWidget(FrontendWidget): metadata = msg['content']['metadata'] # In the regular IPythonWidget, we simply print the plain text # representation. - if data.has_key('text/plain'): - self._append_plain_text(data['text/plain']) + if data.has_key('text/html'): + html = data['text/html'] + self._append_html(html) + elif data.has_key('text/plain'): + text = data['text/plain'] + self._append_plain_text(text) def _started_channels(self): """ Reimplemented to make a history request. diff --git a/IPython/frontend/qt/console/rich_ipython_widget.py b/IPython/frontend/qt/console/rich_ipython_widget.py index 377e778..8658d44 100644 --- a/IPython/frontend/qt/console/rich_ipython_widget.py +++ b/IPython/frontend/qt/console/rich_ipython_widget.py @@ -61,8 +61,25 @@ class RichIPythonWidget(IPythonWidget): # 'BaseFrontendMixin' abstract interface #--------------------------------------------------------------------------- + def _handle_pyout(self, msg): + """ Overridden to handle rich data types, like SVG. + """ + if not self._hidden and self._is_from_this_session(msg): + content = msg['content'] + prompt_number = content['execution_count'] + data = content['data'] + if data.has_key('image/svg+xml'): + self._append_plain_text(self.output_sep) + self._append_html(self._make_out_prompt(prompt_number)) + # TODO: try/except this call. + self._append_svg(data['image/svg+xml']) + self._append_html(self.output_sep2) + else: + # Default back to the plain text representation. + return super(RichIPythonWidget, self)._handle_pyout(msg) + def _handle_display_data(self, msg): - """ A handler for ``display_data`` message that handles html and svg. + """ Overridden to handle rich data types, like SVG. """ if not self._hidden and self._is_from_this_session(msg): source = msg['content']['source'] @@ -74,9 +91,6 @@ class RichIPythonWidget(IPythonWidget): svg = data['image/svg+xml'] # TODO: try/except this call. self._append_svg(svg) - elif data.has_key('text/html'): - html = data['text/html'] - self._append_html(html) else: # Default back to the plain text representation. return super(RichIPythonWidget, self)._handle_display_data(msg) @@ -88,9 +102,9 @@ class RichIPythonWidget(IPythonWidget): def _process_execute_payload(self, item): """ Reimplemented to handle matplotlib plot payloads. """ + # TODO: remove this as all plot data is coming back through the + # display_data message type. if item['source'] == self._payload_source_plot: - # TODO: remove this as all plot data is coming back through the - # display_data message type. if item['format'] == 'svg': svg = item['data'] self._append_svg(svg) diff --git a/IPython/kernel/client.py b/IPython/kernel/client.py index 3178a0b..3eb24be 100644 --- a/IPython/kernel/client.py +++ b/IPython/kernel/client.py @@ -88,7 +88,7 @@ def _result_list_printer(obj, p, cycle): # ResultList is a list subclass and will use the default pretty printer. # This overrides that to use the __repr__ of ResultList. ip = get_ipython() -ip.displayhook.default_formatter.for_type_by_name( +ip.display_formatter.formatters['text/plain'].for_type_by_name( 'IPython.kernel.multiengineclient', 'ResultList', _result_list_printer ) diff --git a/IPython/zmq/pylab/backend_inline.py b/IPython/zmq/pylab/backend_inline.py index 745dbf6..aaa9a8e 100644 --- a/IPython/zmq/pylab/backend_inline.py +++ b/IPython/zmq/pylab/backend_inline.py @@ -87,8 +87,8 @@ def send_svg_canvas(canvas): try: publish_display_data( 'IPython.zmq.pylab.backend_inline.send_svg_canvas', - '', - svg=svg_from_canvas(canvas) + 'Matplotlib Plot', + {'image/svg+xml' : svg_from_canvas(canvas)} ) finally: canvas.figure.set_facecolor(fc) diff --git a/IPython/zmq/zmqshell.py b/IPython/zmq/zmqshell.py index 66e5369..1b66539 100644 --- a/IPython/zmq/zmqshell.py +++ b/IPython/zmq/zmqshell.py @@ -49,6 +49,7 @@ install_payload_page() #----------------------------------------------------------------------------- class ZMQDisplayHook(DisplayHook): + """A displayhook subclass that publishes data using ZeroMQ.""" session = Instance(Session) pub_socket = Instance('zmq.Socket') @@ -66,9 +67,8 @@ class ZMQDisplayHook(DisplayHook): if self.do_full_cache: self.msg['content']['execution_count'] = self.prompt_count - def write_result_repr(self, result_repr, extra_formats): - self.msg['content']['data'] = result_repr - self.msg['content']['extra_formats'] = extra_formats + def write_format_data(self, format_dict): + self.msg['content']['data'] = format_dict def finish_displayhook(self): """Finish up all displayhook activities.""" @@ -77,7 +77,7 @@ class ZMQDisplayHook(DisplayHook): class ZMQDisplayPublisher(DisplayPublisher): - """A ``DisplayPublisher`` that published data using a ZeroMQ PUB socket.""" + """A display publisher that publishes data using a ZeroMQ PUB socket.""" session = Instance(Session) pub_socket = Instance('zmq.Socket') diff --git a/docs/source/development/messaging.txt b/docs/source/development/messaging.txt index 59fdbc6..ef48379 100644 --- a/docs/source/development/messaging.txt +++ b/docs/source/development/messaging.txt @@ -689,10 +689,10 @@ Message type: ``stream``:: content = { # The name of the stream is one of 'stdin', 'stdout', 'stderr' - 'name' : str, + 'name' : str, - # The data is an arbitrary string to be written to that stream - 'data' : str, + # The data is an arbitrary string to be written to that stream + 'data' : str, } When a kernel receives a raw_input call, it should also broadcast it on the pub @@ -719,30 +719,18 @@ Some questions remain about this design: Message type: ``display_data``:: content = { - 'source' : str # Who create the data - 'data' : dict # {'mimetype1' : data1, 'mimetype2' : data2} - 'metadata' : dict # Any metadata that describes the data - } - -Other options for ``display_data`` content:: - # Option 2: allowing for a different source for each representation, - but not keyed by anything. - content = { - 'data' = [(source, type, data), (source, type, data)] - 'metadata' = dict - } + # Who create the data + 'source' : str, - # Option 3: like option 2, but keyed by the MIME types. - content = { - 'data' = {'mimetype1' : (source, data), 'mimetype2' : (source, data)} - 'metadata' = dict - } + # The data dict contains key/value pairs, where the kids are MIME + # types and the values are the raw data of the representation in that + # format. The data dict must minimally contain the ``text/plain`` + # MIME type which is used as a backup representation. + 'data' : dict, - # Option 4: like option 2, but keyed by the source. - content = { - 'data' = {'source' : (mimetype, data), 'source' : (mimetype, data)} - 'metadata' = dict + # Any metadata that describes the data + 'metadata' : dict } Python inputs @@ -784,20 +772,18 @@ any JSON object and depends on the format. It is often, but not always a string. Message type: ``pyout``:: content = { - # The data is typically the repr() of the object. It should be displayed - # as monospaced text. - 'data' : str, - + # The counter for this execution is also provided so that clients can # display it, since IPython automatically creates variables called _N # (for prompt N). 'execution_count' : int, + + # The data dict contains key/value pairs, where the kids are MIME + # types and the values are the raw data of the representation in that + # format. The data dict must minimally contain the ``text/plain`` + # MIME type which is used as a backup representation. + 'data' : dict, - # Any extra formats. - # The tuples are of the form (ID, type, data). - 'extra_formats' : [ - [str, str, object] - ] } Python errors