0001 # Author: Fred L. Drake, Jr. 0002 # fdrake@acm.org 0003 # 0004 # This is a simple little module I wrote to make life easier. I didn't 0005 # see anything quite like it in the library, though I may have overlooked 0006 # something. I wrote this when I was trying to read some heavily nested 0007 # tuples with fairly non-descriptive content. This is modeled very much 0008 # after Lisp/Scheme - style pretty-printing of lists. If you find it 0009 # useful, thank small children who sleep at night. 0010 0011 """Support to pretty-print lists, tuples, & dictionaries recursively. 0012 0013 Very simple, but useful, especially in debugging data structures. 0014 0015 Classes 0016 ------- 0017 0018 PrettyPrinter() 0019 Handle pretty-printing operations onto a stream using a configured 0020 set of formatting parameters. 0021 0022 Functions 0023 --------- 0024 0025 pformat() 0026 Format a Python object into a pretty-printed representation. 0027 0028 pprint() 0029 Pretty-print a Python object to a stream [default is sys.stdout]. 0030 0031 saferepr() 0032 Generate a 'standard' repr()-like value, but protect against recursive 0033 data structures. 0034 0035 """ 0036 0037 import sys as _sys 0038 0039 from cStringIO import StringIO as _StringIO 0040 0041 __all__ = ["pprint","pformat","isreadable","isrecursive","saferepr", 0042 "PrettyPrinter"] 0043 0044 # cache these for faster access: 0045 _commajoin = ", ".join 0046 _id = id 0047 _len = len 0048 _type = type 0049 0050 0051 def pprint(object, stream=None, indent=1, width=80, depth=None): 0052 """Pretty-print a Python object to a stream [default is sys.stdout].""" 0053 printer = PrettyPrinter( 0054 stream=stream, indent=indent, width=width, depth=depth) 0055 printer.pprint(object) 0056 0057 def pformat(object, indent=1, width=80, depth=None): 0058 """Format a Python object into a pretty-printed representation.""" 0059 return PrettyPrinter(indent=indent, width=width, depth=depth).pformat(object) 0060 0061 def saferepr(object): 0062 """Version of repr() which can handle recursive data structures.""" 0063 return _safe_repr(object, {}, None, 0)[0] 0064 0065 def isreadable(object): 0066 """Determine if saferepr(object) is readable by eval().""" 0067 return _safe_repr(object, {}, None, 0)[1] 0068 0069 def isrecursive(object): 0070 """Determine if object requires a recursive representation.""" 0071 return _safe_repr(object, {}, None, 0)[2] 0072 0073 class PrettyPrinter: 0074 def __init__(self, indent=1, width=80, depth=None, stream=None): 0075 """Handle pretty printing operations onto a stream using a set of 0076 configured parameters. 0077 0078 indent 0079 Number of spaces to indent for each level of nesting. 0080 0081 width 0082 Attempted maximum number of columns in the output. 0083 0084 depth 0085 The maximum depth to print out nested structures. 0086 0087 stream 0088 The desired output stream. If omitted (or false), the standard 0089 output stream available at construction will be used. 0090 0091 """ 0092 indent = int(indent) 0093 width = int(width) 0094 assert indent >= 0, "indent must be >= 0" 0095 assert depth is None or depth > 0, "depth must be > 0" 0096 assert width, "width must be != 0" 0097 self._depth = depth 0098 self._indent_per_level = indent 0099 self._width = width 0100 if stream is not None: 0101 self._stream = stream 0102 else: 0103 self._stream = _sys.stdout 0104 0105 def pprint(self, object): 0106 self._stream.write(self.pformat(object) + "\n") 0107 0108 def pformat(self, object): 0109 sio = _StringIO() 0110 self._format(object, sio, 0, 0, {}, 0) 0111 return sio.getvalue() 0112 0113 def isrecursive(self, object): 0114 return self.format(object, {}, 0, 0)[2] 0115 0116 def isreadable(self, object): 0117 s, readable, recursive = self.format(object, {}, 0, 0) 0118 return readable and not recursive 0119 0120 def _format(self, object, stream, indent, allowance, context, level): 0121 level = level + 1 0122 objid = _id(object) 0123 if objid in context: 0124 stream.write(_recursion(object)) 0125 self._recursive = True 0126 self._readable = False 0127 return 0128 rep = self._repr(object, context, level - 1) 0129 typ = _type(object) 0130 sepLines = _len(rep) > (self._width - 1 - indent - allowance) 0131 write = stream.write 0132 0133 if sepLines: 0134 r = typ.__repr__ 0135 if issubclass(typ, dict) and r is dict.__repr__: 0136 write('{') 0137 if self._indent_per_level > 1: 0138 write((self._indent_per_level - 1) * ' ') 0139 length = _len(object) 0140 if length: 0141 context[objid] = 1 0142 indent = indent + self._indent_per_level 0143 items = object.items() 0144 items.sort() 0145 key, ent = items[0] 0146 rep = self._repr(key, context, level) 0147 write(rep) 0148 write(': ') 0149 self._format(ent, stream, indent + _len(rep) + 2, 0150 allowance + 1, context, level) 0151 if length > 1: 0152 for key, ent in items[1:]: 0153 rep = self._repr(key, context, level) 0154 write(',\n%s%s: ' % (' '*indent, rep)) 0155 self._format(ent, stream, indent + _len(rep) + 2, 0156 allowance + 1, context, level) 0157 indent = indent - self._indent_per_level 0158 del context[objid] 0159 write('}') 0160 return 0161 0162 if (issubclass(typ, list) and r is list.__repr__) or \ 0163 (issubclass(typ, tuple) and r is tuple.__repr__): 0164 if issubclass(typ, list): 0165 write('[') 0166 endchar = ']' 0167 else: 0168 write('(') 0169 endchar = ')' 0170 if self._indent_per_level > 1: 0171 write((self._indent_per_level - 1) * ' ') 0172 length = _len(object) 0173 if length: 0174 context[objid] = 1 0175 indent = indent + self._indent_per_level 0176 self._format(object[0], stream, indent, allowance + 1, 0177 context, level) 0178 if length > 1: 0179 for ent in object[1:]: 0180 write(',\n' + ' '*indent) 0181 self._format(ent, stream, indent, 0182 allowance + 1, context, level) 0183 indent = indent - self._indent_per_level 0184 del context[objid] 0185 if issubclass(typ, tuple) and length == 1: 0186 write(',') 0187 write(endchar) 0188 return 0189 0190 write(rep) 0191 0192 def _repr(self, object, context, level): 0193 repr, readable, recursive = self.format(object, context.copy(), 0194 self._depth, level) 0195 if not readable: 0196 self._readable = False 0197 if recursive: 0198 self._recursive = True 0199 return repr 0200 0201 def format(self, object, context, maxlevels, level): 0202 """Format object for a specific context, returning a string 0203 and flags indicating whether the representation is 'readable' 0204 and whether the object represents a recursive construct. 0205 """ 0206 return _safe_repr(object, context, maxlevels, level) 0207 0208 0209 # Return triple (repr_string, isreadable, isrecursive). 0210 0211 def _safe_repr(object, context, maxlevels, level): 0212 typ = _type(object) 0213 if typ is str: 0214 if 'locale' not in _sys.modules: 0215 return repr(object), True, False 0216 if "'" in object and '"' not in object: 0217 closure = '"' 0218 quotes = {'"': '\\"'} 0219 else: 0220 closure = "'" 0221 quotes = {"'": "\\'"} 0222 qget = quotes.get 0223 sio = _StringIO() 0224 write = sio.write 0225 for char in object: 0226 if char.isalpha(): 0227 write(char) 0228 else: 0229 write(qget(char, repr(char)[1:-1])) 0230 return ("%s%s%s" % (closure, sio.getvalue(), closure)), True, False 0231 0232 r = typ.__repr__ 0233 if issubclass(typ, dict) and r is dict.__repr__: 0234 if not object: 0235 return "{}", True, False 0236 objid = _id(object) 0237 if maxlevels and level > maxlevels: 0238 return "{...}", False, objid in context 0239 if objid in context: 0240 return _recursion(object), False, True 0241 context[objid] = 1 0242 readable = True 0243 recursive = False 0244 components = [] 0245 append = components.append 0246 level += 1 0247 saferepr = _safe_repr 0248 for k, v in object.iteritems(): 0249 krepr, kreadable, krecur = saferepr(k, context, maxlevels, level) 0250 vrepr, vreadable, vrecur = saferepr(v, context, maxlevels, level) 0251 append("%s: %s" % (krepr, vrepr)) 0252 readable = readable and kreadable and vreadable 0253 if krecur or vrecur: 0254 recursive = True 0255 del context[objid] 0256 return "{%s}" % _commajoin(components), readable, recursive 0257 0258 if (issubclass(typ, list) and r is list.__repr__) or \ 0259 (issubclass(typ, tuple) and r is tuple.__repr__): 0260 if issubclass(typ, list): 0261 if not object: 0262 return "[]", True, False 0263 format = "[%s]" 0264 elif _len(object) == 1: 0265 format = "(%s,)" 0266 else: 0267 if not object: 0268 return "()", True, False 0269 format = "(%s)" 0270 objid = _id(object) 0271 if maxlevels and level > maxlevels: 0272 return format % "...", False, objid in context 0273 if objid in context: 0274 return _recursion(object), False, True 0275 context[objid] = 1 0276 readable = True 0277 recursive = False 0278 components = [] 0279 append = components.append 0280 level += 1 0281 for o in object: 0282 orepr, oreadable, orecur = _safe_repr(o, context, maxlevels, level) 0283 append(orepr) 0284 if not oreadable: 0285 readable = False 0286 if orecur: 0287 recursive = True 0288 del context[objid] 0289 return format % _commajoin(components), readable, recursive 0290 0291 rep = repr(object) 0292 return rep, (rep and not rep.startswith('<')), False 0293 0294 0295 def _recursion(object): 0296 return ("<Recursion on %s with id=%s>" 0297 % (_type(object).__name__, _id(object))) 0298 0299 0300 def _perfcheck(object=None): 0301 import time 0302 if object is None: 0303 object = [("string", (1, 2), [3, 4], {5: 6, 7: 8})] * 100000 0304 p = PrettyPrinter() 0305 t1 = time.time() 0306 _safe_repr(object, {}, None, 0) 0307 t2 = time.time() 0308 p.pformat(object) 0309 t3 = time.time() 0310 print "_safe_repr:", t2 - t1 0311 print "pformat:", t3 - t2 0312 0313 if __name__ == "__main__": 0314 _perfcheck() 0315
Generated by PyXR 0.9.4