Compare commits

..

No commits in common. "b761524a850d41ac546c1cca45bea2411c8488cc" and "81c33bf550ce0d8c4bb37a7baf3ff90a7b8c6a10" have entirely different histories.

8 changed files with 282 additions and 284 deletions

View File

@ -454,7 +454,7 @@ async def send_back_values(
with ( with (
maybe_apply_codec(nsp_codec) as codec, maybe_apply_codec(nsp_codec) as codec,
limit_plds( limit_plds(
spec=rent_pld_spec, rent_pld_spec,
dec_hook=dec_nsp if add_hooks else None, dec_hook=dec_nsp if add_hooks else None,
ext_types=[NamespacePath] if add_hooks else None, ext_types=[NamespacePath] if add_hooks else None,
) as pld_dec, ) as pld_dec,
@ -665,9 +665,7 @@ def test_ext_types_over_ipc(
expect_codec=nsp_codec, expect_codec=nsp_codec,
enter_value=codec, enter_value=codec,
) )
rent_pld_spec_type_strs: list[str] = _exts.enc_type_union( rent_pld_spec_type_strs: list[str] = _exts.enc_type_union(pld_spec)
pld_spec
)
# XXX should raise an mte (`MsgTypeError`) # XXX should raise an mte (`MsgTypeError`)
# when `add_hooks == False` bc the input # when `add_hooks == False` bc the input
@ -697,7 +695,7 @@ def test_ext_types_over_ipc(
limit_plds( limit_plds(
pld_spec, pld_spec,
dec_hook=dec_nsp if add_hooks else None, dec_hook=dec_nsp if add_hooks else None,
ext_types=[NamespacePath] if add_hooks else None, ext_types=[NamespacePath] if add_hooks else None,
) as pld_dec, ) as pld_dec,
): ):
ctx_pld_dec: MsgDec = ctx._pld_rx._pld_dec ctx_pld_dec: MsgDec = ctx._pld_rx._pld_dec
@ -745,10 +743,204 @@ def test_ext_types_over_ipc(
assert exc.boxed_type is TypeError assert exc.boxed_type is TypeError
# TODO: further SC-msg-specific verification that the overridden # def chk_pld_type(
# subtypes DO NOT have modified type-annots from original! # payload_spec: Type[Struct]|Any,
# 'Start', .pld: FuncSpec # pld: Any,
# 'StartAck', .pld: IpcCtxSpec
# 'Stop', .pld: UNSEt # expect_roundtrip: bool|None = None,
# 'Error', .pld: ErrorData
# def test_per_msg_payload_spec_limits(): # ) -> bool:
# pld_val_type: Type = type(pld)
# # TODO: verify that the overridden subtypes
# # DO NOT have modified type-annots from original!
# # 'Start', .pld: FuncSpec
# # 'StartAck', .pld: IpcCtxSpec
# # 'Stop', .pld: UNSEt
# # 'Error', .pld: ErrorData
# codec: MsgCodec = mk_codec(
# # NOTE: this ONLY accepts `PayloadMsg.pld` fields of a specified
# # type union.
# ipc_pld_spec=payload_spec,
# )
# # make a one-off dec to compare with our `MsgCodec` instance
# # which does the below `mk_msg_spec()` call internally
# ipc_msg_spec: Union[Type[Struct]]
# msg_types: list[PayloadMsg[payload_spec]]
# (
# ipc_msg_spec,
# msg_types,
# ) = mk_msg_spec(
# payload_type_union=payload_spec,
# )
# _enc = msgpack.Encoder()
# _dec = msgpack.Decoder(
# type=ipc_msg_spec or Any, # like `PayloadMsg[Any]`
# )
# assert (
# payload_spec
# ==
# codec.pld_spec
# )
# # assert codec.dec == dec
# #
# # ^-XXX-^ not sure why these aren't "equal" but when cast
# # to `str` they seem to match ?? .. kk
# assert (
# str(ipc_msg_spec)
# ==
# str(codec.msg_spec)
# ==
# str(_dec.type)
# ==
# str(codec.dec.type)
# )
# # verify the boxed-type for all variable payload-type msgs.
# if not msg_types:
# breakpoint()
# roundtrip: bool|None = None
# pld_spec_msg_names: list[str] = [
# td.__name__ for td in _payload_msgs
# ]
# for typedef in msg_types:
# skip_runtime_msg: bool = typedef.__name__ not in pld_spec_msg_names
# if skip_runtime_msg:
# continue
# pld_field = structs.fields(typedef)[1]
# assert pld_field.type is payload_spec # TODO-^ does this need to work to get all subtypes to adhere?
# kwargs: dict[str, Any] = {
# 'cid': '666',
# 'pld': pld,
# }
# enc_msg: PayloadMsg = typedef(**kwargs)
# _wire_bytes: bytes = _enc.encode(enc_msg)
# wire_bytes: bytes = codec.enc.encode(enc_msg)
# assert _wire_bytes == wire_bytes
# ve: ValidationError|None = None
# try:
# dec_msg = codec.dec.decode(wire_bytes)
# _dec_msg = _dec.decode(wire_bytes)
# # decoded msg and thus payload should be exactly same!
# assert (roundtrip := (
# _dec_msg
# ==
# dec_msg
# ==
# enc_msg
# ))
# if (
# expect_roundtrip is not None
# and expect_roundtrip != roundtrip
# ):
# breakpoint()
# assert (
# pld
# ==
# dec_msg.pld
# ==
# enc_msg.pld
# )
# # assert (roundtrip := (_dec_msg == enc_msg))
# except ValidationError as _ve:
# ve = _ve
# roundtrip: bool = False
# if pld_val_type is payload_spec:
# raise ValueError(
# 'Got `ValidationError` despite type-var match!?\n'
# f'pld_val_type: {pld_val_type}\n'
# f'payload_type: {payload_spec}\n'
# ) from ve
# else:
# # ow we good cuz the pld spec mismatched.
# print(
# 'Got expected `ValidationError` since,\n'
# f'{pld_val_type} is not {payload_spec}\n'
# )
# else:
# if (
# payload_spec is not Any
# and
# pld_val_type is not payload_spec
# ):
# raise ValueError(
# 'DID NOT `ValidationError` despite expected type match!?\n'
# f'pld_val_type: {pld_val_type}\n'
# f'payload_type: {payload_spec}\n'
# )
# # full code decode should always be attempted!
# if roundtrip is None:
# breakpoint()
# return roundtrip
# ?TODO? maybe remove since covered in the newer `test_pldrx_limiting`
# via end-2-end testing of all this?
# -[ ] IOW do we really NEED this lowlevel unit testing?
#
# def test_limit_msgspec(
# debug_mode: bool,
# ):
# '''
# Internals unit testing to verify that type-limiting an IPC ctx's
# msg spec with `Pldrx.limit_plds()` results in various
# encapsulated `msgspec` object settings and state.
# '''
# async def main():
# async with tractor.open_root_actor(
# debug_mode=debug_mode,
# ):
# # ensure we can round-trip a boxing `PayloadMsg`
# assert chk_pld_type(
# payload_spec=Any,
# pld=None,
# expect_roundtrip=True,
# )
# # verify that a mis-typed payload value won't decode
# assert not chk_pld_type(
# payload_spec=int,
# pld='doggy',
# )
# # parametrize the boxed `.pld` type as a custom-struct
# # and ensure that parametrization propagates
# # to all payload-msg-spec-able subtypes!
# class CustomPayload(Struct):
# name: str
# value: Any
# assert not chk_pld_type(
# payload_spec=CustomPayload,
# pld='doggy',
# )
# assert chk_pld_type(
# payload_spec=CustomPayload,
# pld=CustomPayload(name='doggy', value='urmom')
# )
# # yah, we can `.pause_from_sync()` now!
# # breakpoint()
# trio.run(main)

View File

@ -2,12 +2,14 @@
`tractor.log`-wrapping unit tests. `tractor.log`-wrapping unit tests.
''' '''
import importlib
from pathlib import Path from pathlib import Path
import shutil import shutil
import sys
from types import ModuleType
import pytest import pytest
import tractor import tractor
from tractor import _code_load
def test_root_pkg_not_duplicated_in_logger_name(): def test_root_pkg_not_duplicated_in_logger_name():
@ -35,6 +37,31 @@ def test_root_pkg_not_duplicated_in_logger_name():
assert 'mod' not in sublog.name assert 'mod' not in sublog.name
# ?TODO, move this into internal libs?
# -[ ] we already use it in `modden.config._pymod` as well
def load_module_from_path(
path: Path,
module_name: str|None = None,
) -> ModuleType:
'''
Taken from SO,
https://stackoverflow.com/a/67208147
which is based on stdlib docs,
https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly
'''
module_name = module_name or path.stem
spec = importlib.util.spec_from_file_location(
module_name,
str(path),
)
module = importlib.util.module_from_spec(spec)
sys.modules[module_name] = module
spec.loader.exec_module(module)
return module
def test_implicit_mod_name_applied_for_child( def test_implicit_mod_name_applied_for_child(
testdir: pytest.Pytester, testdir: pytest.Pytester,
loglevel: str, loglevel: str,
@ -82,7 +109,7 @@ def test_implicit_mod_name_applied_for_child(
# XXX NOTE, once the "top level" pkg mod has been # XXX NOTE, once the "top level" pkg mod has been
# imported, we can then use `import` syntax to # imported, we can then use `import` syntax to
# import it's sub-pkgs and modules. # import it's sub-pkgs and modules.
pkgmod = _code_load.load_module_from_path( pkgmod = load_module_from_path(
Path(pkg / '__init__.py'), Path(pkg / '__init__.py'),
module_name=proj_name, module_name=proj_name,
) )

View File

@ -7,15 +7,7 @@ related settings around IPC contexts.
from contextlib import ( from contextlib import (
asynccontextmanager as acm, asynccontextmanager as acm,
) )
import sys
import types
from typing import (
Any,
Union,
Type,
)
import msgspec
from msgspec import ( from msgspec import (
Struct, Struct,
) )
@ -30,10 +22,11 @@ from tractor import (
Portal, Portal,
) )
from tractor.msg import ( from tractor.msg import (
_codec,
_ops as msgops, _ops as msgops,
Return, Return,
_exts, )
from tractor.msg import (
_codec,
) )
from tractor.msg.types import ( from tractor.msg.types import (
log, log,
@ -48,22 +41,13 @@ class PldMsg(
# case of these details? # case of these details?
# #
# https://jcristharif.com/msgspec/structs.html#tagged-unions # https://jcristharif.com/msgspec/structs.html#tagged-unions
tag=True, # tag=True,
tag_field='msg_type', # tag_field='msg_type',
): ):
field: str field: str
class Msg1(PldMsg): maybe_msg_spec = PldMsg|None
field: str
class Msg2(PldMsg):
field: int
class AnyFieldMsg(PldMsg):
field: Any
@acm @acm
@ -120,15 +104,9 @@ async def maybe_expect_raises(
) )
# NOTE, this decorator is applied dynamically by both the root and @tractor.context(
# 'sub' actor such that we can dynamically apply various cases from pld_spec=maybe_msg_spec,
# a parametrized test. )
#
# maybe_msg_spec = PldMsg|None
#
# @tractor.context(
# pld_spec=maybe_msg_spec,
# )
async def child( async def child(
ctx: Context, ctx: Context,
started_value: int|PldMsg|None, started_value: int|PldMsg|None,
@ -136,13 +114,13 @@ async def child(
validate_pld_spec: bool, validate_pld_spec: bool,
raise_on_started_mte: bool = True, raise_on_started_mte: bool = True,
pack_any_field: bool = False,
) -> None: ) -> None:
''' '''
Call ``Context.started()`` more then once (an error). Call ``Context.started()`` more then once (an error).
''' '''
expect_started_mte: bool = started_value == 10
# sanaity check that child RPC context is the current one # sanaity check that child RPC context is the current one
curr_ctx: Context = current_ipc_ctx() curr_ctx: Context = current_ipc_ctx()
assert ctx is curr_ctx assert ctx is curr_ctx
@ -150,7 +128,6 @@ async def child(
rx: msgops.PldRx = ctx._pld_rx rx: msgops.PldRx = ctx._pld_rx
curr_pldec: _codec.MsgDec = rx.pld_dec curr_pldec: _codec.MsgDec = rx.pld_dec
ctx_meta: dict = getattr( ctx_meta: dict = getattr(
child, child,
'_tractor_context_meta', '_tractor_context_meta',
@ -159,28 +136,10 @@ async def child(
if ctx_meta: if ctx_meta:
assert ( assert (
ctx_meta['pld_spec'] ctx_meta['pld_spec']
is is curr_pldec.spec
curr_pldec.spec is curr_pldec.pld_spec
is
curr_pldec.pld_spec
) )
pld_types: set[Type] = _codec.unpack_spec_types(
curr_pldec.pld_spec,
)
if (
AnyFieldMsg in pld_types
and
pack_any_field
):
started_value = AnyFieldMsg(field=started_value)
expect_started_mte: bool = (
started_value == 10
and
not pack_any_field
)
# 2 cases: hdndle send-side and recv-only validation # 2 cases: hdndle send-side and recv-only validation
# - when `raise_on_started_mte == True`, send validate # - when `raise_on_started_mte == True`, send validate
# - else, parent-recv-side only validation # - else, parent-recv-side only validation
@ -260,65 +219,16 @@ async def child(
# msg-type-error from this RPC task ;) # msg-type-error from this RPC task ;)
return return_value return return_value
def decorate_child_ep(
pld_spec: Union[Type],
) -> types.ModuleType:
'''
Apply parametrized pld_spec to ctx ep like,
@tractor.context(
pld_spec=maybe_msg_spec,
)(child)
'''
this_mod = sys.modules[__name__]
global child # a mod-fn defined above
assert this_mod.child is child
this_mod.child = tractor.context(
pld_spec=pld_spec,
)(child)
return this_mod
@tractor.context
async def set_chld_pldspec(
ctx: tractor.Context,
pld_spec_strs: list[str],
):
'''
Dynamically apply the `@context(pld_spec=pld_spec)` deco to the
current actor's in-mem instance of this test module.
Allows dynamically applying the "payload-spec" in both a parent
and child actor after spawn.
'''
this_mod = sys.modules[__name__]
pld_spec: list[str] = _exts.dec_type_union(
pld_spec_strs,
mods=[
this_mod,
msgspec.inspect,
],
)
decorate_child_ep(pld_spec)
await ctx.started()
await trio.sleep_forever()
@pytest.mark.parametrize( @pytest.mark.parametrize(
'return_value', 'return_value',
[ [
'yo', 'yo',
None, None,
Msg2(field=10),
AnyFieldMsg(field='yo'),
], ],
ids=[ ids=[
'return[invalid-"yo"]', 'return[invalid-"yo"]',
'return[maybe-valid-None]', 'return[valid-None]',
'return[maybe-valid-Msg2]',
'return[maybe-valid-any-packed-yo]',
], ],
) )
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -326,14 +236,10 @@ async def set_chld_pldspec(
[ [
10, 10,
PldMsg(field='yo'), PldMsg(field='yo'),
Msg1(field='yo'),
AnyFieldMsg(field=10),
], ],
ids=[ ids=[
'Started[invalid-10]', 'Started[invalid-10]',
'Started[maybe-valid-PldMsg]', 'Started[valid-PldMsg]',
'Started[maybe-valid-Msg1]',
'Started[maybe-valid-any-packed-10]',
], ],
) )
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -347,31 +253,12 @@ async def set_chld_pldspec(
'no-started-pld-validate', 'no-started-pld-validate',
], ],
) )
@pytest.mark.parametrize(
'pld_spec',
[
PldMsg|None,
# demo how to have strict msgs alongside all other supported
# py-types by embedding the any-types inside a shuttle msg.
Msg1|Msg2|AnyFieldMsg,
# XXX, will never work since Struct overrides dict.
# https://jcristharif.com/msgspec/usage.html#typed-decoding
# Msg1|Msg2|msgspec.inspect.AnyType,
],
ids=[
'maybe_PldMsg_spec',
'Msg1_or_Msg2_or_AnyFieldMsg_spec',
]
)
def test_basic_payload_spec( def test_basic_payload_spec(
debug_mode: bool, debug_mode: bool,
loglevel: str, loglevel: str,
return_value: str|None, return_value: str|None,
started_value: int|PldMsg, started_value: int|PldMsg,
pld_check_started_value: bool, pld_check_started_value: bool,
pld_spec: Union[Type],
): ):
''' '''
Validate the most basic `PldRx` msg-type-spec semantics around Validate the most basic `PldRx` msg-type-spec semantics around
@ -380,33 +267,16 @@ def test_basic_payload_spec(
pld-spec. pld-spec.
''' '''
pld_types: set[Type] = _codec.unpack_spec_types(pld_spec) invalid_return: bool = return_value == 'yo'
invalid_return: bool = ( invalid_started: bool = started_value == 10
return_value == 'yo'
)
invalid_started: bool = (
started_value == 10
)
# dynamically apply ep's pld-spec in 'root'.
decorate_child_ep(pld_spec)
assert (
child._tractor_context_meta['pld_spec'] == pld_spec
)
pld_spec_strs: list[str] = _exts.enc_type_union(
pld_spec,
)
assert len(pld_types) > 1
async def main(): async def main():
nonlocal pld_spec
async with tractor.open_nursery( async with tractor.open_nursery(
debug_mode=debug_mode, debug_mode=debug_mode,
loglevel=loglevel, loglevel=loglevel,
) as an: ) as an:
p: Portal = await an.start_actor( p: Portal = await an.start_actor(
'sub', 'child',
enable_modules=[__name__], enable_modules=[__name__],
) )
@ -416,11 +286,9 @@ def test_basic_payload_spec(
if invalid_started: if invalid_started:
msg_type_str: str = 'Started' msg_type_str: str = 'Started'
bad_value: int = 10 bad_value: int = 10
elif invalid_return: elif invalid_return:
msg_type_str: str = 'Return' msg_type_str: str = 'Return'
bad_value: str = 'yo' bad_value: str = 'yo'
else: else:
# XXX but should never be used below then.. # XXX but should never be used below then..
msg_type_str: str = '' msg_type_str: str = ''
@ -434,7 +302,6 @@ def test_basic_payload_spec(
invalid_started invalid_started
) else None ) else None
) )
async with ( async with (
maybe_expect_raises( maybe_expect_raises(
raises=should_raise, raises=should_raise,
@ -448,11 +315,6 @@ def test_basic_payload_spec(
# only for debug # only for debug
# post_mortem=True, # post_mortem=True,
), ),
p.open_context(
set_chld_pldspec,
pld_spec_strs=pld_spec_strs,
) as (deco_ctx, _),
p.open_context( p.open_context(
child, child,
return_value=return_value, return_value=return_value,
@ -463,18 +325,12 @@ def test_basic_payload_spec(
# now opened with 'child' sub # now opened with 'child' sub
assert current_ipc_ctx() is ctx assert current_ipc_ctx() is ctx
# assert type(first) is PldMsg assert type(first) is PldMsg
assert isinstance(first, PldMsg)
assert first.field == 'yo' assert first.field == 'yo'
try: try:
res: None|PldMsg = await ctx.result(hide_tb=False) res: None|PldMsg = await ctx.result(hide_tb=False)
assert res == return_value assert res is None
if res is None:
await tractor.pause()
if isinstance(res, PldMsg):
assert res.field == 10
except MsgTypeError as mte: except MsgTypeError as mte:
maybe_mte = mte maybe_mte = mte
if not invalid_return: if not invalid_return:
@ -500,9 +356,6 @@ def test_basic_payload_spec(
ctx.outcome ctx.outcome
) )
if should_raise is None:
await deco_ctx.cancel()
if should_raise is None: if should_raise is None:
assert maybe_mte is None assert maybe_mte is None

View File

@ -1,48 +0,0 @@
# 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/>.
'''
(Hot) coad (re-)load utils for python.
'''
import importlib
from pathlib import Path
import sys
from types import ModuleType
# ?TODO, move this into internal libs?
# -[ ] we already use it in `modden.config._pymod` as well
def load_module_from_path(
path: Path,
module_name: str|None = None,
) -> ModuleType:
'''
Taken from SO,
https://stackoverflow.com/a/67208147
which is based on stdlib docs,
https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly
'''
module_name = module_name or path.stem
spec = importlib.util.spec_from_file_location(
module_name,
str(path),
)
module = importlib.util.module_from_spec(spec)
sys.modules[module_name] = module
spec.loader.exec_module(module)
return module

View File

@ -181,11 +181,7 @@ class MsgDec(Struct):
def mk_dec( def mk_dec(
spec: ( spec: Union[Type[Struct]]|Type|None,
Union[Type[Struct]]
|Type # lone type
|None # implying `Union[*ext_types]|None`
),
# NOTE, required for ad-hoc type extensions to the underlying # NOTE, required for ad-hoc type extensions to the underlying
# serialization proto (which is default `msgpack`), # serialization proto (which is default `msgpack`),
@ -198,18 +194,16 @@ def mk_dec(
Create an IPC msg decoder, a slightly higher level wrapper around Create an IPC msg decoder, a slightly higher level wrapper around
a `msgspec.msgpack.Decoder` which provides, a `msgspec.msgpack.Decoder` which provides,
- easier introspection of the underlying type spec via the - easier introspection of the underlying type spec via
`.spec` and `.spec_str` attrs, the `.spec` and `.spec_str` attrs,
- `.hook` access to the `Decoder.dec_hook()`, - `.hook` access to the `Decoder.dec_hook()`,
- automatic custom extension-types decode support when - automatic custom extension-types decode support when
`dec_hook()` is provided such that any `PayloadMsg.pld` tagged `dec_hook()` is provided such that any `PayloadMsg.pld` tagged
as a type from from `ext_types` (presuming the as a type from from `ext_types` (presuming the `MsgCodec.encode()` also used
`MsgCodec.encode()` also used a `.enc_hook()`) is processed and a `.enc_hook()`) is processed and constructed by a `PldRx` implicitily.
constructed by a `PldRx` implicitily.
NOTE, as mentioned a `MsgDec` is normally used for NOTE, as mentioned a `MsgDec` is normally used for `PayloadMsg.pld: PayloadT` field
`PayloadMsg.pld: PayloadT` field decoding inside an decoding inside an IPC-ctx-oriented `PldRx`.
IPC-ctx-oriented `PldRx`.
''' '''
if ( if (
@ -254,16 +248,12 @@ def mk_dec(
# will work? kk B) # will work? kk B)
# #
# maybe_box_struct = mk_boxed_ext_struct(ext_types) # maybe_box_struct = mk_boxed_ext_struct(ext_types)
spec = Raw | Union[*ext_types]
spec = spec | Union[*ext_types]
return MsgDec( return MsgDec(
_dec=msgpack.Decoder( _dec=msgpack.Decoder(
type=spec, type=spec, # like `MsgType[Any]`
dec_hook=dec_hook, dec_hook=dec_hook,
# ?TODO, support it?
# https://jcristharif.com/msgspec/usage.html#strict-vs-lax-mode
# strict=False,
), ),
) )

View File

@ -33,7 +33,9 @@ converters,
|_ https://jcristharif.com/msgspec/changelog.html |_ https://jcristharif.com/msgspec/changelog.html
''' '''
import types from types import (
ModuleType,
)
import typing import typing
from typing import ( from typing import (
Type, Type,
@ -42,51 +44,35 @@ from typing import (
def dec_type_union( def dec_type_union(
type_names: list[str], type_names: list[str],
mods: list[types.ModuleType] = [] mods: list[ModuleType] = []
) -> Type|Union[Type]: ) -> Type|Union[Type]:
''' '''
Look up types by name, compile into a list and then create and Look up types by name, compile into a list and then create and
return a `typing.Union` from the full set. return a `typing.Union` from the full set.
''' '''
_types: list[Type] = [] # import importlib
types: list[Type] = []
for type_name in type_names: for type_name in type_names:
for mod in [ for mod in [
typing, typing,
types, # importlib.import_module(__name__),
] + mods: ] + mods:
if type_ref := getattr( if type_ref := getattr(
mod, mod,
type_name, type_name,
False, False,
): ):
_types.append(type_ref) types.append(type_ref)
break
report: str = '' # special case handling only..
if not _types: # ipc_pld_spec: Union[Type] = eval(
report: str = 'No type-instances could be resolved from `type_names` ??\n' # pld_spec_str,
# {}, # globals
# {'typing': typing}, # locals
# )
elif len(type_names) != len(_types): return Union[*types]
report: str = (
f'Some type-instances could not be resolved from `type_names` ??\n'
f'_types: {_types!r}\n'
)
if report:
raise ValueError(
report
+
f'type_names: {type_names!r}\n'
)
if not _types:
raise ValueError(
f'No type-instance could be resolved from `type_names` ??\n'
f'type_names: {type_names!r}\n'
)
return Union[*_types]
def enc_type_union( def enc_type_union(

View File

@ -119,7 +119,7 @@ class PldRx(Struct):
def limit_plds( def limit_plds(
self, self,
spec: Union[Type[Struct]], spec: Union[Type[Struct]],
**mk_dec_kwargs, **dec_kwargs,
) -> MsgDec: ) -> MsgDec:
''' '''
@ -135,7 +135,7 @@ class PldRx(Struct):
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,
**mk_dec_kwargs, **dec_kwargs,
) )
try: try:
self._pld_dec = limit_dec self._pld_dec = limit_dec
@ -582,7 +582,6 @@ async def drain_to_final_msg(
even after ctx closure and the `.open_context()` block exit. even after ctx closure and the `.open_context()` block exit.
''' '''
__tracebackhide__: bool = hide_tb
raise_overrun: bool = not ctx._allow_overruns raise_overrun: bool = not ctx._allow_overruns
parent_never_opened_stream: bool = ctx._stream is None parent_never_opened_stream: bool = ctx._stream is None
@ -835,8 +834,7 @@ async def drain_to_final_msg(
f'{ctx.outcome}\n' f'{ctx.outcome}\n'
) )
# ?TODO? why was this here and not above? __tracebackhide__: bool = hide_tb
# __tracebackhide__: bool = hide_tb
return ( return (
result_msg, result_msg,
pre_result_drained, pre_result_drained,

View File

@ -1703,7 +1703,7 @@ def run_as_asyncio_guest(
# asyncio.CancelledError, # asyncio.CancelledError,
# ^^XXX `.shield()` call above prevents this?? # ^^XXX `.shield()` call above prevents this??
) as state_err: )as state_err:
# XXX be super dupere noisy about abandonment issues! # XXX be super dupere noisy about abandonment issues!
aio_task: asyncio.Task = asyncio.current_task() aio_task: asyncio.Task = asyncio.current_task()