##// END OF EJS Templates
Merge branch 'formatters' of http://github.com/rkern/ipython into rkern-formatters
Fernando Perez -
r3221:ead71f34 merge
parent child Browse files
Show More
@@ -0,0 +1,160 b''
1 # -*- coding: utf-8 -*-
2 """Displayhook formatters.
3
4 The DefaultFormatter is always present and may be configured from the
5 ipython_config.py file. For example, to add a pretty-printer for a numpy.dtype
6 object::
7
8 def dtype_pprinter(obj, p, cycle):
9 if cycle:
10 return p.text('dtype(...)')
11 if hasattr(obj, 'fields'):
12 if obj.fields is None:
13 p.text(repr(obj))
14 else:
15 p.begin_group(7, 'dtype([')
16 for i, field in enumerate(obj.descr):
17 if i > 0:
18 p.text(',')
19 p.breakable()
20 p.pretty(field)
21 p.end_group(7, '])')
22
23 c.DefaultFormatter.deferred_pprinters = {
24 ('numpy', 'dtype'): dtype_pprinter,
25 }
26
27 The deferred_pprinters dictionary is the preferred way to configure these
28 pretty-printers. This allows you to define the pretty-printer without needing to
29 import the type itself. The dictionary maps (modulename, typename) pairs to
30 a function.
31
32 See the `IPython.external.pretty` documentation for how to write
33 pretty-printer functions.
34
35 Authors:
36
37 * Robert Kern
38 """
39
40 import abc
41 from cStringIO import StringIO
42
43 from IPython.config.configurable import Configurable
44 from IPython.external import pretty
45 from IPython.utils.traitlets import Bool, Dict, Int, Str
46
47
48 class DefaultFormatter(Configurable):
49 """ The default pretty-printer.
50 """
51
52 # The ID of the formatter.
53 id = Str('default')
54
55 # The kind of data returned.
56 # This is often, but not always a MIME type.
57 format = Str('text/plain')
58
59 # Whether to pretty-print or not.
60 pprint = Bool(True, config=True)
61
62 # Whether to be verbose or not.
63 verbose = Bool(False, config=True)
64
65 # The maximum width.
66 max_width = Int(79, config=True)
67
68 # The newline character.
69 newline = Str('\n', config=True)
70
71 # The singleton prettyprinters.
72 # Maps the IDs of the builtin singleton objects to the format functions.
73 singleton_pprinters = Dict(config=True)
74 def _singleton_pprinters_default(self):
75 return pretty._singleton_pprinters.copy()
76
77 # The type-specific prettyprinters.
78 # Map type objects to the format functions.
79 type_pprinters = Dict(config=True)
80 def _type_pprinters_default(self):
81 return pretty._type_pprinters.copy()
82
83 # The deferred-import type-specific prettyprinters.
84 # Map (modulename, classname) pairs to the format functions.
85 deferred_pprinters = Dict(config=True)
86 def _deferred_pprinters_default(self):
87 return pretty._deferred_type_pprinters.copy()
88
89 #### FormatterABC interface ####
90
91 def __call__(self, obj):
92 """ Format the object.
93 """
94 if not self.pprint:
95 try:
96 return repr(obj)
97 except TypeError:
98 return ''
99 else:
100 stream = StringIO()
101 printer = pretty.RepresentationPrinter(stream, self.verbose,
102 self.max_width, self.newline,
103 singleton_pprinters=self.singleton_pprinters,
104 type_pprinters=self.type_pprinters,
105 deferred_pprinters=self.deferred_pprinters)
106 printer.pretty(obj)
107 printer.flush()
108 return stream.getvalue()
109
110
111 #### DefaultFormatter interface ####
112
113 def for_type(self, typ, func):
114 """
115 Add a pretty printer for a given type.
116 """
117 oldfunc = self.type_pprinters.get(typ, None)
118 if func is not None:
119 # To support easy restoration of old pprinters, we need to ignore
120 # Nones.
121 self.type_pprinters[typ] = func
122 return oldfunc
123
124 def for_type_by_name(self, type_module, type_name, func):
125 """
126 Add a pretty printer for a type specified by the module and name of
127 a type rather than the type object itself.
128 """
129 key = (type_module, type_name)
130 oldfunc = self.deferred_pprinters.get(key, None)
131 if func is not None:
132 # To support easy restoration of old pprinters, we need to ignore
133 # Nones.
134 self.deferred_pprinters[key] = func
135 return oldfunc
136
137
138 class FormatterABC(object):
139 """ Abstract base class for Formatters.
140 """
141 __metaclass__ = abc.ABCMeta
142
143 # The ID of the formatter.
144 id = 'abstract'
145
146 # The kind of data returned.
147 format = 'text/plain'
148
149 @abc.abstractmethod
150 def __call__(self, obj):
151 """ Return a JSONable representation of the object.
152
153 If the object cannot be formatted by this formatter, then return None
154 """
155 try:
156 return repr(obj)
157 except TypeError:
158 return None
159
160 FormatterABC.register(DefaultFormatter)
@@ -0,0 +1,30 b''
1 """Tests for the Formatters.
2 """
3
4 import nose.tools as nt
5
6 from IPython.core.formatters import FormatterABC, DefaultFormatter
7
8 class A(object):
9 def __repr__(self):
10 return 'A()'
11
12 class B(A):
13 def __repr__(self):
14 return 'B()'
15
16 def foo_printer(obj, pp, cycle):
17 pp.text('foo')
18
19 def test_pretty():
20 f = DefaultFormatter()
21 f.for_type(A, foo_printer)
22 nt.assert_equals(f(A()), 'foo')
23 nt.assert_equals(f(B()), 'foo')
24 f.pprint = False
25 nt.assert_equals(f(A()), 'A()')
26 nt.assert_equals(f(B()), 'B()')
27
28 def test_deferred():
29 f = DefaultFormatter()
30
@@ -20,15 +20,14 b' Authors:'
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 import __builtin__
22 import __builtin__
23 from pprint import PrettyPrinter
24 pformat = PrettyPrinter().pformat
25
23
26 from IPython.config.configurable import Configurable
24 from IPython.config.configurable import Configurable
27 from IPython.core import prompts
25 from IPython.core import prompts
28 import IPython.utils.generics
26 import IPython.utils.generics
29 import IPython.utils.io
27 import IPython.utils.io
30 from IPython.utils.traitlets import Instance, Int
28 from IPython.utils.traitlets import Instance, List
31 from IPython.utils.warn import warn
29 from IPython.utils.warn import warn
30 from IPython.core.formatters import DefaultFormatter
32
31
33 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
34 # Main displayhook class
33 # Main displayhook class
@@ -55,6 +54,16 b' class DisplayHook(Configurable):'
55
54
56 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
55 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
57
56
57 # The default formatter.
58 default_formatter = Instance('IPython.core.formatters.FormatterABC')
59 def _default_formatter_default(self):
60 # FIXME: backwards compatibility for the InteractiveShell.pprint option?
61 return DefaultFormatter(config=self.config)
62
63 # Any additional FormatterABC instances we use.
64 # FIXME: currently unused.
65 extra_formatters = List(config=True)
66
58 # Each call to the In[] prompt raises it by 1, even the first.
67 # Each call to the In[] prompt raises it by 1, even the first.
59 #prompt_count = Int(0)
68 #prompt_count = Int(0)
60
69
@@ -109,7 +118,6 b' class DisplayHook(Configurable):'
109 self.output_sep = output_sep
118 self.output_sep = output_sep
110 self.output_sep2 = output_sep2
119 self.output_sep2 = output_sep2
111 self._,self.__,self.___ = '','',''
120 self._,self.__,self.___ = '','',''
112 self.pprint_types = map(type,[(),[],{}])
113
121
114 # these are deliberately global:
122 # these are deliberately global:
115 to_user_ns = {'_':self._,'__':self.__,'___':self.___}
123 to_user_ns = {'_':self._,'__':self.__,'___':self.___}
@@ -184,39 +192,35 b' class DisplayHook(Configurable):'
184 if self.do_full_cache:
192 if self.do_full_cache:
185 IPython.utils.io.Term.cout.write(outprompt)
193 IPython.utils.io.Term.cout.write(outprompt)
186
194
187 # TODO: Make this method an extension point. The previous implementation
188 # has both a result_display hook as well as a result_display generic
189 # function to customize the repr on a per class basis. We need to rethink
190 # the hooks mechanism before doing this though.
191 def compute_result_repr(self, result):
195 def compute_result_repr(self, result):
192 """Compute and return the repr of the object to be displayed.
196 """Compute and return the repr of the object to be displayed.
193
197
194 This method only compute the string form of the repr and should NOT
198 This method only compute the string form of the repr and should NOT
195 actual print or write that to a stream. This method may also transform
199 actual print or write that to a stream.
196 the result itself, but the default implementation passes the original
197 through.
198 """
200 """
199 try:
201 result_repr = self.default_formatter(result)
200 if self.shell.pprint:
202 if '\n' in result_repr:
201 try:
203 # So that multi-line strings line up with the left column of
202 result_repr = pformat(result)
204 # the screen, instead of having the output prompt mess up
203 except:
205 # their first line.
204 # Work around possible bugs in pformat
206 outprompt = str(self.prompt_out)
205 result_repr = repr(result)
207 if outprompt and not outprompt.endswith('\n'):
206 if '\n' in result_repr:
208 # But avoid extraneous empty lines.
207 # So that multi-line strings line up with the left column of
209 result_repr = '\n' + result_repr
208 # the screen, instead of having the output prompt mess up
210
209 # their first line.
211 extra_formats = []
210 result_repr = '\n' + result_repr
212 for f in self.extra_formatters:
211 else:
213 try:
212 result_repr = repr(result)
214 data = f(result)
213 except TypeError:
215 except Exception:
214 # This happens when result.__repr__ doesn't return a string,
216 # FIXME: log the exception.
215 # such as when it returns None.
217 continue
216 result_repr = '\n'
218 if data is not None:
217 return result, result_repr
219 extra_formats.append((f.id, f.format, data))
218
220
219 def write_result_repr(self, result_repr):
221 return result_repr, extra_formats
222
223 def write_result_repr(self, result_repr, extra_formats):
220 # We want to print because we want to always make sure we have a
224 # We want to print because we want to always make sure we have a
221 # newline, even if all the prompt separators are ''. This is the
225 # newline, even if all the prompt separators are ''. This is the
222 # standard IPython behavior.
226 # standard IPython behavior.
@@ -271,8 +275,8 b' class DisplayHook(Configurable):'
271 if result is not None and not self.quiet():
275 if result is not None and not self.quiet():
272 self.start_displayhook()
276 self.start_displayhook()
273 self.write_output_prompt()
277 self.write_output_prompt()
274 result, result_repr = self.compute_result_repr(result)
278 result_repr, extra_formats = self.compute_result_repr(result)
275 self.write_result_repr(result_repr)
279 self.write_result_repr(result_repr, extra_formats)
276 self.update_user_ns(result)
280 self.update_user_ns(result)
277 self.log_output(result)
281 self.log_output(result)
278 self.finish_displayhook()
282 self.finish_displayhook()
@@ -475,6 +475,7 b' class InteractiveShell(Configurable, Magic):'
475 def init_displayhook(self):
475 def init_displayhook(self):
476 # Initialize displayhook, set in/out prompts and printing system
476 # Initialize displayhook, set in/out prompts and printing system
477 self.displayhook = self.displayhook_class(
477 self.displayhook = self.displayhook_class(
478 config=self.config,
478 shell=self,
479 shell=self,
479 cache_size=self.cache_size,
480 cache_size=self.cache_size,
480 input_sep = self.separate_in,
481 input_sep = self.separate_in,
@@ -306,10 +306,21 b' class RepresentationPrinter(PrettyPrinter):'
306 verbose mode.
306 verbose mode.
307 """
307 """
308
308
309 def __init__(self, output, verbose=False, max_width=79, newline='\n'):
309 def __init__(self, output, verbose=False, max_width=79, newline='\n',
310 singleton_pprinters=None, type_pprinters=None, deferred_pprinters=None):
311
310 PrettyPrinter.__init__(self, output, max_width, newline)
312 PrettyPrinter.__init__(self, output, max_width, newline)
311 self.verbose = verbose
313 self.verbose = verbose
312 self.stack = []
314 self.stack = []
315 if singleton_pprinters is None:
316 singleton_pprinters = _singleton_pprinters.copy()
317 self.singleton_pprinters = singleton_pprinters
318 if type_pprinters is None:
319 type_pprinters = _type_pprinters.copy()
320 self.type_pprinters = type_pprinters
321 if deferred_pprinters is None:
322 deferred_pprinters = _deferred_type_pprinters.copy()
323 self.deferred_pprinters = deferred_pprinters
313
324
314 def pretty(self, obj):
325 def pretty(self, obj):
315 """Pretty print the given object."""
326 """Pretty print the given object."""
@@ -322,14 +333,14 b' class RepresentationPrinter(PrettyPrinter):'
322 if hasattr(obj_class, '__pretty__'):
333 if hasattr(obj_class, '__pretty__'):
323 return obj_class.__pretty__(obj, self, cycle)
334 return obj_class.__pretty__(obj, self, cycle)
324 try:
335 try:
325 printer = _singleton_pprinters[obj_id]
336 printer = self.singleton_pprinters[obj_id]
326 except (TypeError, KeyError):
337 except (TypeError, KeyError):
327 pass
338 pass
328 else:
339 else:
329 return printer(obj, self, cycle)
340 return printer(obj, self, cycle)
330 for cls in _get_mro(obj_class):
341 for cls in _get_mro(obj_class):
331 if cls in _type_pprinters:
342 if cls in self.type_pprinters:
332 return _type_pprinters[cls](obj, self, cycle)
343 return self.type_pprinters[cls](obj, self, cycle)
333 else:
344 else:
334 printer = self._in_deferred_types(cls)
345 printer = self._in_deferred_types(cls)
335 if printer is not None:
346 if printer is not None:
@@ -351,14 +362,13 b' class RepresentationPrinter(PrettyPrinter):'
351 name = getattr(cls, '__name__', None)
362 name = getattr(cls, '__name__', None)
352 key = (mod, name)
363 key = (mod, name)
353 printer = None
364 printer = None
354 if key in _deferred_type_pprinters:
365 if key in self.deferred_pprinters:
355 # Move the printer over to the regular registry.
366 # Move the printer over to the regular registry.
356 printer = _deferred_type_pprinters.pop(key)
367 printer = self.deferred_pprinters.pop(key)
357 _type_pprinters[cls] = printer
368 self.type_pprinters[cls] = printer
358 return printer
369 return printer
359
370
360
371
361
362 class Printable(object):
372 class Printable(object):
363
373
364 def output(self, stream, output_width):
374 def output(self, stream, output_width):
@@ -1,11 +1,14 b''
1 """ Defines miscellaneous Qt-related helper classes and functions.
1 """ Defines miscellaneous Qt-related helper classes and functions.
2 """
2 """
3
3
4 # Standard library imports.
5 import inspect
6
4 # System library imports.
7 # System library imports.
5 from PyQt4 import QtCore, QtGui
8 from PyQt4 import QtCore, QtGui
6
9
7 # IPython imports.
10 # IPython imports.
8 from IPython.utils.traitlets import HasTraits
11 from IPython.utils.traitlets import HasTraits, TraitType
9
12
10 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
11 # Metaclasses
14 # Metaclasses
@@ -14,7 +17,6 b' from IPython.utils.traitlets import HasTraits'
14 MetaHasTraits = type(HasTraits)
17 MetaHasTraits = type(HasTraits)
15 MetaQObject = type(QtCore.QObject)
18 MetaQObject = type(QtCore.QObject)
16
19
17 # You can switch the order of the parents here and it doesn't seem to matter.
18 class MetaQObjectHasTraits(MetaQObject, MetaHasTraits):
20 class MetaQObjectHasTraits(MetaQObject, MetaHasTraits):
19 """ A metaclass that inherits from the metaclasses of HasTraits and QObject.
21 """ A metaclass that inherits from the metaclasses of HasTraits and QObject.
20
22
@@ -22,7 +24,24 b' class MetaQObjectHasTraits(MetaQObject, MetaHasTraits):'
22 QObject. Using SuperQObject instead of QObject is highly recommended. See
24 QObject. Using SuperQObject instead of QObject is highly recommended. See
23 QtKernelManager for an example.
25 QtKernelManager for an example.
24 """
26 """
25 pass
27 def __new__(mcls, name, bases, classdict):
28 # FIXME: this duplicates the code from MetaHasTraits.
29 # I don't think a super() call will help me here.
30 for k,v in classdict.iteritems():
31 if isinstance(v, TraitType):
32 v.name = k
33 elif inspect.isclass(v):
34 if issubclass(v, TraitType):
35 vinst = v()
36 vinst.name = k
37 classdict[k] = vinst
38 cls = MetaQObject.__new__(mcls, name, bases, classdict)
39 return cls
40
41 def __init__(mcls, name, bases, classdict):
42 # Note: super() did not work, so we explicitly call these.
43 MetaQObject.__init__(mcls, name, bases, classdict)
44 MetaHasTraits.__init__(mcls, name, bases, classdict)
26
45
27 #-----------------------------------------------------------------------------
46 #-----------------------------------------------------------------------------
28 # Classes
47 # Classes
@@ -129,6 +129,29 b' class TestTraitType(TestCase):'
129 a = A()
129 a = A()
130 self.assertRaises(TraitError, A.tt.error, a, 10)
130 self.assertRaises(TraitError, A.tt.error, a, 10)
131
131
132 def test_dynamic_initializer(self):
133 class A(HasTraits):
134 x = Int(10)
135 def _x_default(self):
136 return 11
137 class B(A):
138 x = Int(20)
139 class C(A):
140 def _x_default(self):
141 return 21
142
143 a = A()
144 self.assertEquals(a._trait_values, {})
145 self.assertEquals(a.x, 11)
146 self.assertEquals(a._trait_values, {'x': 11})
147 b = B()
148 self.assertEquals(b._trait_values, {'x': 20})
149 self.assertEquals(b.x, 20)
150 c = C()
151 self.assertEquals(c._trait_values, {})
152 self.assertEquals(c.x, 21)
153 self.assertEquals(c._trait_values, {'x': 21})
154
132
155
133 class TestHasTraitsMeta(TestCase):
156 class TestHasTraitsMeta(TestCase):
134
157
@@ -248,9 +248,21 b' class TraitType(object):'
248 default values must be delayed until the parent :class:`HasTraits`
248 default values must be delayed until the parent :class:`HasTraits`
249 class has been instantiated.
249 class has been instantiated.
250 """
250 """
251 dv = self.get_default_value()
251 # Check for a deferred initializer defined in the same class as the
252 newdv = self._validate(obj, dv)
252 # trait declaration or above.
253 obj._trait_values[self.name] = newdv
253 mro = type(obj).mro()
254 meth_name = '_%s_default' % self.name
255 for cls in mro[:mro.index(self.this_class)+1]:
256 if meth_name in cls.__dict__:
257 break
258 else:
259 # We didn't find one. Do static initialization.
260 dv = self.get_default_value()
261 newdv = self._validate(obj, dv)
262 obj._trait_values[self.name] = newdv
263 return
264 # Complete the dynamic initialization.
265 self.dynamic_initializer = cls.__dict__[meth_name]
254
266
255 def __get__(self, obj, cls=None):
267 def __get__(self, obj, cls=None):
256 """Get the value of the trait by self.name for the instance.
268 """Get the value of the trait by self.name for the instance.
@@ -265,7 +277,19 b' class TraitType(object):'
265 else:
277 else:
266 try:
278 try:
267 value = obj._trait_values[self.name]
279 value = obj._trait_values[self.name]
268 except:
280 except KeyError:
281 # Check for a dynamic initializer.
282 if hasattr(self, 'dynamic_initializer'):
283 value = self.dynamic_initializer(obj)
284 # FIXME: Do we really validate here?
285 value = self._validate(obj, value)
286 obj._trait_values[self.name] = value
287 return value
288 else:
289 raise TraitError('Unexpected error in TraitType: '
290 'both default value and dynamic initializer are '
291 'absent.')
292 except Exception:
269 # HasTraits should call set_default_value to populate
293 # HasTraits should call set_default_value to populate
270 # this. So this should never be reached.
294 # this. So this should never be reached.
271 raise TraitError('Unexpected error in TraitType: '
295 raise TraitError('Unexpected error in TraitType: '
@@ -294,6 +318,11 b' class TraitType(object):'
294 else:
318 else:
295 return value
319 return value
296
320
321 def set_dynamic_initializer(self, method):
322 """ Set the dynamic initializer method, if any.
323 """
324 self.dynamic_initializer = method
325
297 def info(self):
326 def info(self):
298 return self.info_text
327 return self.info_text
299
328
@@ -65,8 +65,9 b' class ZMQDisplayHook(DisplayHook):'
65 if self.do_full_cache:
65 if self.do_full_cache:
66 self.msg['content']['execution_count'] = self.prompt_count
66 self.msg['content']['execution_count'] = self.prompt_count
67
67
68 def write_result_repr(self, result_repr):
68 def write_result_repr(self, result_repr, extra_formats):
69 self.msg['content']['data'] = result_repr
69 self.msg['content']['data'] = result_repr
70 self.msg['content']['extra_formats'] = extra_formats
70
71
71 def finish_displayhook(self):
72 def finish_displayhook(self):
72 """Finish up all displayhook activities."""
73 """Finish up all displayhook activities."""
@@ -725,16 +725,35 b' case, the kernel instantiates as ``sys.displayhook`` an object which has'
725 similar behavior, but which instead of printing to stdout, broadcasts these
725 similar behavior, but which instead of printing to stdout, broadcasts these
726 values as ``pyout`` messages for clients to display appropriately.
726 values as ``pyout`` messages for clients to display appropriately.
727
727
728 IPython's displayhook can handle multiple simultaneous formats depending on its
729 configuration. The default pretty-printed repr text is always given with the
730 ``data`` entry in this message. Any other formats are provided in the
731 ``extra_formats`` list. Frontends are free to display any or all of these
732 according to its capabilities. ``extra_formats`` list contains 3-tuples of an ID
733 string, a type string, and the data. The ID is unique to the formatter
734 implementation that created the data. Frontends will typically ignore the ID
735 unless if it has requested a particular formatter. The type string tells the
736 frontend how to interpret the data. It is often, but not always a MIME type.
737 Frontends should ignore types that it does not understand. The data itself is
738 any JSON object and depends on the format. It is often, but not always a string.
739
728 Message type: ``pyout``::
740 Message type: ``pyout``::
729
741
730 content = {
742 content = {
731 # The data is typically the repr() of the object.
743 # The data is typically the repr() of the object. It should be displayed
732 'data' : str,
744 # as monospaced text.
745 'data' : str,
733
746
734 # The counter for this execution is also provided so that clients can
747 # The counter for this execution is also provided so that clients can
735 # display it, since IPython automatically creates variables called _N (for
748 # display it, since IPython automatically creates variables called _N
736 # prompt N).
749 # (for prompt N).
737 'execution_count' : int,
750 'execution_count' : int,
751
752 # Any extra formats.
753 # The tuples are of the form (ID, type, data).
754 'extra_formats' : [
755 [str, str, object]
756 ]
738 }
757 }
739
758
740 Python errors
759 Python errors
General Comments 0
You need to be logged in to leave comments. Login now