Refactor `_runtime_vars` into pure get/set API

Resetting `_runtime_vars` post-(forking-)spawn was
previously only possible via direct mutation of
`_state._runtime_vars` from an external module + an
inline default dict duplicating the
`_state.py`-internal defaults. Split the access
surface into a pure getter + explicit setter so such
a reset call site becomes a one-liner composition:
`set_runtime_vars(get_runtime_vars(clear_values=True))`.

Deats `tractor/runtime/_state.py`,
- extract initial values into a module-level
  `_RUNTIME_VARS_DEFAULTS: dict[str, Any]` constant; the
  live `_runtime_vars` is now initialised from
  `dict(_RUNTIME_VARS_DEFAULTS)`
- `get_runtime_vars()` grows a `clear_values: bool = False`
  kwarg. When True, returns a fresh copy of
  `_RUNTIME_VARS_DEFAULTS` instead of the live dict —
  still a **pure read**, never mutates anything
- new `set_runtime_vars(rtvars: dict | RuntimeVars)` —
  atomic replacement of the live dict's contents via
  `.clear()` + `.update()`, so existing references to the
  same dict object remain valid. Accepts either the
  historical dict form or the `RuntimeVars` struct

(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code

(cherry picked from commit 7804a9fe57693dd5e15bee6a08e7d2fa14b6a98a)
(factored: kept only the tractor/runtime/_state.py part; dropped
 tractor/spawn/_subint_forkserver.py call-site rewire)
wkt/tooling_enhancements_from_mtf_spawner
Gud Boi 2026-06-09 20:19:56 -04:00
parent 668ad69fd2
commit fc049abe2a
1 changed files with 65 additions and 5 deletions

View File

@ -117,7 +117,14 @@ class RuntimeVars(Struct):
) )
_runtime_vars: dict[str, Any] = { # The "fresh process" defaults — what `_runtime_vars` looks
# like in a just-booted Python process that hasn't yet entered
# `open_root_actor()` nor received a parent `SpawnSpec`. Kept
# as a module-level constant so `get_runtime_vars(clear_values=
# True)` can reset the live dict back to this baseline (see
# `tractor.spawn._subint_forkserver` for the one current caller
# that needs it).
_RUNTIME_VARS_DEFAULTS: dict[str, Any] = {
# root of actor-process tree info # root of actor-process tree info
'_is_root': False, # bool '_is_root': False, # bool
'_root_mailbox': (None, None), # tuple[str|None, str|None] '_root_mailbox': (None, None), # tuple[str|None, str|None]
@ -138,10 +145,12 @@ _runtime_vars: dict[str, Any] = {
# infected-`asyncio`-mode: `trio` running as guest. # infected-`asyncio`-mode: `trio` running as guest.
'_is_infected_aio': False, '_is_infected_aio': False,
} }
_runtime_vars: dict[str, Any] = dict(_RUNTIME_VARS_DEFAULTS)
def get_runtime_vars( def get_runtime_vars(
as_dict: bool = True, as_dict: bool = True,
clear_values: bool = False,
) -> dict: ) -> dict:
''' '''
Deliver a **copy** of the current `Actor`'s "runtime variables". Deliver a **copy** of the current `Actor`'s "runtime variables".
@ -150,11 +159,62 @@ def get_runtime_vars(
form, but the `RuntimeVars` struct should be utilized as possible form, but the `RuntimeVars` struct should be utilized as possible
for future calls. for future calls.
''' Pure read **never mutates** the module-level `_runtime_vars`.
if as_dict:
return dict(_runtime_vars)
return RuntimeVars(**_runtime_vars) If `clear_values=True`, return a copy of the fresh-process
defaults (`_RUNTIME_VARS_DEFAULTS`) instead of the live
dict. Useful in combination with `set_runtime_vars()` to
reset process-global state back to "cold" the main caller
today is the `subint_forkserver` spawn backend's post-fork
child prelude:
set_runtime_vars(get_runtime_vars(clear_values=True))
`os.fork()` inherits the parent's full memory image, so the
child sees the parent's populated `_runtime_vars` (e.g.
`_is_root=True`) which would trip the `assert not
self.enable_modules` gate in `Actor._from_parent()` on the
subsequent parentchild `SpawnSpec` handshake if left alone.
'''
src: dict = (
_RUNTIME_VARS_DEFAULTS
if clear_values
else _runtime_vars
)
snapshot: dict = dict(src)
if as_dict:
return snapshot
return RuntimeVars(**snapshot)
def set_runtime_vars(
rtvars: dict | RuntimeVars,
) -> None:
'''
Atomically replace the module-level `_runtime_vars` contents
with those of `rtvars` (via `.clear()` + `.update()` so
live references to the same dict object remain valid).
Accepts either the historical `dict` form or the `RuntimeVars`
`msgspec.Struct` form (the latter still mostly unused but
the blessed forward shape see the struct's definition).
Paired with `get_runtime_vars()` as the explicit
write-half of the runtime-vars API prefer this over
direct mutation of `_runtime_vars[...]` from new call sites.
'''
if isinstance(rtvars, RuntimeVars):
# `msgspec.Struct` → dict via its declared field set;
# avoids pulling in `msgspec.structs.asdict` just for
# this one call path.
rtvars = {
field_name: getattr(rtvars, field_name)
for field_name in rtvars.__struct_fields__
}
_runtime_vars.clear()
_runtime_vars.update(rtvars)
def last_actor() -> Actor|None: def last_actor() -> Actor|None: