Support a `._state.last_actor()` getter

Not sure if it's really that useful other then for reporting errors from
`current_actor()` but at least it alerts `tractor` devs and/or users
when the runtime has already terminated vs. hasn't been started
yet/correctly.

Set the `._last_actor_terminated: tuple` in the root's final block which
allows testing for an already terminated tree which is the case where
`._state._current_actor == None` and the last is set.
modden_spawn_from_client_req
Tyler Goodlet 2024-03-08 14:11:17 -05:00
parent b29d33d603
commit 4c3c3e4b56
3 changed files with 55 additions and 10 deletions

View File

@ -348,6 +348,7 @@ async def open_root_actor(
await actor.cancel(None) # self cancel
finally:
_state._current_actor = None
_state._last_actor_terminated = actor
# restore built-in `breakpoint()` hook state
sys.breakpointhook = builtin_bp_handler

View File

@ -18,12 +18,18 @@
Per process state
"""
from __future__ import annotations
from typing import (
Optional,
Any,
TYPE_CHECKING,
)
_current_actor: Optional['Actor'] = None # type: ignore # noqa
if TYPE_CHECKING:
from ._runtime import Actor
_current_actor: Actor|None = None # type: ignore # noqa
_last_actor_terminated: Actor|None = None
_runtime_vars: dict[str, Any] = {
'_debug_mode': False,
'_is_root': False,
@ -31,14 +37,49 @@ _runtime_vars: dict[str, Any] = {
}
def current_actor(err_on_no_runtime: bool = True) -> 'Actor': # type: ignore # noqa
def last_actor() -> Actor|None:
'''
Try to return last active `Actor` singleton
for this process.
For case where runtime already exited but someone is asking
about the "last" actor probably to get its `.uid: tuple`.
'''
return _last_actor_terminated
def current_actor(
err_on_no_runtime: bool = True,
) -> Actor:
'''
Get the process-local actor instance.
'''
from ._exceptions import NoRuntime
if _current_actor is None and err_on_no_runtime:
raise NoRuntime("No local actor has been initialized yet")
if (
err_on_no_runtime
and _current_actor is None
):
msg: str = 'No local actor has been initialized yet'
from ._exceptions import NoRuntime
if last := last_actor():
msg += (
f'Apparently the lact active actor was\n'
f'|_{last}\n'
f'|_{last.uid}\n'
)
# no actor runtime has (as of yet) ever been started for
# this process.
else:
msg += (
'No last actor found?\n'
'Did you forget to open one of:\n\n'
'- `tractor.open_root_actor()`\n'
'- `tractor.open_nursery()`\n'
)
raise NoRuntime(msg)
return _current_actor

View File

@ -533,12 +533,15 @@ async def open_nursery(
'''
implicit_runtime: bool = False
actor = current_actor(err_on_no_runtime=False)
actor: Actor = current_actor(
err_on_no_runtime=False
)
try:
if actor is None and is_main_process():
if (
actor is None
and is_main_process()
):
# if we are the parent process start the
# actor runtime implicitly
log.info("Starting actor runtime!")