Show More
@@ -34,7 +34,9 b' from IPython.lib import pretty' | |||
|
34 | 34 | from IPython.utils.traitlets import ( |
|
35 | 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 | 41 | if PY3: |
|
40 | 42 | from io import StringIO |
@@ -46,8 +48,6 b' else:' | |||
|
46 | 48 | # The main DisplayFormatter class |
|
47 | 49 | #----------------------------------------------------------------------------- |
|
48 | 50 | |
|
49 | _current = object() | |
|
50 | ||
|
51 | 51 | class DisplayFormatter(Configurable): |
|
52 | 52 | |
|
53 | 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 | 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 | 222 | class BaseFormatter(Configurable): |
|
211 | 223 | """A base formatter class that is configurable. |
|
212 | 224 | |
@@ -241,53 +253,83 b' class BaseFormatter(Configurable):' | |||
|
241 | 253 | # The singleton printers. |
|
242 | 254 | # Maps the IDs of the builtin singleton objects to the format functions. |
|
243 | 255 | singleton_printers = Dict(config=True) |
|
244 | def _singleton_printers_default(self): | |
|
245 | return {} | |
|
246 | 256 | |
|
247 | 257 | # The type-specific printers. |
|
248 | 258 | # Map type objects to the format functions. |
|
249 | 259 | type_printers = Dict(config=True) |
|
250 | def _type_printers_default(self): | |
|
251 | return {} | |
|
252 | 260 | |
|
253 | 261 | # The deferred-import type-specific printers. |
|
254 | 262 | # Map (modulename, classname) pairs to the format functions. |
|
255 | 263 | deferred_printers = Dict(config=True) |
|
256 | def _deferred_printers_default(self): | |
|
257 | return {} | |
|
258 | 264 | |
|
259 | 265 | def __call__(self, obj): |
|
260 | 266 | """Compute the format for an object.""" |
|
261 | 267 | if self.enabled: |
|
262 | obj_id = id(obj) | |
|
263 | 268 | try: |
|
264 | obj_class = getattr(obj, '__class__', None) or type(obj) | |
|
265 | # First try to find registered singleton printers for the type. | |
|
269 | # lookup registered printer | |
|
266 | 270 | try: |
|
267 |
printer = self. |
|
|
268 |
except |
|
|
271 | printer = self.lookup(obj) | |
|
272 | except KeyError: | |
|
269 | 273 | pass |
|
270 | 274 | else: |
|
271 | 275 | return printer(obj) |
|
272 |
# |
|
|
273 | for cls in pretty._get_mro(obj_class): | |
|
274 | if cls in self.type_printers: | |
|
275 |
|
|
|
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) | |
|
276 | # Finally look for special method names | |
|
277 | method = pretty._safe_getattr(obj, self.print_method, None) | |
|
278 | if method is not None: | |
|
279 | return method() | |
|
284 | 280 | return None |
|
285 | 281 | except Exception: |
|
286 | 282 | pass |
|
287 | 283 | else: |
|
288 | 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 | 333 | """Add a format function for a given type. |
|
292 | 334 | |
|
293 | 335 | Parameters |
@@ -301,10 +343,7 b' class BaseFormatter(Configurable):' | |||
|
301 | 343 | Subclasses may use a different call signature for the |
|
302 | 344 | `func` argument. |
|
303 | 345 | |
|
304 | If None is given, the current formatter for the type, if any, | |
|
305 | will be cleared. | |
|
306 | ||
|
307 | If `func` is not specified, there will be no change, | |
|
346 | If `func` is None or not specified, there will be no change, | |
|
308 | 347 | only returning the current value. |
|
309 | 348 | |
|
310 | 349 | Returns |
@@ -314,14 +353,22 b' class BaseFormatter(Configurable):' | |||
|
314 | 353 | If you are registering a new formatter, |
|
315 | 354 | this will be the previous value (to enable restoring later). |
|
316 | 355 | """ |
|
317 | oldfunc = self.type_printers.get(typ, None) | |
|
318 | if func is None: | |
|
319 | self.type_printers.pop(typ, None) | |
|
320 | elif func is not _current: | |
|
356 | # if string given, interpret as 'pkg.module.class_name' | |
|
357 | if isinstance(typ, string_types): | |
|
358 | type_module, type_name = typ.rsplit('.', 1) | |
|
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 | 367 | self.type_printers[typ] = func |
|
368 | ||
|
322 | 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 | 372 | """Add a format function for a type specified by the full dotted |
|
326 | 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 | 386 | Subclasses may use a different call signature for the |
|
340 | 387 | `func` argument. |
|
341 | 388 | |
|
342 | If None is given, the current formatter for the type, if any, | |
|
343 | will be cleared. | |
|
344 | ||
|
345 | If `func` is not specified, there will be no change, | |
|
389 | If `func` is None or unspecified, there will be no change, | |
|
346 | 390 | only returning the current value. |
|
347 | 391 | |
|
348 | 392 | Returns |
@@ -353,30 +397,63 b' class BaseFormatter(Configurable):' | |||
|
353 | 397 | this will be the previous value (to enable restoring later). |
|
354 | 398 | """ |
|
355 | 399 | key = (type_module, type_name) |
|
400 | ||
|
356 | 401 | oldfunc = self.deferred_printers.get(key, None) |
|
357 | if func is None: | |
|
358 | self.deferred_printers.pop(key, None) | |
|
359 | elif func is not _current: | |
|
402 | if func is not None: | |
|
360 | 403 | self.deferred_printers[key] = func |
|
361 | 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 | 442 | def _in_deferred_types(self, cls): |
|
364 | 443 | """ |
|
365 | 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 | |
|
368 | class is not in the registry. Successful matches will be moved to the | |
|
369 | regular type registry for future use. | |
|
446 | Successful matches will be moved to the regular type registry for future use. | |
|
370 | 447 | """ |
|
371 | 448 | mod = getattr(cls, '__module__', None) |
|
372 | 449 | name = getattr(cls, '__name__', None) |
|
373 | 450 | key = (mod, name) |
|
374 | printer = None | |
|
375 | 451 | if key in self.deferred_printers: |
|
376 | 452 | # Move the printer over to the regular registry. |
|
377 | 453 | printer = self.deferred_printers.pop(key) |
|
378 | 454 | self.type_printers[cls] = printer |
|
379 |
return |
|
|
455 | return True | |
|
456 | return False | |
|
380 | 457 | |
|
381 | 458 | |
|
382 | 459 | class PlainTextFormatter(BaseFormatter): |
@@ -9,7 +9,7 b' except:' | |||
|
9 | 9 | numpy = None |
|
10 | 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 | 14 | class A(object): |
|
15 | 15 | def __repr__(self): |
@@ -19,6 +19,9 b' class B(A):' | |||
|
19 | 19 | def __repr__(self): |
|
20 | 20 | return 'B()' |
|
21 | 21 | |
|
22 | class C: | |
|
23 | pass | |
|
24 | ||
|
22 | 25 | class BadPretty(object): |
|
23 | 26 | _repr_pretty_ = None |
|
24 | 27 | |
@@ -90,45 +93,61 b' def test_bad_precision():' | |||
|
90 | 93 | |
|
91 | 94 | def test_for_type(): |
|
92 | 95 | f = PlainTextFormatter() |
|
93 | class C(): | |
|
94 | pass | |
|
95 | ||
|
96 | def foo(c, p, cycle): | |
|
97 | p.text('C') | |
|
98 | 96 | |
|
99 | 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 | 99 | # no func queries |
|
102 | assert f.for_type(C) is foo | |
|
100 | assert f.for_type(C) is foo_printer | |
|
103 | 101 | # shouldn't change anything |
|
104 | assert f.for_type(C) is foo | |
|
105 |
# None should |
|
|
106 | assert f.for_type(C, None) is foo | |
|
107 | # verify clear | |
|
108 | assert C not in f.type_printers | |
|
109 | assert f.for_type(C) is None | |
|
102 | assert f.for_type(C) is foo_printer | |
|
103 | # None should do the same | |
|
104 | assert f.for_type(C, None) is foo_printer | |
|
105 | assert f.for_type(C, None) is foo_printer | |
|
110 | 106 | |
|
111 | 107 | |
|
112 |
def test_for_type_ |
|
|
108 | def test_for_type_string(): | |
|
113 | 109 | f = PlainTextFormatter() |
|
114 | class C(): | |
|
115 | pass | |
|
116 | 110 | |
|
117 | 111 | mod = C.__module__ |
|
118 | 112 | |
|
119 | def foo(c, p, cycle): | |
|
120 | p.text('C') | |
|
113 | type_str = '%s.%s' % (C.__module__, '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 | 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 | 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 | 134 | # shouldn't change anything |
|
127 | assert f.for_type_by_name(mod, 'C') is foo | |
|
128 |
# None should |
|
|
129 | assert f.for_type_by_name(mod, 'C', None) is foo | |
|
130 | # verify clear | |
|
131 | assert (mod, 'C') not in f.deferred_printers | |
|
132 | assert f.for_type_by_name(mod, 'C') is None | |
|
135 | assert f.for_type_by_name(mod, 'C') is foo_printer | |
|
136 | # None should do the same | |
|
137 | assert f.for_type_by_name(mod, 'C', None) is foo_printer | |
|
138 | assert f.for_type_by_name(mod, 'C', None) is foo_printer | |
|
139 | ||
|
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