33from collections import OrderedDict
44from collections .abc import Generator
55
6- from .utils import DataClassType , LaxMapping , env_true , isatty
6+ from .utils import DataClassType , LaxMapping , SQLAlchemyClassType , env_true , isatty
77
88__all__ = 'PrettyFormat' , 'pformat' , 'pprint'
99MYPY = False
1010if MYPY :
11- from typing import Any , Iterable , Union
11+ from typing import Any , Iterable , Tuple , Union
1212
1313PARENTHESES_LOOKUP = [
1414 (list , '[' , ']' ),
@@ -69,6 +69,7 @@ def __init__(
6969 # put this last as the check can be slow
7070 (LaxMapping , self ._format_dict ),
7171 (DataClassType , self ._format_dataclass ),
72+ (SQLAlchemyClassType , self ._format_sqlalchemy_class ),
7273 ]
7374
7475 def __call__ (self , value : 'Any' , * , indent : int = 0 , indent_first : bool = False , highlight : bool = False ):
@@ -169,15 +170,7 @@ def _format_tuples(self, value: tuple, value_repr: str, indent_current: int, ind
169170 fields = getattr (value , '_fields' , None )
170171 if fields :
171172 # named tuple
172- self ._stream .write (value .__class__ .__name__ + '(\n ' )
173- for field , v in zip (fields , value ):
174- self ._stream .write (indent_new * self ._c )
175- if field : # field is falsy sometimes for odd things like call_args
176- self ._stream .write (str (field ))
177- self ._stream .write ('=' )
178- self ._format (v , indent_new , False )
179- self ._stream .write (',\n ' )
180- self ._stream .write (indent_current * self ._c + ')' )
173+ self ._format_fields (value , zip (fields , value ), indent_current , indent_new )
181174 else :
182175 # normal tuples are just like other similar iterables
183176 return self ._format_list_like (value , value_repr , indent_current , indent_new )
@@ -231,13 +224,15 @@ def _format_bytearray(self, value: 'Any', _: str, indent_current: int, indent_ne
231224 def _format_dataclass (self , value : 'Any' , _ : str , indent_current : int , indent_new : int ):
232225 from dataclasses import asdict
233226
234- before_ = indent_new * self ._c
235- self ._stream .write (f'{ value .__class__ .__name__ } (\n ' )
236- for k , v in asdict (value ).items ():
237- self ._stream .write (f'{ before_ } { k } =' )
238- self ._format (v , indent_new , False )
239- self ._stream .write (',\n ' )
240- self ._stream .write (indent_current * self ._c + ')' )
227+ self ._format_fields (value , asdict (value ).items (), indent_current , indent_new )
228+
229+ def _format_sqlalchemy_class (self , value : 'Any' , _ : str , indent_current : int , indent_new : int ):
230+ fields = [
231+ (field , getattr (value , field ))
232+ for field in dir (value )
233+ if not (field .startswith ('_' ) or field in ['metadata' , 'registry' ])
234+ ]
235+ self ._format_fields (value , fields , indent_current , indent_new )
241236
242237 def _format_raw (self , _ : 'Any' , value_repr : str , indent_current : int , indent_new : int ):
243238 lines = value_repr .splitlines (True )
@@ -256,6 +251,18 @@ def _format_raw(self, _: 'Any', value_repr: str, indent_current: int, indent_new
256251 else :
257252 self ._stream .write (value_repr )
258253
254+ def _format_fields (
255+ self , value : 'Any' , fields : 'Iterable[Tuple[str, Any]]' , indent_current : int , indent_new : int
256+ ) -> None :
257+ self ._stream .write (f'{ value .__class__ .__name__ } (\n ' )
258+ for field , v in fields :
259+ self ._stream .write (indent_new * self ._c )
260+ if field : # field is falsy sometimes for odd things like call_args
261+ self ._stream .write (f'{ field } =' )
262+ self ._format (v , indent_new , False )
263+ self ._stream .write (',\n ' )
264+ self ._stream .write (indent_current * self ._c + ')' )
265+
259266
260267pformat = PrettyFormat ()
261268force_highlight = env_true ('PY_DEVTOOLS_HIGHLIGHT' , None )
0 commit comments