| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  | '''
 | 
					
						
							| 
									
										
										
										
											2024-03-29 17:48:08 +00:00
										 |  |  | Low-level functional audits for our | 
					
						
							|  |  |  | "capability based messaging"-spec feats. | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | B~) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | '''
 | 
					
						
							|  |  |  | from typing import ( | 
					
						
							|  |  |  |     Any, | 
					
						
							|  |  |  |     Type, | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  |     Union, | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  | ) | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | from msgspec import ( | 
					
						
							|  |  |  |     structs, | 
					
						
							|  |  |  |     msgpack, | 
					
						
							| 
									
										
										
										
											2025-03-07 19:13:36 +00:00
										 |  |  |     Raw, | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  |     Struct, | 
					
						
							|  |  |  |     ValidationError, | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2024-03-29 17:48:08 +00:00
										 |  |  | import pytest | 
					
						
							| 
									
										
										
										
											2025-03-07 19:13:36 +00:00
										 |  |  | import trio | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  | import tractor | 
					
						
							| 
									
										
										
										
											2024-04-14 23:50:09 +00:00
										 |  |  | from tractor import ( | 
					
						
							| 
									
										
										
										
											2025-03-07 19:13:36 +00:00
										 |  |  |     Actor, | 
					
						
							| 
									
										
										
										
											2024-04-14 23:50:09 +00:00
										 |  |  |     _state, | 
					
						
							|  |  |  |     MsgTypeError, | 
					
						
							| 
									
										
										
										
											2024-06-28 23:24:03 +00:00
										 |  |  |     Context, | 
					
						
							| 
									
										
										
										
											2024-04-14 23:50:09 +00:00
										 |  |  | ) | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  | from tractor.msg import ( | 
					
						
							| 
									
										
										
										
											2024-04-02 15:14:43 +00:00
										 |  |  |     _codec, | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  |     _ctxvar_MsgCodec, | 
					
						
							| 
									
										
										
										
											2025-03-07 19:38:22 +00:00
										 |  |  |     _exts, | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     NamespacePath, | 
					
						
							|  |  |  |     MsgCodec, | 
					
						
							| 
									
										
										
										
											2025-03-07 19:13:36 +00:00
										 |  |  |     MsgDec, | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  |     mk_codec, | 
					
						
							| 
									
										
										
										
											2025-03-07 19:13:36 +00:00
										 |  |  |     mk_dec, | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  |     apply_codec, | 
					
						
							| 
									
										
										
										
											2024-04-02 15:14:43 +00:00
										 |  |  |     current_codec, | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  | ) | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  | from tractor.msg.types import ( | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |     _payload_msgs, | 
					
						
							|  |  |  |     log, | 
					
						
							| 
									
										
										
										
											2024-06-28 23:24:03 +00:00
										 |  |  |     PayloadMsg, | 
					
						
							| 
									
										
										
										
											2024-04-02 15:14:43 +00:00
										 |  |  |     Started, | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  |     mk_msg_spec, | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2025-03-07 19:13:36 +00:00
										 |  |  | from tractor.msg._ops import ( | 
					
						
							|  |  |  |     limit_plds, | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-29 17:48:08 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def mk_custom_codec( | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |     add_hooks: bool, | 
					
						
							| 
									
										
										
										
											2024-04-02 15:14:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-07 19:13:36 +00:00
										 |  |  | ) -> tuple[ | 
					
						
							|  |  |  |     MsgCodec,  # encode to send | 
					
						
							|  |  |  |     MsgDec,  # pld receive-n-decode | 
					
						
							|  |  |  | ]: | 
					
						
							| 
									
										
										
										
											2024-04-02 15:14:43 +00:00
										 |  |  |     '''
 | 
					
						
							|  |  |  |     Create custom `msgpack` enc/dec-hooks and set a `Decoder` | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |     which only loads `pld_spec` (like `NamespacePath`) types. | 
					
						
							| 
									
										
										
										
											2024-04-02 15:14:43 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     '''
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # XXX NOTE XXX: despite defining `NamespacePath` as a type | 
					
						
							| 
									
										
										
										
											2024-06-28 23:24:03 +00:00
										 |  |  |     # field on our `PayloadMsg.pld`, we still need a enc/dec_hook() pair | 
					
						
							| 
									
										
										
										
											2024-04-02 15:14:43 +00:00
										 |  |  |     # to cast to/from that type on the wire. See the docs: | 
					
						
							|  |  |  |     # https://jcristharif.com/msgspec/extending.html#mapping-to-from-native-types | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-07 19:13:36 +00:00
										 |  |  |     # if pld_spec is Any: | 
					
						
							|  |  |  |     #     pld_spec = Raw | 
					
						
							| 
									
										
										
										
											2024-04-02 15:14:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  |     nsp_codec: MsgCodec = mk_codec( | 
					
						
							| 
									
										
										
										
											2025-03-07 19:13:36 +00:00
										 |  |  |         # ipc_pld_spec=Raw,  # default! | 
					
						
							| 
									
										
										
										
											2024-04-02 15:14:43 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # NOTE XXX: the encode hook MUST be used no matter what since | 
					
						
							|  |  |  |         # our `NamespacePath` is not any of a `Any` native type nor | 
					
						
							|  |  |  |         # a `msgspec.Struct` subtype - so `msgspec` has no way to know | 
					
						
							|  |  |  |         # how to encode it unless we provide the custom hook. | 
					
						
							|  |  |  |         # | 
					
						
							|  |  |  |         # AGAIN that is, regardless of whether we spec an | 
					
						
							|  |  |  |         # `Any`-decoded-pld the enc has no knowledge (by default) | 
					
						
							|  |  |  |         # how to enc `NamespacePath` (nsp), so we add a custom | 
					
						
							|  |  |  |         # hook to do that ALWAYS. | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |         enc_hook=enc_nsp if add_hooks else None, | 
					
						
							| 
									
										
										
										
											2024-04-02 15:14:43 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # XXX NOTE: pretty sure this is mutex with the `type=` to | 
					
						
							|  |  |  |         # `Decoder`? so it won't work in tandem with the | 
					
						
							|  |  |  |         # `ipc_pld_spec` passed above? | 
					
						
							| 
									
										
										
										
											2025-03-07 19:13:36 +00:00
										 |  |  |         ext_types=[NamespacePath], | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  |     ) | 
					
						
							| 
									
										
										
										
											2025-03-07 19:13:36 +00:00
										 |  |  |     # dec_hook=dec_nsp if add_hooks else None, | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  |     return nsp_codec | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  | def chk_codec_applied( | 
					
						
							|  |  |  |     expect_codec: MsgCodec, | 
					
						
							|  |  |  |     enter_value: MsgCodec|None = None, | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  | ) -> MsgCodec: | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  |     '''
 | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |     buncha sanity checks ensuring that the IPC channel's | 
					
						
							|  |  |  |     context-vars are set to the expected codec and that are | 
					
						
							|  |  |  |     ctx-var wrapper APIs match the same. | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     '''
 | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |     # TODO: play with tricyle again, bc this is supposed to work | 
					
						
							|  |  |  |     # the way we want? | 
					
						
							|  |  |  |     # | 
					
						
							| 
									
										
										
										
											2024-04-02 15:14:43 +00:00
										 |  |  |     # TreeVar | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |     # task: trio.Task = trio.lowlevel.current_task() | 
					
						
							| 
									
										
										
										
											2024-04-02 15:14:43 +00:00
										 |  |  |     # curr_codec = _ctxvar_MsgCodec.get_in(task) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # ContextVar | 
					
						
							|  |  |  |     # task_ctx: Context = task.context | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |     # assert _ctxvar_MsgCodec in task_ctx | 
					
						
							|  |  |  |     # curr_codec: MsgCodec = task.context[_ctxvar_MsgCodec] | 
					
						
							| 
									
										
										
										
											2024-04-02 15:14:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |     # NOTE: currently we use this! | 
					
						
							|  |  |  |     # RunVar | 
					
						
							|  |  |  |     curr_codec: MsgCodec = current_codec() | 
					
						
							|  |  |  |     last_read_codec = _ctxvar_MsgCodec.get() | 
					
						
							|  |  |  |     # assert curr_codec is last_read_codec | 
					
						
							| 
									
										
										
										
											2024-04-02 15:14:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |     assert ( | 
					
						
							|  |  |  |         (same_codec := expect_codec) is | 
					
						
							|  |  |  |         # returned from `mk_codec()` | 
					
						
							| 
									
										
										
										
											2024-04-02 15:14:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |         # yielded value from `apply_codec()` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # read from current task's `contextvars.Context` | 
					
						
							|  |  |  |         curr_codec is | 
					
						
							|  |  |  |         last_read_codec | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # the default `msgspec` settings | 
					
						
							|  |  |  |         is not _codec._def_msgspec_codec | 
					
						
							|  |  |  |         is not _codec._def_tractor_codec | 
					
						
							| 
									
										
										
										
											2024-04-02 15:14:43 +00:00
										 |  |  |     ) | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if enter_value: | 
					
						
							|  |  |  |         enter_value is same_codec | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def iter_maybe_sends( | 
					
						
							|  |  |  |     send_items: dict[Union[Type], Any] | list[tuple], | 
					
						
							|  |  |  |     ipc_pld_spec: Union[Type] | Any, | 
					
						
							|  |  |  |     add_codec_hooks: bool, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     codec: MsgCodec|None = None, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ) -> tuple[Any, bool]: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if isinstance(send_items, dict): | 
					
						
							|  |  |  |         send_items = send_items.items() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for ( | 
					
						
							|  |  |  |         send_type_spec, | 
					
						
							|  |  |  |         send_value, | 
					
						
							|  |  |  |     ) in send_items: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         expect_roundtrip: bool = False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # values-to-typespec santiy | 
					
						
							|  |  |  |         send_type = type(send_value) | 
					
						
							|  |  |  |         assert send_type == send_type_spec or ( | 
					
						
							|  |  |  |             (subtypes := getattr(send_type_spec, '__args__', None)) | 
					
						
							|  |  |  |             and send_type in subtypes | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |         spec_subtypes: set[Union[Type]] = ( | 
					
						
							|  |  |  |              getattr( | 
					
						
							|  |  |  |                  ipc_pld_spec, | 
					
						
							|  |  |  |                  '__args__', | 
					
						
							|  |  |  |                  {ipc_pld_spec,}, | 
					
						
							|  |  |  |              ) | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         send_in_spec: bool = ( | 
					
						
							|  |  |  |             send_type == ipc_pld_spec | 
					
						
							|  |  |  |             or ( | 
					
						
							|  |  |  |                 ipc_pld_spec != Any | 
					
						
							|  |  |  |                 and  # presume `Union` of types | 
					
						
							|  |  |  |                 send_type in spec_subtypes | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |             or ( | 
					
						
							|  |  |  |                 ipc_pld_spec == Any | 
					
						
							|  |  |  |                 and | 
					
						
							|  |  |  |                 send_type != NamespacePath | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         expect_roundtrip = ( | 
					
						
							|  |  |  |             send_in_spec | 
					
						
							|  |  |  |             # any spec should support all other | 
					
						
							|  |  |  |             # builtin py values that we send | 
					
						
							|  |  |  |             # except our custom nsp type which | 
					
						
							|  |  |  |             # we should be able to send as long | 
					
						
							|  |  |  |             # as we provide the custom codec hooks. | 
					
						
							|  |  |  |             or ( | 
					
						
							|  |  |  |                 ipc_pld_spec == Any | 
					
						
							|  |  |  |                 and | 
					
						
							|  |  |  |                 send_type == NamespacePath | 
					
						
							|  |  |  |                 and | 
					
						
							|  |  |  |                 add_codec_hooks | 
					
						
							| 
									
										
										
										
											2024-04-02 15:14:43 +00:00
										 |  |  |             ) | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |         if codec is not None: | 
					
						
							|  |  |  |             # XXX FIRST XXX ensure roundtripping works | 
					
						
							|  |  |  |             # before touching any IPC primitives/APIs. | 
					
						
							|  |  |  |             wire_bytes: bytes = codec.encode( | 
					
						
							|  |  |  |                 Started( | 
					
						
							|  |  |  |                     cid='blahblah', | 
					
						
							|  |  |  |                     pld=send_value, | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |             # NOTE: demonstrates the decoder loading | 
					
						
							|  |  |  |             # to via our native SCIPP msg-spec | 
					
						
							|  |  |  |             # (structurred-conc-inter-proc-protocol) | 
					
						
							|  |  |  |             # implemented as per, | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 msg: Started = codec.decode(wire_bytes) | 
					
						
							|  |  |  |                 if not expect_roundtrip: | 
					
						
							|  |  |  |                     pytest.fail( | 
					
						
							|  |  |  |                         f'NOT-EXPECTED able to roundtrip value given spec:\n' | 
					
						
							|  |  |  |                         f'ipc_pld_spec -> {ipc_pld_spec}\n' | 
					
						
							|  |  |  |                         f'value -> {send_value}: {send_type}\n' | 
					
						
							|  |  |  |                     ) | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |                 pld = msg.pld | 
					
						
							|  |  |  |                 assert pld == send_value | 
					
						
							| 
									
										
										
										
											2024-04-02 15:14:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |             except ValidationError: | 
					
						
							|  |  |  |                 if expect_roundtrip: | 
					
						
							|  |  |  |                     pytest.fail( | 
					
						
							|  |  |  |                         f'EXPECTED to roundtrip value given spec:\n' | 
					
						
							|  |  |  |                         f'ipc_pld_spec -> {ipc_pld_spec}\n' | 
					
						
							|  |  |  |                         f'value -> {send_value}: {send_type}\n' | 
					
						
							|  |  |  |                     ) | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |         yield ( | 
					
						
							|  |  |  |             str(send_type), | 
					
						
							|  |  |  |             send_value, | 
					
						
							|  |  |  |             expect_roundtrip, | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  | @tractor.context | 
					
						
							| 
									
										
										
										
											2024-04-08 14:13:14 +00:00
										 |  |  | async def send_back_values( | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |     ctx: Context, | 
					
						
							|  |  |  |     expect_debug: bool, | 
					
						
							|  |  |  |     pld_spec_type_strs: list[str], | 
					
						
							|  |  |  |     add_hooks: bool, | 
					
						
							| 
									
										
										
										
											2025-03-07 19:13:36 +00:00
										 |  |  |     # started_msg_bytes: bytes, | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |     expect_ipc_send: dict[str, tuple[Any, bool]], | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  | ) -> None: | 
					
						
							|  |  |  |     '''
 | 
					
						
							|  |  |  |     Setup up a custom codec to load instances of `NamespacePath` | 
					
						
							|  |  |  |     and ensure we can round trip a func ref with our parent. | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |     '''
 | 
					
						
							| 
									
										
										
										
											2024-04-08 14:13:14 +00:00
										 |  |  |     uid: tuple = tractor.current_actor().uid | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |     # debug mode sanity check (prolly superfluous but, meh) | 
					
						
							|  |  |  |     assert expect_debug == _state.debug_mode() | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |     # init state in sub-actor should be default | 
					
						
							|  |  |  |     chk_codec_applied( | 
					
						
							|  |  |  |         expect_codec=_codec._def_tractor_codec, | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |     # load pld spec from input str | 
					
						
							| 
									
										
										
										
											2025-03-07 19:38:22 +00:00
										 |  |  |     ipc_pld_spec = _exts.dec_type_union( | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |         pld_spec_type_strs, | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     pld_spec_str = str(ipc_pld_spec) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # same as on parent side config. | 
					
						
							|  |  |  |     nsp_codec: MsgCodec = mk_custom_codec( | 
					
						
							|  |  |  |         add_hooks=add_hooks, | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  |     ) | 
					
						
							| 
									
										
										
										
											2024-04-14 23:50:09 +00:00
										 |  |  |     with ( | 
					
						
							|  |  |  |         apply_codec(nsp_codec) as codec, | 
					
						
							| 
									
										
										
										
											2025-03-07 19:13:36 +00:00
										 |  |  |         limit_plds(ipc_pld_spec) as codec, | 
					
						
							| 
									
										
										
										
											2024-04-14 23:50:09 +00:00
										 |  |  |     ): | 
					
						
							| 
									
										
										
										
											2025-03-07 19:13:36 +00:00
										 |  |  |         # we SHOULD NOT be swapping the global codec since it breaks | 
					
						
							|  |  |  |         # `Context.starte()` roundtripping checks! | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |         chk_codec_applied( | 
					
						
							|  |  |  |             expect_codec=nsp_codec, | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2025-03-07 19:13:36 +00:00
										 |  |  |         # XXX SO NOT THIS! | 
					
						
							|  |  |  |         # chk_codec_applied( | 
					
						
							|  |  |  |         #     expect_codec=nsp_codec, | 
					
						
							|  |  |  |         #     enter_value=codec, | 
					
						
							|  |  |  |         # ) | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         print( | 
					
						
							| 
									
										
										
										
											2024-04-08 14:13:14 +00:00
										 |  |  |             f'{uid}: attempting `Started`-bytes DECODE..\n' | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |         ) | 
					
						
							|  |  |  |         try: | 
					
						
							| 
									
										
										
										
											2025-03-07 19:13:36 +00:00
										 |  |  |             # msg: Started = nsp_codec.decode(started_msg_bytes) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             ipc_spec: Type = ctx._pld_rx._pld_dec.spec | 
					
						
							|  |  |  |             expected_pld_spec_str: str = str(ipc_spec) | 
					
						
							|  |  |  |             assert ( | 
					
						
							|  |  |  |                 pld_spec_str == expected_pld_spec_str | 
					
						
							|  |  |  |                 and | 
					
						
							|  |  |  |                 ipc_pld_spec == ipc_spec | 
					
						
							|  |  |  |             ) | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # TODO: maybe we should add our own wrapper error so as to | 
					
						
							|  |  |  |         # be interchange-lib agnostic? | 
					
						
							|  |  |  |         # -[ ] the error type is wtv is raised from the hook so we | 
					
						
							|  |  |  |         #   could also require a type-class of errors for | 
					
						
							|  |  |  |         #   indicating whether the hook-failure can be handled by | 
					
						
							|  |  |  |         #   a nasty-dialog-unprot sub-sys? | 
					
						
							|  |  |  |         except ValidationError: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # NOTE: only in the `Any` spec case do we expect this to | 
					
						
							|  |  |  |             # work since otherwise no spec covers a plain-ol' | 
					
						
							|  |  |  |             # `.pld: str` | 
					
						
							|  |  |  |             if pld_spec_str == 'Any': | 
					
						
							|  |  |  |                 raise | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 print( | 
					
						
							| 
									
										
										
										
											2024-04-08 14:13:14 +00:00
										 |  |  |                     f'{uid}: (correctly) unable to DECODE `Started`-bytes\n' | 
					
						
							| 
									
										
										
										
											2025-03-07 19:13:36 +00:00
										 |  |  |                     # f'{started_msg_bytes}\n' | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |                 ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         iter_send_val_items = iter(expect_ipc_send.values()) | 
					
						
							|  |  |  |         sent: list[Any] = [] | 
					
						
							| 
									
										
										
										
											2025-03-07 19:13:36 +00:00
										 |  |  |         for ( | 
					
						
							|  |  |  |             send_value, | 
					
						
							|  |  |  |             expect_send, | 
					
						
							|  |  |  |         ) in iter_send_val_items: | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |             try: | 
					
						
							|  |  |  |                 print( | 
					
						
							| 
									
										
										
										
											2024-04-08 14:13:14 +00:00
										 |  |  |                     f'{uid}: attempting to `.started({send_value})`\n' | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |                     f'=> expect_send: {expect_send}\n' | 
					
						
							|  |  |  |                     f'SINCE, ipc_pld_spec: {ipc_pld_spec}\n' | 
					
						
							|  |  |  |                     f'AND, codec: {codec}\n' | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |                 await ctx.started(send_value) | 
					
						
							|  |  |  |                 sent.append(send_value) | 
					
						
							|  |  |  |                 if not expect_send: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     # XXX NOTE XXX THIS WON'T WORK WITHOUT SPECIAL | 
					
						
							|  |  |  |                     # `str` handling! or special debug mode IPC | 
					
						
							|  |  |  |                     # msgs! | 
					
						
							| 
									
										
										
										
											2024-04-14 23:50:09 +00:00
										 |  |  |                     await tractor.pause() | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |                     raise RuntimeError( | 
					
						
							|  |  |  |                         f'NOT-EXPECTED able to roundtrip value given spec:\n' | 
					
						
							|  |  |  |                         f'ipc_pld_spec -> {ipc_pld_spec}\n' | 
					
						
							|  |  |  |                         f'value -> {send_value}: {type(send_value)}\n' | 
					
						
							|  |  |  |                     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 break  # move on to streaming block.. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-07 19:13:36 +00:00
										 |  |  |             except tractor.MsgTypeError as _mte: | 
					
						
							|  |  |  |                 mte = _mte | 
					
						
							| 
									
										
										
										
											2024-04-14 23:50:09 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |                 if expect_send: | 
					
						
							| 
									
										
										
										
											2024-04-08 14:13:14 +00:00
										 |  |  |                     raise RuntimeError( | 
					
						
							| 
									
										
										
										
											2025-03-07 19:13:36 +00:00
										 |  |  |                         f'EXPECTED to `.started()` value given spec ??\n\n' | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |                         f'ipc_pld_spec -> {ipc_pld_spec}\n' | 
					
						
							|  |  |  |                         f'value -> {send_value}: {type(send_value)}\n' | 
					
						
							|  |  |  |                     ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-07 19:38:22 +00:00
										 |  |  |                 # await tractor.pause() | 
					
						
							|  |  |  |                 raise mte | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |         async with ctx.open_stream() as ipc: | 
					
						
							| 
									
										
										
										
											2024-04-08 14:13:14 +00:00
										 |  |  |             print( | 
					
						
							|  |  |  |                 f'{uid}: Entering streaming block to send remaining values..' | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |             for send_value, expect_send in iter_send_val_items: | 
					
						
							|  |  |  |                 send_type: Type = type(send_value) | 
					
						
							|  |  |  |                 print( | 
					
						
							| 
									
										
										
										
											2024-04-08 14:13:14 +00:00
										 |  |  |                     '------ - ------\n' | 
					
						
							|  |  |  |                     f'{uid}: SENDING NEXT VALUE\n' | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |                     f'ipc_pld_spec: {ipc_pld_spec}\n' | 
					
						
							|  |  |  |                     f'expect_send: {expect_send}\n' | 
					
						
							|  |  |  |                     f'val: {send_value}\n' | 
					
						
							| 
									
										
										
										
											2024-04-08 14:13:14 +00:00
										 |  |  |                     '------ - ------\n' | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |                 ) | 
					
						
							|  |  |  |                 try: | 
					
						
							|  |  |  |                     await ipc.send(send_value) | 
					
						
							| 
									
										
										
										
											2024-04-08 14:13:14 +00:00
										 |  |  |                     print(f'***\n{uid}-CHILD sent {send_value!r}\n***\n') | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |                     sent.append(send_value) | 
					
						
							| 
									
										
										
										
											2024-04-08 14:13:14 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |                     # NOTE: should only raise above on | 
					
						
							|  |  |  |                     # `.started()` or a `Return` | 
					
						
							|  |  |  |                     # if not expect_send: | 
					
						
							|  |  |  |                     #     raise RuntimeError( | 
					
						
							|  |  |  |                     #         f'NOT-EXPECTED able to roundtrip value given spec:\n' | 
					
						
							|  |  |  |                     #         f'ipc_pld_spec -> {ipc_pld_spec}\n' | 
					
						
							|  |  |  |                     #         f'value -> {send_value}: {send_type}\n' | 
					
						
							|  |  |  |                     #     ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |                 except ValidationError: | 
					
						
							| 
									
										
										
										
											2024-04-08 14:13:14 +00:00
										 |  |  |                     print(f'{uid} FAILED TO SEND {send_value}!') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     # await tractor.pause() | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |                     if expect_send: | 
					
						
							| 
									
										
										
										
											2024-04-08 14:13:14 +00:00
										 |  |  |                         raise RuntimeError( | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |                             f'EXPECTED to roundtrip value given spec:\n' | 
					
						
							|  |  |  |                             f'ipc_pld_spec -> {ipc_pld_spec}\n' | 
					
						
							|  |  |  |                             f'value -> {send_value}: {send_type}\n' | 
					
						
							|  |  |  |                         ) | 
					
						
							| 
									
										
										
										
											2024-04-08 14:13:14 +00:00
										 |  |  |                     # continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 print( | 
					
						
							|  |  |  |                     f'{uid}: finished sending all values\n' | 
					
						
							|  |  |  |                     'Should be exiting stream block!\n' | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         print(f'{uid}: exited streaming block!') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # TODO: this won't be true bc in streaming phase we DO NOT | 
					
						
							|  |  |  |         # msgspec check outbound msgs! | 
					
						
							|  |  |  |         # -[ ] once we implement the receiver side `InvalidMsg` | 
					
						
							|  |  |  |         #   then we can expect it here? | 
					
						
							|  |  |  |         # assert ( | 
					
						
							|  |  |  |         #     len(sent) | 
					
						
							|  |  |  |         #     == | 
					
						
							|  |  |  |         #     len([val | 
					
						
							|  |  |  |         #          for val, expect in | 
					
						
							|  |  |  |         #          expect_ipc_send.values() | 
					
						
							|  |  |  |         #          if expect is True]) | 
					
						
							|  |  |  |         # ) | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 15:14:43 +00:00
										 |  |  | @pytest.mark.parametrize( | 
					
						
							|  |  |  |     'ipc_pld_spec', | 
					
						
							|  |  |  |     [ | 
					
						
							|  |  |  |         Any, | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |         NamespacePath, | 
					
						
							|  |  |  |         NamespacePath|None,  # the "maybe" spec Bo | 
					
						
							| 
									
										
										
										
											2024-04-02 15:14:43 +00:00
										 |  |  |     ], | 
					
						
							|  |  |  |     ids=[ | 
					
						
							|  |  |  |         'any_type', | 
					
						
							|  |  |  |         'nsp_type', | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |         'maybe_nsp_type', | 
					
						
							| 
									
										
										
										
											2024-04-02 15:14:43 +00:00
										 |  |  |     ] | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  | @pytest.mark.parametrize( | 
					
						
							|  |  |  |     'add_codec_hooks', | 
					
						
							|  |  |  |     [ | 
					
						
							|  |  |  |         True, | 
					
						
							|  |  |  |         False, | 
					
						
							|  |  |  |     ], | 
					
						
							|  |  |  |     ids=['use_codec_hooks', 'no_codec_hooks'], | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2024-04-02 15:14:43 +00:00
										 |  |  | def test_codec_hooks_mod( | 
					
						
							|  |  |  |     debug_mode: bool, | 
					
						
							|  |  |  |     ipc_pld_spec: Union[Type]|Any, | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |     # send_value: None|str|NamespacePath, | 
					
						
							|  |  |  |     add_codec_hooks: bool, | 
					
						
							| 
									
										
										
										
											2024-04-02 15:14:43 +00:00
										 |  |  | ): | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  |     '''
 | 
					
						
							|  |  |  |     Audit the `.msg.MsgCodec` override apis details given our impl | 
					
						
							|  |  |  |     uses `contextvars` to accomplish per `trio` task codec | 
					
						
							|  |  |  |     application around an inter-proc-task-comms context. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     '''
 | 
					
						
							|  |  |  |     async def main(): | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |         nsp = NamespacePath.from_ref(ex_func) | 
					
						
							|  |  |  |         send_items: dict[Union, Any] = { | 
					
						
							|  |  |  |             Union[None]: None, | 
					
						
							|  |  |  |             Union[NamespacePath]: nsp, | 
					
						
							|  |  |  |             Union[str]: str(nsp), | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |         # init default state for actor | 
					
						
							|  |  |  |         chk_codec_applied( | 
					
						
							|  |  |  |             expect_codec=_codec._def_tractor_codec, | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2024-04-02 15:14:43 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         async with tractor.open_nursery( | 
					
						
							|  |  |  |             debug_mode=debug_mode, | 
					
						
							|  |  |  |         ) as an: | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  |             p: tractor.Portal = await an.start_actor( | 
					
						
							|  |  |  |                 'sub', | 
					
						
							|  |  |  |                 enable_modules=[__name__], | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # TODO: 2 cases: | 
					
						
							|  |  |  |             # - codec not modified -> decode nsp as `str` | 
					
						
							|  |  |  |             # - codec modified with hooks -> decode nsp as | 
					
						
							|  |  |  |             #   `NamespacePath` | 
					
						
							| 
									
										
										
										
											2024-04-02 15:14:43 +00:00
										 |  |  |             nsp_codec: MsgCodec = mk_custom_codec( | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |                 add_hooks=add_codec_hooks, | 
					
						
							| 
									
										
										
										
											2024-04-02 15:14:43 +00:00
										 |  |  |             ) | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  |             with apply_codec(nsp_codec) as codec: | 
					
						
							|  |  |  |                 chk_codec_applied( | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |                     expect_codec=nsp_codec, | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  |                     enter_value=codec, | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |                 expect_ipc_send: dict[str, tuple[Any, bool]] = {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 report: str = ( | 
					
						
							|  |  |  |                     'Parent report on send values with\n' | 
					
						
							|  |  |  |                     f'ipc_pld_spec: {ipc_pld_spec}\n' | 
					
						
							|  |  |  |                     '       ------ - ------\n' | 
					
						
							|  |  |  |                 ) | 
					
						
							| 
									
										
										
										
											2025-03-07 19:13:36 +00:00
										 |  |  |                 for ( | 
					
						
							|  |  |  |                     val_type_str, | 
					
						
							|  |  |  |                     val, | 
					
						
							|  |  |  |                     expect_send, | 
					
						
							|  |  |  |                 )in iter_maybe_sends( | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |                     send_items, | 
					
						
							|  |  |  |                     ipc_pld_spec, | 
					
						
							|  |  |  |                     add_codec_hooks=add_codec_hooks, | 
					
						
							|  |  |  |                 ): | 
					
						
							|  |  |  |                     report += ( | 
					
						
							|  |  |  |                         f'send_value: {val}: {type(val)} ' | 
					
						
							|  |  |  |                         f'=> expect_send: {expect_send}\n' | 
					
						
							|  |  |  |                     ) | 
					
						
							| 
									
										
										
										
											2025-03-07 19:13:36 +00:00
										 |  |  |                     expect_ipc_send[val_type_str] = ( | 
					
						
							|  |  |  |                         val, | 
					
						
							|  |  |  |                         expect_send, | 
					
						
							|  |  |  |                     ) | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 print( | 
					
						
							|  |  |  |                     report + | 
					
						
							|  |  |  |                     '       ------ - ------\n' | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |                 assert len(expect_ipc_send) == len(send_items) | 
					
						
							|  |  |  |                 # now try over real IPC with a the subactor | 
					
						
							|  |  |  |                 # expect_ipc_rountrip: bool = True | 
					
						
							| 
									
										
										
										
											2025-03-07 19:13:36 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 if ( | 
					
						
							|  |  |  |                     subtypes := getattr( | 
					
						
							|  |  |  |                         ipc_pld_spec, '__args__', False | 
					
						
							|  |  |  |                     ) | 
					
						
							|  |  |  |                 ): | 
					
						
							|  |  |  |                     pld_types_str: str = '|'.join(subtypes) | 
					
						
							| 
									
										
										
										
											2025-03-07 19:38:22 +00:00
										 |  |  |                     # breakpoint() | 
					
						
							| 
									
										
										
										
											2025-03-07 19:13:36 +00:00
										 |  |  |                 else: | 
					
						
							| 
									
										
										
										
											2025-03-07 19:38:22 +00:00
										 |  |  |                     # TODO, use `.msg._exts` utils  instead of this! | 
					
						
							| 
									
										
										
										
											2025-03-07 19:13:36 +00:00
										 |  |  |                     pld_types_str: str = ipc_pld_spec.__name__ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |                 expected_started = Started( | 
					
						
							|  |  |  |                     cid='cid', | 
					
						
							| 
									
										
										
										
											2025-03-07 19:13:36 +00:00
										 |  |  |                     # pld=str(pld_types_str), | 
					
						
							|  |  |  |                     pld=ipc_pld_spec, | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |                 started_msg_bytes: bytes = nsp_codec.encode( | 
					
						
							|  |  |  |                     expected_started, | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |                 ) | 
					
						
							|  |  |  |                 # build list of values we expect to receive from | 
					
						
							|  |  |  |                 # the subactor. | 
					
						
							|  |  |  |                 expect_to_send: list[Any] = [ | 
					
						
							|  |  |  |                     val | 
					
						
							|  |  |  |                     for val, expect_send in expect_ipc_send.values() | 
					
						
							|  |  |  |                     if expect_send | 
					
						
							|  |  |  |                 ] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-07 19:38:22 +00:00
										 |  |  |                 pld_spec_type_strs: list[str] = _exts.enc_type_union(ipc_pld_spec) | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-14 23:50:09 +00:00
										 |  |  |                 # XXX should raise an mte (`MsgTypeError`) | 
					
						
							|  |  |  |                 # when `add_codec_hooks == False` bc the input | 
					
						
							|  |  |  |                 # `expect_ipc_send` kwarg has a nsp which can't be | 
					
						
							|  |  |  |                 # serialized! | 
					
						
							|  |  |  |                 # | 
					
						
							|  |  |  |                 # TODO:can we ensure this happens from the | 
					
						
							|  |  |  |                 # `Return`-side (aka the sub) as well? | 
					
						
							|  |  |  |                 if not add_codec_hooks: | 
					
						
							|  |  |  |                     try: | 
					
						
							|  |  |  |                         async with p.open_context( | 
					
						
							|  |  |  |                             send_back_values, | 
					
						
							|  |  |  |                             expect_debug=debug_mode, | 
					
						
							|  |  |  |                             pld_spec_type_strs=pld_spec_type_strs, | 
					
						
							|  |  |  |                             add_hooks=add_codec_hooks, | 
					
						
							| 
									
										
										
										
											2025-03-07 19:13:36 +00:00
										 |  |  |                             started_msg_bytes=started_msg_bytes, | 
					
						
							| 
									
										
										
										
											2024-04-14 23:50:09 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |                             # XXX NOTE bc we send a `NamespacePath` in this kwarg | 
					
						
							|  |  |  |                             expect_ipc_send=expect_ipc_send, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                         ) as (ctx, first): | 
					
						
							|  |  |  |                             pytest.fail('ctx should fail to open without custom enc_hook!?') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     # this test passes bc we can go no further! | 
					
						
							|  |  |  |                     except MsgTypeError: | 
					
						
							|  |  |  |                         # teardown nursery | 
					
						
							|  |  |  |                         await p.cancel_actor() | 
					
						
							|  |  |  |                         return | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |                 # TODO: send the original nsp here and | 
					
						
							|  |  |  |                 # test with `limit_msg_spec()` above? | 
					
						
							|  |  |  |                 # await tractor.pause() | 
					
						
							|  |  |  |                 print('PARENT opening IPC ctx!\n') | 
					
						
							| 
									
										
										
										
											2025-03-07 19:13:36 +00:00
										 |  |  |                 ctx: tractor.Context | 
					
						
							|  |  |  |                 ipc: tractor.MsgStream | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  |                 async with ( | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-14 23:50:09 +00:00
										 |  |  |                     # XXX should raise an mte (`MsgTypeError`) | 
					
						
							|  |  |  |                     # when `add_codec_hooks == False`.. | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  |                     p.open_context( | 
					
						
							| 
									
										
										
										
											2024-04-08 14:13:14 +00:00
										 |  |  |                         send_back_values, | 
					
						
							| 
									
										
										
										
											2024-04-02 15:14:43 +00:00
										 |  |  |                         expect_debug=debug_mode, | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |                         pld_spec_type_strs=pld_spec_type_strs, | 
					
						
							|  |  |  |                         add_hooks=add_codec_hooks, | 
					
						
							|  |  |  |                         started_msg_bytes=nsp_codec.encode(expected_started), | 
					
						
							|  |  |  |                         expect_ipc_send=expect_ipc_send, | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  |                     ) as (ctx, first), | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  |                     ctx.open_stream() as ipc, | 
					
						
							|  |  |  |                 ): | 
					
						
							|  |  |  |                     # ensure codec is still applied across | 
					
						
							|  |  |  |                     # `tractor.Context` + its embedded nursery. | 
					
						
							|  |  |  |                     chk_codec_applied( | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |                         expect_codec=nsp_codec, | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  |                         enter_value=codec, | 
					
						
							|  |  |  |                     ) | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |                     print( | 
					
						
							|  |  |  |                         'root: ENTERING CONTEXT BLOCK\n' | 
					
						
							|  |  |  |                         f'type(first): {type(first)}\n' | 
					
						
							|  |  |  |                         f'first: {first}\n' | 
					
						
							|  |  |  |                     ) | 
					
						
							|  |  |  |                     expect_to_send.remove(first) | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |                     # TODO: explicit values we expect depending on | 
					
						
							|  |  |  |                     # codec config! | 
					
						
							|  |  |  |                     # assert first == first_val | 
					
						
							|  |  |  |                     # assert first == f'{__name__}:ex_func' | 
					
						
							| 
									
										
										
										
											2024-04-02 15:14:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |                     async for next_sent in ipc: | 
					
						
							|  |  |  |                         print( | 
					
						
							| 
									
										
										
										
											2024-04-08 14:13:14 +00:00
										 |  |  |                             'Parent: child sent next value\n' | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |                             f'{next_sent}: {type(next_sent)}\n' | 
					
						
							| 
									
										
										
										
											2024-04-02 15:14:43 +00:00
										 |  |  |                         ) | 
					
						
							| 
									
										
										
										
											2024-04-08 14:13:14 +00:00
										 |  |  |                         if expect_to_send: | 
					
						
							|  |  |  |                             expect_to_send.remove(next_sent) | 
					
						
							|  |  |  |                         else: | 
					
						
							|  |  |  |                             print('PARENT should terminate stream loop + block!') | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |                     # all sent values should have arrived! | 
					
						
							|  |  |  |                     assert not expect_to_send | 
					
						
							| 
									
										
										
										
											2024-03-26 19:50:47 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |             await p.cancel_actor() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     trio.run(main) | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def chk_pld_type( | 
					
						
							| 
									
										
										
										
											2024-03-29 22:46:37 +00:00
										 |  |  |     payload_spec: Type[Struct]|Any, | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  |     pld: Any, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-29 22:46:37 +00:00
										 |  |  |     expect_roundtrip: bool|None = None, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  | ) -> bool: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pld_val_type: Type = type(pld) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-29 17:48:08 +00:00
										 |  |  |     # TODO: verify that the overridden subtypes | 
					
						
							|  |  |  |     # DO NOT have modified type-annots from original! | 
					
						
							|  |  |  |     # 'Start',  .pld: FuncSpec | 
					
						
							|  |  |  |     # 'StartAck',  .pld: IpcCtxSpec | 
					
						
							|  |  |  |     # 'Stop',  .pld: UNSEt | 
					
						
							|  |  |  |     # 'Error',  .pld: ErrorData | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-29 22:46:37 +00:00
										 |  |  |     codec: MsgCodec = mk_codec( | 
					
						
							| 
									
										
										
										
											2024-06-28 23:24:03 +00:00
										 |  |  |         # NOTE: this ONLY accepts `PayloadMsg.pld` fields of a specified | 
					
						
							| 
									
										
										
										
											2024-03-29 22:46:37 +00:00
										 |  |  |         # type union. | 
					
						
							|  |  |  |         ipc_pld_spec=payload_spec, | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2024-03-29 17:48:08 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # make a one-off dec to compare with our `MsgCodec` instance | 
					
						
							|  |  |  |     # which does the below `mk_msg_spec()` call internally | 
					
						
							| 
									
										
										
										
											2024-03-29 22:46:37 +00:00
										 |  |  |     ipc_msg_spec: Union[Type[Struct]] | 
					
						
							| 
									
										
										
										
											2024-06-28 23:24:03 +00:00
										 |  |  |     msg_types: list[PayloadMsg[payload_spec]] | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  |     ( | 
					
						
							| 
									
										
										
										
											2024-03-29 22:46:37 +00:00
										 |  |  |         ipc_msg_spec, | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  |         msg_types, | 
					
						
							|  |  |  |     ) = mk_msg_spec( | 
					
						
							| 
									
										
										
										
											2024-03-29 22:46:37 +00:00
										 |  |  |         payload_type_union=payload_spec, | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  |     ) | 
					
						
							| 
									
										
										
										
											2024-03-29 22:46:37 +00:00
										 |  |  |     _enc = msgpack.Encoder() | 
					
						
							|  |  |  |     _dec = msgpack.Decoder( | 
					
						
							| 
									
										
										
										
											2024-06-28 23:24:03 +00:00
										 |  |  |         type=ipc_msg_spec or Any,  # like `PayloadMsg[Any]` | 
					
						
							| 
									
										
										
										
											2024-03-29 17:48:08 +00:00
										 |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-29 22:46:37 +00:00
										 |  |  |     assert ( | 
					
						
							|  |  |  |         payload_spec | 
					
						
							|  |  |  |         == | 
					
						
							|  |  |  |         codec.pld_spec | 
					
						
							| 
									
										
										
										
											2024-03-29 17:48:08 +00:00
										 |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # assert codec.dec == dec | 
					
						
							| 
									
										
										
										
											2024-03-29 22:46:37 +00:00
										 |  |  |     # | 
					
						
							|  |  |  |     # ^-XXX-^ not sure why these aren't "equal" but when cast | 
					
						
							| 
									
										
										
										
											2024-03-29 17:48:08 +00:00
										 |  |  |     # to `str` they seem to match ?? .. kk | 
					
						
							| 
									
										
										
										
											2024-03-29 22:46:37 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-29 17:48:08 +00:00
										 |  |  |     assert ( | 
					
						
							| 
									
										
										
										
											2024-03-29 22:46:37 +00:00
										 |  |  |         str(ipc_msg_spec) | 
					
						
							| 
									
										
										
										
											2024-03-29 17:48:08 +00:00
										 |  |  |         == | 
					
						
							| 
									
										
										
										
											2024-03-29 22:46:37 +00:00
										 |  |  |         str(codec.msg_spec) | 
					
						
							| 
									
										
										
										
											2024-03-29 17:48:08 +00:00
										 |  |  |         == | 
					
						
							| 
									
										
										
										
											2024-03-29 22:46:37 +00:00
										 |  |  |         str(_dec.type) | 
					
						
							| 
									
										
										
										
											2024-03-29 17:48:08 +00:00
										 |  |  |         == | 
					
						
							|  |  |  |         str(codec.dec.type) | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # verify the boxed-type for all variable payload-type msgs. | 
					
						
							| 
									
										
										
										
											2024-03-29 22:46:37 +00:00
										 |  |  |     if not msg_types: | 
					
						
							|  |  |  |         breakpoint() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     roundtrip: bool|None = None | 
					
						
							|  |  |  |     pld_spec_msg_names: list[str] = [ | 
					
						
							| 
									
										
										
										
											2024-04-05 15:36:09 +00:00
										 |  |  |         td.__name__ for td in _payload_msgs | 
					
						
							| 
									
										
										
										
											2024-03-29 22:46:37 +00:00
										 |  |  |     ] | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  |     for typedef in msg_types: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-29 22:46:37 +00:00
										 |  |  |         skip_runtime_msg: bool = typedef.__name__ not in pld_spec_msg_names | 
					
						
							|  |  |  |         if skip_runtime_msg: | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  |         pld_field = structs.fields(typedef)[1] | 
					
						
							| 
									
										
										
										
											2024-03-29 22:46:37 +00:00
										 |  |  |         assert pld_field.type is payload_spec # TODO-^ does this need to work to get all subtypes to adhere? | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         kwargs: dict[str, Any] = { | 
					
						
							|  |  |  |             'cid': '666', | 
					
						
							|  |  |  |             'pld': pld, | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2024-06-28 23:24:03 +00:00
										 |  |  |         enc_msg: PayloadMsg = typedef(**kwargs) | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-29 22:46:37 +00:00
										 |  |  |         _wire_bytes: bytes = _enc.encode(enc_msg) | 
					
						
							| 
									
										
										
										
											2024-03-29 17:48:08 +00:00
										 |  |  |         wire_bytes: bytes = codec.enc.encode(enc_msg) | 
					
						
							| 
									
										
										
										
											2024-03-29 22:46:37 +00:00
										 |  |  |         assert _wire_bytes == wire_bytes | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-29 22:46:37 +00:00
										 |  |  |         ve: ValidationError|None = None | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2024-03-29 17:48:08 +00:00
										 |  |  |             dec_msg = codec.dec.decode(wire_bytes) | 
					
						
							| 
									
										
										
										
											2024-03-29 22:46:37 +00:00
										 |  |  |             _dec_msg = _dec.decode(wire_bytes) | 
					
						
							| 
									
										
										
										
											2024-03-29 17:48:08 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-29 22:46:37 +00:00
										 |  |  |             # decoded msg and thus payload should be exactly same! | 
					
						
							|  |  |  |             assert (roundtrip := ( | 
					
						
							|  |  |  |                 _dec_msg | 
					
						
							|  |  |  |                 == | 
					
						
							|  |  |  |                 dec_msg | 
					
						
							|  |  |  |                 == | 
					
						
							|  |  |  |                 enc_msg | 
					
						
							|  |  |  |             )) | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-29 22:46:37 +00:00
										 |  |  |             if ( | 
					
						
							|  |  |  |                 expect_roundtrip is not None | 
					
						
							|  |  |  |                 and expect_roundtrip != roundtrip | 
					
						
							|  |  |  |             ): | 
					
						
							|  |  |  |                 breakpoint() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             assert ( | 
					
						
							|  |  |  |                 pld | 
					
						
							|  |  |  |                 == | 
					
						
							|  |  |  |                 dec_msg.pld | 
					
						
							|  |  |  |                 == | 
					
						
							|  |  |  |                 enc_msg.pld | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |             # assert (roundtrip := (_dec_msg == enc_msg)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         except ValidationError as _ve: | 
					
						
							|  |  |  |             ve = _ve | 
					
						
							|  |  |  |             roundtrip: bool = False | 
					
						
							|  |  |  |             if pld_val_type is payload_spec: | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  |                 raise ValueError( | 
					
						
							|  |  |  |                    'Got `ValidationError` despite type-var match!?\n' | 
					
						
							|  |  |  |                     f'pld_val_type: {pld_val_type}\n' | 
					
						
							| 
									
										
										
										
											2024-03-29 22:46:37 +00:00
										 |  |  |                     f'payload_type: {payload_spec}\n' | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  |                 ) from ve | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 # ow we good cuz the pld spec mismatched. | 
					
						
							|  |  |  |                 print( | 
					
						
							|  |  |  |                     'Got expected `ValidationError` since,\n' | 
					
						
							| 
									
										
										
										
											2024-03-29 22:46:37 +00:00
										 |  |  |                     f'{pld_val_type} is not {payload_spec}\n' | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  |                 ) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             if ( | 
					
						
							| 
									
										
										
										
											2024-03-29 22:46:37 +00:00
										 |  |  |                 payload_spec is not Any | 
					
						
							|  |  |  |                 and | 
					
						
							|  |  |  |                 pld_val_type is not payload_spec | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  |             ): | 
					
						
							|  |  |  |                 raise ValueError( | 
					
						
							|  |  |  |                    'DID NOT `ValidationError` despite expected type match!?\n' | 
					
						
							|  |  |  |                     f'pld_val_type: {pld_val_type}\n' | 
					
						
							| 
									
										
										
										
											2024-03-29 22:46:37 +00:00
										 |  |  |                     f'payload_type: {payload_spec}\n' | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  |                 ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-29 22:46:37 +00:00
										 |  |  |     # full code decode should always be attempted! | 
					
						
							|  |  |  |     if roundtrip is None: | 
					
						
							|  |  |  |         breakpoint() | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-29 22:46:37 +00:00
										 |  |  |     return roundtrip | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-07 19:38:22 +00:00
										 |  |  | # ?TODO? remove since covered in the newer `test_pldrx_limiting`? | 
					
						
							| 
									
										
										
										
											2025-03-03 17:24:29 +00:00
										 |  |  | def test_limit_msgspec( | 
					
						
							|  |  |  |     debug_mode: bool, | 
					
						
							|  |  |  | ): | 
					
						
							| 
									
										
										
										
											2025-03-07 19:13:36 +00:00
										 |  |  |     '''
 | 
					
						
							| 
									
										
										
										
											2025-03-07 19:38:22 +00:00
										 |  |  |     Internals unit testing to verify that type-limiting an IPC ctx's | 
					
						
							|  |  |  |     msg spec with `Pldrx.limit_plds()` results in various | 
					
						
							|  |  |  |     encapsulated `msgspec` object settings and state. | 
					
						
							| 
									
										
										
										
											2025-03-07 19:13:36 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     '''
 | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  |     async def main(): | 
					
						
							|  |  |  |         async with tractor.open_root_actor( | 
					
						
							| 
									
										
										
										
											2025-03-03 17:24:29 +00:00
										 |  |  |             debug_mode=debug_mode, | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  |         ): | 
					
						
							| 
									
										
										
										
											2024-06-28 23:24:03 +00:00
										 |  |  |             # ensure we can round-trip a boxing `PayloadMsg` | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  |             assert chk_pld_type( | 
					
						
							| 
									
										
										
										
											2024-06-28 23:24:03 +00:00
										 |  |  |                 payload_spec=Any, | 
					
						
							|  |  |  |                 pld=None, | 
					
						
							| 
									
										
										
										
											2024-03-29 22:46:37 +00:00
										 |  |  |                 expect_roundtrip=True, | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  |             ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # verify that a mis-typed payload value won't decode | 
					
						
							|  |  |  |             assert not chk_pld_type( | 
					
						
							| 
									
										
										
										
											2024-06-28 23:24:03 +00:00
										 |  |  |                 payload_spec=int, | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  |                 pld='doggy', | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # parametrize the boxed `.pld` type as a custom-struct | 
					
						
							|  |  |  |             # and ensure that parametrization propagates | 
					
						
							|  |  |  |             # to all payload-msg-spec-able subtypes! | 
					
						
							|  |  |  |             class CustomPayload(Struct): | 
					
						
							|  |  |  |                 name: str | 
					
						
							|  |  |  |                 value: Any | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             assert not chk_pld_type( | 
					
						
							| 
									
										
										
										
											2024-06-28 23:24:03 +00:00
										 |  |  |                 payload_spec=CustomPayload, | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  |                 pld='doggy', | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             assert chk_pld_type( | 
					
						
							| 
									
										
										
										
											2024-06-28 23:24:03 +00:00
										 |  |  |                 payload_spec=CustomPayload, | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  |                 pld=CustomPayload(name='doggy', value='urmom') | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-28 23:24:03 +00:00
										 |  |  |             # yah, we can `.pause_from_sync()` now! | 
					
						
							| 
									
										
											  
											
												Init def of "SC shuttle prot" with "msg-spec-limiting"
As per the long outstanding GH issue this starts our rigorous journey
into an attempt at a type-safe, cross-actor SC, IPC protocol Bo
boop -> https://github.com/goodboy/tractor/issues/36
The idea is to "formally" define our SC "shuttle (dialog) protocol" by
specifying a new `.msg.types.Msg` subtype-set which can fully
encapsulate all IPC msg schemas needed in order to accomplish
cross-process SC!
The msg set deviated a little in terms of (type) names from the existing
`dict`-msgs currently used in the runtime impl but, I think the name
changes are much better in terms of explicitly representing the internal
semantics of the actor runtime machinery/subsystems and the
IPC-msg-dialog required for SC enforced RPC.
------ - ------
In cursory, the new formal msgs-spec includes the following msg-subtypes
of a new top-level `Msg` boxing type (that holds the base field schema
for all msgs):
- `Start` to request RPC task scheduling by passing a `FuncSpec` payload
  (to replace the currently used `{'cmd': ... }` dict msg impl)
- `StartAck` to allow the RPC task callee-side to report a `IpcCtxSpec`
  payload immediately back to the caller (currently responded naively via
  a `{'functype': ... }` msg)
- `Started` to deliver the first value from `Context.started()`
  (instead of the existing `{'started': ... }`)
- `Yield` to shuttle `MsgStream.send()`-ed values (instead of
  our `{'yield': ... }`)
- `Stop` to terminate a `Context.open_stream()` session/block
  (over `{'stop': True }`)
- `Return` to deliver the final value from the `Actor.start_remote_task()`
  (which is a `{'return': ... }`)
- `Error` to box `RemoteActorError` exceptions via a `.pld: ErrorData`
  payload, planned to replace/extend the current `RemoteActorError.msgdata`
  mechanism internal to `._exceptions.pack/unpack_error()`
The new `tractor.msg.types` includes all the above msg defs as well an API
for rendering a "payload type specification" using a
`payload_type_spec: Union[Type]` that can be passed to
`msgspec.msgpack.Decoder(type=payload_type_spec)`. This ensures that
(for a subset of the above msg set) `Msg.pld: PayloadT` data is
type-parameterized using `msgspec`'s new `Generic[PayloadT]` field
support and thus enables providing for an API where IPC `Context`
dialogs can strictly define the allowed payload-datatype-set via type
union!
Iow, this is the foundation for supporting `Channel`/`Context`/`MsgStream`
IPC primitives which are type checked/safe as desired in GH issue:
- https://github.com/goodboy/tractor/issues/365
Misc notes on current impl(s) status:
------ - ------
- add a `.msg.types.mk_msg_spec()` which uses the new `msgspec` support
  for `class MyStruct[Struct, Generic[T]]` parameterize-able fields and
  delivers our boxing SC-msg-(sub)set with the desired `payload_types`
  applied to `.pld`:
  - https://jcristharif.com/msgspec/supported-types.html#generic-types
  - as a note this impl seems to need to use `type.new_class()` dynamic
    subtype generation, though i don't really get *why* still.. but
    without that the `msgspec.msgpack.Decoder` doesn't seem to reject
    `.pld` limited `Msg` subtypes as demonstrated in the new test.
- around this ^ add a `.msg._codec.limit_msg_spec()` cm which exposes
  this payload type limiting API such that it can be applied per task
  via a `MsgCodec` in app code.
- the orig approach in https://github.com/goodboy/tractor/pull/311 was
  the idea of making payload fields `.pld: Raw` wherein we could have
  per-field/sub-msg decoders dynamically loaded depending on the
  particular application-layer schema in use. I don't want to lose the
  idea of this since I think it might be useful for an idea I have about
  capability-based-fields(-sharing, maybe using field-subset
  encryption?), and as such i've kept the (ostensibly) working impls in
  TODO-comments in `.msg._codec` wherein maybe we can add
  a `MsgCodec._payload_decs: dict` table for this later on.
  |_ also left in the `.msg.types.enc/decmsg()` impls but renamed as
    `enc/dec_payload()` (but reworked to not rely on the lifo codec
    stack tables; now removed) such that we can prolly move them to
    `MsgCodec` methods in the future.
- add an unused `._codec.mk_tagged_union_dec()` helper which was
  originally factored out the #311 proto-code but didn't end up working
  as desired with the new parameterized generic fields approach (now
  in `msg.types.mk_msg_spec()`)
Testing/deps work:
------ - ------
- new `test_limit_msgspec()` which ensures all the `.types` content is
  correct but without using the wrapping APIs in `._codec`; i.e. using
  a in-line `Decoder` instead of a `MsgCodec`.
- pin us to `msgspec>=0.18.5` which has the needed generic-types support
  (which took me way too long yester to figure out when implementing all
  this XD)!
											
										 
											2024-03-28 14:45:01 +00:00
										 |  |  |             # breakpoint() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     trio.run(main) | 
					
						
							| 
									
										
										
										
											2025-03-07 19:13:36 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def enc_nsp(obj: Any) -> Any: | 
					
						
							|  |  |  |     actor: Actor = tractor.current_actor( | 
					
						
							|  |  |  |         err_on_no_runtime=False, | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     uid: tuple[str, str]|None = None if not actor else actor.uid | 
					
						
							|  |  |  |     print(f'{uid} ENC HOOK') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     match obj: | 
					
						
							|  |  |  |         # case NamespacePath()|str(): | 
					
						
							|  |  |  |         case NamespacePath(): | 
					
						
							|  |  |  |             encoded: str = str(obj) | 
					
						
							|  |  |  |             print( | 
					
						
							|  |  |  |                 f'----- ENCODING `NamespacePath` as `str` ------\n' | 
					
						
							|  |  |  |                 f'|_obj:{type(obj)!r} = {obj!r}\n' | 
					
						
							|  |  |  |                 f'|_encoded: str = {encoded!r}\n' | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |             # if type(obj) != NamespacePath: | 
					
						
							|  |  |  |             #     breakpoint() | 
					
						
							|  |  |  |             return encoded | 
					
						
							|  |  |  |         case _: | 
					
						
							|  |  |  |             logmsg: str = ( | 
					
						
							|  |  |  |                 f'{uid}\n' | 
					
						
							|  |  |  |                 'FAILED ENCODE\n' | 
					
						
							|  |  |  |                 f'obj-> `{obj}: {type(obj)}`\n' | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |             raise NotImplementedError(logmsg) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def dec_nsp( | 
					
						
							|  |  |  |     obj_type: Type, | 
					
						
							|  |  |  |     obj: Any, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ) -> Any: | 
					
						
							|  |  |  |     # breakpoint() | 
					
						
							|  |  |  |     actor: Actor = tractor.current_actor( | 
					
						
							|  |  |  |         err_on_no_runtime=False, | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     uid: tuple[str, str]|None = None if not actor else actor.uid | 
					
						
							|  |  |  |     print( | 
					
						
							|  |  |  |         f'{uid}\n' | 
					
						
							|  |  |  |         'CUSTOM DECODE\n' | 
					
						
							|  |  |  |         f'type-arg-> {obj_type}\n' | 
					
						
							|  |  |  |         f'obj-arg-> `{obj}`: {type(obj)}\n' | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     nsp = None | 
					
						
							|  |  |  |     # XXX, never happens right? | 
					
						
							|  |  |  |     if obj_type is Raw: | 
					
						
							|  |  |  |         breakpoint() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ( | 
					
						
							|  |  |  |         obj_type is NamespacePath | 
					
						
							|  |  |  |         and isinstance(obj, str) | 
					
						
							|  |  |  |         and ':' in obj | 
					
						
							|  |  |  |     ): | 
					
						
							|  |  |  |         nsp = NamespacePath(obj) | 
					
						
							|  |  |  |         # TODO: we could built a generic handler using | 
					
						
							|  |  |  |         # JUST matching the obj_type part? | 
					
						
							|  |  |  |         # nsp = obj_type(obj) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if nsp: | 
					
						
							|  |  |  |         print(f'Returning NSP instance: {nsp}') | 
					
						
							|  |  |  |         return nsp | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     logmsg: str = ( | 
					
						
							|  |  |  |         f'{uid}\n' | 
					
						
							|  |  |  |         'FAILED DECODE\n' | 
					
						
							|  |  |  |         f'type-> {obj_type}\n' | 
					
						
							|  |  |  |         f'obj-arg-> `{obj}`: {type(obj)}\n\n' | 
					
						
							|  |  |  |         f'current codec:\n' | 
					
						
							|  |  |  |         f'{current_codec()}\n' | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     # TODO: figure out the ignore subsys for this! | 
					
						
							|  |  |  |     # -[ ] option whether to defense-relay backc the msg | 
					
						
							|  |  |  |     #   inside an `Invalid`/`Ignore` | 
					
						
							|  |  |  |     # -[ ] how to make this handling pluggable such that a | 
					
						
							|  |  |  |     #   `Channel`/`MsgTransport` can intercept and process | 
					
						
							|  |  |  |     #   back msgs either via exception handling or some other | 
					
						
							|  |  |  |     #   signal? | 
					
						
							|  |  |  |     log.warning(logmsg) | 
					
						
							|  |  |  |     # NOTE: this delivers the invalid | 
					
						
							|  |  |  |     # value up to `msgspec`'s decoding | 
					
						
							|  |  |  |     # machinery for error raising. | 
					
						
							|  |  |  |     return obj | 
					
						
							|  |  |  |     # raise NotImplementedError(logmsg) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def ex_func(*args): | 
					
						
							|  |  |  |     '''
 | 
					
						
							|  |  |  |     A mod level func we can ref and load via our `NamespacePath` | 
					
						
							|  |  |  |     python-object pointer `str` subtype. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     '''
 | 
					
						
							|  |  |  |     print(f'ex_func({args})') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @pytest.mark.parametrize( | 
					
						
							|  |  |  |     'add_codec_hooks', | 
					
						
							|  |  |  |     [ | 
					
						
							|  |  |  |         True, | 
					
						
							|  |  |  |         False, | 
					
						
							|  |  |  |     ], | 
					
						
							|  |  |  |     ids=['use_codec_hooks', 'no_codec_hooks'], | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | def test_custom_extension_types( | 
					
						
							|  |  |  |     debug_mode: bool, | 
					
						
							|  |  |  |     add_codec_hooks: bool | 
					
						
							|  |  |  | ): | 
					
						
							|  |  |  |     '''
 | 
					
						
							|  |  |  |     Verify that a `MsgCodec` (used for encoding all outbound IPC msgs | 
					
						
							|  |  |  |     and decoding all inbound `PayloadMsg`s) and a paired `MsgDec` | 
					
						
							|  |  |  |     (used for decoding the `PayloadMsg.pld: Raw` received within a given | 
					
						
							|  |  |  |     task's ipc `Context` scope) can both send and receive "extension types" | 
					
						
							|  |  |  |     as supported via custom converter hooks passed to `msgspec`. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     '''
 | 
					
						
							|  |  |  |     nsp_pld_dec: MsgDec = mk_dec( | 
					
						
							|  |  |  |         spec=None,  # ONLY support the ext type | 
					
						
							|  |  |  |         dec_hook=dec_nsp if add_codec_hooks else None, | 
					
						
							|  |  |  |         ext_types=[NamespacePath], | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     nsp_codec: MsgCodec = mk_codec( | 
					
						
							|  |  |  |         # ipc_pld_spec=Raw,  # default! | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # NOTE XXX: the encode hook MUST be used no matter what since | 
					
						
							|  |  |  |         # our `NamespacePath` is not any of a `Any` native type nor | 
					
						
							|  |  |  |         # a `msgspec.Struct` subtype - so `msgspec` has no way to know | 
					
						
							|  |  |  |         # how to encode it unless we provide the custom hook. | 
					
						
							|  |  |  |         # | 
					
						
							|  |  |  |         # AGAIN that is, regardless of whether we spec an | 
					
						
							|  |  |  |         # `Any`-decoded-pld the enc has no knowledge (by default) | 
					
						
							|  |  |  |         # how to enc `NamespacePath` (nsp), so we add a custom | 
					
						
							|  |  |  |         # hook to do that ALWAYS. | 
					
						
							|  |  |  |         enc_hook=enc_nsp if add_codec_hooks else None, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # XXX NOTE: pretty sure this is mutex with the `type=` to | 
					
						
							|  |  |  |         # `Decoder`? so it won't work in tandem with the | 
					
						
							|  |  |  |         # `ipc_pld_spec` passed above? | 
					
						
							|  |  |  |         ext_types=[NamespacePath], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # TODO? is it useful to have the `.pld` decoded *prior* to | 
					
						
							|  |  |  |         # the `PldRx`?? like perf or mem related? | 
					
						
							|  |  |  |         # ext_dec=nsp_pld_dec, | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     if add_codec_hooks: | 
					
						
							|  |  |  |         assert nsp_codec.dec.dec_hook is None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # TODO? if we pass `ext_dec` above? | 
					
						
							|  |  |  |         # assert nsp_codec.dec.dec_hook is dec_nsp | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         assert nsp_codec.enc.enc_hook is enc_nsp | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     nsp = NamespacePath.from_ref(ex_func) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         nsp_bytes: bytes = nsp_codec.encode(nsp) | 
					
						
							|  |  |  |         nsp_rt_sin_msg = nsp_pld_dec.decode(nsp_bytes) | 
					
						
							|  |  |  |         nsp_rt_sin_msg.load_ref() is ex_func | 
					
						
							|  |  |  |     except TypeError: | 
					
						
							|  |  |  |         if not add_codec_hooks: | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         msg_bytes: bytes = nsp_codec.encode( | 
					
						
							|  |  |  |             Started( | 
					
						
							|  |  |  |                 cid='cid', | 
					
						
							|  |  |  |                 pld=nsp, | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         # since the ext-type obj should also be set as the msg.pld | 
					
						
							|  |  |  |         assert nsp_bytes in msg_bytes | 
					
						
							|  |  |  |         started_rt: Started = nsp_codec.decode(msg_bytes) | 
					
						
							|  |  |  |         pld: Raw = started_rt.pld | 
					
						
							|  |  |  |         assert isinstance(pld, Raw) | 
					
						
							|  |  |  |         nsp_rt: NamespacePath = nsp_pld_dec.decode(pld) | 
					
						
							|  |  |  |         assert isinstance(nsp_rt, NamespacePath) | 
					
						
							|  |  |  |         # in obj comparison terms they should be the same | 
					
						
							|  |  |  |         assert nsp_rt == nsp | 
					
						
							|  |  |  |         # ensure we've decoded to ext type! | 
					
						
							|  |  |  |         assert nsp_rt.load_ref() is ex_func | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     except TypeError: | 
					
						
							|  |  |  |         if not add_codec_hooks: | 
					
						
							|  |  |  |             pass |