Show More
@@ -34,7 +34,9 b' from IPython.lib import pretty' | |||||
34 | from IPython.utils.traitlets import ( |
|
34 | from IPython.utils.traitlets import ( | |
35 | Bool, Dict, Integer, Unicode, CUnicode, ObjectName, List, |
|
35 | Bool, Dict, Integer, Unicode, CUnicode, ObjectName, List, | |
36 | ) |
|
36 | ) | |
37 |
from IPython.utils.py3compat import |
|
37 | from IPython.utils.py3compat import ( | |
|
38 | unicode_to_str, with_metaclass, PY3, string_types, | |||
|
39 | ) | |||
38 |
|
40 | |||
39 | if PY3: |
|
41 | if PY3: | |
40 | from io import StringIO |
|
42 | from io import StringIO | |
@@ -46,8 +48,6 b' else:' | |||||
46 | # The main DisplayFormatter class |
|
48 | # The main DisplayFormatter class | |
47 | #----------------------------------------------------------------------------- |
|
49 | #----------------------------------------------------------------------------- | |
48 |
|
50 | |||
49 | _current = object() |
|
|||
50 |
|
||||
51 | class DisplayFormatter(Configurable): |
|
51 | class DisplayFormatter(Configurable): | |
52 |
|
52 | |||
53 | # When set to true only the default plain text formatter will be used. |
|
53 | # When set to true only the default plain text formatter will be used. | |
@@ -207,6 +207,18 b' class FormatterABC(with_metaclass(abc.ABCMeta, object)):' | |||||
207 | return None |
|
207 | return None | |
208 |
|
208 | |||
209 |
|
209 | |||
|
210 | def _mod_name_key(typ): | |||
|
211 | """Return a '__module__.__name__' string key for a type.""" | |||
|
212 | module = getattr(typ, '__module__', None) | |||
|
213 | name = getattr(typ, '__name__', None) | |||
|
214 | return (module, name) | |||
|
215 | ||||
|
216 | ||||
|
217 | def _get_type(obj): | |||
|
218 | """Return the type of an instance (old and new-style)""" | |||
|
219 | return getattr(obj, '__class__', None) or type(obj) | |||
|
220 | ||||
|
221 | ||||
210 | class BaseFormatter(Configurable): |
|
222 | class BaseFormatter(Configurable): | |
211 | """A base formatter class that is configurable. |
|
223 | """A base formatter class that is configurable. | |
212 |
|
224 | |||
@@ -241,53 +253,83 b' class BaseFormatter(Configurable):' | |||||
241 | # The singleton printers. |
|
253 | # The singleton printers. | |
242 | # Maps the IDs of the builtin singleton objects to the format functions. |
|
254 | # Maps the IDs of the builtin singleton objects to the format functions. | |
243 | singleton_printers = Dict(config=True) |
|
255 | singleton_printers = Dict(config=True) | |
244 | def _singleton_printers_default(self): |
|
|||
245 | return {} |
|
|||
246 |
|
256 | |||
247 | # The type-specific printers. |
|
257 | # The type-specific printers. | |
248 | # Map type objects to the format functions. |
|
258 | # Map type objects to the format functions. | |
249 | type_printers = Dict(config=True) |
|
259 | type_printers = Dict(config=True) | |
250 | def _type_printers_default(self): |
|
|||
251 | return {} |
|
|||
252 |
|
260 | |||
253 | # The deferred-import type-specific printers. |
|
261 | # The deferred-import type-specific printers. | |
254 | # Map (modulename, classname) pairs to the format functions. |
|
262 | # Map (modulename, classname) pairs to the format functions. | |
255 | deferred_printers = Dict(config=True) |
|
263 | deferred_printers = Dict(config=True) | |
256 | def _deferred_printers_default(self): |
|
|||
257 | return {} |
|
|||
258 |
|
264 | |||
259 | def __call__(self, obj): |
|
265 | def __call__(self, obj): | |
260 | """Compute the format for an object.""" |
|
266 | """Compute the format for an object.""" | |
261 | if self.enabled: |
|
267 | if self.enabled: | |
262 | obj_id = id(obj) |
|
|||
263 | try: |
|
268 | try: | |
264 | obj_class = getattr(obj, '__class__', None) or type(obj) |
|
269 | # lookup registered printer | |
265 | # First try to find registered singleton printers for the type. |
|
|||
266 | try: |
|
270 | try: | |
267 |
printer = self. |
|
271 | printer = self.lookup(obj) | |
268 |
except |
|
272 | except KeyError: | |
269 | pass |
|
273 | pass | |
270 | else: |
|
274 | else: | |
271 | return printer(obj) |
|
275 | return printer(obj) | |
272 |
# |
|
276 | # Finally look for special method names | |
273 | for cls in pretty._get_mro(obj_class): |
|
277 | method = pretty._safe_getattr(obj, self.print_method, None) | |
274 | if cls in self.type_printers: |
|
278 | if method is not None: | |
275 |
|
|
279 | return method() | |
276 | else: |
|
|||
277 | printer = self._in_deferred_types(cls) |
|
|||
278 | if printer is not None: |
|
|||
279 | return printer(obj) |
|
|||
280 | # Finally look for special method names. |
|
|||
281 | if hasattr(obj_class, self.print_method): |
|
|||
282 | printer = getattr(obj_class, self.print_method) |
|
|||
283 | return printer(obj) |
|
|||
284 | return None |
|
280 | return None | |
285 | except Exception: |
|
281 | except Exception: | |
286 | pass |
|
282 | pass | |
287 | else: |
|
283 | else: | |
288 | return None |
|
284 | return None | |
|
285 | ||||
|
286 | def lookup(self, obj): | |||
|
287 | """Look up the formatter for a given instance. | |||
|
288 | ||||
|
289 | Parameters | |||
|
290 | ---------- | |||
|
291 | obj : object instance | |||
|
292 | ||||
|
293 | Returns | |||
|
294 | ------- | |||
|
295 | f : callable | |||
|
296 | The registered fromatting callable for the type. | |||
|
297 | ||||
|
298 | Raises | |||
|
299 | ------ | |||
|
300 | KeyError if the type has not been registered. | |||
|
301 | """ | |||
|
302 | # look for singleton first | |||
|
303 | obj_id = id(obj) | |||
|
304 | if obj_id in self.singleton_printers: | |||
|
305 | return self.singleton_printers[obj_id] | |||
|
306 | # then lookup by type | |||
|
307 | return self.lookup_by_type(_get_type(obj)) | |||
|
308 | ||||
|
309 | def lookup_by_type(self, typ): | |||
|
310 | """ Look up all the registered formatters for a type. | |||
|
311 | ||||
|
312 | Parameters | |||
|
313 | ---------- | |||
|
314 | typ : type | |||
|
315 | ||||
|
316 | Returns | |||
|
317 | ------- | |||
|
318 | f : callable | |||
|
319 | The registered fromatting callable for the type. | |||
|
320 | ||||
|
321 | Raises | |||
|
322 | ------ | |||
|
323 | KeyError if the type has not been registered. | |||
|
324 | """ | |||
|
325 | for cls in pretty._get_mro(typ): | |||
|
326 | if cls in self.type_printers or self._in_deferred_types(cls): | |||
|
327 | return self.type_printers[cls] | |||
|
328 | ||||
|
329 | # If we have reached here, the lookup failed. | |||
|
330 | raise KeyError("No registered printer for {0!r}".format(typ)) | |||
289 |
|
331 | |||
290 |
def for_type(self, typ, func= |
|
332 | def for_type(self, typ, func=None): | |
291 | """Add a format function for a given type. |
|
333 | """Add a format function for a given type. | |
292 |
|
334 | |||
293 | Parameters |
|
335 | Parameters | |
@@ -301,10 +343,7 b' class BaseFormatter(Configurable):' | |||||
301 | Subclasses may use a different call signature for the |
|
343 | Subclasses may use a different call signature for the | |
302 | `func` argument. |
|
344 | `func` argument. | |
303 |
|
345 | |||
304 | If None is given, the current formatter for the type, if any, |
|
346 | If `func` is None or not specified, there will be no change, | |
305 | will be cleared. |
|
|||
306 |
|
||||
307 | If `func` is not specified, there will be no change, |
|
|||
308 | only returning the current value. |
|
347 | only returning the current value. | |
309 |
|
348 | |||
310 | Returns |
|
349 | Returns | |
@@ -314,14 +353,22 b' class BaseFormatter(Configurable):' | |||||
314 | If you are registering a new formatter, |
|
353 | If you are registering a new formatter, | |
315 | this will be the previous value (to enable restoring later). |
|
354 | this will be the previous value (to enable restoring later). | |
316 | """ |
|
355 | """ | |
317 | oldfunc = self.type_printers.get(typ, None) |
|
356 | # if string given, interpret as 'pkg.module.class_name' | |
318 | if func is None: |
|
357 | if isinstance(typ, string_types): | |
319 | self.type_printers.pop(typ, None) |
|
358 | type_module, type_name = typ.rsplit('.', 1) | |
320 | elif func is not _current: |
|
359 | return self.for_type_by_name(type_module, type_name, func) | |
|
360 | ||||
|
361 | try: | |||
|
362 | oldfunc = self.lookup_by_type(typ) | |||
|
363 | except KeyError: | |||
|
364 | oldfunc = None | |||
|
365 | ||||
|
366 | if func is not None: | |||
321 | self.type_printers[typ] = func |
|
367 | self.type_printers[typ] = func | |
|
368 | ||||
322 | return oldfunc |
|
369 | return oldfunc | |
323 |
|
370 | |||
324 |
def for_type_by_name(self, type_module, type_name, func= |
|
371 | def for_type_by_name(self, type_module, type_name, func=None): | |
325 | """Add a format function for a type specified by the full dotted |
|
372 | """Add a format function for a type specified by the full dotted | |
326 | module and name of the type, rather than the type of the object. |
|
373 | module and name of the type, rather than the type of the object. | |
327 |
|
374 | |||
@@ -339,10 +386,7 b' class BaseFormatter(Configurable):' | |||||
339 | Subclasses may use a different call signature for the |
|
386 | Subclasses may use a different call signature for the | |
340 | `func` argument. |
|
387 | `func` argument. | |
341 |
|
388 | |||
342 | If None is given, the current formatter for the type, if any, |
|
389 | If `func` is None or unspecified, there will be no change, | |
343 | will be cleared. |
|
|||
344 |
|
||||
345 | If `func` is not specified, there will be no change, |
|
|||
346 | only returning the current value. |
|
390 | only returning the current value. | |
347 |
|
391 | |||
348 | Returns |
|
392 | Returns | |
@@ -353,30 +397,63 b' class BaseFormatter(Configurable):' | |||||
353 | this will be the previous value (to enable restoring later). |
|
397 | this will be the previous value (to enable restoring later). | |
354 | """ |
|
398 | """ | |
355 | key = (type_module, type_name) |
|
399 | key = (type_module, type_name) | |
|
400 | ||||
356 | oldfunc = self.deferred_printers.get(key, None) |
|
401 | oldfunc = self.deferred_printers.get(key, None) | |
357 | if func is None: |
|
402 | if func is not None: | |
358 | self.deferred_printers.pop(key, None) |
|
|||
359 | elif func is not _current: |
|
|||
360 | self.deferred_printers[key] = func |
|
403 | self.deferred_printers[key] = func | |
361 | return oldfunc |
|
404 | return oldfunc | |
|
405 | ||||
|
406 | def pop(self, typ): | |||
|
407 | """ Pop a registered object for the given type. | |||
|
408 | ||||
|
409 | Parameters | |||
|
410 | ---------- | |||
|
411 | typ : type or '__module__.__name__' string for a type | |||
|
412 | ||||
|
413 | Returns | |||
|
414 | ------- | |||
|
415 | obj : object | |||
|
416 | The last registered object for the type. | |||
|
417 | ||||
|
418 | Raises | |||
|
419 | ------ | |||
|
420 | KeyError if the type is not registered. | |||
|
421 | """ | |||
|
422 | if isinstance(typ, string_types): | |||
|
423 | typ_key = tuple(typ.rsplit('.',1)) | |||
|
424 | if typ_key not in self.deferred_printers: | |||
|
425 | # We may have it cached in the type map. We will have to | |||
|
426 | # iterate over all of the types to check. | |||
|
427 | for cls in self.type_printers: | |||
|
428 | if _mod_name_key(cls) == typ_key: | |||
|
429 | old = self.type_printers.pop(cls) | |||
|
430 | break | |||
|
431 | else: | |||
|
432 | raise KeyError("No registered value for {0!r}".format(typ_key)) | |||
|
433 | else: | |||
|
434 | old = self.deferred_printers.pop(typ) | |||
|
435 | else: | |||
|
436 | if typ in self.type_printers: | |||
|
437 | old = self.type_printers.pop(typ) | |||
|
438 | else: | |||
|
439 | old = self.deferred_printers.pop(_mod_name_key(typ)) | |||
|
440 | return old | |||
362 |
|
441 | |||
363 | def _in_deferred_types(self, cls): |
|
442 | def _in_deferred_types(self, cls): | |
364 | """ |
|
443 | """ | |
365 | Check if the given class is specified in the deferred type registry. |
|
444 | Check if the given class is specified in the deferred type registry. | |
366 |
|
445 | |||
367 | Returns the printer from the registry if it exists, and None if the |
|
446 | Successful matches will be moved to the regular type registry for future use. | |
368 | class is not in the registry. Successful matches will be moved to the |
|
|||
369 | regular type registry for future use. |
|
|||
370 | """ |
|
447 | """ | |
371 | mod = getattr(cls, '__module__', None) |
|
448 | mod = getattr(cls, '__module__', None) | |
372 | name = getattr(cls, '__name__', None) |
|
449 | name = getattr(cls, '__name__', None) | |
373 | key = (mod, name) |
|
450 | key = (mod, name) | |
374 | printer = None |
|
|||
375 | if key in self.deferred_printers: |
|
451 | if key in self.deferred_printers: | |
376 | # Move the printer over to the regular registry. |
|
452 | # Move the printer over to the regular registry. | |
377 | printer = self.deferred_printers.pop(key) |
|
453 | printer = self.deferred_printers.pop(key) | |
378 | self.type_printers[cls] = printer |
|
454 | self.type_printers[cls] = printer | |
379 |
return |
|
455 | return True | |
|
456 | return False | |||
380 |
|
457 | |||
381 |
|
458 | |||
382 | class PlainTextFormatter(BaseFormatter): |
|
459 | class PlainTextFormatter(BaseFormatter): |
@@ -9,7 +9,7 b' except:' | |||||
9 | numpy = None |
|
9 | numpy = None | |
10 | import nose.tools as nt |
|
10 | import nose.tools as nt | |
11 |
|
11 | |||
12 | from IPython.core.formatters import PlainTextFormatter |
|
12 | from IPython.core.formatters import PlainTextFormatter, _mod_name_key | |
13 |
|
13 | |||
14 | class A(object): |
|
14 | class A(object): | |
15 | def __repr__(self): |
|
15 | def __repr__(self): | |
@@ -19,6 +19,9 b' class B(A):' | |||||
19 | def __repr__(self): |
|
19 | def __repr__(self): | |
20 | return 'B()' |
|
20 | return 'B()' | |
21 |
|
21 | |||
|
22 | class C: | |||
|
23 | pass | |||
|
24 | ||||
22 | class BadPretty(object): |
|
25 | class BadPretty(object): | |
23 | _repr_pretty_ = None |
|
26 | _repr_pretty_ = None | |
24 |
|
27 | |||
@@ -90,45 +93,61 b' def test_bad_precision():' | |||||
90 |
|
93 | |||
91 | def test_for_type(): |
|
94 | def test_for_type(): | |
92 | f = PlainTextFormatter() |
|
95 | f = PlainTextFormatter() | |
93 | class C(): |
|
|||
94 | pass |
|
|||
95 |
|
||||
96 | def foo(c, p, cycle): |
|
|||
97 | p.text('C') |
|
|||
98 |
|
96 | |||
99 | # initial return is None |
|
97 | # initial return is None | |
100 | assert f.for_type(C, foo) is None |
|
98 | assert f.for_type(C, foo_printer) is None | |
101 | # no func queries |
|
99 | # no func queries | |
102 | assert f.for_type(C) is foo |
|
100 | assert f.for_type(C) is foo_printer | |
103 | # shouldn't change anything |
|
101 | # shouldn't change anything | |
104 | assert f.for_type(C) is foo |
|
102 | assert f.for_type(C) is foo_printer | |
105 |
# None should |
|
103 | # None should do the same | |
106 | assert f.for_type(C, None) is foo |
|
104 | assert f.for_type(C, None) is foo_printer | |
107 | # verify clear |
|
105 | assert f.for_type(C, None) is foo_printer | |
108 | assert C not in f.type_printers |
|
|||
109 | assert f.for_type(C) is None |
|
|||
110 |
|
106 | |||
111 |
|
107 | |||
112 |
def test_for_type_ |
|
108 | def test_for_type_string(): | |
113 | f = PlainTextFormatter() |
|
109 | f = PlainTextFormatter() | |
114 | class C(): |
|
|||
115 | pass |
|
|||
116 |
|
110 | |||
117 | mod = C.__module__ |
|
111 | mod = C.__module__ | |
118 |
|
112 | |||
119 | def foo(c, p, cycle): |
|
113 | type_str = '%s.%s' % (C.__module__, 'C') | |
120 | p.text('C') |
|
114 | ||
|
115 | # initial return is None | |||
|
116 | assert f.for_type(type_str, foo_printer) is None | |||
|
117 | # no func queries | |||
|
118 | assert f.for_type(type_str) is foo_printer | |||
|
119 | assert _mod_name_key(C) in f.deferred_printers | |||
|
120 | assert f.for_type(C) is foo_printer | |||
|
121 | assert _mod_name_key(C) not in f.deferred_printers | |||
|
122 | assert C in f.type_printers | |||
|
123 | ||||
|
124 | ||||
|
125 | def test_for_type_by_name(): | |||
|
126 | f = PlainTextFormatter() | |||
|
127 | ||||
|
128 | mod = C.__module__ | |||
121 |
|
129 | |||
122 | # initial return is None |
|
130 | # initial return is None | |
123 | assert f.for_type_by_name(mod, 'C', foo) is None |
|
131 | assert f.for_type_by_name(mod, 'C', foo_printer) is None | |
124 | # no func queries |
|
132 | # no func queries | |
125 | assert f.for_type_by_name(mod, 'C') is foo |
|
133 | assert f.for_type_by_name(mod, 'C') is foo_printer | |
126 | # shouldn't change anything |
|
134 | # shouldn't change anything | |
127 | assert f.for_type_by_name(mod, 'C') is foo |
|
135 | assert f.for_type_by_name(mod, 'C') is foo_printer | |
128 |
# None should |
|
136 | # None should do the same | |
129 | assert f.for_type_by_name(mod, 'C', None) is foo |
|
137 | assert f.for_type_by_name(mod, 'C', None) is foo_printer | |
130 | # verify clear |
|
138 | assert f.for_type_by_name(mod, 'C', None) is foo_printer | |
131 | assert (mod, 'C') not in f.deferred_printers |
|
139 | ||
132 | assert f.for_type_by_name(mod, 'C') is None |
|
|||
133 |
|
140 | |||
|
141 | def test_lookup(): | |||
|
142 | f = PlainTextFormatter() | |||
|
143 | ||||
|
144 | mod = C.__module__ | |||
|
145 | c = C() | |||
|
146 | ||||
|
147 | type_str = '%s.%s' % (C.__module__, 'C') | |||
|
148 | ||||
|
149 | # initial return is None | |||
|
150 | assert f.for_type(type_str, foo_printer) is None | |||
|
151 | # no func queries | |||
|
152 | assert f.lookup(c) is foo_printer | |||
134 |
|
153 |
General Comments 0
You need to be logged in to leave comments.
Login now