From affc210033248f13c2d17b12c2a02db7c51d965e Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Mon, 17 Jun 2024 09:24:03 -0400 Subject: [PATCH] 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. --- tests/test_pldrx_limiting.py | 203 +++++++++++++++++------------------ 1 file changed, 99 insertions(+), 104 deletions(-) diff --git a/tests/test_pldrx_limiting.py b/tests/test_pldrx_limiting.py index ddf2a23..e5ce691 100644 --- a/tests/test_pldrx_limiting.py +++ b/tests/test_pldrx_limiting.py @@ -7,9 +7,6 @@ related settings around IPC contexts. from contextlib import ( asynccontextmanager as acm, ) -from contextvars import ( - Context, -) from msgspec import ( Struct, @@ -19,6 +16,7 @@ import trio import tractor from tractor import ( + Context, MsgTypeError, current_ipc_ctx, 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 @@ -96,12 +104,14 @@ async def maybe_expect_raises( ) -@tractor.context +@tractor.context( + pld_spec=maybe_msg_spec, +) async def child( ctx: Context, started_value: int|PldMsg|None, return_value: str|None, - validate_pld_spec: bool, + validate_pld_spec: bool, raise_on_started_mte: bool = True, ) -> None: @@ -116,113 +126,99 @@ async def child( assert ctx is curr_ctx rx: msgops.PldRx = ctx._pld_rx - orig_pldec: _codec.MsgDec = rx.pld_dec - # senity that default pld-spec should be set - assert ( - rx.pld_dec - is - msgops._def_any_pldec + curr_pldec: _codec.MsgDec = rx.pld_dec + + ctx_meta: dict = getattr( + child, + '_tractor_context_meta', + 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: - with msgops.limit_plds( - spec=maybe_msg_spec, - ) as pldec: - # sanity on `MsgDec` state - assert rx.pld_dec is pldec - assert pldec.spec is maybe_msg_spec + await ctx.started( + value=started_value, + validate_pld_spec=validate_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: - await ctx.started( - value=started_value, - 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 + 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' ) - # 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? - # + 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 - # 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 + # since this is a *local error* there should be no + # boxed traceback content! + assert not mte.tb_str - finally: - # sanity on `limit_plds()` reversion - assert ( - rx.pld_dec - is - msgops._def_any_pldec - ) - log.runtime( - 'Reverted to previous pld-spec\n\n' - f'{orig_pldec}\n' + # 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: + # ... + # + # ^-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( 'return_value', @@ -321,7 +317,6 @@ def test_basic_payload_spec( child, return_value=return_value, started_value=started_value, - pld_spec=maybe_msg_spec, validate_pld_spec=pld_check_started_value, ) as (ctx, first), ):