WIP, expand pldrx suite for tagged-multi-msgs
Nowhere near ready yet since this generates many test-body-logic un-handled true-positives which currently fail, but it's a first draft for the general case set. To start, includes a greater-than-one-`Msg` (a strict top level tagged-union-of-structs) pld spec alongside a `AnyFieldMsg`-workaround struct-msg for packing all other builtin non-`Struct`/`Any` python types alongside the other explicit msgs.pld_dec_refinements
parent
39952344cb
commit
b4dbf5dd86
|
@ -10,10 +10,12 @@ from contextlib import (
|
||||||
import sys
|
import sys
|
||||||
import types
|
import types
|
||||||
from typing import (
|
from typing import (
|
||||||
|
Any,
|
||||||
Union,
|
Union,
|
||||||
Type,
|
Type,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
import msgspec
|
||||||
from msgspec import (
|
from msgspec import (
|
||||||
Struct,
|
Struct,
|
||||||
)
|
)
|
||||||
|
@ -46,12 +48,24 @@ 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):
|
||||||
|
field: str
|
||||||
|
|
||||||
|
|
||||||
|
class Msg2(PldMsg):
|
||||||
|
field: int
|
||||||
|
|
||||||
|
|
||||||
|
class AnyFieldMsg(PldMsg):
|
||||||
|
field: Any
|
||||||
|
|
||||||
|
|
||||||
@acm
|
@acm
|
||||||
async def maybe_expect_raises(
|
async def maybe_expect_raises(
|
||||||
raises: BaseException|None = None,
|
raises: BaseException|None = None,
|
||||||
|
@ -122,13 +136,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
|
||||||
|
@ -136,6 +150,7 @@ 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',
|
||||||
|
@ -144,10 +159,28 @@ async def child(
|
||||||
if ctx_meta:
|
if ctx_meta:
|
||||||
assert (
|
assert (
|
||||||
ctx_meta['pld_spec']
|
ctx_meta['pld_spec']
|
||||||
is curr_pldec.spec
|
is
|
||||||
is curr_pldec.pld_spec
|
curr_pldec.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
|
||||||
|
@ -263,7 +296,10 @@ async def set_chld_pldspec(
|
||||||
this_mod = sys.modules[__name__]
|
this_mod = sys.modules[__name__]
|
||||||
pld_spec: list[str] = _exts.dec_type_union(
|
pld_spec: list[str] = _exts.dec_type_union(
|
||||||
pld_spec_strs,
|
pld_spec_strs,
|
||||||
mods=[this_mod],
|
mods=[
|
||||||
|
this_mod,
|
||||||
|
msgspec.inspect,
|
||||||
|
],
|
||||||
)
|
)
|
||||||
decorate_child_ep(pld_spec)
|
decorate_child_ep(pld_spec)
|
||||||
await ctx.started()
|
await ctx.started()
|
||||||
|
@ -275,10 +311,14 @@ async def set_chld_pldspec(
|
||||||
[
|
[
|
||||||
'yo',
|
'yo',
|
||||||
None,
|
None,
|
||||||
|
Msg2(field=10),
|
||||||
|
AnyFieldMsg(field='yo'),
|
||||||
],
|
],
|
||||||
ids=[
|
ids=[
|
||||||
'return[invalid-"yo"]',
|
'return[invalid-"yo"]',
|
||||||
'return[valid-None]',
|
'return[maybe-valid-None]',
|
||||||
|
'return[maybe-valid-Msg2]',
|
||||||
|
'return[maybe-valid-any-packed-yo]',
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
@ -286,10 +326,14 @@ 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[valid-PldMsg]',
|
'Started[maybe-valid-PldMsg]',
|
||||||
|
'Started[maybe-valid-Msg1]',
|
||||||
|
'Started[maybe-valid-any-packed-10]',
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
@ -307,12 +351,18 @@ async def set_chld_pldspec(
|
||||||
'pld_spec',
|
'pld_spec',
|
||||||
[
|
[
|
||||||
PldMsg|None,
|
PldMsg|None,
|
||||||
# !TODO, these cases
|
|
||||||
# Msg1|Msg2|None,
|
# demo how to have strict msgs alongside all other supported
|
||||||
# Msg1|Msg2|Any,
|
# 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=[
|
ids=[
|
||||||
'maybe_PldMsg_spec',
|
'maybe_PldMsg_spec',
|
||||||
|
'Msg1_or_Msg2_or_AnyFieldMsg_spec',
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
def test_basic_payload_spec(
|
def test_basic_payload_spec(
|
||||||
|
@ -330,21 +380,27 @@ def test_basic_payload_spec(
|
||||||
pld-spec.
|
pld-spec.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
invalid_return: bool = return_value == 'yo'
|
pld_types: set[Type] = _codec.unpack_spec_types(pld_spec)
|
||||||
invalid_started: bool = started_value == 10
|
invalid_return: bool = (
|
||||||
|
return_value == 'yo'
|
||||||
|
)
|
||||||
|
invalid_started: bool = (
|
||||||
|
started_value == 10
|
||||||
|
)
|
||||||
|
|
||||||
# dynamically apply ep's pld-spec in 'root'.
|
# dynamically apply ep's pld-spec in 'root'.
|
||||||
decorate_child_ep(pld_spec)
|
decorate_child_ep(pld_spec)
|
||||||
assert (
|
assert (
|
||||||
child._tractor_context_meta['pld_spec'] == pld_spec
|
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_strs: list[str] = _exts.enc_type_union(
|
||||||
pld_spec,
|
pld_spec,
|
||||||
)
|
)
|
||||||
assert len(pld_types) > 1
|
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,
|
||||||
|
@ -407,12 +463,18 @@ 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 is None
|
assert res == return_value
|
||||||
|
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:
|
||||||
|
|
Loading…
Reference in New Issue