diff --git a/tests/test_spawning.py b/tests/test_spawning.py index 283d1785..7dfa51af 100644 --- a/tests/test_spawning.py +++ b/tests/test_spawning.py @@ -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] == {} diff --git a/tractor/runtime/_supervise.py b/tractor/runtime/_supervise.py index 3cd7d4c7..f80d0845 100644 --- a/tractor/runtime/_supervise.py +++ b/tractor/runtime/_supervise.py @@ -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 )