From 7882c37ce0d338088cedc61c84e996bc1fb31325 Mon Sep 17 00:00:00 2001 From: goodboy Date: Wed, 6 May 2026 15:02:13 -0400 Subject: [PATCH] Add `RuntimeVars` env-var lift design plan Draft plan for consolidating pytest CLI flags, ad-hoc env vars, and hardcoded fixture defaults into the existing (but unused) `RuntimeVars` struct as the single source of truth. Deats, - `_rtvars.py` leaf mod w/ `dump`/`load`/`get`/ `update` helpers using `str(dict)` + `ast.literal_eval` encoding - phased migration: test infra first, then runtime callers, then per-session bindspace - addresses concurrent pytest session collisions and subproc env propagation for `devx/` scripts (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code --- .claude/notes/rt_vars_lift_plan.md | 125 +++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 .claude/notes/rt_vars_lift_plan.md diff --git a/.claude/notes/rt_vars_lift_plan.md b/.claude/notes/rt_vars_lift_plan.md new file mode 100644 index 00000000..afa54c1a --- /dev/null +++ b/.claude/notes/rt_vars_lift_plan.md @@ -0,0 +1,125 @@ +# `RuntimeVars` env-var lift — design plan + +Status: **draft, awaiting user edits** + +## Goal + +Consolidate the sprawl of pytest CLI flags + ad-hoc env vars + +hardcoded fixture defaults into a *single* env-var-encoded +runtime-vars envelope, with a typed in-memory representation +(`tractor.runtime._state.RuntimeVars`) as the sole source of +truth. + +## Why now + +- `--tpt-proto`, `--spawn-backend`, `--diag-on-hang`, + `--diag-capture-delay` and (soon) `TRACTOR_REG_ADDR` etc. are + proliferating. Each adds a parsing seam. +- `tests/devx/test_debugger.py` invokes example scripts as + separate subprocesses; they currently can't see the + fixture-allocated `reg_addr` at all (root cause of why + parametrizing devx scripts on `reg_addr` is on your TODO). +- Concurrent pytest sessions on the same host collide on + shared defaults (the `registry@1616` race we just fixed is + one symptom; per-session unique addr is the structural + fix). +- `tractor.runtime._state.RuntimeVars: Struct` is already + defined and **unused** — its docstring even says it + "should be utilized as possible for future calls." + +## Design + +### Module: `tractor/_testing/_rtvars.py` + +Lifted from `modden.runtime.env`, ~50 LOC, no new deps. + +```python +_TRACTOR_RT_VARS_OSENV: str = '_TRACTOR_RT_VARS' + +def dump_rtvars(rtvars: RuntimeVars|dict) -> tuple[str, str]: + '''str-serialize via `str(dict)` — ast.literal_eval-able''' + +def load_rtvars(env: dict) -> RuntimeVars: + '''ast.literal_eval the env-var value, hydrate to struct''' + +def get_rtvars(proc: psutil.Process|None = None) -> RuntimeVars: + '''read the var from a target proc's env (or current)''' + +def update_rtvars( + rtvars: RuntimeVars|dict|None = None, + update_osenv: bool|dict = True, +) -> tuple[str, str]: + '''mutate + re-encode + (optionally) write to os.environ''' +``` + +### Encoding choice: `str(dict)` + `ast.literal_eval` + +Pros: +- stdlib only +- handles all the types tractor's tests need: `str`, `int`, + `float`, `bool`, `None`, `list`, `tuple`, `dict` +- human-readable in the env (greppable, inspectable via + `cat /proc//environ | tr '\0' '\n'`) + +Cons: +- non-stdlib types (msgspec Structs, `Path`, custom classes) + must be lowered first — fine for the test fixture set +- not stable across Python versions for esoteric repr cases + (we don't hit any) + +Alternatives considered: +- **msgpack**: adds a dep + binary form is ungreppable +- **json**: doesn't preserve tuples (becomes lists), which is + a common type for `reg_addr` +- **toml/yaml**: heavier deps, no real benefit + +### `RuntimeVars` becomes the single source of truth + +The legacy `_runtime_vars: dict[str, Any]` global in +`runtime/_state.py` becomes a *cached view* of a +`RuntimeVars` singleton instance: + +- `get_runtime_vars()` returns either the struct or a + `.to_dict()` view depending on caller's preference +- `set_runtime_vars(...)` validates against the struct schema +- spawn-time SpawnSpec sends the struct (already does + conceptually — just gets typed) +- `__setattr__` `breakpoint()` debug instrumentation gets + removed (unrelated cleanup, mentioned in conversation) + +### Migration path + +**Phase 0** *(prep)*: strip the stray `breakpoint()` from +`RuntimeVars.__setattr__`. + +**Phase 1**: land `_rtvars.py` as a leaf module, used only by +test infra. Subprocess-spawned scripts in `tests/devx/` +read `_TRACTOR_RT_VARS` on startup → reconstruct +`RuntimeVars` → call `tractor.open_root_actor(**rtvars.as_kwargs())`. +Concurrent runs become deterministic-isolated because each +session writes a unique `_registry_addrs` into the env. + +**Phase 2**: migrate runtime callers (`_state.get_runtime_vars`, +spawn `SpawnSpec`, `Actor.async_main`) to operate on the +struct directly, with the dict as a compat view that gets +deprecated. + +**Phase 3** *(structural)*: per-session bindspace subdir +`/run/user//tractor//` — encoded in the +rt-vars envelope, picked up by every subactor automatically. +Obsoletes the entire bindspace-leak warning class. + +## Open design questions (user input wanted) + +- (placeholder for your edits) +- (placeholder) +- (placeholder) + +## Out-of-scope for this lift + +- Anything in `modden.runtime.env` related to `Spawn`, + `WmCtl`, `Wks` — that's a workspace orchestration layer, + not an env-var helper. We only lift the four utility + functions + the var name constant. +- Switching to msgpack/json — explicitly chosen against + above.