tractor/tractor/spawn/_spawn.py

448 lines
13 KiB
Python
Raw Normal View History

Re-license code base for distribution under AGPL This commit obviously denotes a re-license of all applicable parts of the code base. Acknowledgement of this change was completed in #274 by the majority of the current set of contributors. From here henceforth all changes will be AGPL licensed and distributed. This is purely an effort to maintain the same copy-left policy whilst closing the (perceived) SaaS loophole the GPL allows for. It is merely for this loophole: to avoid code hiding by any potential "network providers" who are attempting to use the project to make a profit without either compensating the authors or re-distributing their changes. I thought quite a bit about this change and can't see a reason not to close the SaaS loophole in our current license. We still are (hard) copy-left and I plan to keep the code base this way for a couple reasons: - The code base produces income/profit through parent projects and is demonstrably of high value. - I believe firms should not get free lunch for the sake of "contributions from their employees" or "usage as a service" which I have found to be a dubious argument at best. - If a firm who intends to profit from the code base wants to use it they can propose a secondary commercial license to purchase with the proceeds going to the project's authors under some form of well defined contract. - Many successful projects like Qt use this model; I see no reason it can't work in this case until such a time as the authors feel it should be loosened. There has been detailed discussion in #103 on licensing alternatives. The main point of this AGPL change is to protect the code base for the time being from exploitation while it grows and as we move into the next phase of development which will include extension into the multi-host distributed software space.
2021-12-13 18:08:32 +00:00
# tractor: structured concurrent "actors".
# Copyright 2018-eternity Tyler Goodlet.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
Top level routines & machinery for actor-as-process/subint spawning
over multiple backends.
"""
from __future__ import annotations
import multiprocessing as mp
2020-01-20 16:10:51 +00:00
import platform
2021-12-02 17:34:27 +00:00
from typing import (
Any,
Awaitable,
Literal,
Callable,
TypeVar,
TYPE_CHECKING,
2021-12-02 17:34:27 +00:00
)
import trio
from trio import TaskStatus
2019-11-26 14:23:37 +00:00
from ..devx import debug
from tractor.runtime._state import (
_runtime_vars,
)
from tractor.log import get_logger
from tractor.discovery._addr import UnwrappedAddress
from tractor.runtime._portal import Portal
from tractor.runtime._runtime import Actor
from tractor.msg import types as msgtypes
2020-01-20 16:10:51 +00:00
if TYPE_CHECKING:
from tractor.ipc import (
Channel,
)
from tractor.runtime._supervise import ActorNursery
ProcessType = TypeVar('ProcessType', mp.Process, trio.Process)
2020-01-20 16:10:51 +00:00
log = get_logger('tractor')
# placeholder for an mp start context if so using that backend
_ctx: mp.context.BaseContext | None = None
SpawnMethodKey = Literal[
'trio', # supported on all platforms
'mp_spawn',
'mp_forkserver', # posix only
]
_spawn_method: SpawnMethodKey = 'trio'
2020-01-20 16:10:51 +00:00
if platform.system() == 'Windows':
_ctx = mp.get_context("spawn")
2020-01-20 16:10:51 +00:00
async def proc_waiter(proc: mp.Process) -> None:
await trio.lowlevel.WaitForSingleObject(proc.sentinel)
2020-01-20 16:10:51 +00:00
else:
# *NIX systems use ``trio`` primitives as our default as well
2020-01-23 06:23:26 +00:00
2020-01-20 16:10:51 +00:00
async def proc_waiter(proc: mp.Process) -> None:
await trio.lowlevel.wait_readable(proc.sentinel)
2020-01-20 16:10:51 +00:00
def try_set_start_method(
key: SpawnMethodKey
) -> mp.context.BaseContext | None:
'''
Attempt to set the method for process starting, aka the "actor
spawning backend".
If the desired method is not supported this function will error.
On Windows only the ``multiprocessing`` "spawn" method is offered
besides the default ``trio`` which uses async wrapping around
``subprocess.Popen``.
'''
import multiprocessing as mp
global _ctx
global _spawn_method
mp_methods = mp.get_all_start_methods()
if 'fork' in mp_methods:
# forking is incompatible with ``trio``s global task tree
mp_methods.remove('fork')
match key:
case 'mp_forkserver':
from . import _forkserver_override
_forkserver_override.override_stdlib()
_ctx = mp.get_context('forkserver')
case 'mp_spawn':
_ctx = mp.get_context('spawn')
case 'trio':
_ctx = None
case _:
raise ValueError(
f'Spawn method `{key}` is invalid!\n'
f'Please choose one of {SpawnMethodKey}'
)
_spawn_method = key
return _ctx
2020-01-20 16:10:51 +00:00
async def exhaust_portal(
2021-11-20 18:01:22 +00:00
2020-01-20 16:10:51 +00:00
portal: Portal,
actor: Actor
2021-11-20 18:01:22 +00:00
2020-01-20 16:10:51 +00:00
) -> Any:
2021-11-20 18:01:22 +00:00
'''
Pull final result from portal (assuming it has one).
2020-01-20 16:10:51 +00:00
If the main task is an async generator do our best to consume
what's left of it.
2021-11-20 18:01:22 +00:00
'''
__tracebackhide__ = True
2020-01-20 16:10:51 +00:00
try:
log.debug(
Use `.aid.uid` to avoid deprecation warns I started getting annoyed by all the warnings from `pytest` during work on macos suport in CI, so this replaces all `Actor.uid`/`Channel.uid` accesses with `.aid.uid` (or `.aid.reprol()` for log msgs) across the core runtime and IPC subsystems to avoid the noise. This also provides incentive to start the adjustment to all `.uid`-holding/tracking internal `dict`-tables/data-structures to instead use `.msg.types.Aid`. Hopefully that will come a (vibed?) follow up shortly B) Deats, - `._context`: swap all `self._actor.uid`, `self.chan.uid`, and `portal.actor.uid` refs to `.aid.uid`; use `.aid.reprol()` for log/error formatting. - `._rpc`: same treatment for `actor.uid`, `chan.uid` in log msgs and cancel-scope handling; fix `str(err)` typo in `ContextCancelled` log. - `._runtime`: update `chan.uid` -> `chan.aid.uid` in ctx cache lookups, RPC `Start` msg, registration and cancel-request handling; improve ctxc log formatting. - `._spawn`: replace all `subactor.uid` with `.aid.uid` for child-proc tracking, IPC peer waiting, debug-lock acquisition, and nursery child dict ops. - `._supervise`: same for `subactor.uid` in cancel and portal-wait paths; use `actor.aid.uid` for error dict. - `._state`: fix `last.uid` -> `last.aid.uid` in `current_actor()` error msg. Also, - `._chan`: make `Channel.aid` a proper `@property` backed by `._aid` so we can add validation/typing later. - `.log`: use `current_actor().aid.uuid` instead of `.uid[1]` for actor-uid log field. - `.msg.types`: add TODO comment for `Start.aid` field conversion. (this commit msg was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code
2026-03-08 19:27:48 +00:00
f'Waiting on final result from {actor.aid.uid}'
)
# XXX: streams should never be reaped here since they should
# always be established and shutdown using a context manager api
final: Any = await portal.wait_for_result()
except (
Exception,
BaseExceptionGroup,
) as err:
# we reraise in the parent task via a ``BaseExceptionGroup``
2020-01-20 16:10:51 +00:00
return err
except trio.Cancelled as err:
# lol, of course we need this too ;P
# TODO: merge with above?
log.warning(
'Cancelled portal result waiter task:\n'
f'uid: {portal.channel.aid}\n'
f'error: {err}\n'
)
return err
2020-01-20 16:10:51 +00:00
else:
log.debug(
f'Returning final result from portal:\n'
f'uid: {portal.channel.aid}\n'
f'result: {final}\n'
)
2020-01-20 16:10:51 +00:00
return final
async def cancel_on_completion(
2021-11-20 18:01:22 +00:00
2020-01-20 16:10:51 +00:00
portal: Portal,
actor: Actor,
errors: dict[tuple[str, str], Exception],
2020-01-20 16:10:51 +00:00
) -> None:
2021-11-20 18:01:22 +00:00
'''
Cancel actor gracefully once its "main" portal's
2020-01-20 16:10:51 +00:00
result arrives.
Should only be called for actors spawned via the
`Portal.run_in_actor()` API.
=> and really this API will be deprecated and should be
re-implemented as a `.hilevel.one_shot_task_nursery()`..)
2021-11-20 18:01:22 +00:00
'''
# if this call errors we store the exception for later
# in ``errors`` which will be reraised inside
# an exception group and we still send out a cancel request
result: Any|Exception = await exhaust_portal(
portal,
actor,
)
if isinstance(result, Exception):
Use `.aid.uid` to avoid deprecation warns I started getting annoyed by all the warnings from `pytest` during work on macos suport in CI, so this replaces all `Actor.uid`/`Channel.uid` accesses with `.aid.uid` (or `.aid.reprol()` for log msgs) across the core runtime and IPC subsystems to avoid the noise. This also provides incentive to start the adjustment to all `.uid`-holding/tracking internal `dict`-tables/data-structures to instead use `.msg.types.Aid`. Hopefully that will come a (vibed?) follow up shortly B) Deats, - `._context`: swap all `self._actor.uid`, `self.chan.uid`, and `portal.actor.uid` refs to `.aid.uid`; use `.aid.reprol()` for log/error formatting. - `._rpc`: same treatment for `actor.uid`, `chan.uid` in log msgs and cancel-scope handling; fix `str(err)` typo in `ContextCancelled` log. - `._runtime`: update `chan.uid` -> `chan.aid.uid` in ctx cache lookups, RPC `Start` msg, registration and cancel-request handling; improve ctxc log formatting. - `._spawn`: replace all `subactor.uid` with `.aid.uid` for child-proc tracking, IPC peer waiting, debug-lock acquisition, and nursery child dict ops. - `._supervise`: same for `subactor.uid` in cancel and portal-wait paths; use `actor.aid.uid` for error dict. - `._state`: fix `last.uid` -> `last.aid.uid` in `current_actor()` error msg. Also, - `._chan`: make `Channel.aid` a proper `@property` backed by `._aid` so we can add validation/typing later. - `.log`: use `current_actor().aid.uuid` instead of `.uid[1]` for actor-uid log field. - `.msg.types`: add TODO comment for `Start.aid` field conversion. (this commit msg was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code
2026-03-08 19:27:48 +00:00
errors[actor.aid.uid]: Exception = result
log.cancel(
'Cancelling subactor runtime due to error:\n\n'
Use `.aid.uid` to avoid deprecation warns I started getting annoyed by all the warnings from `pytest` during work on macos suport in CI, so this replaces all `Actor.uid`/`Channel.uid` accesses with `.aid.uid` (or `.aid.reprol()` for log msgs) across the core runtime and IPC subsystems to avoid the noise. This also provides incentive to start the adjustment to all `.uid`-holding/tracking internal `dict`-tables/data-structures to instead use `.msg.types.Aid`. Hopefully that will come a (vibed?) follow up shortly B) Deats, - `._context`: swap all `self._actor.uid`, `self.chan.uid`, and `portal.actor.uid` refs to `.aid.uid`; use `.aid.reprol()` for log/error formatting. - `._rpc`: same treatment for `actor.uid`, `chan.uid` in log msgs and cancel-scope handling; fix `str(err)` typo in `ContextCancelled` log. - `._runtime`: update `chan.uid` -> `chan.aid.uid` in ctx cache lookups, RPC `Start` msg, registration and cancel-request handling; improve ctxc log formatting. - `._spawn`: replace all `subactor.uid` with `.aid.uid` for child-proc tracking, IPC peer waiting, debug-lock acquisition, and nursery child dict ops. - `._supervise`: same for `subactor.uid` in cancel and portal-wait paths; use `actor.aid.uid` for error dict. - `._state`: fix `last.uid` -> `last.aid.uid` in `current_actor()` error msg. Also, - `._chan`: make `Channel.aid` a proper `@property` backed by `._aid` so we can add validation/typing later. - `.log`: use `current_actor().aid.uuid` instead of `.uid[1]` for actor-uid log field. - `.msg.types`: add TODO comment for `Start.aid` field conversion. (this commit msg was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code
2026-03-08 19:27:48 +00:00
f'Portal.cancel_actor() => {portal.channel.aid}\n\n'
f'error: {result}\n'
)
else:
log.runtime(
'Cancelling subactor gracefully:\n\n'
Use `.aid.uid` to avoid deprecation warns I started getting annoyed by all the warnings from `pytest` during work on macos suport in CI, so this replaces all `Actor.uid`/`Channel.uid` accesses with `.aid.uid` (or `.aid.reprol()` for log msgs) across the core runtime and IPC subsystems to avoid the noise. This also provides incentive to start the adjustment to all `.uid`-holding/tracking internal `dict`-tables/data-structures to instead use `.msg.types.Aid`. Hopefully that will come a (vibed?) follow up shortly B) Deats, - `._context`: swap all `self._actor.uid`, `self.chan.uid`, and `portal.actor.uid` refs to `.aid.uid`; use `.aid.reprol()` for log/error formatting. - `._rpc`: same treatment for `actor.uid`, `chan.uid` in log msgs and cancel-scope handling; fix `str(err)` typo in `ContextCancelled` log. - `._runtime`: update `chan.uid` -> `chan.aid.uid` in ctx cache lookups, RPC `Start` msg, registration and cancel-request handling; improve ctxc log formatting. - `._spawn`: replace all `subactor.uid` with `.aid.uid` for child-proc tracking, IPC peer waiting, debug-lock acquisition, and nursery child dict ops. - `._supervise`: same for `subactor.uid` in cancel and portal-wait paths; use `actor.aid.uid` for error dict. - `._state`: fix `last.uid` -> `last.aid.uid` in `current_actor()` error msg. Also, - `._chan`: make `Channel.aid` a proper `@property` backed by `._aid` so we can add validation/typing later. - `.log`: use `current_actor().aid.uuid` instead of `.uid[1]` for actor-uid log field. - `.msg.types`: add TODO comment for `Start.aid` field conversion. (this commit msg was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code
2026-03-08 19:27:48 +00:00
f'Portal.cancel_actor() => {portal.channel.aid}\n\n'
f'result: {result}\n'
)
2020-01-20 16:10:51 +00:00
# cancel the process now that we have a final result
await portal.cancel_actor()
2020-01-20 16:10:51 +00:00
async def hard_kill(
proc: trio.Process,
terminate_after: int = 1.6,
# NOTE: for mucking with `.pause()`-ing inside the runtime
# whilst also hacking on it XD
# terminate_after: int = 99999,
) -> None:
2023-12-11 23:17:42 +00:00
'''
Un-gracefully terminate an OS level `trio.Process` after timeout.
Used in 2 main cases:
- "unknown remote runtime state": a hanging/stalled actor that
isn't responding after sending a (graceful) runtime cancel
request via an IPC msg.
- "cancelled during spawn": a process who's actor runtime was
cancelled before full startup completed (such that
cancel-request-handling machinery was never fully
initialized) and thus a "cancel request msg" is never going
to be handled.
'''
log.cancel(
'Terminating sub-proc\n'
f'>x)\n'
f' |_{proc}\n'
)
# NOTE: this timeout used to do nothing since we were shielding
# the ``.wait()`` inside ``new_proc()`` which will pretty much
# never release until the process exits, now it acts as
# a hard-kill time ultimatum.
with trio.move_on_after(terminate_after) as cs:
# NOTE: code below was copied verbatim from the now deprecated
# (in 0.20.0) ``trio._subrocess.Process.aclose()``, orig doc
# string:
#
# Close any pipes we have to the process (both input and output)
# and wait for it to exit. If cancelled, kills the process and
# waits for it to finish exiting before propagating the
# cancellation.
2023-12-11 23:17:42 +00:00
#
# This code was originally triggred by ``proc.__aexit__()``
# but now must be called manually.
with trio.CancelScope(shield=True):
if proc.stdin is not None:
await proc.stdin.aclose()
if proc.stdout is not None:
await proc.stdout.aclose()
if proc.stderr is not None:
await proc.stderr.aclose()
try:
await proc.wait()
finally:
if proc.returncode is None:
proc.kill()
with trio.CancelScope(shield=True):
await proc.wait()
2023-12-11 23:17:42 +00:00
# XXX NOTE XXX: zombie squad dispatch:
# (should ideally never, but) If we do get here it means
# graceful termination of a process failed and we need to
# resort to OS level signalling to interrupt and cancel the
# (presumably stalled or hung) actor. Since we never allow
# zombies (as a feature) we ask the OS to do send in the
# removal swad as the last resort.
if cs.cancelled_caught:
# TODO? attempt at intermediary-rent-sub
# with child in debug lock?
# |_https://github.com/goodboy/tractor/issues/320
#
# if not is_root_process():
# log.warning(
# 'Attempting to acquire debug-REPL-lock before zombie reap!'
# )
# with trio.CancelScope(shield=True):
# async with debug.acquire_debug_lock(
Use `.aid.uid` to avoid deprecation warns I started getting annoyed by all the warnings from `pytest` during work on macos suport in CI, so this replaces all `Actor.uid`/`Channel.uid` accesses with `.aid.uid` (or `.aid.reprol()` for log msgs) across the core runtime and IPC subsystems to avoid the noise. This also provides incentive to start the adjustment to all `.uid`-holding/tracking internal `dict`-tables/data-structures to instead use `.msg.types.Aid`. Hopefully that will come a (vibed?) follow up shortly B) Deats, - `._context`: swap all `self._actor.uid`, `self.chan.uid`, and `portal.actor.uid` refs to `.aid.uid`; use `.aid.reprol()` for log/error formatting. - `._rpc`: same treatment for `actor.uid`, `chan.uid` in log msgs and cancel-scope handling; fix `str(err)` typo in `ContextCancelled` log. - `._runtime`: update `chan.uid` -> `chan.aid.uid` in ctx cache lookups, RPC `Start` msg, registration and cancel-request handling; improve ctxc log formatting. - `._spawn`: replace all `subactor.uid` with `.aid.uid` for child-proc tracking, IPC peer waiting, debug-lock acquisition, and nursery child dict ops. - `._supervise`: same for `subactor.uid` in cancel and portal-wait paths; use `actor.aid.uid` for error dict. - `._state`: fix `last.uid` -> `last.aid.uid` in `current_actor()` error msg. Also, - `._chan`: make `Channel.aid` a proper `@property` backed by `._aid` so we can add validation/typing later. - `.log`: use `current_actor().aid.uuid` instead of `.uid[1]` for actor-uid log field. - `.msg.types`: add TODO comment for `Start.aid` field conversion. (this commit msg was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code
2026-03-08 19:27:48 +00:00
# subactor_uid=current_actor().aid.uid,
# ) as _ctx:
# log.warning(
# 'Acquired debug lock, child ready to be killed ??\n'
# )
# TODO: toss in the skynet-logo face as ascii art?
log.critical(
# 'Well, the #ZOMBIE_LORD_IS_HERE# to collect\n'
'#T-800 deployed to collect zombie B0\n'
f'>x)\n'
f' |_{proc}\n'
)
proc.kill()
async def soft_kill(
2021-12-02 17:34:27 +00:00
proc: ProcessType,
wait_func: Callable[
2021-12-02 17:34:27 +00:00
[ProcessType],
Awaitable,
],
portal: Portal,
) -> None:
2023-12-11 23:17:42 +00:00
'''
Wait for proc termination but **don't yet** teardown
std-streams since it will clobber any ongoing pdb REPL
session.
This is our "soft"/graceful, and thus itself also cancellable,
join/reap on an actor-runtime-in-process shutdown; it is
**not** the same as a "hard kill" via an OS signal (for that
see `.hard_kill()`).
2023-12-11 23:17:42 +00:00
'''
chan: Channel = portal.channel
peer_aid: msgtypes.Aid = chan.aid
try:
log.cancel(
f'Soft killing sub-actor via portal request\n'
f'\n'
f'c)=> {peer_aid.reprol()}@[{chan.maddr}]\n'
f' |_{proc}\n'
)
# wait on sub-proc to signal termination
await wait_func(proc)
except trio.Cancelled:
with trio.CancelScope(shield=True):
await debug.maybe_wait_for_debugger(
child_in_debug=_runtime_vars.get(
'_debug_mode', False
),
header_msg=(
'Delaying `soft_kill()` subproc reaper while debugger locked..\n'
),
# TODO: need a diff value then default?
# poll_steps=9999999,
)
# if cancelled during a soft wait, cancel the child
# actor before entering the hard reap sequence
# below. This means we try to do a graceful teardown
# via sending a cancel message before getting out
# zombie killing tools.
async with trio.open_nursery() as n:
n.cancel_scope.shield = True
async def cancel_on_proc_deth():
'''
"Cancel-the-cancel" request: if we detect that the
underlying sub-process exited prior to
a `Portal.cancel_actor()` call completing .
'''
await wait_func(proc)
n.cancel_scope.cancel()
2023-12-11 23:17:42 +00:00
# start a task to wait on the termination of the
# process by itself waiting on a (caller provided) wait
# function which should unblock when the target process
# has terminated.
n.start_soon(cancel_on_proc_deth)
2023-12-11 23:17:42 +00:00
# send the actor-runtime a cancel request.
await portal.cancel_actor()
if proc.poll() is None: # type: ignore
log.warning(
'Subactor still alive after cancel request?\n\n'
f'uid: {peer_aid}\n'
f'|_{proc}\n'
)
n.cancel_scope.cancel()
raise
2019-11-26 14:23:37 +00:00
async def new_proc(
name: str,
actor_nursery: ActorNursery,
subactor: Actor,
errors: dict[tuple[str, str], Exception],
# passed through to actor main
bind_addrs: list[UnwrappedAddress],
parent_addr: UnwrappedAddress,
_runtime_vars: dict[str, Any], # serialized and sent to _child
*,
infect_asyncio: bool = False,
2025-03-12 19:13:40 +00:00
task_status: TaskStatus[Portal] = trio.TASK_STATUS_IGNORED,
proc_kwargs: dict[str, any] = {}
) -> None:
# lookup backend spawning target
target: Callable = _methods[_spawn_method]
# mark the new actor with the global spawn method
subactor._spawn_method = _spawn_method
await target(
name,
actor_nursery,
subactor,
errors,
Init-support for "multi homed" transports Since we'd like to eventually allow a diverse set of transport (protocol) methods and stacks, and a multi-peer discovery system for distributed actor-tree applications, this reworks all runtime internals to support multi-homing for any given tree on a logical host. In other words any actor can now bind its transport server (currently only unsecured TCP + `msgspec`) to more then one address available in its (linux) network namespace. Further, registry actors (now dubbed "registars" instead of "arbiters") can also similarly bind to multiple network addresses and provide discovery services to remote actors via multiple addresses which can now be provided at runtime startup. Deats: - adjust `._runtime` internals to use a `list[tuple[str, int]]` (and thus pluralized) socket address sequence where applicable for transport server socket binds, now exposed via `Actor.accept_addrs`: - `Actor.__init__()` now takes a `registry_addrs: list`. - `Actor.is_arbiter` -> `.is_registrar`. - `._arb_addr` -> `._reg_addrs: list[tuple]`. - always reg and de-reg from all registrars in `async_main()`. - only set the global runtime var `'_root_mailbox'` to the loopback address since normally all in-tree processes should have access to it, right? - `._serve_forever()` task now takes `listen_sockaddrs: list[tuple]` - make `open_root_actor()` take a `registry_addrs: list[tuple[str, int]]` and defaults when not passed. - change `ActorNursery.start_..()` methods take `bind_addrs: list` and pass down through the spawning layer(s) via the parent-seed-msg. - generalize all `._discovery()` APIs to accept `registry_addrs`-like inputs and move all relevant subsystems to adopt the "registry" style naming instead of "arbiter": - make `find_actor()` support batched concurrent portal queries over all provided input addresses using `.trionics.gather_contexts()` Bo - syntax: move to using `async with <tuples>` 3.9+ style chained @acms. - a general modernization of the code to a python 3.9+ style. - start deprecation and change to "registry" naming / semantics: - `._discovery.get_arbiter()` -> `.get_registry()`
2023-09-27 19:19:30 +00:00
bind_addrs,
parent_addr,
_runtime_vars, # run time vars
infect_asyncio=infect_asyncio,
task_status=task_status,
2025-03-12 19:13:40 +00:00
proc_kwargs=proc_kwargs
)
# NOTE: bottom-of-module to avoid a circular import since the
# backend submodules pull `cancel_on_completion`/`soft_kill`/
# `hard_kill`/`proc_waiter` from this module.
from ._trio import trio_proc
from ._mp import mp_proc
# proc spawning backend target map
_methods: dict[SpawnMethodKey, Callable] = {
'trio': trio_proc,
'mp_spawn': mp_proc,
'mp_forkserver': mp_proc,
}