##// END OF EJS Templates
add lookup logic based on apptools.type_registry...
MinRK -
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 unicode_to_str, with_metaclass, PY3
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.singleton_printers[obj_id]
268 except (TypeError, KeyError):
271 printer = self.lookup(obj)
272 except KeyError:
269 273 pass
270 274 else:
271 275 return printer(obj)
272 # Next look for type_printers.
273 for cls in pretty._get_mro(obj_class):
274 if cls in self.type_printers:
275 return self.type_printers[cls](obj)
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
289 285
290 def for_type(self, typ, func=_current):
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))
331
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=_current):
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
362 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
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 printer
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 clear and return foo
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_by_name():
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')
121 114
122 115 # initial return is None
123 assert f.for_type_by_name(mod, 'C', foo) is None
116 assert f.for_type(type_str, foo_printer) is None
124 117 # no func queries
125 assert f.for_type_by_name(mod, 'C') is foo
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__
129
130 # initial return is None
131 assert f.for_type_by_name(mod, 'C', foo_printer) is None
132 # no func queries
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 clear and return foo
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
140
141 def test_lookup():
142 f = PlainTextFormatter()
133 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