Compare commits
8 Commits
1c9056aced
...
59f3e4f3ef
Author | SHA1 | Date |
---|---|---|
|
59f3e4f3ef | |
|
7aec0fdfa4 | |
|
b5b619e52f | |
|
acf6f123a9 | |
|
27a3fdc473 | |
|
62db4f232d | |
|
8077487efc | |
|
7bd69187d2 |
File diff suppressed because it is too large
Load Diff
|
@ -432,9 +432,13 @@ class RemoteActorError(Exception):
|
||||||
Error type boxed by last actor IPC hop.
|
Error type boxed by last actor IPC hop.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
if self._boxed_type is None:
|
if (
|
||||||
|
self._boxed_type is None
|
||||||
|
and
|
||||||
|
(ipc_msg := self._ipc_msg)
|
||||||
|
):
|
||||||
self._boxed_type = get_err_type(
|
self._boxed_type = get_err_type(
|
||||||
self._ipc_msg.boxed_type_str
|
ipc_msg.boxed_type_str
|
||||||
)
|
)
|
||||||
|
|
||||||
return self._boxed_type
|
return self._boxed_type
|
||||||
|
@ -1143,6 +1147,8 @@ def unpack_error(
|
||||||
which is the responsibilitiy of the caller.
|
which is the responsibilitiy of the caller.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
# XXX, apparently we pass all sorts of msgs here?
|
||||||
|
# kinda odd but seems like maybe they shouldn't be?
|
||||||
if not isinstance(msg, Error):
|
if not isinstance(msg, Error):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ from ._codec import (
|
||||||
|
|
||||||
apply_codec as apply_codec,
|
apply_codec as apply_codec,
|
||||||
mk_codec as mk_codec,
|
mk_codec as mk_codec,
|
||||||
|
mk_dec as mk_dec,
|
||||||
MsgCodec as MsgCodec,
|
MsgCodec as MsgCodec,
|
||||||
MsgDec as MsgDec,
|
MsgDec as MsgDec,
|
||||||
current_codec as current_codec,
|
current_codec as current_codec,
|
||||||
|
|
|
@ -61,6 +61,7 @@ from tractor.msg.pretty_struct import Struct
|
||||||
from tractor.msg.types import (
|
from tractor.msg.types import (
|
||||||
mk_msg_spec,
|
mk_msg_spec,
|
||||||
MsgType,
|
MsgType,
|
||||||
|
PayloadMsg,
|
||||||
)
|
)
|
||||||
from tractor.log import get_logger
|
from tractor.log import get_logger
|
||||||
|
|
||||||
|
@ -80,6 +81,7 @@ class MsgDec(Struct):
|
||||||
|
|
||||||
'''
|
'''
|
||||||
_dec: msgpack.Decoder
|
_dec: msgpack.Decoder
|
||||||
|
# _ext_types_box: Struct|None = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def dec(self) -> msgpack.Decoder:
|
def dec(self) -> msgpack.Decoder:
|
||||||
|
@ -179,23 +181,126 @@ class MsgDec(Struct):
|
||||||
|
|
||||||
|
|
||||||
def mk_dec(
|
def mk_dec(
|
||||||
spec: Union[Type[Struct]]|Any = Any,
|
spec: Union[Type[Struct]]|Type|None,
|
||||||
|
|
||||||
|
# NOTE, required for ad-hoc type extensions to the underlying
|
||||||
|
# serialization proto (which is default `msgpack`),
|
||||||
|
# https://jcristharif.com/msgspec/extending.html#mapping-to-from-native-types
|
||||||
dec_hook: Callable|None = None,
|
dec_hook: Callable|None = None,
|
||||||
|
ext_types: list[Type]|None = None,
|
||||||
|
|
||||||
) -> MsgDec:
|
) -> MsgDec:
|
||||||
'''
|
'''
|
||||||
Create an IPC msg decoder, normally used as the
|
Create an IPC msg decoder, a slightly higher level wrapper around
|
||||||
`PayloadMsg.pld: PayloadT` field decoder inside a `PldRx`.
|
a `msgspec.msgpack.Decoder` which provides,
|
||||||
|
|
||||||
|
- easier introspection of the underlying type spec via
|
||||||
|
the `.spec` and `.spec_str` attrs,
|
||||||
|
- `.hook` access to the `Decoder.dec_hook()`,
|
||||||
|
- automatic custom extension-types decode support when
|
||||||
|
`dec_hook()` is provided such that any `PayloadMsg.pld` tagged
|
||||||
|
as a type from from `ext_types` (presuming the `MsgCodec.encode()` also used
|
||||||
|
a `.enc_hook()`) is processed and constructed by a `PldRx` implicitily.
|
||||||
|
|
||||||
|
NOTE, as mentioned a `MsgDec` is normally used for `PayloadMsg.pld: PayloadT` field
|
||||||
|
decoding inside an IPC-ctx-oriented `PldRx`.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
if (
|
||||||
|
spec is None
|
||||||
|
and
|
||||||
|
ext_types is None
|
||||||
|
):
|
||||||
|
raise TypeError(
|
||||||
|
f'MIssing type-`spec` for msg decoder!\n'
|
||||||
|
f'\n'
|
||||||
|
f'`spec=None` is **only** permitted is if custom extension types '
|
||||||
|
f'are provided via `ext_types`, meaning it must be non-`None`.\n'
|
||||||
|
f'\n'
|
||||||
|
f'In this case it is presumed that only the `ext_types`, '
|
||||||
|
f'which much be handled by a paired `dec_hook()`, '
|
||||||
|
f'will be permitted within the payload type-`spec`!\n'
|
||||||
|
f'\n'
|
||||||
|
f'spec = {spec!r}\n'
|
||||||
|
f'dec_hook = {dec_hook!r}\n'
|
||||||
|
f'ext_types = {ext_types!r}\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
if dec_hook:
|
||||||
|
if ext_types is None:
|
||||||
|
raise TypeError(
|
||||||
|
f'If extending the serializable types with a custom decode hook (`dec_hook()`), '
|
||||||
|
f'you must also provide the expected type set that the hook will handle '
|
||||||
|
f'via a `ext_types: Union[Type]|None = None` argument!\n'
|
||||||
|
f'\n'
|
||||||
|
f'dec_hook = {dec_hook!r}\n'
|
||||||
|
f'ext_types = {ext_types!r}\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
# XXX, i *thought* we would require a boxing struct as per docs,
|
||||||
|
# https://jcristharif.com/msgspec/extending.html#mapping-to-from-native-types
|
||||||
|
# |_ see comment,
|
||||||
|
# > Note that typed deserialization is required for
|
||||||
|
# > successful roundtripping here, so we pass `MyMessage` to
|
||||||
|
# > `Decoder`.
|
||||||
|
#
|
||||||
|
# BUT, turns out as long as you spec a union with `Raw` it
|
||||||
|
# will work? kk B)
|
||||||
|
#
|
||||||
|
# maybe_box_struct = mk_boxed_ext_struct(ext_types)
|
||||||
|
spec = Raw | Union[*ext_types]
|
||||||
|
|
||||||
return MsgDec(
|
return MsgDec(
|
||||||
_dec=msgpack.Decoder(
|
_dec=msgpack.Decoder(
|
||||||
type=spec, # like `MsgType[Any]`
|
type=spec, # like `MsgType[Any]`
|
||||||
dec_hook=dec_hook,
|
dec_hook=dec_hook,
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO? remove since didn't end up needing this?
|
||||||
|
def mk_boxed_ext_struct(
|
||||||
|
ext_types: list[Type],
|
||||||
|
) -> Struct:
|
||||||
|
# NOTE, originally was to wrap non-msgpack-supported "extension
|
||||||
|
# types" in a field-typed boxing struct, see notes around the
|
||||||
|
# `dec_hook()` branch in `mk_dec()`.
|
||||||
|
ext_types_union = Union[*ext_types]
|
||||||
|
repr_ext_types_union: str = (
|
||||||
|
str(ext_types_union)
|
||||||
|
or
|
||||||
|
"|".join(ext_types)
|
||||||
|
)
|
||||||
|
BoxedExtType = msgspec.defstruct(
|
||||||
|
f'BoxedExts[{repr_ext_types_union}]',
|
||||||
|
fields=[
|
||||||
|
('boxed', ext_types_union),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
return BoxedExtType
|
||||||
|
|
||||||
|
|
||||||
|
def unpack_spec_types(
|
||||||
|
spec: Union[Type]|Type,
|
||||||
|
) -> set[Type]:
|
||||||
|
'''
|
||||||
|
Given an input type-`spec`, either a lone type
|
||||||
|
or a `Union` of types (like `str|int|MyThing`),
|
||||||
|
return a set of individual types.
|
||||||
|
|
||||||
|
When `spec` is not a type-union returns `{spec,}`.
|
||||||
|
|
||||||
|
'''
|
||||||
|
spec_subtypes: set[Union[Type]] = set(
|
||||||
|
getattr(
|
||||||
|
spec,
|
||||||
|
'__args__',
|
||||||
|
{spec,},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return spec_subtypes
|
||||||
|
|
||||||
|
|
||||||
def mk_msgspec_table(
|
def mk_msgspec_table(
|
||||||
dec: msgpack.Decoder,
|
dec: msgpack.Decoder,
|
||||||
msg: MsgType|None = None,
|
msg: MsgType|None = None,
|
||||||
|
@ -273,6 +378,8 @@ class MsgCodec(Struct):
|
||||||
_dec: msgpack.Decoder
|
_dec: msgpack.Decoder
|
||||||
_pld_spec: Type[Struct]|Raw|Any
|
_pld_spec: Type[Struct]|Raw|Any
|
||||||
|
|
||||||
|
# _ext_types_box: Struct|None = None
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
speclines: str = textwrap.indent(
|
speclines: str = textwrap.indent(
|
||||||
pformat_msgspec(codec=self),
|
pformat_msgspec(codec=self),
|
||||||
|
@ -339,12 +446,15 @@ class MsgCodec(Struct):
|
||||||
|
|
||||||
def encode(
|
def encode(
|
||||||
self,
|
self,
|
||||||
py_obj: Any,
|
py_obj: Any|PayloadMsg,
|
||||||
|
|
||||||
use_buf: bool = False,
|
use_buf: bool = False,
|
||||||
# ^-XXX-^ uhh why am i getting this?
|
# ^-XXX-^ uhh why am i getting this?
|
||||||
# |_BufferError: Existing exports of data: object cannot be re-sized
|
# |_BufferError: Existing exports of data: object cannot be re-sized
|
||||||
|
|
||||||
|
as_ext_type: bool = False,
|
||||||
|
hide_tb: bool = True,
|
||||||
|
|
||||||
) -> bytes:
|
) -> bytes:
|
||||||
'''
|
'''
|
||||||
Encode input python objects to `msgpack` bytes for
|
Encode input python objects to `msgpack` bytes for
|
||||||
|
@ -354,11 +464,46 @@ class MsgCodec(Struct):
|
||||||
https://jcristharif.com/msgspec/perf-tips.html#reusing-an-output-buffer
|
https://jcristharif.com/msgspec/perf-tips.html#reusing-an-output-buffer
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
__tracebackhide__: bool = hide_tb
|
||||||
if use_buf:
|
if use_buf:
|
||||||
self._enc.encode_into(py_obj, self._buf)
|
self._enc.encode_into(py_obj, self._buf)
|
||||||
return self._buf
|
return self._buf
|
||||||
else:
|
|
||||||
return self._enc.encode(py_obj)
|
return self._enc.encode(py_obj)
|
||||||
|
# try:
|
||||||
|
# return self._enc.encode(py_obj)
|
||||||
|
# except TypeError as typerr:
|
||||||
|
# typerr.add_note(
|
||||||
|
# '|_src error from `msgspec`'
|
||||||
|
# # f'|_{self._enc.encode!r}'
|
||||||
|
# )
|
||||||
|
# raise typerr
|
||||||
|
|
||||||
|
# TODO! REMOVE once i'm confident we won't ever need it!
|
||||||
|
#
|
||||||
|
# box: Struct = self._ext_types_box
|
||||||
|
# if (
|
||||||
|
# as_ext_type
|
||||||
|
# or
|
||||||
|
# (
|
||||||
|
# # XXX NOTE, auto-detect if the input type
|
||||||
|
# box
|
||||||
|
# and
|
||||||
|
# (ext_types := unpack_spec_types(
|
||||||
|
# spec=box.__annotations__['boxed'])
|
||||||
|
# )
|
||||||
|
# )
|
||||||
|
# ):
|
||||||
|
# match py_obj:
|
||||||
|
# # case PayloadMsg(pld=pld) if (
|
||||||
|
# # type(pld) in ext_types
|
||||||
|
# # ):
|
||||||
|
# # py_obj.pld = box(boxed=py_obj)
|
||||||
|
# # breakpoint()
|
||||||
|
# case _ if (
|
||||||
|
# type(py_obj) in ext_types
|
||||||
|
# ):
|
||||||
|
# py_obj = box(boxed=py_obj)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def dec(self) -> msgpack.Decoder:
|
def dec(self) -> msgpack.Decoder:
|
||||||
|
@ -378,21 +523,30 @@ class MsgCodec(Struct):
|
||||||
return self._dec.decode(msg)
|
return self._dec.decode(msg)
|
||||||
|
|
||||||
|
|
||||||
# [x] TODO: a sub-decoder system as well? => No!
|
# ?TODO? time to remove this finally?
|
||||||
|
#
|
||||||
|
# -[x] TODO: a sub-decoder system as well?
|
||||||
|
# => No! already re-architected to include a "payload-receiver"
|
||||||
|
# now found in `._ops`.
|
||||||
#
|
#
|
||||||
# -[x] do we still want to try and support the sub-decoder with
|
# -[x] do we still want to try and support the sub-decoder with
|
||||||
# `.Raw` technique in the case that the `Generic` approach gives
|
# `.Raw` technique in the case that the `Generic` approach gives
|
||||||
# future grief?
|
# future grief?
|
||||||
# => NO, since we went with the `PldRx` approach instead B)
|
# => well YES but NO, since we went with the `PldRx` approach
|
||||||
|
# instead!
|
||||||
#
|
#
|
||||||
# IF however you want to see the code that was staged for this
|
# IF however you want to see the code that was staged for this
|
||||||
# from wayyy back, see the pure removal commit.
|
# from wayyy back, see the pure removal commit.
|
||||||
|
|
||||||
|
|
||||||
def mk_codec(
|
def mk_codec(
|
||||||
# struct type unions set for `Decoder`
|
ipc_pld_spec: Union[Type[Struct]]|Any|Raw = Raw,
|
||||||
# https://jcristharif.com/msgspec/structs.html#tagged-unions
|
# tagged-struct-types-union set for `Decoder`ing of payloads, as
|
||||||
ipc_pld_spec: Union[Type[Struct]]|Any = Any,
|
# per https://jcristharif.com/msgspec/structs.html#tagged-unions.
|
||||||
|
# NOTE that the default `Raw` here **is very intentional** since
|
||||||
|
# the `PldRx._pld_dec: MsgDec` is responsible for per ipc-ctx-task
|
||||||
|
# decoding of msg-specs defined by the user as part of **their**
|
||||||
|
# `tractor` "app's" type-limited IPC msg-spec.
|
||||||
|
|
||||||
# TODO: offering a per-msg(-field) type-spec such that
|
# TODO: offering a per-msg(-field) type-spec such that
|
||||||
# the fields can be dynamically NOT decoded and left as `Raw`
|
# the fields can be dynamically NOT decoded and left as `Raw`
|
||||||
|
@ -405,13 +559,18 @@ def mk_codec(
|
||||||
|
|
||||||
libname: str = 'msgspec',
|
libname: str = 'msgspec',
|
||||||
|
|
||||||
# proxy as `Struct(**kwargs)` for ad-hoc type extensions
|
# settings for encoding-to-send extension-types,
|
||||||
# https://jcristharif.com/msgspec/extending.html#mapping-to-from-native-types
|
# https://jcristharif.com/msgspec/extending.html#mapping-to-from-native-types
|
||||||
# ------ - ------
|
# dec_hook: Callable|None = None,
|
||||||
dec_hook: Callable|None = None,
|
|
||||||
enc_hook: Callable|None = None,
|
enc_hook: Callable|None = None,
|
||||||
# ------ - ------
|
ext_types: list[Type]|None = None,
|
||||||
|
|
||||||
|
# optionally provided msg-decoder from which we pull its,
|
||||||
|
# |_.dec_hook()
|
||||||
|
# |_.type
|
||||||
|
ext_dec: MsgDec|None = None
|
||||||
#
|
#
|
||||||
|
# ?TODO? other params we might want to support
|
||||||
# Encoder:
|
# Encoder:
|
||||||
# write_buffer_size=write_buffer_size,
|
# write_buffer_size=write_buffer_size,
|
||||||
#
|
#
|
||||||
|
@ -425,26 +584,44 @@ def mk_codec(
|
||||||
`msgspec` ;).
|
`msgspec` ;).
|
||||||
|
|
||||||
'''
|
'''
|
||||||
# (manually) generate a msg-payload-spec for all relevant
|
pld_spec = ipc_pld_spec
|
||||||
# god-boxing-msg subtypes, parameterizing the `PayloadMsg.pld: PayloadT`
|
if enc_hook:
|
||||||
# for the decoder such that all sub-type msgs in our SCIPP
|
if not ext_types:
|
||||||
# will automatically decode to a type-"limited" payload (`Struct`)
|
raise TypeError(
|
||||||
# object (set).
|
f'If extending the serializable types with a custom encode hook (`enc_hook()`), '
|
||||||
|
f'you must also provide the expected type set that the hook will handle '
|
||||||
|
f'via a `ext_types: Union[Type]|None = None` argument!\n'
|
||||||
|
f'\n'
|
||||||
|
f'enc_hook = {enc_hook!r}\n'
|
||||||
|
f'ext_types = {ext_types!r}\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
dec_hook: Callable|None = None
|
||||||
|
if ext_dec:
|
||||||
|
dec: msgspec.Decoder = ext_dec.dec
|
||||||
|
dec_hook = dec.dec_hook
|
||||||
|
pld_spec |= dec.type
|
||||||
|
if ext_types:
|
||||||
|
pld_spec |= Union[*ext_types]
|
||||||
|
|
||||||
|
# (manually) generate a msg-spec (how appropes) for all relevant
|
||||||
|
# payload-boxing-struct-msg-types, parameterizing the
|
||||||
|
# `PayloadMsg.pld: PayloadT` for the decoder such that all msgs
|
||||||
|
# in our SC-RPC-protocol will automatically decode to
|
||||||
|
# a type-"limited" payload (`Struct`) object (set).
|
||||||
(
|
(
|
||||||
ipc_msg_spec,
|
ipc_msg_spec,
|
||||||
msg_types,
|
msg_types,
|
||||||
) = mk_msg_spec(
|
) = mk_msg_spec(
|
||||||
payload_type_union=ipc_pld_spec,
|
payload_type_union=pld_spec,
|
||||||
)
|
)
|
||||||
assert len(ipc_msg_spec.__args__) == len(msg_types)
|
|
||||||
assert ipc_msg_spec
|
|
||||||
|
|
||||||
# TODO: use this shim instead?
|
msg_spec_types: set[Type] = unpack_spec_types(ipc_msg_spec)
|
||||||
# bc.. unification, err somethin?
|
assert (
|
||||||
# dec: MsgDec = mk_dec(
|
len(ipc_msg_spec.__args__) == len(msg_types)
|
||||||
# spec=ipc_msg_spec,
|
and
|
||||||
# dec_hook=dec_hook,
|
len(msg_spec_types) == len(msg_types)
|
||||||
# )
|
)
|
||||||
|
|
||||||
dec = msgpack.Decoder(
|
dec = msgpack.Decoder(
|
||||||
type=ipc_msg_spec,
|
type=ipc_msg_spec,
|
||||||
|
@ -453,22 +630,29 @@ def mk_codec(
|
||||||
enc = msgpack.Encoder(
|
enc = msgpack.Encoder(
|
||||||
enc_hook=enc_hook,
|
enc_hook=enc_hook,
|
||||||
)
|
)
|
||||||
|
|
||||||
codec = MsgCodec(
|
codec = MsgCodec(
|
||||||
_enc=enc,
|
_enc=enc,
|
||||||
_dec=dec,
|
_dec=dec,
|
||||||
_pld_spec=ipc_pld_spec,
|
_pld_spec=pld_spec,
|
||||||
)
|
)
|
||||||
|
|
||||||
# sanity on expected backend support
|
# sanity on expected backend support
|
||||||
assert codec.lib.__name__ == libname
|
assert codec.lib.__name__ == libname
|
||||||
|
|
||||||
return codec
|
return codec
|
||||||
|
|
||||||
|
|
||||||
# instance of the default `msgspec.msgpack` codec settings, i.e.
|
# instance of the default `msgspec.msgpack` codec settings, i.e.
|
||||||
# no custom structs, hooks or other special types.
|
# no custom structs, hooks or other special types.
|
||||||
_def_msgspec_codec: MsgCodec = mk_codec(ipc_pld_spec=Any)
|
#
|
||||||
|
# XXX NOTE XXX, this will break our `Context.start()` call!
|
||||||
|
#
|
||||||
|
# * by default we roundtrip the started pld-`value` and if you apply
|
||||||
|
# this codec (globally anyway with `apply_codec()`) then the
|
||||||
|
# `roundtripped` value will include a non-`.pld: Raw` which will
|
||||||
|
# then type-error on the consequent `._ops.validte_payload_msg()`..
|
||||||
|
#
|
||||||
|
_def_msgspec_codec: MsgCodec = mk_codec(
|
||||||
|
ipc_pld_spec=Any,
|
||||||
|
)
|
||||||
|
|
||||||
# The built-in IPC `Msg` spec.
|
# The built-in IPC `Msg` spec.
|
||||||
# Our composing "shuttle" protocol which allows `tractor`-app code
|
# Our composing "shuttle" protocol which allows `tractor`-app code
|
||||||
|
@ -476,13 +660,13 @@ _def_msgspec_codec: MsgCodec = mk_codec(ipc_pld_spec=Any)
|
||||||
# https://jcristharif.com/msgspec/supported-types.html
|
# https://jcristharif.com/msgspec/supported-types.html
|
||||||
#
|
#
|
||||||
_def_tractor_codec: MsgCodec = mk_codec(
|
_def_tractor_codec: MsgCodec = mk_codec(
|
||||||
# TODO: use this for debug mode locking prot?
|
ipc_pld_spec=Raw, # XXX should be default righ!?
|
||||||
# ipc_pld_spec=Any,
|
|
||||||
ipc_pld_spec=Raw,
|
|
||||||
)
|
)
|
||||||
# TODO: IDEALLY provides for per-`trio.Task` specificity of the
|
|
||||||
|
# -[x] TODO, IDEALLY provides for per-`trio.Task` specificity of the
|
||||||
# IPC msging codec used by the transport layer when doing
|
# IPC msging codec used by the transport layer when doing
|
||||||
# `Channel.send()/.recv()` of wire data.
|
# `Channel.send()/.recv()` of wire data.
|
||||||
|
# => impled as our `PldRx` which is `Context` scoped B)
|
||||||
|
|
||||||
# ContextVar-TODO: DIDN'T WORK, kept resetting in every new task to default!?
|
# ContextVar-TODO: DIDN'T WORK, kept resetting in every new task to default!?
|
||||||
# _ctxvar_MsgCodec: ContextVar[MsgCodec] = ContextVar(
|
# _ctxvar_MsgCodec: ContextVar[MsgCodec] = ContextVar(
|
||||||
|
@ -559,17 +743,6 @@ def apply_codec(
|
||||||
)
|
)
|
||||||
token: Token = var.set(codec)
|
token: Token = var.set(codec)
|
||||||
|
|
||||||
# ?TODO? for TreeVar approach which copies from the
|
|
||||||
# cancel-scope of the prior value, NOT the prior task
|
|
||||||
# See the docs:
|
|
||||||
# - https://tricycle.readthedocs.io/en/latest/reference.html#tree-variables
|
|
||||||
# - https://github.com/oremanj/tricycle/blob/master/tricycle/_tests/test_tree_var.py
|
|
||||||
# ^- see docs for @cm `.being()` API
|
|
||||||
# with _ctxvar_MsgCodec.being(codec):
|
|
||||||
# new = _ctxvar_MsgCodec.get()
|
|
||||||
# assert new is codec
|
|
||||||
# yield codec
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
yield var.get()
|
yield var.get()
|
||||||
finally:
|
finally:
|
||||||
|
@ -580,6 +753,19 @@ def apply_codec(
|
||||||
)
|
)
|
||||||
assert var.get() is orig
|
assert var.get() is orig
|
||||||
|
|
||||||
|
# ?TODO? for TreeVar approach which copies from the
|
||||||
|
# cancel-scope of the prior value, NOT the prior task
|
||||||
|
#
|
||||||
|
# See the docs:
|
||||||
|
# - https://tricycle.readthedocs.io/en/latest/reference.html#tree-variables
|
||||||
|
# - https://github.com/oremanj/tricycle/blob/master/tricycle/_tests/test_tree_var.py
|
||||||
|
# ^- see docs for @cm `.being()` API
|
||||||
|
#
|
||||||
|
# with _ctxvar_MsgCodec.being(codec):
|
||||||
|
# new = _ctxvar_MsgCodec.get()
|
||||||
|
# assert new is codec
|
||||||
|
# yield codec
|
||||||
|
|
||||||
|
|
||||||
def current_codec() -> MsgCodec:
|
def current_codec() -> MsgCodec:
|
||||||
'''
|
'''
|
||||||
|
@ -599,6 +785,7 @@ def limit_msg_spec(
|
||||||
# -> related to the `MsgCodec._payload_decs` stuff above..
|
# -> related to the `MsgCodec._payload_decs` stuff above..
|
||||||
# tagged_structs: list[Struct]|None = None,
|
# tagged_structs: list[Struct]|None = None,
|
||||||
|
|
||||||
|
hide_tb: bool = True,
|
||||||
**codec_kwargs,
|
**codec_kwargs,
|
||||||
|
|
||||||
) -> MsgCodec:
|
) -> MsgCodec:
|
||||||
|
@ -609,7 +796,7 @@ def limit_msg_spec(
|
||||||
for all IPC contexts in use by the current `trio.Task`.
|
for all IPC contexts in use by the current `trio.Task`.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
__tracebackhide__: bool = True
|
__tracebackhide__: bool = hide_tb
|
||||||
curr_codec: MsgCodec = current_codec()
|
curr_codec: MsgCodec = current_codec()
|
||||||
msgspec_codec: MsgCodec = mk_codec(
|
msgspec_codec: MsgCodec = mk_codec(
|
||||||
ipc_pld_spec=payload_spec,
|
ipc_pld_spec=payload_spec,
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
# tractor: structured concurrent "actors".
|
||||||
|
# Copyright 2018-eternity Tyler Goodlet.
|
||||||
|
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
'''
|
||||||
|
Type-extension-utils for codec-ing (python) objects not
|
||||||
|
covered by the `msgspec.msgpack` protocol.
|
||||||
|
|
||||||
|
See the various API docs from `msgspec`.
|
||||||
|
|
||||||
|
extending from native types,
|
||||||
|
- https://jcristharif.com/msgspec/extending.html#mapping-to-from-native-types
|
||||||
|
|
||||||
|
converters,
|
||||||
|
- https://jcristharif.com/msgspec/converters.html
|
||||||
|
- https://jcristharif.com/msgspec/api.html#msgspec.convert
|
||||||
|
|
||||||
|
`Raw` fields,
|
||||||
|
- https://jcristharif.com/msgspec/api.html#raw
|
||||||
|
- support for `.convert()` and `Raw`,
|
||||||
|
|_ https://jcristharif.com/msgspec/changelog.html
|
||||||
|
|
||||||
|
'''
|
||||||
|
from types import (
|
||||||
|
ModuleType,
|
||||||
|
)
|
||||||
|
import typing
|
||||||
|
from typing import (
|
||||||
|
Type,
|
||||||
|
Union,
|
||||||
|
)
|
||||||
|
|
||||||
|
def dec_type_union(
|
||||||
|
type_names: list[str],
|
||||||
|
mods: list[ModuleType] = []
|
||||||
|
) -> Type|Union[Type]:
|
||||||
|
'''
|
||||||
|
Look up types by name, compile into a list and then create and
|
||||||
|
return a `typing.Union` from the full set.
|
||||||
|
|
||||||
|
'''
|
||||||
|
# import importlib
|
||||||
|
types: list[Type] = []
|
||||||
|
for type_name in type_names:
|
||||||
|
for mod in [
|
||||||
|
typing,
|
||||||
|
# importlib.import_module(__name__),
|
||||||
|
] + mods:
|
||||||
|
if type_ref := getattr(
|
||||||
|
mod,
|
||||||
|
type_name,
|
||||||
|
False,
|
||||||
|
):
|
||||||
|
types.append(type_ref)
|
||||||
|
|
||||||
|
# special case handling only..
|
||||||
|
# ipc_pld_spec: Union[Type] = eval(
|
||||||
|
# pld_spec_str,
|
||||||
|
# {}, # globals
|
||||||
|
# {'typing': typing}, # locals
|
||||||
|
# )
|
||||||
|
|
||||||
|
return Union[*types]
|
||||||
|
|
||||||
|
|
||||||
|
def enc_type_union(
|
||||||
|
union_or_type: Union[Type]|Type,
|
||||||
|
) -> list[str]:
|
||||||
|
'''
|
||||||
|
Encode a type-union or single type to a list of type-name-strings
|
||||||
|
ready for IPC interchange.
|
||||||
|
|
||||||
|
'''
|
||||||
|
type_strs: list[str] = []
|
||||||
|
for typ in getattr(
|
||||||
|
union_or_type,
|
||||||
|
'__args__',
|
||||||
|
{union_or_type,},
|
||||||
|
):
|
||||||
|
type_strs.append(typ.__qualname__)
|
||||||
|
|
||||||
|
return type_strs
|
|
@ -50,7 +50,9 @@ from tractor._exceptions import (
|
||||||
_mk_recv_mte,
|
_mk_recv_mte,
|
||||||
pack_error,
|
pack_error,
|
||||||
)
|
)
|
||||||
from tractor._state import current_ipc_ctx
|
from tractor._state import (
|
||||||
|
current_ipc_ctx,
|
||||||
|
)
|
||||||
from ._codec import (
|
from ._codec import (
|
||||||
mk_dec,
|
mk_dec,
|
||||||
MsgDec,
|
MsgDec,
|
||||||
|
@ -78,7 +80,7 @@ if TYPE_CHECKING:
|
||||||
log = get_logger(__name__)
|
log = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
_def_any_pldec: MsgDec[Any] = mk_dec()
|
_def_any_pldec: MsgDec[Any] = mk_dec(spec=Any)
|
||||||
|
|
||||||
|
|
||||||
class PldRx(Struct):
|
class PldRx(Struct):
|
||||||
|
@ -148,6 +150,10 @@ class PldRx(Struct):
|
||||||
exit.
|
exit.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
# TODO, ensure we pull the current `MsgCodec`'s custom
|
||||||
|
# dec/enc_hook settings as well ?
|
||||||
|
# -[ ] see `._codec.mk_codec()` inputs
|
||||||
|
#
|
||||||
orig_dec: MsgDec = self._pld_dec
|
orig_dec: MsgDec = self._pld_dec
|
||||||
limit_dec: MsgDec = mk_dec(
|
limit_dec: MsgDec = mk_dec(
|
||||||
spec=spec,
|
spec=spec,
|
||||||
|
@ -455,11 +461,16 @@ def limit_plds(
|
||||||
|
|
||||||
'''
|
'''
|
||||||
__tracebackhide__: bool = True
|
__tracebackhide__: bool = True
|
||||||
|
curr_ctx: Context|None = current_ipc_ctx()
|
||||||
|
if curr_ctx is None:
|
||||||
|
raise RuntimeError(
|
||||||
|
'No IPC `Context` is active !?\n'
|
||||||
|
'Did you open `limit_plds()` from outside '
|
||||||
|
'a `Portal.open_context()` scope-block?'
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
curr_ctx: Context = current_ipc_ctx()
|
|
||||||
rx: PldRx = curr_ctx._pld_rx
|
rx: PldRx = curr_ctx._pld_rx
|
||||||
orig_pldec: MsgDec = rx.pld_dec
|
orig_pldec: MsgDec = rx.pld_dec
|
||||||
|
|
||||||
with rx.limit_plds(
|
with rx.limit_plds(
|
||||||
spec=spec,
|
spec=spec,
|
||||||
**dec_kwargs,
|
**dec_kwargs,
|
||||||
|
@ -469,6 +480,11 @@ def limit_plds(
|
||||||
f'{pldec}\n'
|
f'{pldec}\n'
|
||||||
)
|
)
|
||||||
yield pldec
|
yield pldec
|
||||||
|
|
||||||
|
except BaseException:
|
||||||
|
__tracebackhide__: bool = False
|
||||||
|
raise
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
log.runtime(
|
log.runtime(
|
||||||
'Reverted to previous payload-decoder\n\n'
|
'Reverted to previous payload-decoder\n\n'
|
||||||
|
|
|
@ -599,15 +599,15 @@ def mk_msg_spec(
|
||||||
Msg[payload_type_union],
|
Msg[payload_type_union],
|
||||||
Generic[PayloadT],
|
Generic[PayloadT],
|
||||||
)
|
)
|
||||||
defstruct_bases: tuple = (
|
# defstruct_bases: tuple = (
|
||||||
Msg, # [payload_type_union],
|
# Msg, # [payload_type_union],
|
||||||
# Generic[PayloadT],
|
# # Generic[PayloadT],
|
||||||
# ^-XXX-^: not allowed? lul..
|
# # ^-XXX-^: not allowed? lul..
|
||||||
)
|
# )
|
||||||
ipc_msg_types: list[Msg] = []
|
ipc_msg_types: list[Msg] = []
|
||||||
|
|
||||||
idx_msg_types: list[Msg] = []
|
idx_msg_types: list[Msg] = []
|
||||||
defs_msg_types: list[Msg] = []
|
# defs_msg_types: list[Msg] = []
|
||||||
nc_msg_types: list[Msg] = []
|
nc_msg_types: list[Msg] = []
|
||||||
|
|
||||||
for msgtype in __msg_types__:
|
for msgtype in __msg_types__:
|
||||||
|
@ -625,7 +625,7 @@ def mk_msg_spec(
|
||||||
# TODO: wait why do we need the dynamic version here?
|
# TODO: wait why do we need the dynamic version here?
|
||||||
# XXX ANSWER XXX -> BC INHERITANCE.. don't work w generics..
|
# XXX ANSWER XXX -> BC INHERITANCE.. don't work w generics..
|
||||||
#
|
#
|
||||||
# NOTE previously bc msgtypes WERE NOT inheritting
|
# NOTE previously bc msgtypes WERE NOT inheriting
|
||||||
# directly the `Generic[PayloadT]` type, the manual method
|
# directly the `Generic[PayloadT]` type, the manual method
|
||||||
# of generic-paraming with `.__class_getitem__()` wasn't
|
# of generic-paraming with `.__class_getitem__()` wasn't
|
||||||
# working..
|
# working..
|
||||||
|
@ -662,38 +662,35 @@ def mk_msg_spec(
|
||||||
|
|
||||||
# with `msgspec.structs.defstruct`
|
# with `msgspec.structs.defstruct`
|
||||||
# XXX ALSO DOESN'T WORK
|
# XXX ALSO DOESN'T WORK
|
||||||
defstruct_msgtype = defstruct(
|
# defstruct_msgtype = defstruct(
|
||||||
name=msgtype.__name__,
|
# name=msgtype.__name__,
|
||||||
fields=[
|
# fields=[
|
||||||
('cid', str),
|
# ('cid', str),
|
||||||
|
|
||||||
# XXX doesn't seem to work..
|
# # XXX doesn't seem to work..
|
||||||
# ('pld', PayloadT),
|
# # ('pld', PayloadT),
|
||||||
|
|
||||||
('pld', payload_type_union),
|
|
||||||
],
|
|
||||||
bases=defstruct_bases,
|
|
||||||
)
|
|
||||||
defs_msg_types.append(defstruct_msgtype)
|
|
||||||
|
|
||||||
|
# ('pld', payload_type_union),
|
||||||
|
# ],
|
||||||
|
# bases=defstruct_bases,
|
||||||
|
# )
|
||||||
|
# defs_msg_types.append(defstruct_msgtype)
|
||||||
# assert index_paramed_msg_type == manual_paramed_msg_subtype
|
# assert index_paramed_msg_type == manual_paramed_msg_subtype
|
||||||
|
|
||||||
# paramed_msg_type = manual_paramed_msg_subtype
|
# paramed_msg_type = manual_paramed_msg_subtype
|
||||||
|
|
||||||
# ipc_payload_msgs_type_union |= index_paramed_msg_type
|
# ipc_payload_msgs_type_union |= index_paramed_msg_type
|
||||||
|
|
||||||
idx_spec: Union[Type[Msg]] = Union[*idx_msg_types]
|
idx_spec: Union[Type[Msg]] = Union[*idx_msg_types]
|
||||||
def_spec: Union[Type[Msg]] = Union[*defs_msg_types]
|
# def_spec: Union[Type[Msg]] = Union[*defs_msg_types]
|
||||||
nc_spec: Union[Type[Msg]] = Union[*nc_msg_types]
|
nc_spec: Union[Type[Msg]] = Union[*nc_msg_types]
|
||||||
|
|
||||||
specs: dict[str, Union[Type[Msg]]] = {
|
specs: dict[str, Union[Type[Msg]]] = {
|
||||||
'indexed_generics': idx_spec,
|
'indexed_generics': idx_spec,
|
||||||
'defstruct': def_spec,
|
# 'defstruct': def_spec,
|
||||||
'types_new_class': nc_spec,
|
'types_new_class': nc_spec,
|
||||||
}
|
}
|
||||||
msgtypes_table: dict[str, list[Msg]] = {
|
msgtypes_table: dict[str, list[Msg]] = {
|
||||||
'indexed_generics': idx_msg_types,
|
'indexed_generics': idx_msg_types,
|
||||||
'defstruct': defs_msg_types,
|
# 'defstruct': defs_msg_types,
|
||||||
'types_new_class': nc_msg_types,
|
'types_new_class': nc_msg_types,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue