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