Add buncha notes on `Start` field for "params"

Such that the current `kwargs: dict` field can eventually be strictly
msg-typed (eventually directly from a `@context` def) using modern typed
python's hippest syntactical approach B)

Also proto a new `CancelAck(Return)` subtype msg for supporting msg-spec
agnostic `Actor.cancel_xx()` method calls in the runtime such that
a user can't break cancellation (and thus SC) by dynamically setting
a codec that doesn't allow `bool` results (as an eg. in this case).
Note that the msg isn't used yet in `._rpc` but that's a comin!
runtime_to_msgspec
Tyler Goodlet 2024-04-05 13:59:43 -04:00
parent 10c98946bd
commit 7f1c2b8ecf
1 changed files with 113 additions and 11 deletions

View File

@ -45,6 +45,10 @@ from msgspec import (
from tractor.msg import ( from tractor.msg import (
pretty_struct, pretty_struct,
) )
from tractor.log import get_logger
log = get_logger('tractor.msgspec')
# type variable for the boxed payload field `.pld` # type variable for the boxed payload field `.pld`
PayloadT = TypeVar('PayloadT') PayloadT = TypeVar('PayloadT')
@ -185,7 +189,47 @@ class SpawnSpec(
# | Union[DebugLock, DebugLocked, DebugRelease] # | Union[DebugLock, DebugLocked, DebugRelease]
# ) # )
# class Params(
# Struct,
# Generic[PayloadT],
# ):
# spec: PayloadT|ParamSpec
# inputs: InputsT|dict[str, Any]
# TODO: for eg. we could stringently check the target
# task-func's type sig and enforce it?
# as an example for an IPTC,
# @tractor.context
# async def send_back_nsp(
# ctx: Context,
# expect_debug: bool,
# pld_spec_str: str,
# add_hooks: bool,
# started_msg_dict: dict,
# ) -> <WhatHere!>:
# TODO: figure out which of the `typing` feats we want to
# support:
# - plain ol `ParamSpec`:
# https://docs.python.org/3/library/typing.html#typing.ParamSpec
# - new in 3.12 type parameter lists Bo
# |_ https://docs.python.org/3/reference/compound_stmts.html#type-params
# |_ historical pep 695: https://peps.python.org/pep-0695/
# |_ full lang spec: https://typing.readthedocs.io/en/latest/spec/
# |_ on annotation scopes:
# https://docs.python.org/3/reference/executionmodel.html#annotation-scopes
# spec: ParamSpec[
# expect_debug: bool,
# pld_spec_str: str,
# add_hooks: bool,
# started_msg_dict: dict,
# ]
# TODO: possibly sub-type for runtime method requests?
# -[ ] `Runtime(Start)` with a `.ns: str = 'self' or
# we can just enforce any such method as having a strict
# ns for calling funcs, namely the `Actor` instance?
class Start( class Start(
Struct, Struct,
tag=True, tag=True,
@ -212,9 +256,45 @@ class Start(
ns: str ns: str
func: str func: str
kwargs: dict # TODO: make this a sub-struct which can be further
# type-limited, maybe `Inputs`?
# => SEE ABOVE <=
kwargs: dict[str, Any]
uid: tuple[str, str] # (calling) actor-id uid: tuple[str, str] # (calling) actor-id
# TODO: enforcing a msg-spec in terms `Msg.pld`
# parameterizable msgs to be used in the appls IPC dialog.
#
# -[ ] both as part of the `.open_context()` call AND as part of the
# immediate ack-reponse (see similar below)
# we should do spec matching and fail if anything is awry?
#
# -[ ] eventually spec should be generated/parsed from the
# type-annots as # desired in GH issue:
# https://github.com/goodboy/tractor/issues/365
#
# -[ ] semantics of the mismatch case
# - when caller-callee specs we should raise
# a `MsgTypeError` or `MsgSpecError` or similar?
#
# -[ ] wrapper types for both spec types such that we can easily
# IPC transport them?
# - `TypeSpec: Union[Type]`
# * also a `.__contains__()` for doing `None in
# TypeSpec[None|int]` since rn you need to do it on
# `.__args__` for unions..
# - `MsgSpec: Union[Type[Msg]]
#
# -[ ] auto-genning this from new (in 3.12) type parameter lists Bo
# |_ https://docs.python.org/3/reference/compound_stmts.html#type-params
# |_ historical pep 695: https://peps.python.org/pep-0695/
# |_ full lang spec: https://typing.readthedocs.io/en/latest/spec/
# |_ on annotation scopes:
# https://docs.python.org/3/reference/executionmodel.html#annotation-scopes
# |_ 3.13 will have subscriptable funcs Bo
# https://peps.python.org/pep-0718/
pld_spec: str = str(Any)
class StartAck( class StartAck(
Struct, Struct,
@ -235,14 +315,10 @@ class StartAck(
'context', # TODO: the only one eventually? 'context', # TODO: the only one eventually?
] ]
# TODO: as part of the reponse we should report our allowed # import typing
# msg spec which should be generated from the type-annots as # eval(str(Any), {}, {'typing': typing})
# desired in # https://github.com/goodboy/tractor/issues/365 # started_spec: str = str(Any)
# When this does not match what the starter/caller side # return_spec
# expects we of course raise a `TypeError` just like if
# a function had been called using an invalid signature.
#
# msgspec: MsgSpec
class Started( class Started(
@ -290,6 +366,7 @@ class Stop(
# pld: UnsetType = UNSET # pld: UnsetType = UNSET
# TODO: is `Result` or `Out[come]` a better name?
class Return( class Return(
Msg, Msg,
Generic[PayloadT], Generic[PayloadT],
@ -302,6 +379,27 @@ class Return(
pld: PayloadT pld: PayloadT
class CancelAck(
Return,
):
'''
Deliver the `bool` return-value from a cancellation `Actor`
method scheduled via and prior RPC request.
- `Actor.cancel()`
`|_.cancel_soon()`
`|_.cancel_rpc_tasks()`
`|_._cancel_task()`
`|_.cancel_server()`
RPCs to these methods must **always** be able to deliver a result
despite the currently configured IPC msg spec such that graceful
cancellation is always functional in the runtime.
'''
pld: bool
class Error( class Error(
Struct, Struct,
tag=True, tag=True,
@ -530,9 +628,13 @@ def mk_msg_spec(
pld_spec: Union[Type] = specs[spec_build_method] pld_spec: Union[Type] = specs[spec_build_method]
runtime_spec: Union[Type] = Union[*ipc_msg_types] runtime_spec: Union[Type] = Union[*ipc_msg_types]
ipc_spec = pld_spec | runtime_spec
log.runtime(
'Generating new IPC msg-spec\n'
f'{ipc_spec}\n'
)
return ( return (
pld_spec | runtime_spec, ipc_spec,
msgtypes_table[spec_build_method] + ipc_msg_types, msgtypes_table[spec_build_method] + ipc_msg_types,
) )