##// END OF EJS Templates
another pass on formatter lookup...
MinRK -
Show More
@@ -1,758 +1,791
1 1 # -*- coding: utf-8 -*-
2 2 """Display formatters.
3 3
4 4 Inheritance diagram:
5 5
6 6 .. inheritance-diagram:: IPython.core.formatters
7 7 :parts: 3
8 8
9 9 Authors:
10 10
11 11 * Robert Kern
12 12 * Brian Granger
13 13 """
14 14 #-----------------------------------------------------------------------------
15 15 # Copyright (C) 2010-2011, IPython Development Team.
16 16 #
17 17 # Distributed under the terms of the Modified BSD License.
18 18 #
19 19 # The full license is in the file COPYING.txt, distributed with this software.
20 20 #-----------------------------------------------------------------------------
21 21
22 22 #-----------------------------------------------------------------------------
23 23 # Imports
24 24 #-----------------------------------------------------------------------------
25 25
26 26 # Stdlib imports
27 27 import abc
28 28 import sys
29 29 import warnings
30 30
31 31 # Our own imports
32 32 from IPython.config.configurable import Configurable
33 33 from IPython.lib import pretty
34 34 from IPython.utils.traitlets import (
35 35 Bool, Dict, Integer, Unicode, CUnicode, ObjectName, List,
36 36 )
37 37 from IPython.utils.py3compat import (
38 38 unicode_to_str, with_metaclass, PY3, string_types,
39 39 )
40 40
41 41 if PY3:
42 42 from io import StringIO
43 43 else:
44 44 from StringIO import StringIO
45 45
46 46
47 47 #-----------------------------------------------------------------------------
48 48 # The main DisplayFormatter class
49 49 #-----------------------------------------------------------------------------
50 50
51 51 class DisplayFormatter(Configurable):
52 52
53 53 # When set to true only the default plain text formatter will be used.
54 54 plain_text_only = Bool(False, config=True)
55 55 def _plain_text_only_changed(self, name, old, new):
56 56 warnings.warn("""DisplayFormatter.plain_text_only is deprecated.
57 57
58 58 Use DisplayFormatter.active_types = ['text/plain']
59 59 for the same effect.
60 60 """, DeprecationWarning)
61 61 if new:
62 62 self.active_types = ['text/plain']
63 63 else:
64 64 self.active_types = self.format_types
65 65
66 66 active_types = List(Unicode, config=True,
67 67 help="""List of currently active mime-types to display.
68 68 You can use this to set a white-list for formats to display.
69 69
70 70 Most users will not need to change this value.
71 71 """)
72 72 def _active_types_default(self):
73 73 return self.format_types
74 74
75 75 def _active_types_changed(self, name, old, new):
76 76 for key, formatter in self.formatters.items():
77 77 if key in new:
78 78 formatter.enabled = True
79 79 else:
80 80 formatter.enabled = False
81 81
82 82 # A dict of formatter whose keys are format types (MIME types) and whose
83 83 # values are subclasses of BaseFormatter.
84 84 formatters = Dict()
85 85 def _formatters_default(self):
86 86 """Activate the default formatters."""
87 87 formatter_classes = [
88 88 PlainTextFormatter,
89 89 HTMLFormatter,
90 90 SVGFormatter,
91 91 PNGFormatter,
92 92 JPEGFormatter,
93 93 LatexFormatter,
94 94 JSONFormatter,
95 95 JavascriptFormatter
96 96 ]
97 97 d = {}
98 98 for cls in formatter_classes:
99 99 f = cls(parent=self)
100 100 d[f.format_type] = f
101 101 return d
102 102
103 103 def format(self, obj, include=None, exclude=None):
104 104 """Return a format data dict for an object.
105 105
106 106 By default all format types will be computed.
107 107
108 108 The following MIME types are currently implemented:
109 109
110 110 * text/plain
111 111 * text/html
112 112 * text/latex
113 113 * application/json
114 114 * application/javascript
115 115 * image/png
116 116 * image/jpeg
117 117 * image/svg+xml
118 118
119 119 Parameters
120 120 ----------
121 121 obj : object
122 122 The Python object whose format data will be computed.
123 123 include : list or tuple, optional
124 124 A list of format type strings (MIME types) to include in the
125 125 format data dict. If this is set *only* the format types included
126 126 in this list will be computed.
127 127 exclude : list or tuple, optional
128 128 A list of format type string (MIME types) to exclude in the format
129 129 data dict. If this is set all format types will be computed,
130 130 except for those included in this argument.
131 131
132 132 Returns
133 133 -------
134 134 (format_dict, metadata_dict) : tuple of two dicts
135 135
136 136 format_dict is a dictionary of key/value pairs, one of each format that was
137 137 generated for the object. The keys are the format types, which
138 138 will usually be MIME type strings and the values and JSON'able
139 139 data structure containing the raw data for the representation in
140 140 that format.
141 141
142 142 metadata_dict is a dictionary of metadata about each mime-type output.
143 143 Its keys will be a strict subset of the keys in format_dict.
144 144 """
145 145 format_dict = {}
146 146 md_dict = {}
147 147
148 148 for format_type, formatter in self.formatters.items():
149 149 if include and format_type not in include:
150 150 continue
151 151 if exclude and format_type in exclude:
152 152 continue
153 153
154 154 md = None
155 155 try:
156 156 data = formatter(obj)
157 157 except:
158 158 # FIXME: log the exception
159 159 raise
160 160
161 161 # formatters can return raw data or (data, metadata)
162 162 if isinstance(data, tuple) and len(data) == 2:
163 163 data, md = data
164 164
165 165 if data is not None:
166 166 format_dict[format_type] = data
167 167 if md is not None:
168 168 md_dict[format_type] = md
169 169
170 170 return format_dict, md_dict
171 171
172 172 @property
173 173 def format_types(self):
174 174 """Return the format types (MIME types) of the active formatters."""
175 175 return list(self.formatters.keys())
176 176
177 177
178 178 #-----------------------------------------------------------------------------
179 179 # Formatters for specific format types (text, html, svg, etc.)
180 180 #-----------------------------------------------------------------------------
181 181
182 182
183 183 class FormatterABC(with_metaclass(abc.ABCMeta, object)):
184 184 """ Abstract base class for Formatters.
185 185
186 186 A formatter is a callable class that is responsible for computing the
187 187 raw format data for a particular format type (MIME type). For example,
188 188 an HTML formatter would have a format type of `text/html` and would return
189 189 the HTML representation of the object when called.
190 190 """
191 191
192 192 # The format type of the data returned, usually a MIME type.
193 193 format_type = 'text/plain'
194 194
195 195 # Is the formatter enabled...
196 196 enabled = True
197 197
198 198 @abc.abstractmethod
199 199 def __call__(self, obj):
200 200 """Return a JSON'able representation of the object.
201 201
202 202 If the object cannot be formatted by this formatter, then return None
203 203 """
204 204 try:
205 205 return repr(obj)
206 206 except Exception:
207 207 return None
208 208
209 209
210 210 def _mod_name_key(typ):
211 """Return a '__module__.__name__' string key for a type."""
211 """Return a (__module__, __name__) tuple for a type.
212
213 Used as key in Formatter.deferred_printers.
214 """
212 215 module = getattr(typ, '__module__', None)
213 216 name = getattr(typ, '__name__', None)
214 217 return (module, name)
215 218
216 219
217 220 def _get_type(obj):
218 221 """Return the type of an instance (old and new-style)"""
219 222 return getattr(obj, '__class__', None) or type(obj)
220 223
224 _raise_key_error = object()
221 225
222 226 class BaseFormatter(Configurable):
223 227 """A base formatter class that is configurable.
224 228
225 229 This formatter should usually be used as the base class of all formatters.
226 230 It is a traited :class:`Configurable` class and includes an extensible
227 231 API for users to determine how their objects are formatted. The following
228 232 logic is used to find a function to format an given object.
229 233
230 234 1. The object is introspected to see if it has a method with the name
231 235 :attr:`print_method`. If is does, that object is passed to that method
232 236 for formatting.
233 237 2. If no print method is found, three internal dictionaries are consulted
234 238 to find print method: :attr:`singleton_printers`, :attr:`type_printers`
235 239 and :attr:`deferred_printers`.
236 240
237 241 Users should use these dictionaries to register functions that will be
238 242 used to compute the format data for their objects (if those objects don't
239 243 have the special print methods). The easiest way of using these
240 244 dictionaries is through the :meth:`for_type` and :meth:`for_type_by_name`
241 245 methods.
242 246
243 247 If no function/callable is found to compute the format data, ``None`` is
244 248 returned and this format type is not used.
245 249 """
246 250
247 251 format_type = Unicode('text/plain')
248 252
249 253 enabled = Bool(True, config=True)
250 254
251 255 print_method = ObjectName('__repr__')
252 256
253 257 # The singleton printers.
254 258 # Maps the IDs of the builtin singleton objects to the format functions.
255 259 singleton_printers = Dict(config=True)
256 260
257 261 # The type-specific printers.
258 262 # Map type objects to the format functions.
259 263 type_printers = Dict(config=True)
260 264
261 265 # The deferred-import type-specific printers.
262 266 # Map (modulename, classname) pairs to the format functions.
263 267 deferred_printers = Dict(config=True)
264 268
265 269 def __call__(self, obj):
266 270 """Compute the format for an object."""
267 271 if self.enabled:
268 272 try:
269 273 # lookup registered printer
270 274 try:
271 275 printer = self.lookup(obj)
272 276 except KeyError:
273 277 pass
274 278 else:
275 279 return printer(obj)
276 280 # Finally look for special method names
277 281 method = pretty._safe_getattr(obj, self.print_method, None)
278 282 if method is not None:
279 283 return method()
280 284 return None
281 285 except Exception:
282 286 pass
283 287 else:
284 288 return None
285 289
290 def __contains__(self, typ):
291 """map in to lookup_by_type"""
292 try:
293 self.lookup_by_type(typ)
294 except KeyError:
295 return False
296 else:
297 return True
298
286 299 def lookup(self, obj):
287 300 """Look up the formatter for a given instance.
288 301
289 302 Parameters
290 303 ----------
291 304 obj : object instance
292 305
293 306 Returns
294 307 -------
295 308 f : callable
296 The registered fromatting callable for the type.
309 The registered formatting callable for the type.
297 310
298 311 Raises
299 312 ------
300 313 KeyError if the type has not been registered.
301 314 """
302 315 # look for singleton first
303 316 obj_id = id(obj)
304 317 if obj_id in self.singleton_printers:
305 318 return self.singleton_printers[obj_id]
306 319 # then lookup by type
307 320 return self.lookup_by_type(_get_type(obj))
308 321
309 322 def lookup_by_type(self, typ):
310 """ Look up all the registered formatters for a type.
323 """Look up the registered formatter for a type.
311 324
312 325 Parameters
313 326 ----------
314 327 typ : type
315 328
316 329 Returns
317 330 -------
318 331 f : callable
319 The registered fromatting callable for the type.
332 The registered formatting callable for the type.
320 333
321 334 Raises
322 335 ------
323 336 KeyError if the type has not been registered.
324 337 """
338 if isinstance(typ, string_types):
339 typ_key = tuple(typ.rsplit('.',1))
340 if typ_key not in self.deferred_printers:
341 # We may have it cached in the type map. We will have to
342 # iterate over all of the types to check.
343 for cls in self.type_printers:
344 if _mod_name_key(cls) == typ_key:
345 return self.type_printers[cls]
346 else:
347 return self.deferred_printers[typ_key]
348 else:
325 349 for cls in pretty._get_mro(typ):
326 350 if cls in self.type_printers or self._in_deferred_types(cls):
327 351 return self.type_printers[cls]
328 352
329 353 # If we have reached here, the lookup failed.
330 354 raise KeyError("No registered printer for {0!r}".format(typ))
331 355
332 356 def for_type(self, typ, func=None):
333 357 """Add a format function for a given type.
334 358
335 359 Parameters
336 360 -----------
337 361 typ : class
338 362 The class of the object that will be formatted using `func`.
339 363 func : callable
340 364 A callable for computing the format data.
341 365 `func` will be called with the object to be formatted,
342 366 and will return the raw data in this formatter's format.
343 367 Subclasses may use a different call signature for the
344 368 `func` argument.
345 369
346 370 If `func` is None or not specified, there will be no change,
347 371 only returning the current value.
348 372
349 373 Returns
350 374 -------
351 375 oldfunc : callable
352 376 The currently registered callable.
353 377 If you are registering a new formatter,
354 378 this will be the previous value (to enable restoring later).
355 379 """
356 380 # if string given, interpret as 'pkg.module.class_name'
357 381 if isinstance(typ, string_types):
358 382 type_module, type_name = typ.rsplit('.', 1)
359 383 return self.for_type_by_name(type_module, type_name, func)
360 384
361 385 try:
362 386 oldfunc = self.lookup_by_type(typ)
363 387 except KeyError:
364 388 oldfunc = None
365 389
366 390 if func is not None:
367 391 self.type_printers[typ] = func
368 392
369 393 return oldfunc
370 394
371 395 def for_type_by_name(self, type_module, type_name, func=None):
372 396 """Add a format function for a type specified by the full dotted
373 397 module and name of the type, rather than the type of the object.
374 398
375 399 Parameters
376 400 ----------
377 401 type_module : str
378 402 The full dotted name of the module the type is defined in, like
379 403 ``numpy``.
380 404 type_name : str
381 405 The name of the type (the class name), like ``dtype``
382 406 func : callable
383 407 A callable for computing the format data.
384 408 `func` will be called with the object to be formatted,
385 409 and will return the raw data in this formatter's format.
386 410 Subclasses may use a different call signature for the
387 411 `func` argument.
388 412
389 413 If `func` is None or unspecified, there will be no change,
390 414 only returning the current value.
391 415
392 416 Returns
393 417 -------
394 418 oldfunc : callable
395 419 The currently registered callable.
396 420 If you are registering a new formatter,
397 421 this will be the previous value (to enable restoring later).
398 422 """
399 423 key = (type_module, type_name)
400 424
401 oldfunc = self.deferred_printers.get(key, None)
425 try:
426 oldfunc = self.lookup_by_type("%s.%s" % key)
427 except KeyError:
428 oldfunc = None
429
402 430 if func is not None:
403 431 self.deferred_printers[key] = func
404 432 return oldfunc
405 433
406 def pop(self, typ):
407 """ Pop a registered object for the given type.
434 def pop(self, typ, default=_raise_key_error):
435 """Pop a formatter for the given type.
408 436
409 437 Parameters
410 438 ----------
411 439 typ : type or '__module__.__name__' string for a type
440 default : object
441 value to be returned if no formatter is registered for typ.
412 442
413 443 Returns
414 444 -------
415 445 obj : object
416 446 The last registered object for the type.
417 447
418 448 Raises
419 449 ------
420 KeyError if the type is not registered.
450 KeyError if the type is not registered and default is not specified.
421 451 """
452
422 453 if isinstance(typ, string_types):
423 454 typ_key = tuple(typ.rsplit('.',1))
424 455 if typ_key not in self.deferred_printers:
425 456 # We may have it cached in the type map. We will have to
426 457 # iterate over all of the types to check.
427 458 for cls in self.type_printers:
428 459 if _mod_name_key(cls) == typ_key:
429 460 old = self.type_printers.pop(cls)
430 461 break
431 462 else:
432 raise KeyError("No registered value for {0!r}".format(typ_key))
463 old = default
433 464 else:
434 465 old = self.deferred_printers.pop(typ_key)
435 466 else:
436 467 if typ in self.type_printers:
437 468 old = self.type_printers.pop(typ)
438 469 else:
439 old = self.deferred_printers.pop(_mod_name_key(typ))
470 old = self.deferred_printers.pop(_mod_name_key(typ), default)
471 if old is _raise_key_error:
472 raise KeyError("No registered value for {0!r}".format(typ))
440 473 return old
441 474
442 475 def _in_deferred_types(self, cls):
443 476 """
444 477 Check if the given class is specified in the deferred type registry.
445 478
446 479 Successful matches will be moved to the regular type registry for future use.
447 480 """
448 481 mod = getattr(cls, '__module__', None)
449 482 name = getattr(cls, '__name__', None)
450 483 key = (mod, name)
451 484 if key in self.deferred_printers:
452 485 # Move the printer over to the regular registry.
453 486 printer = self.deferred_printers.pop(key)
454 487 self.type_printers[cls] = printer
455 488 return True
456 489 return False
457 490
458 491
459 492 class PlainTextFormatter(BaseFormatter):
460 493 """The default pretty-printer.
461 494
462 495 This uses :mod:`IPython.lib.pretty` to compute the format data of
463 496 the object. If the object cannot be pretty printed, :func:`repr` is used.
464 497 See the documentation of :mod:`IPython.lib.pretty` for details on
465 498 how to write pretty printers. Here is a simple example::
466 499
467 500 def dtype_pprinter(obj, p, cycle):
468 501 if cycle:
469 502 return p.text('dtype(...)')
470 503 if hasattr(obj, 'fields'):
471 504 if obj.fields is None:
472 505 p.text(repr(obj))
473 506 else:
474 507 p.begin_group(7, 'dtype([')
475 508 for i, field in enumerate(obj.descr):
476 509 if i > 0:
477 510 p.text(',')
478 511 p.breakable()
479 512 p.pretty(field)
480 513 p.end_group(7, '])')
481 514 """
482 515
483 516 # The format type of data returned.
484 517 format_type = Unicode('text/plain')
485 518
486 519 # This subclass ignores this attribute as it always need to return
487 520 # something.
488 521 enabled = Bool(True, config=False)
489 522
490 523 # Look for a _repr_pretty_ methods to use for pretty printing.
491 524 print_method = ObjectName('_repr_pretty_')
492 525
493 526 # Whether to pretty-print or not.
494 527 pprint = Bool(True, config=True)
495 528
496 529 # Whether to be verbose or not.
497 530 verbose = Bool(False, config=True)
498 531
499 532 # The maximum width.
500 533 max_width = Integer(79, config=True)
501 534
502 535 # The newline character.
503 536 newline = Unicode('\n', config=True)
504 537
505 538 # format-string for pprinting floats
506 539 float_format = Unicode('%r')
507 540 # setter for float precision, either int or direct format-string
508 541 float_precision = CUnicode('', config=True)
509 542
510 543 def _float_precision_changed(self, name, old, new):
511 544 """float_precision changed, set float_format accordingly.
512 545
513 546 float_precision can be set by int or str.
514 547 This will set float_format, after interpreting input.
515 548 If numpy has been imported, numpy print precision will also be set.
516 549
517 550 integer `n` sets format to '%.nf', otherwise, format set directly.
518 551
519 552 An empty string returns to defaults (repr for float, 8 for numpy).
520 553
521 554 This parameter can be set via the '%precision' magic.
522 555 """
523 556
524 557 if '%' in new:
525 558 # got explicit format string
526 559 fmt = new
527 560 try:
528 561 fmt%3.14159
529 562 except Exception:
530 563 raise ValueError("Precision must be int or format string, not %r"%new)
531 564 elif new:
532 565 # otherwise, should be an int
533 566 try:
534 567 i = int(new)
535 568 assert i >= 0
536 569 except ValueError:
537 570 raise ValueError("Precision must be int or format string, not %r"%new)
538 571 except AssertionError:
539 572 raise ValueError("int precision must be non-negative, not %r"%i)
540 573
541 574 fmt = '%%.%if'%i
542 575 if 'numpy' in sys.modules:
543 576 # set numpy precision if it has been imported
544 577 import numpy
545 578 numpy.set_printoptions(precision=i)
546 579 else:
547 580 # default back to repr
548 581 fmt = '%r'
549 582 if 'numpy' in sys.modules:
550 583 import numpy
551 584 # numpy default is 8
552 585 numpy.set_printoptions(precision=8)
553 586 self.float_format = fmt
554 587
555 588 # Use the default pretty printers from IPython.lib.pretty.
556 589 def _singleton_printers_default(self):
557 590 return pretty._singleton_pprinters.copy()
558 591
559 592 def _type_printers_default(self):
560 593 d = pretty._type_pprinters.copy()
561 594 d[float] = lambda obj,p,cycle: p.text(self.float_format%obj)
562 595 return d
563 596
564 597 def _deferred_printers_default(self):
565 598 return pretty._deferred_type_pprinters.copy()
566 599
567 600 #### FormatterABC interface ####
568 601
569 602 def __call__(self, obj):
570 603 """Compute the pretty representation of the object."""
571 604 if not self.pprint:
572 605 return pretty._safe_repr(obj)
573 606 else:
574 607 # This uses use StringIO, as cStringIO doesn't handle unicode.
575 608 stream = StringIO()
576 609 # self.newline.encode() is a quick fix for issue gh-597. We need to
577 610 # ensure that stream does not get a mix of unicode and bytestrings,
578 611 # or it will cause trouble.
579 612 printer = pretty.RepresentationPrinter(stream, self.verbose,
580 613 self.max_width, unicode_to_str(self.newline),
581 614 singleton_pprinters=self.singleton_printers,
582 615 type_pprinters=self.type_printers,
583 616 deferred_pprinters=self.deferred_printers)
584 617 printer.pretty(obj)
585 618 printer.flush()
586 619 return stream.getvalue()
587 620
588 621
589 622 class HTMLFormatter(BaseFormatter):
590 623 """An HTML formatter.
591 624
592 625 To define the callables that compute the HTML representation of your
593 626 objects, define a :meth:`_repr_html_` method or use the :meth:`for_type`
594 627 or :meth:`for_type_by_name` methods to register functions that handle
595 628 this.
596 629
597 630 The return value of this formatter should be a valid HTML snippet that
598 631 could be injected into an existing DOM. It should *not* include the
599 632 ```<html>`` or ```<body>`` tags.
600 633 """
601 634 format_type = Unicode('text/html')
602 635
603 636 print_method = ObjectName('_repr_html_')
604 637
605 638
606 639 class SVGFormatter(BaseFormatter):
607 640 """An SVG formatter.
608 641
609 642 To define the callables that compute the SVG representation of your
610 643 objects, define a :meth:`_repr_svg_` method or use the :meth:`for_type`
611 644 or :meth:`for_type_by_name` methods to register functions that handle
612 645 this.
613 646
614 647 The return value of this formatter should be valid SVG enclosed in
615 648 ```<svg>``` tags, that could be injected into an existing DOM. It should
616 649 *not* include the ```<html>`` or ```<body>`` tags.
617 650 """
618 651 format_type = Unicode('image/svg+xml')
619 652
620 653 print_method = ObjectName('_repr_svg_')
621 654
622 655
623 656 class PNGFormatter(BaseFormatter):
624 657 """A PNG formatter.
625 658
626 659 To define the callables that compute the PNG representation of your
627 660 objects, define a :meth:`_repr_png_` method or use the :meth:`for_type`
628 661 or :meth:`for_type_by_name` methods to register functions that handle
629 662 this.
630 663
631 664 The return value of this formatter should be raw PNG data, *not*
632 665 base64 encoded.
633 666 """
634 667 format_type = Unicode('image/png')
635 668
636 669 print_method = ObjectName('_repr_png_')
637 670
638 671
639 672 class JPEGFormatter(BaseFormatter):
640 673 """A JPEG formatter.
641 674
642 675 To define the callables that compute the JPEG representation of your
643 676 objects, define a :meth:`_repr_jpeg_` method or use the :meth:`for_type`
644 677 or :meth:`for_type_by_name` methods to register functions that handle
645 678 this.
646 679
647 680 The return value of this formatter should be raw JPEG data, *not*
648 681 base64 encoded.
649 682 """
650 683 format_type = Unicode('image/jpeg')
651 684
652 685 print_method = ObjectName('_repr_jpeg_')
653 686
654 687
655 688 class LatexFormatter(BaseFormatter):
656 689 """A LaTeX formatter.
657 690
658 691 To define the callables that compute the LaTeX representation of your
659 692 objects, define a :meth:`_repr_latex_` method or use the :meth:`for_type`
660 693 or :meth:`for_type_by_name` methods to register functions that handle
661 694 this.
662 695
663 696 The return value of this formatter should be a valid LaTeX equation,
664 697 enclosed in either ```$```, ```$$``` or another LaTeX equation
665 698 environment.
666 699 """
667 700 format_type = Unicode('text/latex')
668 701
669 702 print_method = ObjectName('_repr_latex_')
670 703
671 704
672 705 class JSONFormatter(BaseFormatter):
673 706 """A JSON string formatter.
674 707
675 708 To define the callables that compute the JSON string representation of
676 709 your objects, define a :meth:`_repr_json_` method or use the :meth:`for_type`
677 710 or :meth:`for_type_by_name` methods to register functions that handle
678 711 this.
679 712
680 713 The return value of this formatter should be a valid JSON string.
681 714 """
682 715 format_type = Unicode('application/json')
683 716
684 717 print_method = ObjectName('_repr_json_')
685 718
686 719
687 720 class JavascriptFormatter(BaseFormatter):
688 721 """A Javascript formatter.
689 722
690 723 To define the callables that compute the Javascript representation of
691 724 your objects, define a :meth:`_repr_javascript_` method or use the
692 725 :meth:`for_type` or :meth:`for_type_by_name` methods to register functions
693 726 that handle this.
694 727
695 728 The return value of this formatter should be valid Javascript code and
696 729 should *not* be enclosed in ```<script>``` tags.
697 730 """
698 731 format_type = Unicode('application/javascript')
699 732
700 733 print_method = ObjectName('_repr_javascript_')
701 734
702 735 FormatterABC.register(BaseFormatter)
703 736 FormatterABC.register(PlainTextFormatter)
704 737 FormatterABC.register(HTMLFormatter)
705 738 FormatterABC.register(SVGFormatter)
706 739 FormatterABC.register(PNGFormatter)
707 740 FormatterABC.register(JPEGFormatter)
708 741 FormatterABC.register(LatexFormatter)
709 742 FormatterABC.register(JSONFormatter)
710 743 FormatterABC.register(JavascriptFormatter)
711 744
712 745
713 746 def format_display_data(obj, include=None, exclude=None):
714 747 """Return a format data dict for an object.
715 748
716 749 By default all format types will be computed.
717 750
718 751 The following MIME types are currently implemented:
719 752
720 753 * text/plain
721 754 * text/html
722 755 * text/latex
723 756 * application/json
724 757 * application/javascript
725 758 * image/png
726 759 * image/jpeg
727 760 * image/svg+xml
728 761
729 762 Parameters
730 763 ----------
731 764 obj : object
732 765 The Python object whose format data will be computed.
733 766
734 767 Returns
735 768 -------
736 769 format_dict : dict
737 770 A dictionary of key/value pairs, one or each format that was
738 771 generated for the object. The keys are the format types, which
739 772 will usually be MIME type strings and the values and JSON'able
740 773 data structure containing the raw data for the representation in
741 774 that format.
742 775 include : list or tuple, optional
743 776 A list of format type strings (MIME types) to include in the
744 777 format data dict. If this is set *only* the format types included
745 778 in this list will be computed.
746 779 exclude : list or tuple, optional
747 780 A list of format type string (MIME types) to exclue in the format
748 781 data dict. If this is set all format types will be computed,
749 782 except for those included in this argument.
750 783 """
751 784 from IPython.core.interactiveshell import InteractiveShell
752 785
753 786 InteractiveShell.instance().display_formatter.format(
754 787 obj,
755 788 include,
756 789 exclude
757 790 )
758 791
@@ -1,349 +1,340
1 1 # -*- coding: utf-8 -*-
2 2 """Pylab (matplotlib) support utilities.
3 3
4 4 Authors
5 5 -------
6 6
7 7 * Fernando Perez.
8 8 * Brian Granger
9 9 """
10 10 from __future__ import print_function
11 11
12 12 #-----------------------------------------------------------------------------
13 13 # Copyright (C) 2009 The IPython Development Team
14 14 #
15 15 # Distributed under the terms of the BSD License. The full license is in
16 16 # the file COPYING, distributed as part of this software.
17 17 #-----------------------------------------------------------------------------
18 18
19 19 #-----------------------------------------------------------------------------
20 20 # Imports
21 21 #-----------------------------------------------------------------------------
22 22
23 23 import sys
24 24 from io import BytesIO
25 25
26 26 from IPython.core.display import _pngxy
27 27 from IPython.utils.decorators import flag_calls
28 28
29 29 # If user specifies a GUI, that dictates the backend, otherwise we read the
30 30 # user's mpl default from the mpl rc structure
31 31 backends = {'tk': 'TkAgg',
32 32 'gtk': 'GTKAgg',
33 33 'wx': 'WXAgg',
34 34 'qt': 'Qt4Agg', # qt3 not supported
35 35 'qt4': 'Qt4Agg',
36 36 'osx': 'MacOSX',
37 37 'inline' : 'module://IPython.kernel.zmq.pylab.backend_inline'}
38 38
39 39 # We also need a reverse backends2guis mapping that will properly choose which
40 40 # GUI support to activate based on the desired matplotlib backend. For the
41 41 # most part it's just a reverse of the above dict, but we also need to add a
42 42 # few others that map to the same GUI manually:
43 43 backend2gui = dict(zip(backends.values(), backends.keys()))
44 44 # Our tests expect backend2gui to just return 'qt'
45 45 backend2gui['Qt4Agg'] = 'qt'
46 46 # In the reverse mapping, there are a few extra valid matplotlib backends that
47 47 # map to the same GUI support
48 48 backend2gui['GTK'] = backend2gui['GTKCairo'] = 'gtk'
49 49 backend2gui['WX'] = 'wx'
50 50 backend2gui['CocoaAgg'] = 'osx'
51 51
52 52 #-----------------------------------------------------------------------------
53 53 # Matplotlib utilities
54 54 #-----------------------------------------------------------------------------
55 55
56 56
57 57 def getfigs(*fig_nums):
58 58 """Get a list of matplotlib figures by figure numbers.
59 59
60 60 If no arguments are given, all available figures are returned. If the
61 61 argument list contains references to invalid figures, a warning is printed
62 62 but the function continues pasting further figures.
63 63
64 64 Parameters
65 65 ----------
66 66 figs : tuple
67 67 A tuple of ints giving the figure numbers of the figures to return.
68 68 """
69 69 from matplotlib._pylab_helpers import Gcf
70 70 if not fig_nums:
71 71 fig_managers = Gcf.get_all_fig_managers()
72 72 return [fm.canvas.figure for fm in fig_managers]
73 73 else:
74 74 figs = []
75 75 for num in fig_nums:
76 76 f = Gcf.figs.get(num)
77 77 if f is None:
78 78 print('Warning: figure %s not available.' % num)
79 79 else:
80 80 figs.append(f.canvas.figure)
81 81 return figs
82 82
83 83
84 84 def figsize(sizex, sizey):
85 85 """Set the default figure size to be [sizex, sizey].
86 86
87 87 This is just an easy to remember, convenience wrapper that sets::
88 88
89 89 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
90 90 """
91 91 import matplotlib
92 92 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
93 93
94 94
95 95 def print_figure(fig, fmt='png'):
96 96 """Convert a figure to svg or png for inline display."""
97 97 from matplotlib import rcParams
98 98 # When there's an empty figure, we shouldn't return anything, otherwise we
99 99 # get big blank areas in the qt console.
100 100 if not fig.axes and not fig.lines:
101 101 return
102 102
103 103 fc = fig.get_facecolor()
104 104 ec = fig.get_edgecolor()
105 105 bytes_io = BytesIO()
106 106 dpi = rcParams['savefig.dpi']
107 107 if fmt == 'retina':
108 108 dpi = dpi * 2
109 109 fmt = 'png'
110 110 fig.canvas.print_figure(bytes_io, format=fmt, bbox_inches='tight',
111 111 facecolor=fc, edgecolor=ec, dpi=dpi)
112 112 data = bytes_io.getvalue()
113 113 return data
114 114
115 115 def retina_figure(fig):
116 116 """format a figure as a pixel-doubled (retina) PNG"""
117 117 pngdata = print_figure(fig, fmt='retina')
118 118 w, h = _pngxy(pngdata)
119 119 metadata = dict(width=w//2, height=h//2)
120 120 return pngdata, metadata
121 121
122 122 # We need a little factory function here to create the closure where
123 123 # safe_execfile can live.
124 124 def mpl_runner(safe_execfile):
125 125 """Factory to return a matplotlib-enabled runner for %run.
126 126
127 127 Parameters
128 128 ----------
129 129 safe_execfile : function
130 130 This must be a function with the same interface as the
131 131 :meth:`safe_execfile` method of IPython.
132 132
133 133 Returns
134 134 -------
135 135 A function suitable for use as the ``runner`` argument of the %run magic
136 136 function.
137 137 """
138 138
139 139 def mpl_execfile(fname,*where,**kw):
140 140 """matplotlib-aware wrapper around safe_execfile.
141 141
142 142 Its interface is identical to that of the :func:`execfile` builtin.
143 143
144 144 This is ultimately a call to execfile(), but wrapped in safeties to
145 145 properly handle interactive rendering."""
146 146
147 147 import matplotlib
148 148 import matplotlib.pylab as pylab
149 149
150 150 #print '*** Matplotlib runner ***' # dbg
151 151 # turn off rendering until end of script
152 152 is_interactive = matplotlib.rcParams['interactive']
153 153 matplotlib.interactive(False)
154 154 safe_execfile(fname,*where,**kw)
155 155 matplotlib.interactive(is_interactive)
156 156 # make rendering call now, if the user tried to do it
157 157 if pylab.draw_if_interactive.called:
158 158 pylab.draw()
159 159 pylab.draw_if_interactive.called = False
160 160
161 161 return mpl_execfile
162 162
163 163
164 164 def select_figure_format(shell, fmt):
165 165 """Select figure format for inline backend, can be 'png', 'retina', or 'svg'.
166 166
167 167 Using this method ensures only one figure format is active at a time.
168 168 """
169 169 from matplotlib.figure import Figure
170 170 from IPython.kernel.zmq.pylab import backend_inline
171 171
172 172 svg_formatter = shell.display_formatter.formatters['image/svg+xml']
173 173 png_formatter = shell.display_formatter.formatters['image/png']
174 174
175 175 if fmt == 'png':
176 try:
177 svg_formatter.pop(Figure)
178 except KeyError:
179 pass
176 svg_formatter.pop(Figure, None)
180 177 png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png'))
181 178 elif fmt in ('png2x', 'retina'):
182 try:
183 svg_formatter.pop(Figure)
184 except KeyError:
185 pass
179 svg_formatter.pop(Figure, None)
186 180 png_formatter.for_type(Figure, retina_figure)
187 181 elif fmt == 'svg':
188 try:
189 svg_formatter.pop(Figure)
190 except KeyError:
191 pass
182 png_formatter.pop(Figure, None)
192 183 svg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'svg'))
193 184 else:
194 185 raise ValueError("supported formats are: 'png', 'retina', 'svg', not %r" % fmt)
195 186
196 187 # set the format to be used in the backend()
197 188 backend_inline._figure_format = fmt
198 189
199 190 #-----------------------------------------------------------------------------
200 191 # Code for initializing matplotlib and importing pylab
201 192 #-----------------------------------------------------------------------------
202 193
203 194
204 195 def find_gui_and_backend(gui=None, gui_select=None):
205 196 """Given a gui string return the gui and mpl backend.
206 197
207 198 Parameters
208 199 ----------
209 200 gui : str
210 201 Can be one of ('tk','gtk','wx','qt','qt4','inline').
211 202 gui_select : str
212 203 Can be one of ('tk','gtk','wx','qt','qt4','inline').
213 204 This is any gui already selected by the shell.
214 205
215 206 Returns
216 207 -------
217 208 A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg',
218 209 'WXAgg','Qt4Agg','module://IPython.kernel.zmq.pylab.backend_inline').
219 210 """
220 211
221 212 import matplotlib
222 213
223 214 if gui and gui != 'auto':
224 215 # select backend based on requested gui
225 216 backend = backends[gui]
226 217 else:
227 218 # We need to read the backend from the original data structure, *not*
228 219 # from mpl.rcParams, since a prior invocation of %matplotlib may have
229 220 # overwritten that.
230 221 # WARNING: this assumes matplotlib 1.1 or newer!!
231 222 backend = matplotlib.rcParamsOrig['backend']
232 223 # In this case, we need to find what the appropriate gui selection call
233 224 # should be for IPython, so we can activate inputhook accordingly
234 225 gui = backend2gui.get(backend, None)
235 226
236 227 # If we have already had a gui active, we need it and inline are the
237 228 # ones allowed.
238 229 if gui_select and gui != gui_select:
239 230 gui = gui_select
240 231 backend = backends[gui]
241 232
242 233 return gui, backend
243 234
244 235
245 236 def activate_matplotlib(backend):
246 237 """Activate the given backend and set interactive to True."""
247 238
248 239 import matplotlib
249 240 matplotlib.interactive(True)
250 241
251 242 # Matplotlib had a bug where even switch_backend could not force
252 243 # the rcParam to update. This needs to be set *before* the module
253 244 # magic of switch_backend().
254 245 matplotlib.rcParams['backend'] = backend
255 246
256 247 import matplotlib.pyplot
257 248 matplotlib.pyplot.switch_backend(backend)
258 249
259 250 # This must be imported last in the matplotlib series, after
260 251 # backend/interactivity choices have been made
261 252 import matplotlib.pylab as pylab
262 253
263 254 pylab.show._needmain = False
264 255 # We need to detect at runtime whether show() is called by the user.
265 256 # For this, we wrap it into a decorator which adds a 'called' flag.
266 257 pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive)
267 258
268 259
269 260 def import_pylab(user_ns, import_all=True):
270 261 """Populate the namespace with pylab-related values.
271 262
272 263 Imports matplotlib, pylab, numpy, and everything from pylab and numpy.
273 264
274 265 Also imports a few names from IPython (figsize, display, getfigs)
275 266
276 267 """
277 268
278 269 # Import numpy as np/pyplot as plt are conventions we're trying to
279 270 # somewhat standardize on. Making them available to users by default
280 271 # will greatly help this.
281 272 s = ("import numpy\n"
282 273 "import matplotlib\n"
283 274 "from matplotlib import pylab, mlab, pyplot\n"
284 275 "np = numpy\n"
285 276 "plt = pyplot\n"
286 277 )
287 278 exec(s, user_ns)
288 279
289 280 if import_all:
290 281 s = ("from matplotlib.pylab import *\n"
291 282 "from numpy import *\n")
292 283 exec(s, user_ns)
293 284
294 285 # IPython symbols to add
295 286 user_ns['figsize'] = figsize
296 287 from IPython.core.display import display
297 288 # Add display and getfigs to the user's namespace
298 289 user_ns['display'] = display
299 290 user_ns['getfigs'] = getfigs
300 291
301 292
302 293 def configure_inline_support(shell, backend):
303 294 """Configure an IPython shell object for matplotlib use.
304 295
305 296 Parameters
306 297 ----------
307 298 shell : InteractiveShell instance
308 299
309 300 backend : matplotlib backend
310 301 """
311 302 # If using our svg payload backend, register the post-execution
312 303 # function that will pick up the results for display. This can only be
313 304 # done with access to the real shell object.
314 305
315 306 # Note: if we can't load the inline backend, then there's no point
316 307 # continuing (such as in terminal-only shells in environments without
317 308 # zeromq available).
318 309 try:
319 310 from IPython.kernel.zmq.pylab.backend_inline import InlineBackend
320 311 except ImportError:
321 312 return
322 313 from matplotlib import pyplot
323 314
324 315 cfg = InlineBackend.instance(parent=shell)
325 316 cfg.shell = shell
326 317 if cfg not in shell.configurables:
327 318 shell.configurables.append(cfg)
328 319
329 320 if backend == backends['inline']:
330 321 from IPython.kernel.zmq.pylab.backend_inline import flush_figures
331 322 shell.register_post_execute(flush_figures)
332 323
333 324 # Save rcParams that will be overwrittern
334 325 shell._saved_rcParams = dict()
335 326 for k in cfg.rc:
336 327 shell._saved_rcParams[k] = pyplot.rcParams[k]
337 328 # load inline_rc
338 329 pyplot.rcParams.update(cfg.rc)
339 330 else:
340 331 from IPython.kernel.zmq.pylab.backend_inline import flush_figures
341 332 if flush_figures in shell._post_execute:
342 333 shell._post_execute.pop(flush_figures)
343 334 if hasattr(shell, '_saved_rcParams'):
344 335 pyplot.rcParams.update(shell._saved_rcParams)
345 336 del shell._saved_rcParams
346 337
347 338 # Setup the default figure format
348 339 select_figure_format(shell, cfg.figure_format)
349 340
@@ -1,210 +1,234
1 1 """Tests for the Formatters.
2 2 """
3 3
4 4 from math import pi
5 5
6 6 try:
7 7 import numpy
8 8 except:
9 9 numpy = None
10 10 import nose.tools as nt
11 11
12 12 from IPython.core.formatters import PlainTextFormatter, _mod_name_key
13 13
14 14 class A(object):
15 15 def __repr__(self):
16 16 return 'A()'
17 17
18 18 class B(A):
19 19 def __repr__(self):
20 20 return 'B()'
21 21
22 22 class C:
23 23 pass
24 24
25 25 class BadPretty(object):
26 26 _repr_pretty_ = None
27 27
28 28 class GoodPretty(object):
29 29 def _repr_pretty_(self, pp, cycle):
30 30 pp.text('foo')
31 31
32 32 def __repr__(self):
33 33 return 'GoodPretty()'
34 34
35 35 def foo_printer(obj, pp, cycle):
36 36 pp.text('foo')
37 37
38 38 def test_pretty():
39 39 f = PlainTextFormatter()
40 40 f.for_type(A, foo_printer)
41 41 nt.assert_equal(f(A()), 'foo')
42 42 nt.assert_equal(f(B()), 'foo')
43 43 nt.assert_equal(f(GoodPretty()), 'foo')
44 44 # Just don't raise an exception for the following:
45 45 f(BadPretty())
46 46
47 47 f.pprint = False
48 48 nt.assert_equal(f(A()), 'A()')
49 49 nt.assert_equal(f(B()), 'B()')
50 50 nt.assert_equal(f(GoodPretty()), 'GoodPretty()')
51 51
52 52
53 53 def test_deferred():
54 54 f = PlainTextFormatter()
55 55
56 56 def test_precision():
57 57 """test various values for float_precision."""
58 58 f = PlainTextFormatter()
59 59 nt.assert_equal(f(pi), repr(pi))
60 60 f.float_precision = 0
61 61 if numpy:
62 62 po = numpy.get_printoptions()
63 63 nt.assert_equal(po['precision'], 0)
64 64 nt.assert_equal(f(pi), '3')
65 65 f.float_precision = 2
66 66 if numpy:
67 67 po = numpy.get_printoptions()
68 68 nt.assert_equal(po['precision'], 2)
69 69 nt.assert_equal(f(pi), '3.14')
70 70 f.float_precision = '%g'
71 71 if numpy:
72 72 po = numpy.get_printoptions()
73 73 nt.assert_equal(po['precision'], 2)
74 74 nt.assert_equal(f(pi), '3.14159')
75 75 f.float_precision = '%e'
76 76 nt.assert_equal(f(pi), '3.141593e+00')
77 77 f.float_precision = ''
78 78 if numpy:
79 79 po = numpy.get_printoptions()
80 80 nt.assert_equal(po['precision'], 8)
81 81 nt.assert_equal(f(pi), repr(pi))
82 82
83 83 def test_bad_precision():
84 84 """test various invalid values for float_precision."""
85 85 f = PlainTextFormatter()
86 86 def set_fp(p):
87 87 f.float_precision=p
88 88 nt.assert_raises(ValueError, set_fp, '%')
89 89 nt.assert_raises(ValueError, set_fp, '%.3f%i')
90 90 nt.assert_raises(ValueError, set_fp, 'foo')
91 91 nt.assert_raises(ValueError, set_fp, -1)
92 92
93 93 def test_for_type():
94 94 f = PlainTextFormatter()
95 95
96 96 # initial return, None
97 97 nt.assert_is(f.for_type(C, foo_printer), None)
98 98 # no func queries
99 99 nt.assert_is(f.for_type(C), foo_printer)
100 100 # shouldn't change anything
101 101 nt.assert_is(f.for_type(C), foo_printer)
102 102 # None should do the same
103 103 nt.assert_is(f.for_type(C, None), foo_printer)
104 104 nt.assert_is(f.for_type(C, None), foo_printer)
105 105
106 106 def test_for_type_string():
107 107 f = PlainTextFormatter()
108 108
109 109 mod = C.__module__
110 110
111 111 type_str = '%s.%s' % (C.__module__, 'C')
112 112
113 113 # initial return, None
114 114 nt.assert_is(f.for_type(type_str, foo_printer), None)
115 115 # no func queries
116 116 nt.assert_is(f.for_type(type_str), foo_printer)
117 117 nt.assert_in(_mod_name_key(C), f.deferred_printers)
118 118 nt.assert_is(f.for_type(C), foo_printer)
119 119 nt.assert_not_in(_mod_name_key(C), f.deferred_printers)
120 120 nt.assert_in(C, f.type_printers)
121 121
122 122 def test_for_type_by_name():
123 123 f = PlainTextFormatter()
124 124
125 125 mod = C.__module__
126 126
127 127 # initial return, None
128 128 nt.assert_is(f.for_type_by_name(mod, 'C', foo_printer), None)
129 129 # no func queries
130 130 nt.assert_is(f.for_type_by_name(mod, 'C'), foo_printer)
131 131 # shouldn't change anything
132 132 nt.assert_is(f.for_type_by_name(mod, 'C'), foo_printer)
133 133 # None should do the same
134 134 nt.assert_is(f.for_type_by_name(mod, 'C', None), foo_printer)
135 135 nt.assert_is(f.for_type_by_name(mod, 'C', None), foo_printer)
136 136
137 137 def test_lookup():
138 138 f = PlainTextFormatter()
139 139
140 140 f.for_type(C, foo_printer)
141 141 nt.assert_is(f.lookup(C()), foo_printer)
142 142 with nt.assert_raises(KeyError):
143 143 f.lookup(A())
144 144
145 145 def test_lookup_string():
146 146 f = PlainTextFormatter()
147 147 type_str = '%s.%s' % (C.__module__, 'C')
148 148
149 149 f.for_type(type_str, foo_printer)
150 150 nt.assert_is(f.lookup(C()), foo_printer)
151 151 # should move from deferred to imported dict
152 152 nt.assert_not_in(_mod_name_key(C), f.deferred_printers)
153 153 nt.assert_in(C, f.type_printers)
154 154
155 155 def test_lookup_by_type():
156 156 f = PlainTextFormatter()
157 157 f.for_type(C, foo_printer)
158 158 nt.assert_is(f.lookup_by_type(C), foo_printer)
159 159 type_str = '%s.%s' % (C.__module__, 'C')
160 160 with nt.assert_raises(KeyError):
161 161 f.lookup_by_type(A)
162 162
163 163 def test_lookup_by_type_string():
164 164 f = PlainTextFormatter()
165 165 type_str = '%s.%s' % (C.__module__, 'C')
166 166 f.for_type(type_str, foo_printer)
167 167
168 168 # verify insertion
169 169 nt.assert_in(_mod_name_key(C), f.deferred_printers)
170 170 nt.assert_not_in(C, f.type_printers)
171 171
172 nt.assert_is(f.lookup_by_type(type_str), foo_printer)
173 # lookup by string doesn't cause import
174 nt.assert_in(_mod_name_key(C), f.deferred_printers)
175 nt.assert_not_in(C, f.type_printers)
176
172 177 nt.assert_is(f.lookup_by_type(C), foo_printer)
173 178 # should move from deferred to imported dict
174 179 nt.assert_not_in(_mod_name_key(C), f.deferred_printers)
175 180 nt.assert_in(C, f.type_printers)
176 181
182 def test_in_formatter():
183 f = PlainTextFormatter()
184 f.for_type(C, foo_printer)
185 type_str = '%s.%s' % (C.__module__, 'C')
186 nt.assert_in(C, f)
187 nt.assert_in(type_str, f)
188
189 def test_string_in_formatter():
190 f = PlainTextFormatter()
191 type_str = '%s.%s' % (C.__module__, 'C')
192 f.for_type(type_str, foo_printer)
193 nt.assert_in(type_str, f)
194 nt.assert_in(C, f)
195
177 196 def test_pop():
178 197 f = PlainTextFormatter()
179 198 f.for_type(C, foo_printer)
180 199 nt.assert_is(f.lookup_by_type(C), foo_printer)
181 f.pop(C)
200 nt.assert_is(f.pop(C, None), foo_printer)
201 f.for_type(C, foo_printer)
202 nt.assert_is(f.pop(C), foo_printer)
182 203 with nt.assert_raises(KeyError):
183 204 f.lookup_by_type(C)
184 205 with nt.assert_raises(KeyError):
185 206 f.pop(C)
186 207 with nt.assert_raises(KeyError):
187 208 f.pop(A)
209 nt.assert_is(f.pop(A, None), None)
188 210
189 211 def test_pop_string():
190 212 f = PlainTextFormatter()
191 213 type_str = '%s.%s' % (C.__module__, 'C')
192 214
193 215 with nt.assert_raises(KeyError):
194 216 f.pop(type_str)
195 217
196 218 f.for_type(type_str, foo_printer)
197 219 f.pop(type_str)
198 220 with nt.assert_raises(KeyError):
199 221 f.lookup_by_type(C)
200 222 with nt.assert_raises(KeyError):
201 223 f.pop(type_str)
202 224
203 225 f.for_type(C, foo_printer)
204 f.pop(type_str)
226 nt.assert_is(f.pop(type_str, None), foo_printer)
205 227 with nt.assert_raises(KeyError):
206 228 f.lookup_by_type(C)
207 229 with nt.assert_raises(KeyError):
208 230 f.pop(type_str)
231 nt.assert_is(f.pop(type_str, None), None)
232
209 233
210 234
General Comments 0
You need to be logged in to leave comments. Login now