Add `'subint'` spawn backend scaffold (#379)
Land the scaffolding for a future sub-interpreter (PEP 734 `concurrent.interpreters`) actor spawn backend per issue #379. The spawn flow itself is not yet implemented; `subint_proc()` raises a placeholder `NotImplementedError` pointing at the tracking issue — this commit only wires up the registry, the py-version gate, and the harness. Deats, - bump `pyproject.toml` `requires-python` to `>=3.12, <3.15` and list the `3.14` classifier — the new stdlib `concurrent.interpreters` module only ships on 3.14 - extend `SpawnMethodKey = Literal[..., 'subint']` - `try_set_start_method('subint')` grows a new `match` arm that feature-detects the stdlib module and raises `RuntimeError` with a clear banner on py<3.14 - `_methods` registers the new `subint_proc()` via the same bottom-of-module late-import pattern used for `._trio` / `._mp` Also, - new `tractor/spawn/_subint.py` — top-level `try: from concurrent import interpreters` guards `_has_subints: bool`; `subint_proc()` signature mirrors `trio_proc`/`mp_proc` so the Phase B.2 impl can drop in without touching the registry - re-add `import sys` to `_spawn.py` (needed for the py-version msg in the gate-error) - `_testing.pytest.pytest_configure` wraps `try_set_start_method()` in a `pytest.UsageError` handler so `--spawn-backend=subint` on py<3.14 prints a clean banner instead of a traceback (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-codesubint_spawner_backend
parent
3773ad8b77
commit
7cee62ce42
|
|
@ -9,7 +9,7 @@ name = "tractor"
|
|||
version = "0.1.0a6dev0"
|
||||
description = 'structured concurrent `trio`-"actors"'
|
||||
authors = [{ name = "Tyler Goodlet", email = "goodboy_foss@protonmail.com" }]
|
||||
requires-python = ">=3.12, <3.14"
|
||||
requires-python = ">=3.12, <3.15"
|
||||
readme = "docs/README.rst"
|
||||
license = "AGPL-3.0-or-later"
|
||||
keywords = [
|
||||
|
|
@ -31,6 +31,7 @@ classifiers = [
|
|||
"Programming Language :: Python :: 3 :: Only",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
"Programming Language :: Python :: 3.14",
|
||||
"Topic :: System :: Distributed Computing",
|
||||
]
|
||||
dependencies = [
|
||||
|
|
|
|||
|
|
@ -227,7 +227,13 @@ def pytest_addoption(
|
|||
def pytest_configure(config):
|
||||
backend = config.option.spawn_backend
|
||||
from tractor.spawn._spawn import try_set_start_method
|
||||
try:
|
||||
try_set_start_method(backend)
|
||||
except RuntimeError as err:
|
||||
# e.g. `--spawn-backend=subint` on Python < 3.14 — turn the
|
||||
# runtime gate error into a clean pytest usage error so the
|
||||
# suite exits with a helpful banner instead of a traceback.
|
||||
raise pytest.UsageError(str(err)) from err
|
||||
|
||||
# register custom marks to avoid warnings see,
|
||||
# https://docs.pytest.org/en/stable/how-to/writing_plugins.html#registering-custom-markers
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ over multiple backends.
|
|||
from __future__ import annotations
|
||||
import multiprocessing as mp
|
||||
import platform
|
||||
import sys
|
||||
from typing import (
|
||||
Any,
|
||||
Awaitable,
|
||||
|
|
@ -61,6 +62,7 @@ SpawnMethodKey = Literal[
|
|||
'trio', # supported on all platforms
|
||||
'mp_spawn',
|
||||
'mp_forkserver', # posix only
|
||||
'subint', # py3.14+ via `concurrent.interpreters` (PEP 734)
|
||||
]
|
||||
_spawn_method: SpawnMethodKey = 'trio'
|
||||
|
||||
|
|
@ -113,6 +115,17 @@ def try_set_start_method(
|
|||
case 'trio':
|
||||
_ctx = None
|
||||
|
||||
case 'subint':
|
||||
# subints need no `mp.context`; feature-gate 3.14+
|
||||
from ._subint import _has_subints
|
||||
if not _has_subints:
|
||||
raise RuntimeError(
|
||||
f'Spawn method {key!r} requires Python 3.14+ '
|
||||
f'(stdlib `concurrent.interpreters`, PEP 734).\n'
|
||||
f'Current runtime: {sys.version}'
|
||||
)
|
||||
_ctx = None
|
||||
|
||||
case _:
|
||||
raise ValueError(
|
||||
f'Spawn method `{key}` is invalid!\n'
|
||||
|
|
@ -437,6 +450,7 @@ async def new_proc(
|
|||
# `hard_kill`/`proc_waiter` from this module.
|
||||
from ._trio import trio_proc
|
||||
from ._mp import mp_proc
|
||||
from ._subint import subint_proc
|
||||
|
||||
|
||||
# proc spawning backend target map
|
||||
|
|
@ -444,4 +458,5 @@ _methods: dict[SpawnMethodKey, Callable] = {
|
|||
'trio': trio_proc,
|
||||
'mp_spawn': mp_proc,
|
||||
'mp_forkserver': mp_proc,
|
||||
'subint': subint_proc,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,100 @@
|
|||
# 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/>.
|
||||
|
||||
'''
|
||||
Sub-interpreter (`subint`) actor spawning backend.
|
||||
|
||||
Spawns each sub-actor as a CPython PEP 734 sub-interpreter
|
||||
(`concurrent.interpreters.Interpreter`) — same-process state
|
||||
isolation with faster start-up than an OS subproc, while
|
||||
preserving tractor's IPC-based actor boundaries.
|
||||
|
||||
Availability
|
||||
------------
|
||||
Requires Python 3.14+ for the stdlib `concurrent.interpreters`
|
||||
module. On older runtimes the module still imports (so the
|
||||
registry stays introspectable) but `subint_proc()` raises.
|
||||
|
||||
Status
|
||||
------
|
||||
SCAFFOLDING STUB — `subint_proc()` is **not yet implemented**.
|
||||
The real impl lands in Phase B.2 (see issue #379).
|
||||
|
||||
'''
|
||||
from __future__ import annotations
|
||||
import sys
|
||||
from typing import (
|
||||
Any,
|
||||
TYPE_CHECKING,
|
||||
)
|
||||
|
||||
import trio
|
||||
from trio import TaskStatus
|
||||
|
||||
|
||||
try:
|
||||
from concurrent import interpreters as _interpreters # type: ignore
|
||||
_has_subints: bool = True
|
||||
except ImportError:
|
||||
_interpreters = None # type: ignore
|
||||
_has_subints: bool = False
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from tractor.discovery._addr import UnwrappedAddress
|
||||
from tractor.runtime._portal import Portal
|
||||
from tractor.runtime._runtime import Actor
|
||||
from tractor.runtime._supervise import ActorNursery
|
||||
|
||||
|
||||
async def subint_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,
|
||||
task_status: TaskStatus[Portal] = trio.TASK_STATUS_IGNORED,
|
||||
proc_kwargs: dict[str, any] = {}
|
||||
|
||||
) -> None:
|
||||
'''
|
||||
Create a new sub-actor hosted inside a PEP 734
|
||||
sub-interpreter running in a dedicated OS thread,
|
||||
reusing tractor's existing UDS/TCP IPC handshake
|
||||
for parent<->child channel setup.
|
||||
|
||||
NOT YET IMPLEMENTED — placeholder stub pending the
|
||||
Phase B.2 impl.
|
||||
|
||||
'''
|
||||
if not _has_subints:
|
||||
raise RuntimeError(
|
||||
f'The {"subint"!r} spawn backend requires Python 3.14+ '
|
||||
f'(stdlib `concurrent.interpreters`, PEP 734).\n'
|
||||
f'Current runtime: {sys.version}'
|
||||
)
|
||||
|
||||
raise NotImplementedError(
|
||||
'The `subint` spawn backend scaffolding is in place but '
|
||||
'the spawn-flow itself is not yet implemented.\n'
|
||||
'Tracking: https://github.com/goodboy/tractor/issues/379'
|
||||
)
|
||||
Loading…
Reference in New Issue