##// END OF EJS Templates
ENH: Refactor pretty to allow it to run without global type registries.
Robert Kern -
Show More
@@ -1,690 +1,700 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 pretty
4 4 ~~
5 5
6 6 Python advanced pretty printer. This pretty printer is intended to
7 7 replace the old `pprint` python module which does not allow developers
8 8 to provide their own pretty print callbacks.
9 9
10 10 This module is based on ruby's `prettyprint.rb` library by `Tanaka Akira`.
11 11
12 12
13 13 Example Usage
14 14 =============
15 15
16 16 To directly print the representation of an object use `pprint`::
17 17
18 18 from pretty import pprint
19 19 pprint(complex_object)
20 20
21 21 To get a string of the output use `pretty`::
22 22
23 23 from pretty import pretty
24 24 string = pretty(complex_object)
25 25
26 26
27 27 Extending
28 28 =========
29 29
30 30 The pretty library allows developers to add pretty printing rules for their
31 31 own objects. This process is straightforward. All you have to do is to
32 32 add a `__pretty__` method to your object and call the methods on the
33 33 pretty printer passed::
34 34
35 35 class MyObject(object):
36 36
37 37 def __pretty__(self, p, cycle):
38 38 ...
39 39
40 40 Depending on the python version you want to support you have two
41 41 possibilities. The following list shows the python 2.5 version and the
42 42 compatibility one.
43 43
44 44
45 45 Here the example implementation of a `__pretty__` method for a list
46 46 subclass for python 2.5 and higher (python 2.5 requires the with statement
47 47 __future__ import)::
48 48
49 49 class MyList(list):
50 50
51 51 def __pretty__(self, p, cycle):
52 52 if cycle:
53 53 p.text('MyList(...)')
54 54 else:
55 55 with p.group(8, 'MyList([', '])'):
56 56 for idx, item in enumerate(self):
57 57 if idx:
58 58 p.text(',')
59 59 p.breakable()
60 60 p.pretty(item)
61 61
62 62 The `cycle` parameter is `True` if pretty detected a cycle. You *have* to
63 63 react to that or the result is an infinite loop. `p.text()` just adds
64 64 non breaking text to the output, `p.breakable()` either adds a whitespace
65 65 or breaks here. If you pass it an argument it's used instead of the
66 66 default space. `p.pretty` prettyprints another object using the pretty print
67 67 method.
68 68
69 69 The first parameter to the `group` function specifies the extra indentation
70 70 of the next line. In this example the next item will either be not
71 71 breaked (if the items are short enough) or aligned with the right edge of
72 72 the opening bracked of `MyList`.
73 73
74 74 If you want to support python 2.4 and lower you can use this code::
75 75
76 76 class MyList(list):
77 77
78 78 def __pretty__(self, p, cycle):
79 79 if cycle:
80 80 p.text('MyList(...)')
81 81 else:
82 82 p.begin_group(8, 'MyList([')
83 83 for idx, item in enumerate(self):
84 84 if idx:
85 85 p.text(',')
86 86 p.breakable()
87 87 p.pretty(item)
88 88 p.end_group(8, '])')
89 89
90 90 If you just want to indent something you can use the group function
91 91 without open / close parameters. Under python 2.5 you can also use this
92 92 code::
93 93
94 94 with p.indent(2):
95 95 ...
96 96
97 97 Or under python2.4 you might want to modify ``p.indentation`` by hand but
98 98 this is rather ugly.
99 99
100 100 :copyright: 2007 by Armin Ronacher.
101 101 Portions (c) 2009 by Robert Kern.
102 102 :license: BSD License.
103 103 """
104 104 from __future__ import with_statement
105 105 from contextlib import contextmanager
106 106 import sys
107 107 import types
108 108 import re
109 109 import datetime
110 110 from StringIO import StringIO
111 111 from collections import deque
112 112
113 113
114 114 __all__ = ['pretty', 'pprint', 'PrettyPrinter', 'RepresentationPrinter',
115 115 'for_type', 'for_type_by_name']
116 116
117 117
118 118 _re_pattern_type = type(re.compile(''))
119 119
120 120
121 121 def pretty(obj, verbose=False, max_width=79, newline='\n'):
122 122 """
123 123 Pretty print the object's representation.
124 124 """
125 125 stream = StringIO()
126 126 printer = RepresentationPrinter(stream, verbose, max_width, newline)
127 127 printer.pretty(obj)
128 128 printer.flush()
129 129 return stream.getvalue()
130 130
131 131
132 132 def pprint(obj, verbose=False, max_width=79, newline='\n'):
133 133 """
134 134 Like `pretty` but print to stdout.
135 135 """
136 136 printer = RepresentationPrinter(sys.stdout, verbose, max_width, newline)
137 137 printer.pretty(obj)
138 138 printer.flush()
139 139 sys.stdout.write(newline)
140 140 sys.stdout.flush()
141 141
142 142 class _PrettyPrinterBase(object):
143 143
144 144 @contextmanager
145 145 def indent(self, indent):
146 146 """with statement support for indenting/dedenting."""
147 147 self.indentation += indent
148 148 try:
149 149 yield
150 150 finally:
151 151 self.indentation -= indent
152 152
153 153 @contextmanager
154 154 def group(self, indent=0, open='', close=''):
155 155 """like begin_group / end_group but for the with statement."""
156 156 self.begin_group(indent, open)
157 157 try:
158 158 with self.indent(indent):
159 159 yield
160 160 finally:
161 161 self.end_group(indent, close)
162 162
163 163 class PrettyPrinter(_PrettyPrinterBase):
164 164 """
165 165 Baseclass for the `RepresentationPrinter` prettyprinter that is used to
166 166 generate pretty reprs of objects. Contrary to the `RepresentationPrinter`
167 167 this printer knows nothing about the default pprinters or the `__pretty__`
168 168 callback method.
169 169 """
170 170
171 171 def __init__(self, output, max_width=79, newline='\n'):
172 172 self.output = output
173 173 self.max_width = max_width
174 174 self.newline = newline
175 175 self.output_width = 0
176 176 self.buffer_width = 0
177 177 self.buffer = deque()
178 178
179 179 root_group = Group(0)
180 180 self.group_stack = [root_group]
181 181 self.group_queue = GroupQueue(root_group)
182 182 self.indentation = 0
183 183
184 184 def _break_outer_groups(self):
185 185 while self.max_width < self.output_width + self.buffer_width:
186 186 group = self.group_queue.deq()
187 187 if not group:
188 188 return
189 189 while group.breakables:
190 190 x = self.buffer.popleft()
191 191 self.output_width = x.output(self.output, self.output_width)
192 192 self.buffer_width -= x.width
193 193 while self.buffer and isinstance(self.buffer[0], Text):
194 194 x = self.buffer.popleft()
195 195 self.output_width = x.output(self.output, self.output_width)
196 196 self.buffer_width -= x.width
197 197
198 198 def text(self, obj):
199 199 """Add literal text to the output."""
200 200 width = len(obj)
201 201 if self.buffer:
202 202 text = self.buffer[-1]
203 203 if not isinstance(text, Text):
204 204 text = Text()
205 205 self.buffer.append(text)
206 206 text.add(obj, width)
207 207 self.buffer_width += width
208 208 self._break_outer_groups()
209 209 else:
210 210 self.output.write(obj)
211 211 self.output_width += width
212 212
213 213 def breakable(self, sep=' '):
214 214 """
215 215 Add a breakable separator to the output. This does not mean that it
216 216 will automatically break here. If no breaking on this position takes
217 217 place the `sep` is inserted which default to one space.
218 218 """
219 219 width = len(sep)
220 220 group = self.group_stack[-1]
221 221 if group.want_break:
222 222 self.flush()
223 223 self.output.write(self.newline)
224 224 self.output.write(' ' * self.indentation)
225 225 self.output_width = self.indentation
226 226 self.buffer_width = 0
227 227 else:
228 228 self.buffer.append(Breakable(sep, width, self))
229 229 self.buffer_width += width
230 230 self._break_outer_groups()
231 231
232 232
233 233 def begin_group(self, indent=0, open=''):
234 234 """
235 235 Begin a group. If you want support for python < 2.5 which doesn't has
236 236 the with statement this is the preferred way:
237 237
238 238 p.begin_group(1, '{')
239 239 ...
240 240 p.end_group(1, '}')
241 241
242 242 The python 2.5 expression would be this:
243 243
244 244 with p.group(1, '{', '}'):
245 245 ...
246 246
247 247 The first parameter specifies the indentation for the next line (usually
248 248 the width of the opening text), the second the opening text. All
249 249 parameters are optional.
250 250 """
251 251 if open:
252 252 self.text(open)
253 253 group = Group(self.group_stack[-1].depth + 1)
254 254 self.group_stack.append(group)
255 255 self.group_queue.enq(group)
256 256 self.indentation += indent
257 257
258 258 def end_group(self, dedent=0, close=''):
259 259 """End a group. See `begin_group` for more details."""
260 260 self.indentation -= dedent
261 261 group = self.group_stack.pop()
262 262 if not group.breakables:
263 263 self.group_queue.remove(group)
264 264 if close:
265 265 self.text(close)
266 266
267 267 def flush(self):
268 268 """Flush data that is left in the buffer."""
269 269 for data in self.buffer:
270 270 self.output_width += data.output(self.output, self.output_width)
271 271 self.buffer.clear()
272 272 self.buffer_width = 0
273 273
274 274
275 275 def _get_mro(obj_class):
276 276 """ Get a reasonable method resolution order of a class and its superclasses
277 277 for both old-style and new-style classes.
278 278 """
279 279 if not hasattr(obj_class, '__mro__'):
280 280 # Old-style class. Mix in object to make a fake new-style class.
281 281 try:
282 282 obj_class = type(obj_class.__name__, (obj_class, object), {})
283 283 except TypeError:
284 284 # Old-style extension type that does not descend from object.
285 285 # FIXME: try to construct a more thorough MRO.
286 286 mro = [obj_class]
287 287 else:
288 288 mro = obj_class.__mro__[1:-1]
289 289 else:
290 290 mro = obj_class.__mro__
291 291 return mro
292 292
293 293
294 294 class RepresentationPrinter(PrettyPrinter):
295 295 """
296 296 Special pretty printer that has a `pretty` method that calls the pretty
297 297 printer for a python object.
298 298
299 299 This class stores processing data on `self` so you must *never* use
300 300 this class in a threaded environment. Always lock it or reinstanciate
301 301 it.
302 302
303 303 Instances also have a verbose flag callbacks can access to control their
304 304 output. For example the default instance repr prints all attributes and
305 305 methods that are not prefixed by an underscore if the printer is in
306 306 verbose mode.
307 307 """
308 308
309 def __init__(self, output, verbose=False, max_width=79, newline='\n'):
309 def __init__(self, output, verbose=False, max_width=79, newline='\n',
310 singleton_pprinters=None, type_pprinters=None, deferred_pprinters=None):
311
310 312 PrettyPrinter.__init__(self, output, max_width, newline)
311 313 self.verbose = verbose
312 314 self.stack = []
315 if singleton_pprinters is None:
316 singleton_pprinters = _singleton_pprinters.copy()
317 self.singleton_pprinters = singleton_pprinters
318 if type_pprinters is None:
319 type_pprinters = _type_pprinters.copy()
320 self.type_pprinters = type_pprinters
321 if deferred_pprinters is None:
322 deferred_pprinters = _deferred_type_pprinters.copy()
323 self.deferred_pprinters = deferred_pprinters
313 324
314 325 def pretty(self, obj):
315 326 """Pretty print the given object."""
316 327 obj_id = id(obj)
317 328 cycle = obj_id in self.stack
318 329 self.stack.append(obj_id)
319 330 self.begin_group()
320 331 try:
321 332 obj_class = getattr(obj, '__class__', None) or type(obj)
322 333 if hasattr(obj_class, '__pretty__'):
323 334 return obj_class.__pretty__(obj, self, cycle)
324 335 try:
325 printer = _singleton_pprinters[obj_id]
336 printer = self.singleton_pprinters[obj_id]
326 337 except (TypeError, KeyError):
327 338 pass
328 339 else:
329 340 return printer(obj, self, cycle)
330 341 for cls in _get_mro(obj_class):
331 if cls in _type_pprinters:
332 return _type_pprinters[cls](obj, self, cycle)
342 if cls in self.type_pprinters:
343 return self.type_pprinters[cls](obj, self, cycle)
333 344 else:
334 345 printer = self._in_deferred_types(cls)
335 346 if printer is not None:
336 347 return printer(obj, self, cycle)
337 348 return _default_pprint(obj, self, cycle)
338 349 finally:
339 350 self.end_group()
340 351 self.stack.pop()
341 352
342 353 def _in_deferred_types(self, cls):
343 354 """
344 355 Check if the given class is specified in the deferred type registry.
345 356
346 357 Returns the printer from the registry if it exists, and None if the
347 358 class is not in the registry. Successful matches will be moved to the
348 359 regular type registry for future use.
349 360 """
350 361 mod = getattr(cls, '__module__', None)
351 362 name = getattr(cls, '__name__', None)
352 363 key = (mod, name)
353 364 printer = None
354 if key in _deferred_type_pprinters:
365 if key in self.deferred_pprinters:
355 366 # Move the printer over to the regular registry.
356 printer = _deferred_type_pprinters.pop(key)
357 _type_pprinters[cls] = printer
367 printer = self.deferred_pprinters.pop(key)
368 self.type_pprinters[cls] = printer
358 369 return printer
359 370
360 371
361
362 372 class Printable(object):
363 373
364 374 def output(self, stream, output_width):
365 375 return output_width
366 376
367 377
368 378 class Text(Printable):
369 379
370 380 def __init__(self):
371 381 self.objs = []
372 382 self.width = 0
373 383
374 384 def output(self, stream, output_width):
375 385 for obj in self.objs:
376 386 stream.write(obj)
377 387 return output_width + self.width
378 388
379 389 def add(self, obj, width):
380 390 self.objs.append(obj)
381 391 self.width += width
382 392
383 393
384 394 class Breakable(Printable):
385 395
386 396 def __init__(self, seq, width, pretty):
387 397 self.obj = seq
388 398 self.width = width
389 399 self.pretty = pretty
390 400 self.indentation = pretty.indentation
391 401 self.group = pretty.group_stack[-1]
392 402 self.group.breakables.append(self)
393 403
394 404 def output(self, stream, output_width):
395 405 self.group.breakables.popleft()
396 406 if self.group.want_break:
397 407 stream.write(self.pretty.newline)
398 408 stream.write(' ' * self.indentation)
399 409 return self.indentation
400 410 if not self.group.breakables:
401 411 self.pretty.group_queue.remove(self.group)
402 412 stream.write(self.obj)
403 413 return output_width + self.width
404 414
405 415
406 416 class Group(Printable):
407 417
408 418 def __init__(self, depth):
409 419 self.depth = depth
410 420 self.breakables = deque()
411 421 self.want_break = False
412 422
413 423
414 424 class GroupQueue(object):
415 425
416 426 def __init__(self, *groups):
417 427 self.queue = []
418 428 for group in groups:
419 429 self.enq(group)
420 430
421 431 def enq(self, group):
422 432 depth = group.depth
423 433 while depth > len(self.queue) - 1:
424 434 self.queue.append([])
425 435 self.queue[depth].append(group)
426 436
427 437 def deq(self):
428 438 for stack in self.queue:
429 439 for idx, group in enumerate(reversed(stack)):
430 440 if group.breakables:
431 441 del stack[idx]
432 442 group.want_break = True
433 443 return group
434 444 for group in stack:
435 445 group.want_break = True
436 446 del stack[:]
437 447
438 448 def remove(self, group):
439 449 try:
440 450 self.queue[group.depth].remove(group)
441 451 except ValueError:
442 452 pass
443 453
444 454
445 455 _baseclass_reprs = (object.__repr__, types.InstanceType.__repr__)
446 456
447 457
448 458 def _default_pprint(obj, p, cycle):
449 459 """
450 460 The default print function. Used if an object does not provide one and
451 461 it's none of the builtin objects.
452 462 """
453 463 klass = getattr(obj, '__class__', None) or type(obj)
454 464 if getattr(klass, '__repr__', None) not in _baseclass_reprs:
455 465 # A user-provided repr.
456 466 p.text(repr(obj))
457 467 return
458 468 p.begin_group(1, '<')
459 469 p.pretty(klass)
460 470 p.text(' at 0x%x' % id(obj))
461 471 if cycle:
462 472 p.text(' ...')
463 473 elif p.verbose:
464 474 first = True
465 475 for key in dir(obj):
466 476 if not key.startswith('_'):
467 477 try:
468 478 value = getattr(obj, key)
469 479 except AttributeError:
470 480 continue
471 481 if isinstance(value, types.MethodType):
472 482 continue
473 483 if not first:
474 484 p.text(',')
475 485 p.breakable()
476 486 p.text(key)
477 487 p.text('=')
478 488 step = len(key) + 1
479 489 p.indentation += step
480 490 p.pretty(value)
481 491 p.indentation -= step
482 492 first = False
483 493 p.end_group(1, '>')
484 494
485 495
486 496 def _seq_pprinter_factory(start, end):
487 497 """
488 498 Factory that returns a pprint function useful for sequences. Used by
489 499 the default pprint for tuples, dicts, lists, sets and frozensets.
490 500 """
491 501 def inner(obj, p, cycle):
492 502 if cycle:
493 503 return p.text(start + '...' + end)
494 504 step = len(start)
495 505 p.begin_group(step, start)
496 506 for idx, x in enumerate(obj):
497 507 if idx:
498 508 p.text(',')
499 509 p.breakable()
500 510 p.pretty(x)
501 511 if len(obj) == 1 and type(obj) is tuple:
502 512 # Special case for 1-item tuples.
503 513 p.text(',')
504 514 p.end_group(step, end)
505 515 return inner
506 516
507 517
508 518 def _dict_pprinter_factory(start, end):
509 519 """
510 520 Factory that returns a pprint function used by the default pprint of
511 521 dicts and dict proxies.
512 522 """
513 523 def inner(obj, p, cycle):
514 524 if cycle:
515 525 return p.text('{...}')
516 526 p.begin_group(1, start)
517 527 keys = obj.keys()
518 528 try:
519 529 keys.sort()
520 530 except Exception, e:
521 531 # Sometimes the keys don't sort.
522 532 pass
523 533 for idx, key in enumerate(keys):
524 534 if idx:
525 535 p.text(',')
526 536 p.breakable()
527 537 p.pretty(key)
528 538 p.text(': ')
529 539 p.pretty(obj[key])
530 540 p.end_group(1, end)
531 541 return inner
532 542
533 543
534 544 def _super_pprint(obj, p, cycle):
535 545 """The pprint for the super type."""
536 546 p.begin_group(8, '<super: ')
537 547 p.pretty(obj.__self_class__)
538 548 p.text(',')
539 549 p.breakable()
540 550 p.pretty(obj.__self__)
541 551 p.end_group(8, '>')
542 552
543 553
544 554 def _re_pattern_pprint(obj, p, cycle):
545 555 """The pprint function for regular expression patterns."""
546 556 p.text('re.compile(')
547 557 pattern = repr(obj.pattern)
548 558 if pattern[:1] in 'uU':
549 559 pattern = pattern[1:]
550 560 prefix = 'ur'
551 561 else:
552 562 prefix = 'r'
553 563 pattern = prefix + pattern.replace('\\\\', '\\')
554 564 p.text(pattern)
555 565 if obj.flags:
556 566 p.text(',')
557 567 p.breakable()
558 568 done_one = False
559 569 for flag in ('TEMPLATE', 'IGNORECASE', 'LOCALE', 'MULTILINE', 'DOTALL',
560 570 'UNICODE', 'VERBOSE', 'DEBUG'):
561 571 if obj.flags & getattr(re, flag):
562 572 if done_one:
563 573 p.text('|')
564 574 p.text('re.' + flag)
565 575 done_one = True
566 576 p.text(')')
567 577
568 578
569 579 def _type_pprint(obj, p, cycle):
570 580 """The pprint for classes and types."""
571 581 if obj.__module__ in ('__builtin__', 'exceptions'):
572 582 name = obj.__name__
573 583 else:
574 584 name = obj.__module__ + '.' + obj.__name__
575 585 p.text(name)
576 586
577 587
578 588 def _repr_pprint(obj, p, cycle):
579 589 """A pprint that just redirects to the normal repr function."""
580 590 p.text(repr(obj))
581 591
582 592
583 593 def _function_pprint(obj, p, cycle):
584 594 """Base pprint for all functions and builtin functions."""
585 595 if obj.__module__ in ('__builtin__', 'exceptions') or not obj.__module__:
586 596 name = obj.__name__
587 597 else:
588 598 name = obj.__module__ + '.' + obj.__name__
589 599 p.text('<function %s>' % name)
590 600
591 601
592 602 def _exception_pprint(obj, p, cycle):
593 603 """Base pprint for all exceptions."""
594 604 if obj.__class__.__module__ == 'exceptions':
595 605 name = obj.__class__.__name__
596 606 else:
597 607 name = '%s.%s' % (
598 608 obj.__class__.__module__,
599 609 obj.__class__.__name__
600 610 )
601 611 step = len(name) + 1
602 612 p.begin_group(step, '(')
603 613 for idx, arg in enumerate(getattr(obj, 'args', ())):
604 614 if idx:
605 615 p.text(',')
606 616 p.breakable()
607 617 p.pretty(arg)
608 618 p.end_group(step, ')')
609 619
610 620
611 621 #: the exception base
612 622 try:
613 623 _exception_base = BaseException
614 624 except NameError:
615 625 _exception_base = Exception
616 626
617 627
618 628 #: printers for builtin types
619 629 _type_pprinters = {
620 630 int: _repr_pprint,
621 631 long: _repr_pprint,
622 632 float: _repr_pprint,
623 633 str: _repr_pprint,
624 634 unicode: _repr_pprint,
625 635 tuple: _seq_pprinter_factory('(', ')'),
626 636 list: _seq_pprinter_factory('[', ']'),
627 637 dict: _dict_pprinter_factory('{', '}'),
628 638 types.DictProxyType: _dict_pprinter_factory('<dictproxy {', '}>'),
629 639 set: _seq_pprinter_factory('set([', '])'),
630 640 frozenset: _seq_pprinter_factory('frozenset([', '])'),
631 641 super: _super_pprint,
632 642 _re_pattern_type: _re_pattern_pprint,
633 643 type: _type_pprint,
634 644 types.ClassType: _type_pprint,
635 645 types.FunctionType: _function_pprint,
636 646 types.BuiltinFunctionType: _function_pprint,
637 647 types.SliceType: _repr_pprint,
638 648 types.MethodType: _repr_pprint,
639 649 xrange: _repr_pprint,
640 650 datetime.datetime: _repr_pprint,
641 651 datetime.timedelta: _repr_pprint,
642 652 _exception_base: _exception_pprint
643 653 }
644 654
645 655 #: printers for types specified by name
646 656 _deferred_type_pprinters = {
647 657 }
648 658
649 659 def for_type(typ, func):
650 660 """
651 661 Add a pretty printer for a given type.
652 662 """
653 663 oldfunc = _type_pprinters.get(typ, None)
654 664 if func is not None:
655 665 # To support easy restoration of old pprinters, we need to ignore Nones.
656 666 _type_pprinters[typ] = func
657 667 return oldfunc
658 668
659 669 def for_type_by_name(type_module, type_name, func):
660 670 """
661 671 Add a pretty printer for a type specified by the module and name of a type
662 672 rather than the type object itself.
663 673 """
664 674 key = (type_module, type_name)
665 675 oldfunc = _deferred_type_pprinters.get(key, None)
666 676 if func is not None:
667 677 # To support easy restoration of old pprinters, we need to ignore Nones.
668 678 _deferred_type_pprinters[key] = func
669 679 return oldfunc
670 680
671 681
672 682 #: printers for the default singletons
673 683 _singleton_pprinters = dict.fromkeys(map(id, [None, True, False, Ellipsis,
674 684 NotImplemented]), _repr_pprint)
675 685
676 686
677 687 if __name__ == '__main__':
678 688 from random import randrange
679 689 class Foo(object):
680 690 def __init__(self):
681 691 self.foo = 1
682 692 self.bar = re.compile(r'\s+')
683 693 self.blub = dict.fromkeys(range(30), randrange(1, 40))
684 694 self.hehe = 23424.234234
685 695 self.list = ["blub", "blah", self]
686 696
687 697 def get_foo(self):
688 698 print "foo"
689 699
690 700 pprint(Foo(), verbose=True)
General Comments 0
You need to be logged in to leave comments. Login now