forked from goodboy/tractor
1
0
Fork 0

Expose `MsgType` and extend `MsgCodec` API a bit

Make a new `MsgType: TypeAlias` for the union of all msg types such that
it can be used in annots throughout the code base; just make
`.msg.__msg_spec__` delegate to it.

Add some new codec methods:
- `pld_spec_str`: for the `str`-casted value of the payload spec,
  generally useful in logging content.
- `msg_spec_items()`: to render a `dict` of msg types to their
  `str()`-casted values with support for singling out a specific
  `MsgType`, type by input `msg` instance.
- `pformat_msg_spec()`: for rendering the (partial) `.msg_spec` as
  a formatted `str` useful in logging.

Oh right, add a `Error._msg_dict: dict` in support of the previous
commit (for `MsgTypeError` packing as RAEs) such that our error msg type
can house a non-type-spec decoded wire-bytes for error
reporting/analysis purposes.
runtime_to_msgspec
Tyler Goodlet 2024-04-09 10:09:05 -04:00
parent cf48fdecfe
commit 15549f7c26
3 changed files with 71 additions and 10 deletions

View File

@ -19,7 +19,6 @@ Built-in messaging patterns, types, APIs and helpers.
''' '''
from typing import ( from typing import (
Union,
TypeAlias, TypeAlias,
) )
from .ptr import ( from .ptr import (
@ -56,8 +55,9 @@ from .types import (
# full msg class set from above as list # full msg class set from above as list
__msg_types__ as __msg_types__, __msg_types__ as __msg_types__,
# type-alias for union of all msgs
MsgType as MsgType,
) )
# TODO: use new type declaration syntax for msg-type-spec
# https://docs.python.org/3/library/typing.html#type-aliases __msg_spec__: TypeAlias = MsgType
# https://docs.python.org/3/reference/simple_stmts.html#type
__msg_spec__: TypeAlias = Union[*__msg_types__]

View File

@ -57,7 +57,7 @@ from trio.lowlevel import (
from tractor.msg.pretty_struct import Struct from tractor.msg.pretty_struct import Struct
from tractor.msg.types import ( from tractor.msg.types import (
mk_msg_spec, mk_msg_spec,
Msg, MsgType,
) )
@ -87,12 +87,50 @@ class MsgCodec(Struct):
pld_spec: Union[Type[Struct]]|None pld_spec: Union[Type[Struct]]|None
@property
def pld_spec_str(self) -> str:
spec: Union[Type]|Type = self.pld_spec
# TODO: could also use match: instead?
if getattr(spec, '__args__', False):
# `typing.Union` case
return str(spec)
else:
return spec.__name__
# struct type unions # struct type unions
# https://jcristharif.com/msgspec/structs.html#tagged-unions # https://jcristharif.com/msgspec/structs.html#tagged-unions
@property @property
def msg_spec(self) -> Union[Type[Struct]]: def msg_spec(self) -> Union[Type[Struct]]:
return self._dec.type return self._dec.type
def msg_spec_items(
self,
msg: MsgType|None = None,
) -> dict[str, MsgType]|str:
msgt_table: dict[str, MsgType] = {
msgt: str(msgt)
for msgt in self.msg_spec.__args__
}
if msg:
msgt: MsgType = type(msg)
str_repr: str = msgt_table[msgt]
return {msgt: str_repr}
return msgt_table
# TODO: some way to make `pretty_struct.Struct` use this
# wrapped field over the `.msg_spec` one?
def pformat_msg_spec(
self,
msg: MsgType|None = None,
) -> str:
return '\n'.join(
self.msg_spec_items(msg=msg).values()
)
lib: ModuleType = msgspec lib: ModuleType = msgspec
# TODO: a sub-decoder system as well? # TODO: a sub-decoder system as well?
@ -108,7 +146,7 @@ class MsgCodec(Struct):
# OR # OR
# ) = { # ) = {
# # pre-seed decoders for std-py-type-set for use when # # pre-seed decoders for std-py-type-set for use when
# # `Msg.pld == None|Any`. # # `MsgType.pld == None|Any`.
# None: msgpack.Decoder(Any), # None: msgpack.Decoder(Any),
# Any: msgpack.Decoder(Any), # Any: msgpack.Decoder(Any),
# } # }
@ -303,7 +341,7 @@ def mk_codec(
# by `tag_field: str` value key? # by `tag_field: str` value key?
# payload_msg_specs: dict[ # payload_msg_specs: dict[
# str, # tag_field value as sub-decoder key # str, # tag_field value as sub-decoder key
# Union[Type[Struct]] # `Msg.pld` type spec # Union[Type[Struct]] # `MsgType.pld` type spec
# ]|None = None, # ]|None = None,
libname: str = 'msgspec', libname: str = 'msgspec',
@ -336,7 +374,7 @@ def mk_codec(
raise RuntimeError( raise RuntimeError(
f'If a payload spec is provided,\n' f'If a payload spec is provided,\n'
"the builtin SC-shuttle-protocol's msg set\n" "the builtin SC-shuttle-protocol's msg set\n"
f'(i.e. `{Msg}`) MUST be used!\n\n' f'(i.e. a `{MsgType}`) MUST be used!\n\n'
f'However both values were passed as => mk_codec(\n' f'However both values were passed as => mk_codec(\n'
f' ipc_msg_spec={ipc_msg_spec}`\n' f' ipc_msg_spec={ipc_msg_spec}`\n'
f' ipc_pld_spec={ipc_pld_spec}`\n)\n' f' ipc_pld_spec={ipc_pld_spec}`\n)\n'

View File

@ -31,6 +31,7 @@ from typing import (
Literal, Literal,
Type, Type,
TypeVar, TypeVar,
TypeAlias,
Union, Union,
) )
@ -400,16 +401,29 @@ class CancelAck(
pld: bool pld: bool
# TODO: unify this with `._exceptions.RemoteActorError`
# such that we can have a msg which is both raisable and
# IPC-wire ready?
# B~o
class Error( class Error(
Struct, Struct,
tag=True, tag=True,
tag_field='msg_type', tag_field='msg_type',
# TODO may omit defaults?
# https://jcristharif.com/msgspec/structs.html#omitting-default-values
# omit_defaults=True,
): ):
''' '''
A pkt that wraps `RemoteActorError`s for relay and raising. A pkt that wraps `RemoteActorError`s for relay and raising.
Fields are 1-to-1 meta-data as needed originally by Fields are 1-to-1 meta-data as needed originally by
`RemoteActorError.msgdata: dict`. `RemoteActorError.msgdata: dict` but now are defined here.
Note: this msg shuttles `ContextCancelled` and `StreamOverrun`
as well is used to rewrap any `MsgTypeError` for relay-reponse
to bad `Yield.pld` senders during an IPC ctx's streaming dialog
phase.
''' '''
src_uid: tuple[str, str] src_uid: tuple[str, str]
@ -428,6 +442,10 @@ class Error(
# `StreamOverrun` # `StreamOverrun`
sender: tuple[str, str]|None = None sender: tuple[str, str]|None = None
# for the `MsgTypeError` case where the receiver side
# decodes the underlying original `Msg`-subtype
_msg_dict: dict|None = None
# TODO: should be make a msg version of `ContextCancelled?` # TODO: should be make a msg version of `ContextCancelled?`
# and/or with a scope field or a full `ActorCancelled`? # and/or with a scope field or a full `ActorCancelled`?
@ -486,6 +504,11 @@ __msg_types__: list[Msg] = (
_payload_msgs _payload_msgs
) )
# TODO: use new type declaration syntax for msg-type-spec
# https://docs.python.org/3/library/typing.html#type-aliases
# https://docs.python.org/3/reference/simple_stmts.html#type
MsgType: TypeAlias = Union[*__msg_types__]
def mk_msg_spec( def mk_msg_spec(
payload_type_union: Union[Type] = Any, payload_type_union: Union[Type] = Any,