44from collections import OrderedDict
55from collections .abc import Generator
66from typing import Any , Union
7+ from unittest .mock import _Call as MockCall
78
89from .ansi import isatty
910
2930 (frozenset , 'frozenset({' , '})' ),
3031]
3132DEFAULT_WIDTH = int (os .getenv ('PY_DEVTOOLS_WIDTH' , 120 ))
33+ MISSING = object ()
34+ PRETTY_KEY = '__prettier_formatted_value__'
3235
3336
3437def env_true (var_name , alt = None ):
@@ -39,6 +42,14 @@ def env_true(var_name, alt=None):
3942 return alt
4043
4144
45+ def fmt (v ):
46+ return {PRETTY_KEY : v }
47+
48+
49+ class SkipPretty (Exception ):
50+ pass
51+
52+
4253class PrettyFormat :
4354 def __init__ (self ,
4455 indent_step = 4 ,
@@ -77,6 +88,16 @@ def _format(self, value: Any, indent_current: int, indent_first: bool):
7788 if indent_first :
7889 self ._stream .write (indent_current * self ._c )
7990
91+ pretty_func = getattr (value , '__pretty__' , None )
92+ if pretty_func and not isinstance (value , MockCall ):
93+ try :
94+ gen = pretty_func (fmt = fmt , skip_exc = SkipPretty )
95+ self ._render_pretty (gen , indent_current )
96+ except SkipPretty :
97+ pass
98+ else :
99+ return
100+
80101 value_repr = repr (value )
81102 if len (value_repr ) <= self ._simple_cutoff and not isinstance (value , Generator ):
82103 self ._stream .write (value_repr )
@@ -88,6 +109,26 @@ def _format(self, value: Any, indent_current: int, indent_first: bool):
88109 return
89110 self ._format_raw (value , value_repr , indent_current , indent_new )
90111
112+ def _render_pretty (self , gen , indent : int ):
113+ prefix = False
114+ for v in gen :
115+ if isinstance (v , int ) and v in {- 1 , 0 , 1 }:
116+ indent += v * self ._indent_step
117+ prefix = True
118+ else :
119+ if prefix :
120+ self ._stream .write ('\n ' + self ._c * indent )
121+ prefix = False
122+
123+ pretty_value = v .get (PRETTY_KEY , MISSING ) if (isinstance (v , dict ) and len (v ) == 1 ) else MISSING
124+ if pretty_value is not MISSING :
125+ self ._format (pretty_value , indent , False )
126+ elif isinstance (v , str ):
127+ self ._stream .write (v )
128+ else :
129+ # shouldn't happen but will
130+ self ._stream .write (repr (v ))
131+
91132 def _format_dict (self , value : dict , value_repr : str , indent_current : int , indent_new : int ):
92133 open_ , before_ , split_ , after_ , close_ = '{\n ' , indent_new * self ._c , ': ' , ',\n ' , '}'
93134 if isinstance (value , OrderedDict ):
0 commit comments