##// 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 22 import __builtin__
23 from pprint import PrettyPrinter
24 pformat = PrettyPrinter().pformat
25 23
26 24 from IPython.config.configurable import Configurable
27 25 from IPython.core import prompts
28 26 import IPython.utils.generics
29 27 import IPython.utils.io
30 from IPython.utils.traitlets import Instance, Int
28 from IPython.utils.traitlets import Instance, List
31 29 from IPython.utils.warn import warn
30 from IPython.core.formatters import DefaultFormatter
32 31
33 32 #-----------------------------------------------------------------------------
34 33 # Main displayhook class
@@ -55,6 +54,16 b' class DisplayHook(Configurable):'
55 54
56 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 67 # Each call to the In[] prompt raises it by 1, even the first.
59 68 #prompt_count = Int(0)
60 69
@@ -109,7 +118,6 b' class DisplayHook(Configurable):'
109 118 self.output_sep = output_sep
110 119 self.output_sep2 = output_sep2
111 120 self._,self.__,self.___ = '','',''
112 self.pprint_types = map(type,[(),[],{}])
113 121
114 122 # these are deliberately global:
115 123 to_user_ns = {'_':self._,'__':self.__,'___':self.___}
@@ -184,39 +192,35 b' class DisplayHook(Configurable):'
184 192 if self.do_full_cache:
185 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 195 def compute_result_repr(self, result):
192 196 """Compute and return the repr of the object to be displayed.
193 197
194 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
196 the result itself, but the default implementation passes the original
197 through.
199 actual print or write that to a stream.
198 200 """
199 try:
200 if self.shell.pprint:
201 try:
202 result_repr = pformat(result)
203 except:
204 # Work around possible bugs in pformat
205 result_repr = repr(result)
206 if '\n' in result_repr:
207 # So that multi-line strings line up with the left column of
208 # the screen, instead of having the output prompt mess up
209 # their first line.
210 result_repr = '\n' + result_repr
211 else:
212 result_repr = repr(result)
213 except TypeError:
214 # This happens when result.__repr__ doesn't return a string,
215 # such as when it returns None.
216 result_repr = '\n'
217 return result, result_repr
218
219 def write_result_repr(self, result_repr):
201 result_repr = self.default_formatter(result)
202 if '\n' in result_repr:
203 # So that multi-line strings line up with the left column of
204 # the screen, instead of having the output prompt mess up
205 # their first line.
206 outprompt = str(self.prompt_out)
207 if outprompt and not outprompt.endswith('\n'):
208 # But avoid extraneous empty lines.
209 result_repr = '\n' + result_repr
210
211 extra_formats = []
212 for f in self.extra_formatters:
213 try:
214 data = f(result)
215 except Exception:
216 # FIXME: log the exception.
217 continue
218 if data is not None:
219 extra_formats.append((f.id, f.format, data))
220
221 return result_repr, extra_formats
222
223 def write_result_repr(self, result_repr, extra_formats):
220 224 # We want to print because we want to always make sure we have a
221 225 # newline, even if all the prompt separators are ''. This is the
222 226 # standard IPython behavior.
@@ -271,8 +275,8 b' class DisplayHook(Configurable):'
271 275 if result is not None and not self.quiet():
272 276 self.start_displayhook()
273 277 self.write_output_prompt()
274 result, result_repr = self.compute_result_repr(result)
275 self.write_result_repr(result_repr)
278 result_repr, extra_formats = self.compute_result_repr(result)
279 self.write_result_repr(result_repr, extra_formats)
276 280 self.update_user_ns(result)
277 281 self.log_output(result)
278 282 self.finish_displayhook()
@@ -475,6 +475,7 b' class InteractiveShell(Configurable, Magic):'
475 475 def init_displayhook(self):
476 476 # Initialize displayhook, set in/out prompts and printing system
477 477 self.displayhook = self.displayhook_class(
478 config=self.config,
478 479 shell=self,
479 480 cache_size=self.cache_size,
480 481 input_sep = self.separate_in,
@@ -306,10 +306,21 b' class RepresentationPrinter(PrettyPrinter):'
306 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 312 PrettyPrinter.__init__(self, output, max_width, newline)
311 313 self.verbose = verbose
312 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 325 def pretty(self, obj):
315 326 """Pretty print the given object."""
@@ -322,14 +333,14 b' class RepresentationPrinter(PrettyPrinter):'
322 333 if hasattr(obj_class, '__pretty__'):
323 334 return obj_class.__pretty__(obj, self, cycle)
324 335 try:
325 printer = _singleton_pprinters[obj_id]
336 printer = self.singleton_pprinters[obj_id]
326 337 except (TypeError, KeyError):
327 338 pass
328 339 else:
329 340 return printer(obj, self, cycle)
330 341 for cls in _get_mro(obj_class):
331 if cls in _type_pprinters:
332 return _type_pprinters[cls](obj, self, cycle)
342 if cls in self.type_pprinters:
343 return self.type_pprinters[cls](obj, self, cycle)
333 344 else:
334 345 printer = self._in_deferred_types(cls)
335 346 if printer is not None:
@@ -351,14 +362,13 b' class RepresentationPrinter(PrettyPrinter):'
351 362 name = getattr(cls, '__name__', None)
352 363 key = (mod, name)
353 364 printer = None
354 if key in _deferred_type_pprinters:
365 if key in self.deferred_pprinters:
355 366 # Move the printer over to the regular registry.
356 printer = _deferred_type_pprinters.pop(key)
357 _type_pprinters[cls] = printer
367 printer = self.deferred_pprinters.pop(key)
368 self.type_pprinters[cls] = printer
358 369 return printer
359 370
360 371
361
362 372 class Printable(object):
363 373
364 374 def output(self, stream, output_width):
@@ -1,11 +1,14 b''
1 1 """ Defines miscellaneous Qt-related helper classes and functions.
2 2 """
3 3
4 # Standard library imports.
5 import inspect
6
4 7 # System library imports.
5 8 from PyQt4 import QtCore, QtGui
6 9
7 10 # IPython imports.
8 from IPython.utils.traitlets import HasTraits
11 from IPython.utils.traitlets import HasTraits, TraitType
9 12
10 13 #-----------------------------------------------------------------------------
11 14 # Metaclasses
@@ -14,7 +17,6 b' from IPython.utils.traitlets import HasTraits'
14 17 MetaHasTraits = type(HasTraits)
15 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 20 class MetaQObjectHasTraits(MetaQObject, MetaHasTraits):
19 21 """ A metaclass that inherits from the metaclasses of HasTraits and QObject.
20 22
@@ -22,7 +24,24 b' class MetaQObjectHasTraits(MetaQObject, MetaHasTraits):'
22 24 QObject. Using SuperQObject instead of QObject is highly recommended. See
23 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 47 # Classes
@@ -129,6 +129,29 b' class TestTraitType(TestCase):'
129 129 a = A()
130 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 156 class TestHasTraitsMeta(TestCase):
134 157
@@ -248,9 +248,21 b' class TraitType(object):'
248 248 default values must be delayed until the parent :class:`HasTraits`
249 249 class has been instantiated.
250 250 """
251 dv = self.get_default_value()
252 newdv = self._validate(obj, dv)
253 obj._trait_values[self.name] = newdv
251 # Check for a deferred initializer defined in the same class as the
252 # trait declaration or above.
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 267 def __get__(self, obj, cls=None):
256 268 """Get the value of the trait by self.name for the instance.
@@ -265,7 +277,19 b' class TraitType(object):'
265 277 else:
266 278 try:
267 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 293 # HasTraits should call set_default_value to populate
270 294 # this. So this should never be reached.
271 295 raise TraitError('Unexpected error in TraitType: '
@@ -294,6 +318,11 b' class TraitType(object):'
294 318 else:
295 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 326 def info(self):
298 327 return self.info_text
299 328
@@ -65,8 +65,9 b' class ZMQDisplayHook(DisplayHook):'
65 65 if self.do_full_cache:
66 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 69 self.msg['content']['data'] = result_repr
70 self.msg['content']['extra_formats'] = extra_formats
70 71
71 72 def finish_displayhook(self):
72 73 """Finish up all displayhook activities."""
@@ -725,16 +725,35 b' case, the kernel instantiates as ``sys.displayhook`` an object which has'
725 725 similar behavior, but which instead of printing to stdout, broadcasts these
726 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 740 Message type: ``pyout``::
729 741
730 742 content = {
731 # The data is typically the repr() of the object.
732 'data' : str,
743 # The data is typically the repr() of the object. It should be displayed
744 # as monospaced text.
745 'data' : str,
733 746
734 # The counter for this execution is also provided so that clients can
735 # display it, since IPython automatically creates variables called _N (for
736 # prompt N).
737 'execution_count' : int,
747 # The counter for this execution is also provided so that clients can
748 # display it, since IPython automatically creates variables called _N
749 # (for prompt N).
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 759 Python errors
General Comments 0
You need to be logged in to leave comments. Login now