Modular transports via tractor.ipc! #17
Closed
guille
wants to merge 51 commits from
structural_dynamics_of_flow into shm_apis
pull from: structural_dynamics_of_flow
merge into: goodboy:shm_apis
goodboy:macos_fixed_2025
goodboy:macos_support
goodboy:piker_pin
goodboy:dereg_on_oserror
goodboy:pld_dec_refinements
goodboy:free_threading_prep
goodboy:oco_supervisor_prototype
goodboy:hilevel_serman
goodboy:multicast_revertable_streams
goodboy:ns_aware
goodboy:factor_into_hotbaud
goodboy:final_eg_refinements
goodboy:actor_cancelled_exc_type
goodboy:log_sys_testing
goodboy:main
goodboy:oob_cancel_testing
goodboy:cancelled_masking_guards
goodboy:strict_egs_everywhere
goodboy:POST_final_eg_refinements_failafter_investigation
goodboy:to_asyncio_eoc_signal
goodboy:enable_tpts
goodboy:repl_fixture
goodboy:SDOF_pre_sin_testing_XPSBACKUP
goodboy:cluster_api_egs_conflict
goodboy:moar_eg_smoothing
goodboy:pytest_pluginize
goodboy:one_ring_to_rule_them_all
goodboy:one_ring_to_rule_them_all_FROZEN_20250619
goodboy:auto_codecs
goodboy:leslies_extra_appendix
goodboy:shm_apis
goodboy:ext_type_plds
goodboy:py313_support
goodboy:aio_abandons
goodboy:sc_super_proto_dgrams
goodboy:runtime_to_msgspec
goodboy:pkg_tidying
goodboy:multihost_exs
goodboy:uv_migration_pre_msgspec_in_runtime
goodboy:remote_inceptions
goodboy:ext_type_plds_XPS_BACKUP
goodboy:modden_spawn_from_client_req
goodboy:multihomed
goodboy:devx_subpkg
goodboy:asyncio_debugger_support
goodboy:ctx_cancel_semantics_and_overruns
goodboy:pre_pretty_struct_dep_commit_b54cb66
goodboy:ctx_cancel_semantics_and_overruns_REVERSED_FACEPALM
goodboy:uv_migration
goodboy:to_asyncio_refinery
goodboy:runtime_to_msgspec_XPS_BACKUP
goodboy:rae_message_packing
goodboy:msg_codecs
goodboy:old_msg_types
goodboy:asyncio_debug_mode
goodboy:pause_from_sync_w_greenback
goodboy:mv_to_new_trio_py3.11
goodboy:modden_spawn_from_client_req_XPS_BACKUP
goodboy:shielded_ctx_cancel
goodboy:ctx_cancel_semantics_and_overruns_XPS_GH_BACKUP
goodboy:msgtypes
goodboy:master
goodboy:switch_to_pdbp
goodboy:proper_breakpoint_hooking
goodboy:drop_proc_actxmngr
goodboy:ctx_result_consumption
goodboy:readme_touchups
goodboy:ipython_integration
goodboy:breceiver_internals
goodboy:ipc_failure_while_streaming
goodboy:deprecate_arbiter_addr
goodboy:prompt_on_ctrlc
goodboy:dun_unset_current_actor
goodboy:ipc_failwhilestream_backup
goodboy:macos_in_ci
goodboy:harden_cluster_tests
goodboy:eg_backup
goodboy:exceptiongroups
goodboy:egs_with_ctx_res_consumption
goodboy:debug_lock_blocking
goodboy:callable_key_maybe_open_context
goodboy:spawn_backend_table
goodboy:pin_pre_trio_0.22
goodboy:pytest_report_workaround
goodboy:lifetime_stack_tests
goodboy:we_bein_all_matchy
goodboy:debug_event_guard
goodboy:disable_win_ci
goodboy:alpha5
goodboy:signint_saviour
goodboy:sigintsaviour_citesthackin
goodboy:sigintsaviour_ci_worked
goodboy:aio_error_propagation
goodboy:drop_msgpack
goodboy:310_windows
goodboy:ci_sdist_install
goodboy:include_readme
goodboy:310_plus
goodboy:name_query
goodboy:sort_subs_results_infected_aio
goodboy:aio_explicit_task_cancels
goodboy:fence_mp
goodboy:sigint_ignore_in_pdb_repl
goodboy:sigint2
goodboy:msgpack_lists_by_default
goodboy:nspaths
goodboy:experimental_subpkg
goodboy:maybe_cancel_the_cancel_
goodboy:moar_timeoutz
goodboy:drop_old_nooz_files
goodboy:raise_runinactor_error
goodboy:win_ci_timeout
goodboy:alpha4
goodboy:infect_asyncio
goodboy:expected_ctx_cancelled
goodboy:new_mypy
goodboy:context_caching
goodboy:end_of_channel_fixes
goodboy:agpl_commit_msg_fix
goodboy:agpl
goodboy:stricter_context_starting
goodboy:acked_backup
goodboy:faster_daemon_cancels
goodboy:early_deth_fixes
goodboy:clusters_and_hot_tips
goodboy:alpha3
goodboy:pubsub_startup_response_msg
goodboy:iaio_backup
goodboy:trionics
goodboy:graceful_gather
goodboy:246_facepalm_backup
goodboy:patch-async-enter-all
goodboy:immediate_remote_cancels
goodboy:less_logging
goodboy:zombie_lord_infinite
goodboy:optional_msgspec_support
goodboy:fix_kbi_in_ctx_block
goodboy:logo_tweaks
goodboy:use_trio_on_win
goodboy:alpha2
goodboy:msgspec_infect_asyncio
goodboy:live_on_air_from_tokio
goodboy:tokio_backup
goodboy:debugger_test_tweaks
goodboy:fix_news_links
goodboy:wats_da_nooz
goodboy:ctx_debugger
goodboy:bi_streaming_no_debugger_stuff
goodboy:round_2_ci_windows
goodboy:CI_increment_for_windows_bidirstreaming
goodboy:ctx_debugger_from_hardening
goodboy:infect_asyncio_backup
goodboy:debugger_hardening
goodboy:bi_streaming
goodboy:transport_cleaning
goodboy:context_finesse
goodboy:cf_backup
goodboy:db_backup
goodboy:pre_bad_close
goodboy:stdstream_clobber_fix
goodboy:bistream_backup
goodboy:transport_hardening
goodboy:msgspec_not_fucked
goodboy:try_msgspec
goodboy:prehardkill
goodboy:windows_bi_streaming
goodboy:docs_revamp
goodboy:new_docs_polish
goodboy:wip_fix_asyncio_gen_streaming
goodboy:drop_run
goodboy:mp_teardown_hardening
goodboy:stream_contexts
goodboy:drop_sync_funcs
goodboy:pub_connect_msg
goodboy:sync_cancel
goodboy:stream_clones
goodboy:first_pypi_release
goodboy:single_func_example
goodboy:readme_pump
goodboy:kinda_drop_run
goodboy:mp_hang_search
goodboy:eg_worker_poolz
goodboy:sync_breakpoint
goodboy:actor_state_via_messages
goodboy:we_aint_got_zombie_shields
goodboy:deprecate_rpcmodpaths
goodboy:implicit_runtime
goodboy:drop_tractor_run
goodboy:py3.9
goodboy:denoise_logging
goodboy:func_refs_always
goodboy:fix_debug_tests_in_ci_again
goodboy:stream_channel_shield
goodboy:pdb_madness
goodboy:advanced_debugger_testing
goodboy:clean_log_header
goodboy:debug_refine
goodboy:debug_refinements
goodboy:drop_warn
goodboy:multiproc_debug
goodboy:debugger_on_windows
goodboy:bug_in_debug
goodboy:debug_tests
goodboy:native_debugging
goodboy:matrix
goodboy:dereg_on_channel_aclose
goodboy:ensure_deregister
goodboy:start_up_sequence_trickery
goodboy:fix_win_ci_again
goodboy:stin_char_relay
goodboy:flaky_tests
goodboy:drop_cloudpickle
goodboy:reorg_entry_points
goodboy:drop-trip-update-trio
goodboy:init_sphinx_docs
goodboy:example_tests
goodboy:implicit_rpc
goodboy:fix_examples_in_docs
goodboy:try_trip
goodboy:log_task_context
goodboy:drop_event_clear
goodboy:more_thorough_super_tests
goodboy:pip_ci_fix
goodboy:windows_support
goodboy:rename_forkserver_mod
goodboy:user_update
goodboy:win_ci
goodboy:stream_functions
goodboy:propagate_loglevel
goodboy:ipc_iternals_renaming
goodboy:close_mem_chans
goodboy:docs_example_fixes
goodboy:spawn_method_support
goodboy:trio_memchans
goodboy:contexts
goodboy:remote_module_errors
goodboy:remote_task_cancelling
goodboy:fix_46
goodboy:loglevel_to_tractor_tests
goodboy:expose_tractor_test
goodboy:improved_errors
goodboy:self_register
goodboy:multi_program_tests
goodboy:tests_reorg
goodboy:type_annotations
goodboy:py3.7_tweaks
goodboy:reliable_cancel_tests
goodboy:attrs_it_up
goodboy:wait_for_actor
goodboy:draft_readme
goodboy:init_docs
goodboy:reg_with_uid
goodboy:forkserver_singleton
goodboy:drop_main_kwarg
goodboy:asyncgen_closing_fix
No reviewers
Labels
Clear labels
No items
No Label
Milestone
Clear milestone
No items
No Milestone
Projects
Clear projects
No project
Assignees
Clear assignees
No Assignees
2 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.
No due date set.
Dependencies
No dependencies set.
Reference: goodboy/tractor#17
Reference in New Issue
There is no content yet.
Delete Branch "structural_dynamics_of_flow"
Deleting a branch is permanent. Although the deleted branch may exist for a short time before cleaning up, in most cases it CANNOT be undone. Continue?
9c8d23f41bto804cf44f04804cf44f04toefd11f7d74Non TCP specific addressing everywhereto Modular transports via `tractor.ipc`!Modular transports via `tractor.ipc`!to Modular transports via `tractor.ipc`!Modular transports via `tractor.ipc`!to Modular transports via `tractor.ipc`!efd11f7d74to1762b3eb64As per #24 I think i’m going to close this and either open a new PR or use that one (with that history fast-forwarded into this same structural_dynamics_of_flow branch) but with a slightly more formal description then (well obviously this ;P) but at least as much of summary.
A few things that can fundamentally change, - UDS addresses now always encapsulate the local and remote pid such that it denotes each side's process much like a TCP *port*. |_ `.__init__()` takes a new `maybe_pid: int`. |_ this required changes to the `.ipc._uds` backend which will come in an subsequent commit! |_ `UDSAddress.address_type` becomes a `tuple[str, int]` just like the TCP case. |_ adjust `wrap_address()` to match. - use a new `_state.get_rt_dir() -> Path` as the default location for UDS socket file: now under `XDG_RUNTIME_DIR'/tractor/` subdir by default. - re-implement `USDAddress.get_random()` to use both the local `Actor.uid` (if available) and at least the pid for its socket file name. Removals, - drop the loop generated `_default_addrs`, simplify to just `_default_lo_addrs` for per-transport default registry addresses. |_ change to `_address_types: dict[str, Type[Address]]` instead of separate types `list`. |_ adjust `is_wrapped_addr()` to just check `in _addr_types.values()`. - comment out `Address.open_stream()` it's unused and i think the wrong place for this API. Renames, - from `AddressTypes` -> `UnwrappedAddress`, since it's a simple type union and all this type set is, is the simple python data-structures we encode to for the wire. |_ see note about possibly implementing the `.[un]wrap()` stuff as `msgspec` codec `enc/dec_hook()`s instead! Additions, - add a `mk_uuid()` to be used throughout the runtime including for generating the `Aid.uuid` part. - tons of notes around follow up refinements!Such that any UDS socket pair is represented (and with the recent updates to) a `USDAddress` via a similar pair-`tuple[str, int]` as TCP sockets, a pair of the `.filepath: Path` & the peer proc's `.pid: int` which we read from the underlying `socket.socket` using `.set/getsockopt()` calls Impl deats, - using the Linux specific APIs, we add a `get_peer_info()` which reads the `(pid, uid, gid)` using the `SOL_SOCKET` and `SOL_PEECRED` opts to `sock.getsockopt()`. |_ this presumes the client has been correspondingly configured to deliver the creds via a `sock.setsockopt(SOL_SOCKET, SO_PASSCRED, 1)` call - this required us to override `trio.open_unix_socket()`. - override `trio.open_unix_socket()` as per the above bullet to ensure connecting peers always transmit "credentials" options info to the listener. - update `.get_stream_addrs()` to always call `get_peer_info()` and extract the peer's pid for the `raddr` and use `os.getpid()` for `laddr` (obvi). |_ as part of the new impl also `log.info()` the creds-info deats and socket-file path. |_ handle the oddity where it depends which of `.getpeername()` or `.getsockname()` will return the file-path; i think it's to do with who is client vs. server? Related refinements, - set `.layer_key: int = 4` for the "transport layer" ;) - tweak some typing and multi-line unpacking in `.ipc/_tcp`.Previously whenever an `ActorNursery.start_actor()` call did not receive a `bind_addrs` arg we would allocate the default `(localhost, 0)` pairs in the parent, for UDS this obviously won't work nor is it ideal bc it's nicer to have the actor to be a socket server (who calls `Address.open_listener()`) define the socket-file-name containing their unique ID info such as pid, actor-uuid etc. As such this moves "random" generation of server addresses to the child-side of a subactor's spawn-sequence when it's sin-`bind_addrs`; i.e. we do the allocation of the `Address.get_random()` addrs inside `._runtime.async_main()` instead of `Portal.start_actor()` and **only when** `accept_addrs`/`bind_addrs` was **not provided by the spawning parent**. Further this patch get's way more rigorous about the `SpawnSpec` processing in the child inside `Actor._from_parent()` such that we handle any invalid msgs **very loudly and pedantically!** Impl deats, - do the "random addr generation" in an explicit `for` loop (instead of prior comprehension) to allow for more detailed typing of the layered calls to the new `._addr` mod. - use a `match:/case:` for process any invalid `SpawnSpec` payload case where we can instead receive a `MsgTypeError` from the `chan.recv()` call in `Actor._from_parent()` to raise it immediately instead of triggering downstream type-errors XD |_ as per the big `#TODO` we prolly want to take from other callers of `Channel.recv()` (like in the `._rpc.process_messages()` loop). |_ always raise `InternalError` on non-match/fall-through case! |_ add a note about not being able to use `breakpoint()` in this section due to causality of `SpawnSpec._runtime_vars` not having been processed yet.. |_ always return a third element from `._from_rent()` eventually to be the `preferred_transports: list[str]` from the spawning rent. - use new `._addr.mk_uuid()` and pass to new `Actor.__init__(uuid: str)` for all actor creation (including in all the mods tweaked here). - Move to new type-alias-name `UnwrappedAddress` throughout.The more I think about it, it seems @guille's orig approach of unwrapping UDS socket-file addresses to strings (or `Path`) is making the most sense. I had originally thought that pairing it with the listening side's pid would add clarity (and it definitely does for introspection/debug/logging) but since we don't end up passing that pid to the eventual `.connect()` call on the client side, it doesn't make much sense to wrap it for the wire just to discard.. Further, the `tuple[str, int]` makes `wrap_address()` break for TCP since it will always match on uds first. So, on that note this patch refines a few things in prep for going back to that original `UnwrappedAddress` as `str` type though longer run i think the more "builtin approach" would be to add `msgspec` codec hooks for these types to avoid all the `.wrap()`/`.unwrap()` calls throughout the runtime. Down-low deats, - add `wrap_address()` doc string, detailed (todo) comments and handle the `[None, None]` case that can come directly from `._state._runtime_vars['_root_mailbox']`. - buncha adjustments to `UDSAddress`, - add a `filedir`, chng `filepath` -> `filename` and mk `maybe_pid` optional. - the intent `filedir` is act as the equivalent of the host part in a network proto's socket address and when it's null use the `.def_bindspace = get_rt_dir()`. - always ensure the `filedir / filename` is an absolute path and expose it as a new `.sockpath: Path` property. - mk `.is_valid` actually verify the `.sockpath` is in the valid `.bindspace: namely just checking it's in the expected dir. - add pedantic `match:`ing to `.from_addr()` such that we error on unexpected `type(addr)` inputs and otherwise parse any `sockpath: Path` inputs using a new `unwrap_sockpath()` which simply splits an abs file path to dir, file-name parts. - `.unwrap()` now just `str`-ifies the `.sockpath: Path` - adjust `.open/close_listener()` to use `.sockpath`.Via a new accumulative `--tpt-proto` arg you can select which `tpt_protos: list[str]`-fixture protocol keys will be delivered to opting in tests! B) Also includes, - CLI quote handling/stripping. - default of 'tcp'. - only support one selection per session at the moment (until we figure out how we want to support multiples, either simultaneously or sequentially). - draft a (masked) dynamic-`metafunc` parametrization in the `pytest_generate_tests()` hook. - first proven and working use in the `test_advanced_faults`-suite (and thus its underlying `examples/advanced_faults/ipc_failure_during_stream.py` script)! |_ actually needed this to prove that the suite only has 2 failures on 'uds' seemingly due to low-level `trio` error semantics translation differences to do with with calling `socket.close()`.. On a very nearly related topic, - draft an (also commented out) `set_script_runtime_args()` fixture idea for a std way of `partial`-ling in runtime args to `examples/` scripts-as-modules defining a `main()` which would proxy to `tractor.open_nursery()`.By borrowing from the implementation of `RemoteActorError.pformat()` which is now factored into a new `.devx.pformat_exc()` and re-used for both error types while maintaining the same func-sig. Obviously delegate `RemoteActorError.pformat()` to the new helper accordingly and keeping the prior `body` generation from `.devx.pformat_boxed_tb()` as before. The new helper allows for, - passing any of a `header|message|body: str` which are all combined in that order in the final output. - getting the `exc.message` as the default `message` part. - generating an objecty-looking "type-name" header to be rendered by default when `header` is not overridden. - "first-line-of `message`" processing which we split-off and then re-inject as a `f'<{type(exc).__name__}( {first} )>'` top line header. - an optional `tail: str = '>'` to "close the object"-look only added when `with_type_header: bool = True`. Adjustments to `TransportClosed` around this include, - replacing the init `cause` arg for a `src_exc` which is now always assigned to a same named instance var. - displaying that new `.src_exc` in the `body: str` arg to the `.devx.pformat.pformat_exc()` call so you can always see the underlying (normally `trio`) source error. - just make it inherit from `Exception` not `trio.BrokenResourceError` to avoid handlers catching `TransportClosed` as the former particularly in testing when we want to sometimes to distinguish them.Yah, so replaced by #24.
Pull request closed