Be explicit about the spawning backend default
Set `trio-run-in-process` as the default on *nix systems and `multiprocessing`'s spawn method on Windows. Enable overriding the default choice using `tractor._spawn.try_set_start_method()`. Allows for easy runs of the test suite using a user chosen backend.try_trip^2
parent
783fe53b06
commit
27c9760f96
|
@ -4,7 +4,6 @@ tractor: An actor model micro-framework built on
|
|||
"""
|
||||
import importlib
|
||||
from functools import partial
|
||||
import platform
|
||||
from typing import Tuple, Any, Optional
|
||||
import typing
|
||||
|
||||
|
@ -103,16 +102,15 @@ def run(
|
|||
# either the `multiprocessing` start method:
|
||||
# https://docs.python.org/3/library/multiprocessing.html#contexts-and-start-methods
|
||||
# OR `trio_run_in_process` (the new default).
|
||||
start_method: str = 'trio_run_in_process',
|
||||
start_method: Optional[str] = None,
|
||||
**kwargs,
|
||||
) -> Any:
|
||||
"""Run a trio-actor async function in process.
|
||||
|
||||
This is tractor's main entry and the start point for any async actor.
|
||||
"""
|
||||
if platform.system() == 'Windows':
|
||||
start_method = 'spawn' # only one supported for now
|
||||
_spawn.try_set_start_method(start_method)
|
||||
if start_method is not None:
|
||||
_spawn.try_set_start_method(start_method)
|
||||
return trio.run(_main, async_fn, args, kwargs, arbiter_addr, name)
|
||||
|
||||
|
||||
|
|
|
@ -537,6 +537,7 @@ class Actor:
|
|||
f"Setting loglevel for {self.uid} to {self.loglevel}")
|
||||
get_console_log(self.loglevel)
|
||||
|
||||
assert spawn_ctx
|
||||
log.info(
|
||||
f"Started new {spawn_ctx.current_process()} for {self.uid}")
|
||||
|
||||
|
@ -555,6 +556,11 @@ class Actor:
|
|||
accept_addr: Tuple[str, int],
|
||||
parent_addr: Tuple[str, int] = None
|
||||
) -> None:
|
||||
"""Entry point for a `trio_run_in_process` subactor.
|
||||
|
||||
Here we don't need to call `trio.run()` since trip does that as
|
||||
part of its subprocess startup sequence.
|
||||
"""
|
||||
if self.loglevel is not None:
|
||||
log.info(
|
||||
f"Setting loglevel for {self.uid} to {self.loglevel}")
|
||||
|
|
|
@ -6,7 +6,7 @@ Mostly just wrapping around ``multiprocessing``.
|
|||
import inspect
|
||||
import multiprocessing as mp
|
||||
import platform
|
||||
from typing import Any, List, Dict
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
import trio
|
||||
from trio_typing import TaskStatus
|
||||
|
@ -32,8 +32,13 @@ from ._actor import Actor, ActorFailure
|
|||
|
||||
log = get_logger('tractor')
|
||||
|
||||
_ctx: mp.context.BaseContext = mp.get_context("spawn") # type: ignore
|
||||
_spawn_method: str = "spawn"
|
||||
# use trip as our default for now
|
||||
if platform.system() != 'Windows':
|
||||
_spawn_method: str = "trio_run_in_process"
|
||||
else:
|
||||
_spawn_method = "spawn"
|
||||
|
||||
_ctx: Optional[mp.context.BaseContext] = None
|
||||
|
||||
|
||||
if platform.system() == 'Windows':
|
||||
|
@ -46,7 +51,7 @@ else:
|
|||
await trio.hazmat.wait_readable(proc.sentinel)
|
||||
|
||||
|
||||
def try_set_start_method(name: str) -> mp.context.BaseContext:
|
||||
def try_set_start_method(name: str) -> Optional[mp.context.BaseContext]:
|
||||
"""Attempt to set the start method for ``multiprocess.Process`` spawning.
|
||||
|
||||
If the desired method is not supported the sub-interpreter (aka "spawn"
|
||||
|
@ -55,15 +60,16 @@ def try_set_start_method(name: str) -> mp.context.BaseContext:
|
|||
global _ctx
|
||||
global _spawn_method
|
||||
|
||||
allowed = mp.get_all_start_methods()
|
||||
methods = mp.get_all_start_methods()
|
||||
methods.remove('fork')
|
||||
|
||||
# no Windows support for trip yet (afaik)
|
||||
# no Windows support for trip yet
|
||||
if platform.system() != 'Windows':
|
||||
allowed += ['trio_run_in_process']
|
||||
methods += ['trio_run_in_process']
|
||||
|
||||
if name not in allowed:
|
||||
if name not in methods:
|
||||
raise ValueError(
|
||||
f"Spawn method {name} is unsupported please choose one of {allowed}"
|
||||
f"Spawn method `{name}` is invalid please choose one of {methods}"
|
||||
)
|
||||
|
||||
elif name == 'fork':
|
||||
|
@ -73,6 +79,10 @@ def try_set_start_method(name: str) -> mp.context.BaseContext:
|
|||
elif name == 'forkserver':
|
||||
_forkserver_override.override_stdlib()
|
||||
_ctx = mp.get_context(name)
|
||||
elif name == 'trio_run_in_process':
|
||||
_ctx = None
|
||||
else:
|
||||
_ctx = mp.get_context(name)
|
||||
|
||||
_spawn_method = name
|
||||
return _ctx
|
||||
|
@ -191,6 +201,7 @@ async def new_proc(
|
|||
# TRIP blocks here until process is complete
|
||||
else:
|
||||
# `multiprocessing`
|
||||
assert _ctx
|
||||
start_method = _ctx.get_start_method()
|
||||
if start_method == 'forkserver':
|
||||
# XXX do our hackery on the stdlib to avoid multiple
|
||||
|
|
Loading…
Reference in New Issue