Update pld-rx limiting test(s) to use deco input

The tests only use one input spec (conveniently) so there's not much to
change in the logic,
- only pass the `maybe_msg_spec` to the child-side decorator and obvi
  drop the surrounding `msgops.limit_plds()` block in the child.
- tweak a few `MsgDec` asserts, mostly dropping the
  `msg._ops._def_any_spec` state checks since the child-side won't have
  any pre pld-spec state given the runtime now applies the `pld_spec`
  before running the task's func body.
  - also allowed dropping the `finally:` which did a similar check
    outside the `.limit_plds()` block.
runtime_to_msgspec
Tyler Goodlet 2024-06-17 09:24:03 -04:00
parent 04bd111037
commit affc210033
1 changed files with 99 additions and 104 deletions

View File

@ -7,9 +7,6 @@ related settings around IPC contexts.
from contextlib import ( from contextlib import (
asynccontextmanager as acm, asynccontextmanager as acm,
) )
from contextvars import (
Context,
)
from msgspec import ( from msgspec import (
Struct, Struct,
@ -19,6 +16,7 @@ import trio
import tractor import tractor
from tractor import ( from tractor import (
Context,
MsgTypeError, MsgTypeError,
current_ipc_ctx, current_ipc_ctx,
Portal, Portal,
@ -35,7 +33,17 @@ from tractor.msg.types import (
) )
class PldMsg(Struct): class PldMsg(
Struct,
# TODO: with multiple structs in-spec we need to tag them!
# -[ ] offer a built-in `PldMsg` type to inherit from which takes
# case of these details?
#
# https://jcristharif.com/msgspec/structs.html#tagged-unions
# tag=True,
# tag_field='msg_type',
):
field: str field: str
@ -96,12 +104,14 @@ async def maybe_expect_raises(
) )
@tractor.context @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,
return_value: str|None, return_value: str|None,
validate_pld_spec: bool, validate_pld_spec: bool,
raise_on_started_mte: bool = True, raise_on_started_mte: bool = True,
) -> None: ) -> None:
@ -116,113 +126,99 @@ async def child(
assert ctx is curr_ctx assert ctx is curr_ctx
rx: msgops.PldRx = ctx._pld_rx rx: msgops.PldRx = ctx._pld_rx
orig_pldec: _codec.MsgDec = rx.pld_dec curr_pldec: _codec.MsgDec = rx.pld_dec
# senity that default pld-spec should be set
assert ( ctx_meta: dict = getattr(
rx.pld_dec child,
is '_tractor_context_meta',
msgops._def_any_pldec None,
) )
if ctx_meta:
assert (
ctx_meta['pld_spec']
is curr_pldec.spec
is curr_pldec.pld_spec
)
# 2 cases: hdndle send-side and recv-only validation
# - when `raise_on_started_mte == True`, send validate
# - else, parent-recv-side only validation
mte: MsgTypeError|None = None
try: try:
with msgops.limit_plds( await ctx.started(
spec=maybe_msg_spec, value=started_value,
) as pldec: validate_pld_spec=validate_pld_spec,
# sanity on `MsgDec` state )
assert rx.pld_dec is pldec
assert pldec.spec is maybe_msg_spec
# 2 cases: hdndle send-side and recv-only validation except MsgTypeError as _mte:
# - when `raise_on_started_mte == True`, send validate mte = _mte
# - else, parent-recv-side only validation log.exception('started()` raised an MTE!\n')
mte: MsgTypeError|None = None if not expect_started_mte:
try: raise RuntimeError(
await ctx.started( 'Child-ctx-task SHOULD NOT HAVE raised an MTE for\n\n'
value=started_value, f'{started_value!r}\n'
validate_pld_spec=validate_pld_spec,
)
except MsgTypeError as _mte:
mte = _mte
log.exception('started()` raised an MTE!\n')
if not expect_started_mte:
raise RuntimeError(
'Child-ctx-task SHOULD NOT HAVE raised an MTE for\n\n'
f'{started_value!r}\n'
)
boxed_div: str = '------ - ------'
assert boxed_div not in mte._message
assert boxed_div not in mte.tb_str
assert boxed_div not in repr(mte)
assert boxed_div not in str(mte)
mte_repr: str = repr(mte)
for line in mte.message.splitlines():
assert line in mte_repr
# since this is a *local error* there should be no
# boxed traceback content!
assert not mte.tb_str
# propagate to parent?
if raise_on_started_mte:
raise
# no-send-side-error fallthrough
if (
validate_pld_spec
and
expect_started_mte
):
raise RuntimeError(
'Child-ctx-task SHOULD HAVE raised an MTE for\n\n'
f'{started_value!r}\n'
)
assert (
not expect_started_mte
or
not validate_pld_spec
) )
# if wait_for_parent_to_cancel: boxed_div: str = '------ - ------'
# ... assert boxed_div not in mte._message
# assert boxed_div not in mte.tb_str
# ^-TODO-^ logic for diff validation policies on each side: assert boxed_div not in repr(mte)
# assert boxed_div not in str(mte)
# -[ ] ensure that if we don't validate on the send mte_repr: str = repr(mte)
# side, that we are eventually error-cancelled by our for line in mte.message.splitlines():
# parent due to the bad `Started` payload! assert line in mte_repr
# -[ ] the boxed error should be srced from the parent's
# runtime NOT ours!
# -[ ] we should still error on bad `return_value`s
# despite the parent not yet error-cancelling us?
# |_ how do we want the parent side to look in that
# case?
# -[ ] maybe the equiv of "during handling of the
# above error another occurred" for the case where
# the parent sends a MTE to this child and while
# waiting for the child to terminate it gets back
# the MTE for this case?
#
# XXX should always fail on recv side since we can't # since this is a *local error* there should be no
# really do much else beside terminate and relay the # boxed traceback content!
# msg-type-error from this RPC task ;) assert not mte.tb_str
return return_value
finally: # propagate to parent?
# sanity on `limit_plds()` reversion if raise_on_started_mte:
assert ( raise
rx.pld_dec
is # no-send-side-error fallthrough
msgops._def_any_pldec if (
) validate_pld_spec
log.runtime( and
'Reverted to previous pld-spec\n\n' expect_started_mte
f'{orig_pldec}\n' ):
raise RuntimeError(
'Child-ctx-task SHOULD HAVE raised an MTE for\n\n'
f'{started_value!r}\n'
) )
assert (
not expect_started_mte
or
not validate_pld_spec
)
# if wait_for_parent_to_cancel:
# ...
#
# ^-TODO-^ logic for diff validation policies on each side:
#
# -[ ] ensure that if we don't validate on the send
# side, that we are eventually error-cancelled by our
# parent due to the bad `Started` payload!
# -[ ] the boxed error should be srced from the parent's
# runtime NOT ours!
# -[ ] we should still error on bad `return_value`s
# despite the parent not yet error-cancelling us?
# |_ how do we want the parent side to look in that
# case?
# -[ ] maybe the equiv of "during handling of the
# above error another occurred" for the case where
# the parent sends a MTE to this child and while
# waiting for the child to terminate it gets back
# the MTE for this case?
#
# XXX should always fail on recv side since we can't
# really do much else beside terminate and relay the
# msg-type-error from this RPC task ;)
return return_value
@pytest.mark.parametrize( @pytest.mark.parametrize(
'return_value', 'return_value',
@ -321,7 +317,6 @@ def test_basic_payload_spec(
child, child,
return_value=return_value, return_value=return_value,
started_value=started_value, started_value=started_value,
pld_spec=maybe_msg_spec,
validate_pld_spec=pld_check_started_value, validate_pld_spec=pld_check_started_value,
) as (ctx, first), ) as (ctx, first),
): ):