forked from goodboy/tractor
				
			Draft a (pretty)`Struct.fields_diff()`
For comparing a `msgspec.Struct` against an input `dict` presumably to be used as input for struct instantiation. The main diff with `.__sub__()` is that non-existing fields on either are reported (loudly).aio_abandons
							parent
							
								
									aa1f6fa4b5
								
							
						
					
					
						commit
						fb04f74605
					
				|  | @ -30,9 +30,9 @@ from msgspec import ( | ||||||
|     Struct as _Struct, |     Struct as _Struct, | ||||||
|     structs, |     structs, | ||||||
| ) | ) | ||||||
| from pprint import ( | # from pprint import ( | ||||||
|     saferepr, | #     saferepr, | ||||||
| ) | # ) | ||||||
| 
 | 
 | ||||||
| from tractor.log import get_logger | from tractor.log import get_logger | ||||||
| 
 | 
 | ||||||
|  | @ -75,8 +75,8 @@ class DiffDump(UserList): | ||||||
|         for k, left, right in self: |         for k, left, right in self: | ||||||
|             repstr += ( |             repstr += ( | ||||||
|                 f'({k},\n' |                 f'({k},\n' | ||||||
|                 f'\t{repr(left)},\n' |                 f' |_{repr(left)},\n' | ||||||
|                 f'\t{repr(right)},\n' |                 f' |_{repr(right)},\n' | ||||||
|                 ')\n' |                 ')\n' | ||||||
|             ) |             ) | ||||||
|         repstr += ']\n' |         repstr += ']\n' | ||||||
|  | @ -144,15 +144,22 @@ def pformat( | ||||||
|                 field_indent=indent + field_indent, |                 field_indent=indent + field_indent, | ||||||
|             ) |             ) | ||||||
| 
 | 
 | ||||||
|         else:  # the `pprint` recursion-safe format: |         else: | ||||||
|  |             val_str: str = repr(v) | ||||||
|  | 
 | ||||||
|  |             # XXX LOL, below just seems to be f#$%in causing | ||||||
|  |             # recursion errs.. | ||||||
|  |             # | ||||||
|  |             # the `pprint` recursion-safe format: | ||||||
|             # https://docs.python.org/3.11/library/pprint.html#pprint.saferepr |             # https://docs.python.org/3.11/library/pprint.html#pprint.saferepr | ||||||
|             try: |             # try: | ||||||
|                 val_str: str = saferepr(v) |             #     val_str: str = saferepr(v) | ||||||
|             except Exception: |             # except Exception: | ||||||
|                 log.exception( |             #     log.exception( | ||||||
|                     'Failed to `saferepr({type(struct)})` !?\n' |             #         'Failed to `saferepr({type(struct)})` !?\n' | ||||||
|                 ) |             #     ) | ||||||
|             return _Struct.__repr__(struct) |                 # raise | ||||||
|  |                 # return _Struct.__repr__(struct) | ||||||
| 
 | 
 | ||||||
|         # TODO: LOLOL use `textwrap.indent()` instead dawwwwwg! |         # TODO: LOLOL use `textwrap.indent()` instead dawwwwwg! | ||||||
|         obj_str += (field_ws + f'{k}: {typ_name} = {val_str},\n') |         obj_str += (field_ws + f'{k}: {typ_name} = {val_str},\n') | ||||||
|  | @ -203,12 +210,7 @@ class Struct( | ||||||
|         return sin_props |         return sin_props | ||||||
| 
 | 
 | ||||||
|     pformat = pformat |     pformat = pformat | ||||||
|     # __repr__ = pformat | 
 | ||||||
|     # __str__ = __repr__ = pformat |  | ||||||
|     # TODO: use a pprint.PrettyPrinter instance around ONLY rendering |  | ||||||
|     # inside a known tty? |  | ||||||
|     # def __repr__(self) -> str: |  | ||||||
|     #     ... |  | ||||||
|     def __repr__(self) -> str: |     def __repr__(self) -> str: | ||||||
|         try: |         try: | ||||||
|             return pformat(self) |             return pformat(self) | ||||||
|  | @ -218,6 +220,13 @@ class Struct( | ||||||
|             ) |             ) | ||||||
|             return _Struct.__repr__(self) |             return _Struct.__repr__(self) | ||||||
| 
 | 
 | ||||||
|  |     # __repr__ = pformat | ||||||
|  |     # __str__ = __repr__ = pformat | ||||||
|  |     # TODO: use a pprint.PrettyPrinter instance around ONLY rendering | ||||||
|  |     # inside a known tty? | ||||||
|  |     # def __repr__(self) -> str: | ||||||
|  |     #     ... | ||||||
|  | 
 | ||||||
|     def copy( |     def copy( | ||||||
|         self, |         self, | ||||||
|         update: dict | None = None, |         update: dict | None = None, | ||||||
|  | @ -267,13 +276,15 @@ class Struct( | ||||||
|                 fi.type(getattr(self, fi.name)), |                 fi.type(getattr(self, fi.name)), | ||||||
|             ) |             ) | ||||||
| 
 | 
 | ||||||
|  |     # TODO: make a mod func instead and just point to it here for | ||||||
|  |     # method impl? | ||||||
|     def __sub__( |     def __sub__( | ||||||
|         self, |         self, | ||||||
|         other: Struct, |         other: Struct, | ||||||
| 
 | 
 | ||||||
|     ) -> DiffDump[tuple[str, Any, Any]]: |     ) -> DiffDump[tuple[str, Any, Any]]: | ||||||
|         ''' |         ''' | ||||||
|         Compare fields/items key-wise and return a ``DiffDump`` |         Compare fields/items key-wise and return a `DiffDump` | ||||||
|         for easy visual REPL comparison B) |         for easy visual REPL comparison B) | ||||||
| 
 | 
 | ||||||
|         ''' |         ''' | ||||||
|  | @ -290,3 +301,42 @@ class Struct( | ||||||
|                 )) |                 )) | ||||||
| 
 | 
 | ||||||
|         return diffs |         return diffs | ||||||
|  | 
 | ||||||
|  |     @classmethod | ||||||
|  |     def fields_diff( | ||||||
|  |         cls, | ||||||
|  |         other: dict|Struct, | ||||||
|  | 
 | ||||||
|  |     ) -> DiffDump[tuple[str, Any, Any]]: | ||||||
|  |         ''' | ||||||
|  |         Very similar to `PrettyStruct.__sub__()` except accepts an | ||||||
|  |         input `other: dict` (presumably that would normally be called | ||||||
|  |         like `Struct(**other)`) which returns a `DiffDump` of the | ||||||
|  |         fields of the struct and the `dict`'s fields. | ||||||
|  | 
 | ||||||
|  |         ''' | ||||||
|  |         nullish = object() | ||||||
|  |         consumed: dict = other.copy() | ||||||
|  |         diffs: DiffDump[tuple[str, Any, Any]] = DiffDump() | ||||||
|  |         for fi in structs.fields(cls): | ||||||
|  |             field_name: str = fi.name | ||||||
|  |             # ours: Any = getattr(self, field_name) | ||||||
|  |             theirs: Any = consumed.pop(field_name, nullish) | ||||||
|  |             if theirs is nullish: | ||||||
|  |                 diffs.append(( | ||||||
|  |                     field_name, | ||||||
|  |                     f'{fi.type!r}', | ||||||
|  |                     'NOT-DEFINED in `other: dict`', | ||||||
|  |                 )) | ||||||
|  | 
 | ||||||
|  |         # when there are lingering fields in `other` that this struct | ||||||
|  |         # DOES NOT define we also append those. | ||||||
|  |         if consumed: | ||||||
|  |             for k, v in consumed.items(): | ||||||
|  |                 diffs.append(( | ||||||
|  |                     k, | ||||||
|  |                     f'NOT-DEFINED for `{cls.__name__}`', | ||||||
|  |                     f'`other: dict` has value = {v!r}', | ||||||
|  |                 )) | ||||||
|  | 
 | ||||||
|  |         return diffs | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue