Add per-actor parent-main replay opt-out

Let actor callers skip replaying the parent __main__ during child startup so downstream integrations can avoid inheriting incompatible bootstrap state without changing the default spawn behavior.
subint_spawner_backend
mahmoud 2026-04-06 05:51:02 +00:00 committed by mahmoudhas
parent 9f8e9eb739
commit f5301d3fb0
2 changed files with 56 additions and 0 deletions

View File

@ -197,3 +197,50 @@ def test_loglevel_propagated_to_subactor(
# ensure subactor spits log message on stderr
captured = capfd.readouterr()
assert 'yoyoyo' in captured.err
def test_start_actor_can_skip_parent_main_replay(monkeypatch, reg_addr):
captured_parent_main_data: list[dict[str, str]] = []
from tractor.runtime import _supervise as supervise_module
async def fake_new_proc(
name: str,
actor_nursery,
subactor,
errors,
bind_addrs,
parent_addr,
_runtime_vars,
*,
infect_asyncio: bool = False,
task_status=trio.TASK_STATUS_IGNORED,
proc_kwargs: dict[str, Any] = {},
) -> None:
captured_parent_main_data.append(dict(subactor._parent_main_data))
task_status.started(object())
monkeypatch.setattr(
supervise_module._spawn,
'new_proc',
fake_new_proc,
)
async def main() -> None:
async with tractor.open_root_actor(
registry_addrs=[reg_addr],
):
async with tractor.open_nursery() as an:
await an.start_actor(
'replaying-parent-main',
enable_modules=[__name__],
)
await an.start_actor(
'isolated-parent-main',
enable_modules=[__name__],
replay_parent_main=False,
)
trio.run(main)
assert captured_parent_main_data[0]
assert captured_parent_main_data[1] == {}

View File

@ -194,6 +194,7 @@ class ActorNursery:
loglevel: str|None = None, # set log level per subactor
debug_mode: bool|None = None,
infect_asyncio: bool = False,
replay_parent_main: bool = True,
# TODO: ideally we can rm this once we no longer have
# a `._ria_nursery` since the dependent APIs have been
@ -206,6 +207,10 @@ class ActorNursery:
Start a (daemon) actor: an process that has no designated
"main task" besides the runtime.
Pass ``replay_parent_main=False`` to keep this child on its own
bootstrap module instead of re-running the parent's ``__main__``
during startup.
'''
__runtimeframe__: int = 1 # noqa
loglevel: str = (
@ -246,6 +251,8 @@ class ActorNursery:
# verbatim relay this actor's registrar addresses
registry_addrs=current_actor().registry_addrs,
)
if not replay_parent_main:
subactor._parent_main_data = {}
parent_addr: UnwrappedAddress = self._actor.accept_addr
assert parent_addr
@ -289,6 +296,7 @@ class ActorNursery:
enable_modules: list[str] | None = None,
loglevel: str | None = None, # set log level per subactor
infect_asyncio: bool = False,
replay_parent_main: bool = True,
proc_kwargs: dict[str, any] = {},
**kwargs, # explicit args to ``fn``
@ -320,6 +328,7 @@ class ActorNursery:
# use the run_in_actor nursery
nursery=self._ria_nursery,
infect_asyncio=infect_asyncio,
replay_parent_main=replay_parent_main,
proc_kwargs=proc_kwargs
)