4545__all__ = ["pprint" ,"pformat" ,"isreadable" ,"isrecursive" ,"saferepr" ,
4646 "PrettyPrinter" ]
4747
48+ # cache these for faster access:
49+ _commajoin = ", " .join
50+ _sys_modules = sys .modules
51+ _id = id
52+ _len = len
53+ _type = type
54+
55+
4856def pprint (object , stream = None ):
4957 """Pretty-print a Python object to a stream [default is sys.sydout]."""
5058 printer = PrettyPrinter (stream = stream )
@@ -56,15 +64,15 @@ def pformat(object):
5664
5765def saferepr (object ):
5866 """Version of repr() which can handle recursive data structures."""
59- return _safe_repr (object , {})[0 ]
67+ return _safe_repr (object , {}, None , 0 )[0 ]
6068
6169def isreadable (object ):
6270 """Determine if saferepr(object) is readable by eval()."""
63- return _safe_repr (object , {})[1 ]
71+ return _safe_repr (object , {}, None , 0 )[1 ]
6472
6573def isrecursive (object ):
6674 """Determine if object requires a recursive representation."""
67- return _safe_repr (object , {})[2 ]
75+ return _safe_repr (object , {}, None , 0 )[2 ]
6876
6977class PrettyPrinter :
7078 def __init__ (self , indent = 1 , width = 80 , depth = None , stream = None ):
@@ -108,73 +116,84 @@ def pformat(self, object):
108116
109117 def isrecursive (self , object ):
110118 self .__recursive = 0
111- self .pformat (object )
119+ self .__repr (object , {}, 0 )
112120 return self .__recursive
113121
114122 def isreadable (self , object ):
115123 self .__recursive = 0
116124 self .__readable = 1
117- self .pformat (object )
125+ self .__repr (object , {}, 0 )
118126 return self .__readable and not self .__recursive
119127
120128 def __format (self , object , stream , indent , allowance , context , level ):
121129 level = level + 1
122- if context .has_key (id (object )):
123- object = _Recursion (object )
130+ objid = _id (object )
131+ if objid in context :
132+ stream .write (_recursion (object ))
124133 self .__recursive = 1
134+ self .__readable = 0
135+ return
125136 rep = self .__repr (object , context , level - 1 )
126- objid = id (object )
127- context [objid ] = 1
128- typ = type (object )
129- sepLines = len (rep ) > (self .__width - 1 - indent - allowance )
130-
131- if sepLines and typ in (ListType , TupleType ):
132- # Pretty-print the sequence.
133- stream .write ((typ is ListType ) and '[' or '(' )
134- if self .__indent_per_level > 1 :
135- stream .write ((self .__indent_per_level - 1 ) * ' ' )
136- length = len (object )
137- if length :
138- indent = indent + self .__indent_per_level
139- self .__format (object [0 ], stream , indent , allowance + 1 ,
140- context , level )
141- if length > 1 :
142- for ent in object [1 :]:
143- stream .write (',\n ' + ' ' * indent )
144- self .__format (ent , stream , indent ,
145- allowance + 1 , context , level )
146- indent = indent - self .__indent_per_level
147- if typ is TupleType and length == 1 :
148- stream .write (',' )
149- stream .write (((typ is ListType ) and ']' ) or ')' )
150-
151- elif sepLines and typ is DictType :
152- stream .write ('{' )
153- if self .__indent_per_level > 1 :
154- stream .write ((self .__indent_per_level - 1 ) * ' ' )
155- length = len (object )
156- if length :
157- indent = indent + self .__indent_per_level
158- items = object .items ()
159- items .sort ()
160- key , ent = items [0 ]
161- rep = self .__repr (key , context , level ) + ': '
162- stream .write (rep )
163- self .__format (ent , stream , indent + len (rep ),
164- allowance + 1 , context , level )
165- if len (items ) > 1 :
166- for key , ent in items [1 :]:
167- rep = self .__repr (key , context , level ) + ': '
168- stream .write (',\n ' + ' ' * indent + rep )
169- self .__format (ent , stream , indent + len (rep ),
170- allowance + 1 , context , level )
171- indent = indent - self .__indent_per_level
172- stream .write ('}' )
173-
174- else :
175- stream .write (rep )
176-
177- del context [objid ]
137+ typ = _type (object )
138+ sepLines = _len (rep ) > (self .__width - 1 - indent - allowance )
139+ write = stream .write
140+
141+ if sepLines :
142+ if typ is DictType :
143+ write ('{' )
144+ if self .__indent_per_level > 1 :
145+ write ((self .__indent_per_level - 1 ) * ' ' )
146+ length = _len (object )
147+ if length :
148+ context [objid ] = 1
149+ indent = indent + self .__indent_per_level
150+ items = object .items ()
151+ items .sort ()
152+ key , ent = items [0 ]
153+ rep = self .__repr (key , context , level )
154+ write (rep )
155+ write (': ' )
156+ self .__format (ent , stream , indent + _len (rep ) + 2 ,
157+ allowance + 1 , context , level )
158+ if length > 1 :
159+ for key , ent in items [1 :]:
160+ rep = self .__repr (key , context , level )
161+ write (',\n %s: %s' % (' ' * indent , rep ))
162+ self .__format (ent , stream , indent + _len (rep ) + 2 ,
163+ allowance + 1 , context , level )
164+ indent = indent - self .__indent_per_level
165+ del context [objid ]
166+ write ('}' )
167+ return
168+
169+ if typ is ListType or typ is TupleType :
170+ if typ is ListType :
171+ write ('[' )
172+ endchar = ']'
173+ else :
174+ write ('(' )
175+ endchar = ')'
176+ if self .__indent_per_level > 1 :
177+ write ((self .__indent_per_level - 1 ) * ' ' )
178+ length = _len (object )
179+ if length :
180+ context [objid ] = 1
181+ indent = indent + self .__indent_per_level
182+ self .__format (object [0 ], stream , indent , allowance + 1 ,
183+ context , level )
184+ if length > 1 :
185+ for ent in object [1 :]:
186+ write (',\n ' + ' ' * indent )
187+ self .__format (ent , stream , indent ,
188+ allowance + 1 , context , level )
189+ indent = indent - self .__indent_per_level
190+ del context [objid ]
191+ if typ is TupleType and length == 1 :
192+ write (',' )
193+ write (endchar )
194+ return
195+
196+ write (rep )
178197
179198 def __repr (self , object , context , level ):
180199 repr , readable , recursive = _safe_repr (object , context ,
@@ -187,80 +206,105 @@ def __repr(self, object, context, level):
187206
188207# Return triple (repr_string, isreadable, isrecursive).
189208
190- _have_module = sys .modules .has_key
191-
192- def _safe_repr (object , context , maxlevels = None , level = 0 ):
193- level += 1
194- typ = type (object )
195- if not (typ in (DictType , ListType , TupleType , StringType ) and object ):
196- rep = `object`
197- return rep , (rep and (rep [0 ] != '<' )), 0
198- elif typ is StringType :
199- if not _have_module ('locale' ):
209+ def _safe_repr (object , context , maxlevels , level ):
210+ typ = _type (object )
211+ if typ is StringType :
212+ if 'locale' not in _sys_modules :
200213 return `object` , 1 , 0
201214 if "'" in object and '"' not in object :
202215 closure = '"'
203216 quotes = {'"' : '\\ "' }
204217 else :
205218 closure = "'"
206219 quotes = {"'" : "\\ '" }
220+ qget = quotes .get
207221 sio = StringIO ()
222+ write = sio .write
208223 for char in object :
209224 if char .isalpha ():
210- sio . write (char )
225+ write (char )
211226 else :
212- sio .write (quotes .get (char , `char` [1 :- 1 ]))
213- return closure + sio .getvalue () + closure , 1 , 0
214-
215- if context .has_key (id (object )):
216- return `_Recursion(object)` , 0 , 1
217- objid = id (object )
218- context [objid ] = 1
219-
220- readable = 1
221- recursive = 0
222- startchar , endchar = {ListType : "[]" ,
223- TupleType : "()" ,
224- DictType : "{}" }[typ ]
225- if maxlevels and level > maxlevels :
226- with_commas = "..."
227- readable = 0
228-
229- elif typ is DictType :
227+ write (qget (char , `char` [1 :- 1 ]))
228+ return ("%s%s%s" % (closure , sio .getvalue (), closure )), 1 , 0
229+
230+ if typ is DictType :
231+ if not object :
232+ return "{}" , 1 , 0
233+ objid = _id (object )
234+ if maxlevels and level > maxlevels :
235+ return "{...}" , 0 , objid in context
236+ if objid in context :
237+ return _recursion (object ), 0 , 1
238+ context [objid ] = 1
239+ readable = 1
240+ recursive = 0
230241 components = []
242+ append = components .append
243+ level += 1
244+ saferepr = _safe_repr
231245 for k , v in object .iteritems ():
232- krepr , kreadable , krecur = _safe_repr (k , context , maxlevels ,
233- level )
234- vrepr , vreadable , vrecur = _safe_repr (v , context , maxlevels ,
235- level )
236- components .append ("%s: %s" % (krepr , vrepr ))
246+ krepr , kreadable , krecur = saferepr (k , context , maxlevels , level )
247+ vrepr , vreadable , vrecur = saferepr (v , context , maxlevels , level )
248+ append ("%s: %s" % (krepr , vrepr ))
237249 readable = readable and kreadable and vreadable
238- recursive = recursive or krecur or vrecur
239- with_commas = ", " .join (components )
240-
241- else : # list or tuple
242- assert typ in (ListType , TupleType )
250+ if krecur or vrecur :
251+ recursive = 1
252+ del context [objid ]
253+ return "{%s}" % _commajoin (components ), readable , recursive
254+
255+ if typ is ListType or typ is TupleType :
256+ if typ is ListType :
257+ if not object :
258+ return "[]" , 1 , 0
259+ format = "[%s]"
260+ elif _len (object ) == 1 :
261+ format = "(%s,)"
262+ else :
263+ if not object :
264+ return "()" , 1 , 0
265+ format = "(%s)"
266+ objid = _id (object )
267+ if maxlevels and level > maxlevels :
268+ return format % "..." , 0 , objid in context
269+ if objid in context :
270+ return _recursion (object ), 0 , 1
271+ context [objid ] = 1
272+ readable = 1
273+ recursive = 0
243274 components = []
244- for element in object :
245- subrepr , subreadable , subrecur = _safe_repr (
246- element , context , maxlevels , level )
247- components .append (subrepr )
248- readable = readable and subreadable
249- recursive = recursive or subrecur
250- if len (components ) == 1 and typ is TupleType :
251- components [0 ] += ","
252- with_commas = ", " .join (components )
253-
254- s = "%s%s%s" % (startchar , with_commas , endchar )
255- del context [objid ]
256- return s , readable and not recursive , recursive
257-
258- class _Recursion :
259- # represent a recursive relationship; really only used for the __repr__()
260- # method...
261- def __init__ (self , object ):
262- self .__repr = "<Recursion on %s with id=%s>" \
263- % (type (object ).__name__ , id (object ))
264-
265- def __repr__ (self ):
266- return self .__repr
275+ append = components .append
276+ level += 1
277+ for o in object :
278+ orepr , oreadable , orecur = _safe_repr (o , context , maxlevels , level )
279+ append (orepr )
280+ if not oreadable :
281+ readable = 0
282+ if orecur :
283+ recursive = 1
284+ del context [objid ]
285+ return format % _commajoin (components ), readable , recursive
286+
287+ rep = `object`
288+ return rep , (rep and not rep .startswith ('<' )), 0
289+
290+
291+ def _recursion (object ):
292+ return ("<Recursion on %s with id=%s>"
293+ % (_type (object ).__name__ , _id (object )))
294+
295+
296+ def _perfcheck (object = None ):
297+ import time
298+ if object is None :
299+ object = [("string" , (1 , 2 ), [3 , 4 ], {5 : 6 , 7 : 8 })] * 100000
300+ p = PrettyPrinter ()
301+ t1 = time .time ()
302+ _safe_repr (object , {}, None , 0 )
303+ t2 = time .time ()
304+ p .pformat (object )
305+ t3 = time .time ()
306+ print "_safe_repr:" , t2 - t1
307+ print "pformat:" , t3 - t2
308+
309+ if __name__ == "__main__" :
310+ _perfcheck ()
0 commit comments