Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(21)

Delta Between Two Patch Sets: Lib/pprint.py

Issue 19105: pprint doesn't use all width
Left Patch Set: Created 6 years, 3 months ago
Right Patch Set: Created 5 years, 1 month ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « no previous file | Lib/test/test_pprint.py » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 # Author: Fred L. Drake, Jr. 1 # Author: Fred L. Drake, Jr.
2 # fdrake@acm.org 2 # fdrake@acm.org
3 # 3 #
4 # This is a simple little module I wrote to make life easier. I didn't 4 # This is a simple little module I wrote to make life easier. I didn't
5 # see anything quite like it in the library, though I may have overlooked 5 # see anything quite like it in the library, though I may have overlooked
6 # something. I wrote this when I was trying to read some heavily nested 6 # something. I wrote this when I was trying to read some heavily nested
7 # tuples with fairly non-descriptive content. This is modeled very much 7 # tuples with fairly non-descriptive content. This is modeled very much
8 # after Lisp/Scheme - style pretty-printing of lists. If you find it 8 # after Lisp/Scheme - style pretty-printing of lists. If you find it
9 # useful, thank small children who sleep at night. 9 # useful, thank small children who sleep at night.
10 10
(...skipping 24 matching lines...) Expand all
35 """ 35 """
36 36
37 import re 37 import re
38 import sys as _sys 38 import sys as _sys
39 from collections import OrderedDict as _OrderedDict 39 from collections import OrderedDict as _OrderedDict
40 from io import StringIO as _StringIO 40 from io import StringIO as _StringIO
41 41
42 __all__ = ["pprint","pformat","isreadable","isrecursive","saferepr", 42 __all__ = ["pprint","pformat","isreadable","isrecursive","saferepr",
43 "PrettyPrinter"] 43 "PrettyPrinter"]
44 44
45 # cache these for faster access:
46 _commajoin = ", ".join
47 _id = id
48 _len = len
49 _type = type
50
51 45
52 def pprint(object, stream=None, indent=1, width=80, depth=None, *, 46 def pprint(object, stream=None, indent=1, width=80, depth=None, *,
53 compact=False): 47 compact=False):
54 """Pretty-print a Python object to a stream [default is sys.stdout].""" 48 """Pretty-print a Python object to a stream [default is sys.stdout]."""
55 printer = PrettyPrinter( 49 printer = PrettyPrinter(
56 stream=stream, indent=indent, width=width, depth=depth, 50 stream=stream, indent=indent, width=width, depth=depth,
57 compact=compact) 51 compact=compact)
58 printer.pprint(object) 52 printer.pprint(object)
59 53
60 def pformat(object, indent=1, width=80, depth=None, *, compact=False): 54 def pformat(object, indent=1, width=80, depth=None, *, compact=False):
(...skipping 28 matching lines...) Expand all
89 def __init__(self, obj): 83 def __init__(self, obj):
90 self.obj = obj 84 self.obj = obj
91 85
92 def __lt__(self, other): 86 def __lt__(self, other):
93 try: 87 try:
94 rv = self.obj.__lt__(other.obj) 88 rv = self.obj.__lt__(other.obj)
95 except TypeError: 89 except TypeError:
96 rv = NotImplemented 90 rv = NotImplemented
97 91
98 if rv is NotImplemented: 92 if rv is NotImplemented:
99 rv = (str(_type(self.obj)), _id(self.obj)) < \ 93 rv = (str(type(self.obj)), id(self.obj)) < \
100 (str(_type(other.obj)), _id(other.obj)) 94 (str(type(other.obj)), id(other.obj))
101 return rv 95 return rv
102 96
103 def _safe_tuple(t): 97 def _safe_tuple(t):
104 "Helper function for comparing 2-tuples" 98 "Helper function for comparing 2-tuples"
105 return _safe_key(t[0]), _safe_key(t[1]) 99 return _safe_key(t[0]), _safe_key(t[1])
106 100
107 class PrettyPrinter: 101 class PrettyPrinter:
108 def __init__(self, indent=1, width=80, depth=None, stream=None, *, 102 def __init__(self, indent=1, width=80, depth=None, stream=None, *,
109 compact=False): 103 compact=False):
110 """Handle pretty printing operations onto a stream using a set of 104 """Handle pretty printing operations onto a stream using a set of
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
152 146
153 def isrecursive(self, object): 147 def isrecursive(self, object):
154 return self.format(object, {}, 0, 0)[2] 148 return self.format(object, {}, 0, 0)[2]
155 149
156 def isreadable(self, object): 150 def isreadable(self, object):
157 s, readable, recursive = self.format(object, {}, 0, 0) 151 s, readable, recursive = self.format(object, {}, 0, 0)
158 return readable and not recursive 152 return readable and not recursive
159 153
160 def _format(self, object, stream, indent, allowance, context, level): 154 def _format(self, object, stream, indent, allowance, context, level):
161 level = level + 1 155 level = level + 1
162 objid = _id(object) 156 objid = id(object)
163 if objid in context: 157 if objid in context:
164 stream.write(_recursion(object)) 158 stream.write(_recursion(object))
165 self._recursive = True 159 self._recursive = True
166 self._readable = False 160 self._readable = False
167 return 161 return
168 rep = self._repr(object, context, level - 1) 162 rep = self._repr(object, context, level - 1)
169 typ = _type(object) 163 typ = type(object)
170 max_width = self._width - indent - allowance 164 max_width = self._width - indent - allowance
171 sepLines = _len(rep) > max_width 165 sepLines = len(rep) > max_width
172 write = stream.write 166 write = stream.write
173 167
174 if sepLines: 168 if sepLines:
175 r = getattr(typ, "__repr__", None) 169 r = getattr(typ, "__repr__", None)
176 if issubclass(typ, dict): 170 if issubclass(typ, dict):
177 write('{') 171 write('{')
178 if self._indent_per_level > 1: 172 if self._indent_per_level > 1:
179 write((self._indent_per_level - 1) * ' ') 173 write((self._indent_per_level - 1) * ' ')
180 length = _len(object) 174 length = len(object)
181 if length: 175 if length:
182 context[objid] = 1 176 context[objid] = 1
183 if issubclass(typ, _OrderedDict): 177 if issubclass(typ, _OrderedDict):
184 items = list(object.items()) 178 items = list(object.items())
185 else: 179 else:
186 items = sorted(object.items(), key=_safe_tuple) 180 items = sorted(object.items(), key=_safe_tuple)
187 self._format_dict_items(items, stream, 181 self._format_dict_items(items, stream,
188 indent + self._indent_per_level, 182 indent + self._indent_per_level,
189 allowance + 1, 183 allowance + 1,
190 context, level) 184 context, level)
191 del context[objid] 185 del context[objid]
192 write('}') 186 write('}')
193 return 187 return
194 188
195 if ((issubclass(typ, list) and r is list.__repr__) or 189 if ((issubclass(typ, list) and r is list.__repr__) or
196 (issubclass(typ, tuple) and r is tuple.__repr__) or 190 (issubclass(typ, tuple) and r is tuple.__repr__) or
197 (issubclass(typ, set) and r is set.__repr__) or 191 (issubclass(typ, set) and r is set.__repr__) or
198 (issubclass(typ, frozenset) and r is frozenset.__repr__) 192 (issubclass(typ, frozenset) and r is frozenset.__repr__)
199 ): 193 ):
200 length = _len(object) 194 length = len(object)
201 if issubclass(typ, list): 195 if issubclass(typ, list):
202 write('[') 196 write('[')
203 endchar = ']' 197 endchar = ']'
204 elif issubclass(typ, tuple): 198 elif issubclass(typ, tuple):
205 write('(') 199 write('(')
206 if length == 1: 200 if length == 1:
207 endchar = ',)' 201 endchar = ',)'
208 else: 202 else:
209 endchar = ')' 203 endchar = ')'
210 else: 204 else:
211 if not length: 205 if not length:
212 write(rep) 206 write(rep)
213 return 207 return
214 if typ is set: 208 if typ is set:
215 write('{') 209 write('{')
216 endchar = '}' 210 endchar = '}'
217 else: 211 else:
218 write(typ.__name__) 212 write(typ.__name__)
219 write('({') 213 write('({')
220 endchar = '})' 214 endchar = '})'
221 indent += _len(typ.__name__) + 1 215 indent += len(typ.__name__) + 1
222 object = sorted(object, key=_safe_key) 216 object = sorted(object, key=_safe_key)
223 if self._indent_per_level > 1: 217 if self._indent_per_level > 1:
224 write((self._indent_per_level - 1) * ' ') 218 write((self._indent_per_level - 1) * ' ')
225 if length: 219 if length:
226 context[objid] = 1 220 context[objid] = 1
227 self._format_items(object, stream, 221 self._format_items(object, stream,
228 indent + self._indent_per_level, 222 indent + self._indent_per_level,
229 allowance + _len(endchar), 223 allowance + len(endchar),
230 context, level) 224 context, level)
231 del context[objid] 225 del context[objid]
232 write(endchar) 226 write(endchar)
233 return 227 return
234 228
235 if issubclass(typ, str) and _len(object) > 0 and r is str.__repr__: 229 if issubclass(typ, str) and len(object) > 0 and r is str.__repr__:
236 def _str_parts(s): 230 chunks = []
237 """ 231 lines = object.splitlines(True)
238 Return a list of string literals comprising the repr() 232 if level == 1:
239 of the given string using literal concatenation. 233 indent += 1
240 """ 234 allowance += 1
241 lines = s.splitlines(True) 235 max_width1 = max_width = self._width - indent
242 for i, line in enumerate(lines): 236 for i, line in enumerate(lines):
243 rep = repr(line) 237 rep = repr(line)
244 if _len(rep) <= max_width: 238 if i == len(lines) - 1:
245 yield rep 239 max_width1 -= allowance
246 else: 240 if len(rep) <= max_width1:
247 # A list of alternating (non-space, space) strings 241 chunks.append(rep)
248 parts = re.split(r'(\s+)', line) + [''] 242 else:
249 current = '' 243 # A list of alternating (non-space, space) strings
250 for i in range(0, _len(parts), 2): 244 parts = re.findall(r'\S*\s*', line)
251 part = parts[i] + parts[i+1] 245 assert parts
252 candidate = current + part 246 assert not parts[-1]
253 if _len(repr(candidate)) > max_width: 247 parts.pop() # drop empty last part
254 if current: 248 max_width2 = max_width
255 yield repr(current) 249 current = ''
256 current = part 250 for j, part in enumerate(parts):
257 else: 251 candidate = current + part
258 current = candidate 252 if j == len(parts) - 1 and i == len(lines) - 1:
259 if current: 253 max_width2 -= allowance
260 yield repr(current) 254 if len(repr(candidate)) > max_width2:
261 for i, rep in enumerate(_str_parts(object)): 255 if current:
256 chunks.append(repr(current))
257 current = part
258 else:
259 current = candidate
260 if current:
261 chunks.append(repr(current))
262 if len(chunks) == 1:
263 write(rep)
264 return
265 if level == 1:
266 write('(')
267 for i, rep in enumerate(chunks):
262 if i > 0: 268 if i > 0:
263 write('\n' + ' '*indent) 269 write('\n' + ' '*indent)
264 write(rep) 270 write(rep)
271 if level == 1:
272 write(')')
265 return 273 return
266 write(rep) 274 write(rep)
267 275
268 def _format_dict_items(self, items, stream, indent, allowance, context, 276 def _format_dict_items(self, items, stream, indent, allowance, context,
269 level): 277 level):
270 write = stream.write 278 write = stream.write
271 delimnl = ',\n' + ' ' * indent 279 delimnl = ',\n' + ' ' * indent
272 last_index = _len(items) - 1 280 last_index = len(items) - 1
273 for i, (key, ent) in enumerate(items): 281 for i, (key, ent) in enumerate(items):
274 last = i == last_index 282 last = i == last_index
275 rep = self._repr(key, context, level) 283 rep = self._repr(key, context, level)
276 write(rep) 284 write(rep)
277 write(': ') 285 write(': ')
278 self._format(ent, stream, indent + _len(rep) + 2, 286 self._format(ent, stream, indent + len(rep) + 2,
279 allowance if last else 1, 287 allowance if last else 1,
280 context, level) 288 context, level)
281 if not last: 289 if not last:
282 write(delimnl) 290 write(delimnl)
283 291
284 def _format_items(self, items, stream, indent, allowance, context, level): 292 def _format_items(self, items, stream, indent, allowance, context, level):
285 write = stream.write 293 write = stream.write
286 delimnl = ',\n' + ' ' * indent 294 delimnl = ',\n' + ' ' * indent
287 delim = '' 295 delim = ''
288 width = max_width = self._width - indent + 1 296 width = max_width = self._width - indent + 1
289 it = iter(items) 297 it = iter(items)
290 try: 298 try:
291 next_ent = next(it) 299 next_ent = next(it)
292 except StopIteration: 300 except StopIteration:
293 return 301 return
294 last = False 302 last = False
295 while not last: 303 while not last:
296 ent = next_ent 304 ent = next_ent
297 try: 305 try:
298 next_ent = next(it) 306 next_ent = next(it)
299 except StopIteration: 307 except StopIteration:
300 last = True 308 last = True
301 max_width -= allowance 309 max_width -= allowance
302 width -= allowance 310 width -= allowance
303 if self._compact: 311 if self._compact:
304 rep = self._repr(ent, context, level) 312 rep = self._repr(ent, context, level)
305 w = _len(rep) + 2 313 w = len(rep) + 2
306 if width < w: 314 if width < w:
307 width = max_width 315 width = max_width
308 if delim: 316 if delim:
309 delim = delimnl 317 delim = delimnl
310 if width >= w: 318 if width >= w:
311 width -= w 319 width -= w
312 write(delim) 320 write(delim)
313 delim = ', ' 321 delim = ', '
314 write(rep) 322 write(rep)
315 continue 323 continue
(...skipping 16 matching lines...) Expand all
332 """Format object for a specific context, returning a string 340 """Format object for a specific context, returning a string
333 and flags indicating whether the representation is 'readable' 341 and flags indicating whether the representation is 'readable'
334 and whether the object represents a recursive construct. 342 and whether the object represents a recursive construct.
335 """ 343 """
336 return _safe_repr(object, context, maxlevels, level) 344 return _safe_repr(object, context, maxlevels, level)
337 345
338 346
339 # Return triple (repr_string, isreadable, isrecursive). 347 # Return triple (repr_string, isreadable, isrecursive).
340 348
341 def _safe_repr(object, context, maxlevels, level): 349 def _safe_repr(object, context, maxlevels, level):
342 typ = _type(object) 350 typ = type(object)
343 if typ is str: 351 if typ is str:
344 if 'locale' not in _sys.modules: 352 if 'locale' not in _sys.modules:
345 return repr(object), True, False 353 return repr(object), True, False
346 if "'" in object and '"' not in object: 354 if "'" in object and '"' not in object:
347 closure = '"' 355 closure = '"'
348 quotes = {'"': '\\"'} 356 quotes = {'"': '\\"'}
349 else: 357 else:
350 closure = "'" 358 closure = "'"
351 quotes = {"'": "\\'"} 359 quotes = {"'": "\\'"}
352 qget = quotes.get 360 qget = quotes.get
353 sio = _StringIO() 361 sio = _StringIO()
354 write = sio.write 362 write = sio.write
355 for char in object: 363 for char in object:
356 if char.isalpha(): 364 if char.isalpha():
357 write(char) 365 write(char)
358 else: 366 else:
359 write(qget(char, repr(char)[1:-1])) 367 write(qget(char, repr(char)[1:-1]))
360 return ("%s%s%s" % (closure, sio.getvalue(), closure)), True, False 368 return ("%s%s%s" % (closure, sio.getvalue(), closure)), True, False
361 369
362 r = getattr(typ, "__repr__", None) 370 r = getattr(typ, "__repr__", None)
363 if issubclass(typ, dict) and r is dict.__repr__: 371 if issubclass(typ, dict) and r is dict.__repr__:
364 if not object: 372 if not object:
365 return "{}", True, False 373 return "{}", True, False
366 objid = _id(object) 374 objid = id(object)
367 if maxlevels and level >= maxlevels: 375 if maxlevels and level >= maxlevels:
368 return "{...}", False, objid in context 376 return "{...}", False, objid in context
369 if objid in context: 377 if objid in context:
370 return _recursion(object), False, True 378 return _recursion(object), False, True
371 context[objid] = 1 379 context[objid] = 1
372 readable = True 380 readable = True
373 recursive = False 381 recursive = False
374 components = [] 382 components = []
375 append = components.append 383 append = components.append
376 level += 1 384 level += 1
377 saferepr = _safe_repr 385 saferepr = _safe_repr
378 items = sorted(object.items(), key=_safe_tuple) 386 items = sorted(object.items(), key=_safe_tuple)
379 for k, v in items: 387 for k, v in items:
380 krepr, kreadable, krecur = saferepr(k, context, maxlevels, level) 388 krepr, kreadable, krecur = saferepr(k, context, maxlevels, level)
381 vrepr, vreadable, vrecur = saferepr(v, context, maxlevels, level) 389 vrepr, vreadable, vrecur = saferepr(v, context, maxlevels, level)
382 append("%s: %s" % (krepr, vrepr)) 390 append("%s: %s" % (krepr, vrepr))
383 readable = readable and kreadable and vreadable 391 readable = readable and kreadable and vreadable
384 if krecur or vrecur: 392 if krecur or vrecur:
385 recursive = True 393 recursive = True
386 del context[objid] 394 del context[objid]
387 return "{%s}" % _commajoin(components), readable, recursive 395 return "{%s}" % ", ".join(components), readable, recursive
388 396
389 if (issubclass(typ, list) and r is list.__repr__) or \ 397 if (issubclass(typ, list) and r is list.__repr__) or \
390 (issubclass(typ, tuple) and r is tuple.__repr__): 398 (issubclass(typ, tuple) and r is tuple.__repr__):
391 if issubclass(typ, list): 399 if issubclass(typ, list):
392 if not object: 400 if not object:
393 return "[]", True, False 401 return "[]", True, False
394 format = "[%s]" 402 format = "[%s]"
395 elif _len(object) == 1: 403 elif len(object) == 1:
396 format = "(%s,)" 404 format = "(%s,)"
397 else: 405 else:
398 if not object: 406 if not object:
399 return "()", True, False 407 return "()", True, False
400 format = "(%s)" 408 format = "(%s)"
401 objid = _id(object) 409 objid = id(object)
402 if maxlevels and level >= maxlevels: 410 if maxlevels and level >= maxlevels:
403 return format % "...", False, objid in context 411 return format % "...", False, objid in context
404 if objid in context: 412 if objid in context:
405 return _recursion(object), False, True 413 return _recursion(object), False, True
406 context[objid] = 1 414 context[objid] = 1
407 readable = True 415 readable = True
408 recursive = False 416 recursive = False
409 components = [] 417 components = []
410 append = components.append 418 append = components.append
411 level += 1 419 level += 1
412 for o in object: 420 for o in object:
413 orepr, oreadable, orecur = _safe_repr(o, context, maxlevels, level) 421 orepr, oreadable, orecur = _safe_repr(o, context, maxlevels, level)
414 append(orepr) 422 append(orepr)
415 if not oreadable: 423 if not oreadable:
416 readable = False 424 readable = False
417 if orecur: 425 if orecur:
418 recursive = True 426 recursive = True
419 del context[objid] 427 del context[objid]
420 return format % _commajoin(components), readable, recursive 428 return format % ", ".join(components), readable, recursive
421 429
422 rep = repr(object) 430 rep = repr(object)
423 return rep, (rep and not rep.startswith('<')), False 431 return rep, (rep and not rep.startswith('<')), False
424 432
425 433
426 def _recursion(object): 434 def _recursion(object):
427 return ("<Recursion on %s with id=%s>" 435 return ("<Recursion on %s with id=%s>"
428 % (_type(object).__name__, _id(object))) 436 % (type(object).__name__, id(object)))
429 437
430 438
431 def _perfcheck(object=None): 439 def _perfcheck(object=None):
432 import time 440 import time
433 if object is None: 441 if object is None:
434 object = [("string", (1, 2), [3, 4], {5: 6, 7: 8})] * 100000 442 object = [("string", (1, 2), [3, 4], {5: 6, 7: 8})] * 100000
435 p = PrettyPrinter() 443 p = PrettyPrinter()
436 t1 = time.time() 444 t1 = time.time()
437 _safe_repr(object, {}, None, 0) 445 _safe_repr(object, {}, None, 0)
438 t2 = time.time() 446 t2 = time.time()
439 p.pformat(object) 447 p.pformat(object)
440 t3 = time.time() 448 t3 = time.time()
441 print("_safe_repr:", t2 - t1) 449 print("_safe_repr:", t2 - t1)
442 print("pformat:", t3 - t2) 450 print("pformat:", t3 - t2)
443 451
444 if __name__ == "__main__": 452 if __name__ == "__main__":
445 _perfcheck() 453 _perfcheck()
LEFTRIGHT

RSS Feeds Recent Issues | This issue
This is Rietveld 894c83f36cb7+