@@ -198,9 +198,9 @@ def _format(self, object, stream, indent, allowance, context, level):
198198 self ._recursive = True
199199 self ._readable = False
200200 return
201- rep = self ._repr (object , context , level )
202201 max_width = self ._width - indent - prefix_len - allowance
203- if len (rep ) > max_width :
202+ rep = self ._repr_if_fits (object , context , level , max_width )
203+ if rep is None or len (rep ) > max_width :
204204 p = self ._dispatch .get (type (object ).__repr__ , None )
205205 # Lazy import to improve module import time
206206 from dataclasses import is_dataclass
@@ -220,6 +220,8 @@ def _format(self, object, stream, indent, allowance, context, level):
220220 self ._pprint_dataclass (object , stream , indent , allowance , context , level + 1 )
221221 del context [objid ]
222222 return
223+ if rep is None :
224+ rep = self ._repr (object , context , level )
223225 stream .write (rep )
224226
225227 def _format_block_start (self , start_str , indent ):
@@ -626,8 +628,8 @@ def _format_items(self, items, stream, indent, allowance, context, level):
626628 max_width -= allowance
627629 width -= allowance
628630 if self ._compact :
629- rep = self ._repr (ent , context , level )
630- w = len (rep ) + 2
631+ rep = self ._repr_if_fits (ent , context , level , max_width - 2 )
632+ w = max_width + 1 if rep is None else len (rep ) + 2
631633 if width < w :
632634 width = max_width
633635 if delim :
@@ -655,6 +657,33 @@ def _repr(self, object, context, level):
655657 self ._recursive = True
656658 return repr
657659
660+ # Set by _repr_if_fits() for the duration of its _repr() call and
661+ # consumed by _safe_repr(), which raises _TooWide as soon as the
662+ # representation of a container grows beyond it. Finding out that an
663+ # object does not fit on one line thus costs no more than the width of
664+ # the line, instead of the size of the object, for each level of nesting.
665+ # It is an attribute, not an argument, because subclasses override
666+ # format() with its existing signature.
667+ _max_width = None
668+
669+ def _repr_if_fits (self , object , context , level , max_width ):
670+ # Return the repr of object, or None if building it was cut short
671+ # because it cannot fit in max_width. A completed repr is returned
672+ # even when it is too wide, so that _format() does not have to
673+ # build it again when it writes it.
674+ if type (self ).format is not PrettyPrinter .format :
675+ # An override may shorten what PrettyPrinter.format() returns,
676+ # so only the complete result can tell whether it fits.
677+ return self ._repr (object , context , level )
678+ saved = self ._max_width
679+ self ._max_width = max_width
680+ try :
681+ return self ._repr (object , context , level )
682+ except _TooWide :
683+ return None
684+ finally :
685+ self ._max_width = saved
686+
658687 def format (self , object , context , maxlevels , level ):
659688 """Format object for a specific context, returning a string
660689 and flags indicating whether the representation is 'readable'
@@ -852,6 +881,12 @@ def _safe_repr(self, object, context, maxlevels, level):
852881 components = []
853882 append = components .append
854883 level += 1
884+ max_width = self ._max_width
885+ width = 0
886+ if max_width is not None and 4 * len (object ) > max_width :
887+ # Every item costs at least "k: v, ": do not bother
888+ # formatting (and sorting) them.
889+ raise _TooWide
855890 if self ._sort_dicts :
856891 items = sorted (object .items (), key = _safe_tuple )
857892 else :
@@ -865,6 +900,10 @@ def _safe_repr(self, object, context, maxlevels, level):
865900 readable = readable and kreadable and vreadable
866901 if krecur or vrecur :
867902 recursive = True
903+ if max_width is not None :
904+ width += len (krepr ) + len (vrepr ) + 4
905+ if width > max_width :
906+ raise _TooWide
868907 del context [objid ]
869908 rep = "{%s}" % ", " .join (components )
870909 if is_frozendict :
@@ -894,6 +933,8 @@ def _safe_repr(self, object, context, maxlevels, level):
894933 components = []
895934 append = components .append
896935 level += 1
936+ max_width = self ._max_width
937+ width = 0
897938 for o in object :
898939 orepr , oreadable , orecur = self .format (
899940 o , context , maxlevels , level )
@@ -902,6 +943,10 @@ def _safe_repr(self, object, context, maxlevels, level):
902943 readable = False
903944 if orecur :
904945 recursive = True
946+ if max_width is not None :
947+ width += len (orepr ) + 2
948+ if width > max_width :
949+ raise _TooWide
905950 del context [objid ]
906951 return format % ", " .join (components ), readable , recursive
907952
@@ -923,6 +968,9 @@ def _safe_repr(self, object, context, maxlevels, level):
923968 # We have a view that somehow has lost its type's _mapping, raise
924969 # an error by calling repr() instead of failing cryptically later
925970 return repr (object ), True , False
971+ max_width = self ._max_width
972+ if max_width is not None and 2 * len (object ) > max_width :
973+ raise _TooWide
926974 if self ._sort_dicts :
927975 object = sorted (object , key = key )
928976 context [objid ] = 1
@@ -931,13 +979,18 @@ def _safe_repr(self, object, context, maxlevels, level):
931979 components = []
932980 append = components .append
933981 level += 1
982+ width = 0
934983 for val in object :
935984 vrepr , vreadable , vrecur = self .format (
936985 val , context , maxlevels , level )
937986 append (vrepr )
938987 readable = readable and vreadable
939988 if vrecur :
940989 recursive = True
990+ if max_width is not None :
991+ width += len (vrepr ) + 2
992+ if width > max_width :
993+ raise _TooWide
941994 del context [objid ]
942995 return typ .__name__ + '([%s])' % ", " .join (components ), readable , recursive
943996
@@ -954,6 +1007,13 @@ def _recursion(object):
9541007 % (type (object ).__name__ , id (object )))
9551008
9561009
1010+ class _TooWide (BaseException ):
1011+ """Raised by _safe_repr() when the representation it is building is
1012+ already wider than PrettyPrinter._max_width. It is not an Exception,
1013+ so that "except Exception" handlers it passes through do not catch it.
1014+ """
1015+
1016+
9571017def _wrap_bytes_repr (object , width , allowance ):
9581018 current = b''
9591019 last = len (object ) // 4 * 4
0 commit comments