refactor: Non-working first-move of serialization logic
parent
e1f11f6d68
commit
3def85e24f
4
TODO.md
4
TODO.md
|
@ -510,7 +510,7 @@ Unreported:
|
||||||
|
|
||||||
|
|
||||||
## Keyed Cache
|
## Keyed Cache
|
||||||
- [ ] Implement `bl_cache.KeyedCache` for, especially, abstracting the caches underlying the input and output sockets.
|
- [x] Implement `bl_cache.KeyedCache` for, especially, abstracting the caches underlying the input and output sockets.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -546,7 +546,7 @@ We need support for arbitrary objects, but still backed by the persistance seman
|
||||||
- [ ] Similarly, a field method that gets the 'blfield__' prop data as a dictionary.
|
- [ ] Similarly, a field method that gets the 'blfield__' prop data as a dictionary.
|
||||||
|
|
||||||
### Parallel Features
|
### Parallel Features
|
||||||
- [ ] Move serialization work to a `utils`.
|
- [x] Move serialization work to a `utils`.
|
||||||
- [ ] Also make ENCODER a function that can shortcut the easy cases.
|
- [ ] Also make ENCODER a function that can shortcut the easy cases.
|
||||||
- [ ] For serializeability, let the encoder/decoder be able to make use of an optional `.msgspec_encodable()` and similar decoder respectively, and add support for these in the ENCODER/DECODER functions.
|
- [ ] For serializeability, let the encoder/decoder be able to make use of an optional `.msgspec_encodable()` and similar decoder respectively, and add support for these in the ENCODER/DECODER functions.
|
||||||
- [ ] Define a superclass for `SocketDef` and make everyone inherit from it
|
- [ ] Define a superclass for `SocketDef` and make everyone inherit from it
|
||||||
|
|
|
@ -5,14 +5,8 @@ import inspect
|
||||||
import typing as typ
|
import typing as typ
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
import msgspec
|
|
||||||
import sympy as sp
|
|
||||||
import sympy.physics.units as spu
|
|
||||||
|
|
||||||
from ...utils import extra_sympy_units as spux
|
from ...utils import logger, serialize
|
||||||
from ...utils import logger
|
|
||||||
from . import contracts as ct
|
|
||||||
from . import managed_objs, sockets
|
|
||||||
|
|
||||||
log = logger.get(__name__)
|
log = logger.get(__name__)
|
||||||
|
|
||||||
|
@ -39,149 +33,10 @@ class BLInstance(typ.Protocol):
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
|
|
||||||
|
|
||||||
EncodableValue: typ.TypeAlias = typ.Any ## msgspec-compatible
|
PropGetMethod: typ.TypeAlias = typ.Callable[[BLInstance], serialize.EncodableValue]
|
||||||
PropGetMethod: typ.TypeAlias = typ.Callable[[BLInstance], EncodableValue]
|
PropSetMethod: typ.TypeAlias = typ.Callable[
|
||||||
PropSetMethod: typ.TypeAlias = typ.Callable[[BLInstance, EncodableValue], None]
|
[BLInstance, serialize.EncodableValue], None
|
||||||
|
]
|
||||||
####################
|
|
||||||
# - (De)Serialization
|
|
||||||
####################
|
|
||||||
EncodedComplex: typ.TypeAlias = tuple[float, float] | list[float, float]
|
|
||||||
EncodedSympy: typ.TypeAlias = str
|
|
||||||
EncodedManagedObj: typ.TypeAlias = tuple[str, str] | list[str, str]
|
|
||||||
EncodedPydanticModel: typ.TypeAlias = tuple[str, str] | list[str, str]
|
|
||||||
|
|
||||||
|
|
||||||
def _enc_hook(obj: typ.Any) -> EncodableValue:
|
|
||||||
"""Translates types not natively supported by `msgspec`, to an encodable form supported by `msgspec`.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
obj: The object of arbitrary type to transform into an encodable value.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A value encodable by `msgspec`.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
NotImplementedError: When the type transformation hasn't been implemented.
|
|
||||||
"""
|
|
||||||
if isinstance(obj, complex):
|
|
||||||
return (obj.real, obj.imag)
|
|
||||||
if isinstance(obj, sp.Basic | sp.MatrixBase | sp.Expr | spu.Quantity):
|
|
||||||
return sp.srepr(obj)
|
|
||||||
if isinstance(obj, managed_objs.ManagedObj):
|
|
||||||
return (obj.name, obj.__class__.__name__)
|
|
||||||
if isinstance(obj, ct.schemas.SocketDef):
|
|
||||||
return (obj.model_dump(), obj.__class__.__name__)
|
|
||||||
|
|
||||||
msg = f'Can\'t encode "{obj}" of type {type(obj)}'
|
|
||||||
raise NotImplementedError(msg)
|
|
||||||
|
|
||||||
|
|
||||||
def _dec_hook(_type: type, obj: EncodableValue) -> typ.Any:
|
|
||||||
"""Translates the `msgspec`-encoded form of an object back to its true form.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
_type: The type to transform the `msgspec`-encoded object back into.
|
|
||||||
obj: The encoded object of to transform back into an encodable value.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A value encodable by `msgspec`.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
NotImplementedError: When the type transformation hasn't been implemented.
|
|
||||||
"""
|
|
||||||
if _type is complex and isinstance(obj, EncodedComplex):
|
|
||||||
return complex(obj[0], obj[1])
|
|
||||||
if (
|
|
||||||
_type is sp.Basic
|
|
||||||
and isinstance(obj, EncodedSympy)
|
|
||||||
or _type is sp.Expr
|
|
||||||
and isinstance(obj, EncodedSympy)
|
|
||||||
or _type is sp.MatrixBase
|
|
||||||
and isinstance(obj, EncodedSympy)
|
|
||||||
or _type is spu.Quantity
|
|
||||||
and isinstance(obj, EncodedSympy)
|
|
||||||
):
|
|
||||||
return sp.sympify(obj).subs(spux.ALL_UNIT_SYMBOLS)
|
|
||||||
if (
|
|
||||||
_type is managed_objs.ManagedBLMesh
|
|
||||||
and isinstance(obj, EncodedManagedObj)
|
|
||||||
or _type is managed_objs.ManagedBLImage
|
|
||||||
and isinstance(obj, EncodedManagedObj)
|
|
||||||
or _type is managed_objs.ManagedBLModifier
|
|
||||||
and isinstance(obj, EncodedManagedObj)
|
|
||||||
):
|
|
||||||
return {
|
|
||||||
'ManagedBLMesh': managed_objs.ManagedBLMesh,
|
|
||||||
'ManagedBLImage': managed_objs.ManagedBLImage,
|
|
||||||
'ManagedBLModifier': managed_objs.ManagedBLModifier,
|
|
||||||
}[obj[1]](obj[0])
|
|
||||||
if _type is ct.schemas.SocketDef:
|
|
||||||
return getattr(sockets, obj[1])(**obj[0])
|
|
||||||
|
|
||||||
msg = f'Can\'t decode "{obj}" to type {type(obj)}'
|
|
||||||
raise NotImplementedError(msg)
|
|
||||||
|
|
||||||
|
|
||||||
ENCODER = msgspec.json.Encoder(enc_hook=_enc_hook, order='deterministic')
|
|
||||||
|
|
||||||
_DECODERS: dict[type, msgspec.json.Decoder] = {
|
|
||||||
complex: msgspec.json.Decoder(type=complex, dec_hook=_dec_hook),
|
|
||||||
sp.Basic: msgspec.json.Decoder(type=sp.Basic, dec_hook=_dec_hook),
|
|
||||||
sp.Expr: msgspec.json.Decoder(type=sp.Expr, dec_hook=_dec_hook),
|
|
||||||
sp.MatrixBase: msgspec.json.Decoder(type=sp.MatrixBase, dec_hook=_dec_hook),
|
|
||||||
spu.Quantity: msgspec.json.Decoder(type=spu.Quantity, dec_hook=_dec_hook),
|
|
||||||
managed_objs.ManagedBLMesh: msgspec.json.Decoder(
|
|
||||||
type=managed_objs.ManagedBLMesh,
|
|
||||||
dec_hook=_dec_hook,
|
|
||||||
),
|
|
||||||
managed_objs.ManagedBLImage: msgspec.json.Decoder(
|
|
||||||
type=managed_objs.ManagedBLImage,
|
|
||||||
dec_hook=_dec_hook,
|
|
||||||
),
|
|
||||||
managed_objs.ManagedBLModifier: msgspec.json.Decoder(
|
|
||||||
type=managed_objs.ManagedBLModifier,
|
|
||||||
dec_hook=_dec_hook,
|
|
||||||
),
|
|
||||||
# managed_objs.ManagedObj: msgspec.json.Decoder(
|
|
||||||
# type=managed_objs.ManagedObj, dec_hook=_dec_hook
|
|
||||||
# ), ## Doesn't work b/c unions are not explicit
|
|
||||||
ct.schemas.SocketDef: msgspec.json.Decoder(
|
|
||||||
type=ct.schemas.SocketDef,
|
|
||||||
dec_hook=_dec_hook,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
_DECODER_FALLBACK: msgspec.json.Decoder = msgspec.json.Decoder(dec_hook=_dec_hook)
|
|
||||||
|
|
||||||
|
|
||||||
@functools.cache
|
|
||||||
def DECODER(_type: type) -> msgspec.json.Decoder: # noqa: N802
|
|
||||||
"""Retrieve a suitable `msgspec.json.Decoder` by-type.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
_type: The type to retrieve a decoder for.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A suitable decoder.
|
|
||||||
"""
|
|
||||||
if (decoder := _DECODERS.get(_type)) is not None:
|
|
||||||
return decoder
|
|
||||||
|
|
||||||
return _DECODER_FALLBACK
|
|
||||||
|
|
||||||
|
|
||||||
def decode_any(_type: type, obj: str) -> typ.Any:
|
|
||||||
naive_decode = DECODER(_type).decode(obj)
|
|
||||||
if _type == dict[str, ct.schemas.SocketDef]:
|
|
||||||
return {
|
|
||||||
socket_name: getattr(sockets, socket_def_list[1])(**socket_def_list[0])
|
|
||||||
for socket_name, socket_def_list in naive_decode.items()
|
|
||||||
}
|
|
||||||
|
|
||||||
log.critical(
|
|
||||||
'Naive Decode of "%s" to "%s" (%s)', str(obj), str(naive_decode), str(_type)
|
|
||||||
)
|
|
||||||
return naive_decode
|
|
||||||
|
|
||||||
|
|
||||||
####################
|
####################
|
||||||
|
|
|
@ -0,0 +1,153 @@
|
||||||
|
import functools
|
||||||
|
import typing as typ
|
||||||
|
|
||||||
|
import msgspec
|
||||||
|
import sympy as sp
|
||||||
|
import sympy.physics.units as spu
|
||||||
|
|
||||||
|
from . import extra_sympy_units as spux
|
||||||
|
from . import logger
|
||||||
|
|
||||||
|
log = logger.get(__name__)
|
||||||
|
|
||||||
|
EncodableValue: typ.TypeAlias = typ.Any ## msgspec-compatible
|
||||||
|
|
||||||
|
####################
|
||||||
|
# - (De)Serialization
|
||||||
|
####################
|
||||||
|
EncodedComplex: typ.TypeAlias = tuple[float, float] | list[float, float]
|
||||||
|
EncodedSympy: typ.TypeAlias = str
|
||||||
|
EncodedManagedObj: typ.TypeAlias = tuple[str, str] | list[str, str]
|
||||||
|
EncodedPydanticModel: typ.TypeAlias = tuple[str, str] | list[str, str]
|
||||||
|
|
||||||
|
|
||||||
|
def _enc_hook(obj: typ.Any) -> EncodableValue:
|
||||||
|
"""Translates types not natively supported by `msgspec`, to an encodable form supported by `msgspec`.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
obj: The object of arbitrary type to transform into an encodable value.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A value encodable by `msgspec`.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
NotImplementedError: When the type transformation hasn't been implemented.
|
||||||
|
"""
|
||||||
|
if isinstance(obj, complex):
|
||||||
|
return (obj.real, obj.imag)
|
||||||
|
if isinstance(obj, sp.Basic | sp.MatrixBase | sp.Expr | spu.Quantity):
|
||||||
|
return sp.srepr(obj)
|
||||||
|
if isinstance(obj, managed_objs.ManagedObj):
|
||||||
|
return (obj.name, obj.__class__.__name__)
|
||||||
|
if isinstance(obj, ct.schemas.SocketDef):
|
||||||
|
return (obj.model_dump(), obj.__class__.__name__)
|
||||||
|
|
||||||
|
msg = f'Can\'t encode "{obj}" of type {type(obj)}'
|
||||||
|
raise NotImplementedError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
def _dec_hook(_type: type, obj: EncodableValue) -> typ.Any:
|
||||||
|
"""Translates the `msgspec`-encoded form of an object back to its true form.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
_type: The type to transform the `msgspec`-encoded object back into.
|
||||||
|
obj: The encoded object of to transform back into an encodable value.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A value encodable by `msgspec`.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
NotImplementedError: When the type transformation hasn't been implemented.
|
||||||
|
"""
|
||||||
|
if _type is complex and isinstance(obj, EncodedComplex):
|
||||||
|
return complex(obj[0], obj[1])
|
||||||
|
if (
|
||||||
|
_type is sp.Basic
|
||||||
|
and isinstance(obj, EncodedSympy)
|
||||||
|
or _type is sp.Expr
|
||||||
|
and isinstance(obj, EncodedSympy)
|
||||||
|
or _type is sp.MatrixBase
|
||||||
|
and isinstance(obj, EncodedSympy)
|
||||||
|
or _type is spu.Quantity
|
||||||
|
and isinstance(obj, EncodedSympy)
|
||||||
|
):
|
||||||
|
return sp.sympify(obj).subs(spux.ALL_UNIT_SYMBOLS)
|
||||||
|
if (
|
||||||
|
_type is managed_objs.ManagedBLMesh
|
||||||
|
and isinstance(obj, EncodedManagedObj)
|
||||||
|
or _type is managed_objs.ManagedBLImage
|
||||||
|
and isinstance(obj, EncodedManagedObj)
|
||||||
|
or _type is managed_objs.ManagedBLModifier
|
||||||
|
and isinstance(obj, EncodedManagedObj)
|
||||||
|
):
|
||||||
|
return {
|
||||||
|
'ManagedBLMesh': managed_objs.ManagedBLMesh,
|
||||||
|
'ManagedBLImage': managed_objs.ManagedBLImage,
|
||||||
|
'ManagedBLModifier': managed_objs.ManagedBLModifier,
|
||||||
|
}[obj[1]](obj[0])
|
||||||
|
if _type is ct.schemas.SocketDef:
|
||||||
|
return getattr(sockets, obj[1])(**obj[0])
|
||||||
|
|
||||||
|
msg = f'Can\'t decode "{obj}" to type {type(obj)}'
|
||||||
|
raise NotImplementedError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
ENCODER = msgspec.json.Encoder(enc_hook=_enc_hook, order='deterministic')
|
||||||
|
|
||||||
|
_DECODERS: dict[type, msgspec.json.Decoder] = {
|
||||||
|
complex: msgspec.json.Decoder(type=complex, dec_hook=_dec_hook),
|
||||||
|
sp.Basic: msgspec.json.Decoder(type=sp.Basic, dec_hook=_dec_hook),
|
||||||
|
sp.Expr: msgspec.json.Decoder(type=sp.Expr, dec_hook=_dec_hook),
|
||||||
|
sp.MatrixBase: msgspec.json.Decoder(type=sp.MatrixBase, dec_hook=_dec_hook),
|
||||||
|
spu.Quantity: msgspec.json.Decoder(type=spu.Quantity, dec_hook=_dec_hook),
|
||||||
|
managed_objs.ManagedBLMesh: msgspec.json.Decoder(
|
||||||
|
type=managed_objs.ManagedBLMesh,
|
||||||
|
dec_hook=_dec_hook,
|
||||||
|
),
|
||||||
|
managed_objs.ManagedBLImage: msgspec.json.Decoder(
|
||||||
|
type=managed_objs.ManagedBLImage,
|
||||||
|
dec_hook=_dec_hook,
|
||||||
|
),
|
||||||
|
managed_objs.ManagedBLModifier: msgspec.json.Decoder(
|
||||||
|
type=managed_objs.ManagedBLModifier,
|
||||||
|
dec_hook=_dec_hook,
|
||||||
|
),
|
||||||
|
# managed_objs.ManagedObj: msgspec.json.Decoder(
|
||||||
|
# type=managed_objs.ManagedObj, dec_hook=_dec_hook
|
||||||
|
# ), ## Doesn't work b/c unions are not explicit
|
||||||
|
ct.schemas.SocketDef: msgspec.json.Decoder(
|
||||||
|
type=ct.schemas.SocketDef,
|
||||||
|
dec_hook=_dec_hook,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
_DECODER_FALLBACK: msgspec.json.Decoder = msgspec.json.Decoder(dec_hook=_dec_hook)
|
||||||
|
|
||||||
|
|
||||||
|
@functools.cache
|
||||||
|
def DECODER(_type: type) -> msgspec.json.Decoder: # noqa: N802
|
||||||
|
"""Retrieve a suitable `msgspec.json.Decoder` by-type.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
_type: The type to retrieve a decoder for.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A suitable decoder.
|
||||||
|
"""
|
||||||
|
if (decoder := _DECODERS.get(_type)) is not None:
|
||||||
|
return decoder
|
||||||
|
|
||||||
|
return _DECODER_FALLBACK
|
||||||
|
|
||||||
|
|
||||||
|
def decode_any(_type: type, obj: str) -> typ.Any:
|
||||||
|
naive_decode = DECODER(_type).decode(obj)
|
||||||
|
if _type == dict[str, ct.schemas.SocketDef]:
|
||||||
|
return {
|
||||||
|
socket_name: getattr(sockets, socket_def_list[1])(**socket_def_list[0])
|
||||||
|
for socket_name, socket_def_list in naive_decode.items()
|
||||||
|
}
|
||||||
|
|
||||||
|
log.critical(
|
||||||
|
'Naive Decode of "%s" to "%s" (%s)', str(obj), str(naive_decode), str(_type)
|
||||||
|
)
|
||||||
|
return naive_decode
|
Loading…
Reference in New Issue