Dynamically set `pld_spec` for `test_basic_payload_spec()
Such that we can parametrize the `@context(pld_spec)` endpoint setting using `pytest` and of course enable testing more then just the lone `maybe_msg_spec` case. The implementation was a bit tricky because subactors import any `enable_modules` just after subproc spawn, so there's no easy way to indicate from the parent should should be passed to the `@context()` decorator since it's already resolved by the time an IPC is established. Thus the bulk of this patch is implementing a pre-ctx which monkey-patches the (test) `child()`-ep-defining-module before running test logic. Impl deats, - drop `maybe_msg_spec` global instead providing the same value via a new `pld_spec: Union[Type]` parametrized input to the test suite. - add a `decorate_child_ep()` helper which (re-)decorates the mod-defined `child()` IPC-context endpoint with the provided `pld_spec`. - add a new "pre IPC context" endpoint: `set_chld_pldspec()` which can be opened (from another actor) just prior to opening the `child()` ep and it will decorate the latter (using `decorate_child_ep()`) presuming a `.msg._exts.enc_type_union()` generated `pld_spec_strs` is provided. - actually open the `set_chld_pldspec()` as a `deco_ctx` rom the root-actor and ensure we cancel it on block teardown in non-raising cases.pld_dec_refinements
parent
7d947d3776
commit
ccedee3b87
|
@ -7,6 +7,12 @@ related settings around IPC contexts.
|
||||||
from contextlib import (
|
from contextlib import (
|
||||||
asynccontextmanager as acm,
|
asynccontextmanager as acm,
|
||||||
)
|
)
|
||||||
|
import sys
|
||||||
|
import types
|
||||||
|
from typing import (
|
||||||
|
Union,
|
||||||
|
Type,
|
||||||
|
)
|
||||||
|
|
||||||
from msgspec import (
|
from msgspec import (
|
||||||
Struct,
|
Struct,
|
||||||
|
@ -22,11 +28,10 @@ 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,
|
||||||
|
@ -47,9 +52,6 @@ class PldMsg(
|
||||||
field: str
|
field: str
|
||||||
|
|
||||||
|
|
||||||
maybe_msg_spec = PldMsg|None
|
|
||||||
|
|
||||||
|
|
||||||
@acm
|
@acm
|
||||||
async def maybe_expect_raises(
|
async def maybe_expect_raises(
|
||||||
raises: BaseException|None = None,
|
raises: BaseException|None = None,
|
||||||
|
@ -104,9 +106,15 @@ async def maybe_expect_raises(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@tractor.context(
|
# NOTE, this decorator is applied dynamically by both the root and
|
||||||
pld_spec=maybe_msg_spec,
|
# 'sub' actor such that we can dynamically apply various cases from
|
||||||
)
|
# 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,
|
||||||
|
@ -219,6 +227,48 @@ 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],
|
||||||
|
)
|
||||||
|
decorate_child_ep(pld_spec)
|
||||||
|
await ctx.started()
|
||||||
|
await trio.sleep_forever()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
'return_value',
|
'return_value',
|
||||||
|
@ -253,12 +303,25 @@ async def child(
|
||||||
'no-started-pld-validate',
|
'no-started-pld-validate',
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'pld_spec',
|
||||||
|
[
|
||||||
|
PldMsg|None,
|
||||||
|
# !TODO, these cases
|
||||||
|
# Msg1|Msg2|None,
|
||||||
|
# Msg1|Msg2|Any,
|
||||||
|
],
|
||||||
|
ids=[
|
||||||
|
'maybe_PldMsg_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
|
||||||
|
@ -270,13 +333,24 @@ def test_basic_payload_spec(
|
||||||
invalid_return: bool = return_value == 'yo'
|
invalid_return: bool = return_value == 'yo'
|
||||||
invalid_started: bool = started_value == 10
|
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_types: set[Type] = _codec.unpack_spec_types(pld_spec)
|
||||||
|
pld_spec_strs: list[str] = _exts.enc_type_union(
|
||||||
|
pld_spec,
|
||||||
|
)
|
||||||
|
assert len(pld_types) > 1
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
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(
|
||||||
'child',
|
'sub',
|
||||||
enable_modules=[__name__],
|
enable_modules=[__name__],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -286,9 +360,11 @@ 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 = ''
|
||||||
|
@ -302,6 +378,7 @@ 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,
|
||||||
|
@ -315,6 +392,11 @@ 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,
|
||||||
|
@ -356,6 +438,9 @@ 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
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue