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
Tyler Goodlet 2020-01-26 21:13:29 -05:00
parent 783fe53b06
commit 27c9760f96
3 changed files with 29 additions and 14 deletions

View File

@ -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)

View File

@ -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}")

View File

@ -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