recon_gen.common.env_keys
Y.2.gate.b.15 — Typed registry for every RECON_GEN_* / RECON_E2E_* env var.
AC.B.3 — env vars renamed from QS_GEN_* / QS_E2E_* to
RECON_GEN_* / RECON_E2E_* alongside the package rename.
Each spec carries a legacy_name pointing at the old name so
operators with export QS_GEN_*=... in their shell rc continue
to work for a 1-2 month grace window (mirrors the PyPI shim
window). Reads emit a one-shot DeprecationWarning per legacy
env-var name. The legacy fallback gets dropped in the same release
that drops the quicksight-gen PyPI shim.
Why this exists
Bare os.environ.get("RECON_GEN_RUN_DIR") strings spread across runner
+ config + conftest + harness + helpers as the test layer chain
runner grew. Three independent failure modes:
Typos.
os.environ.get("RECON_GEN_DEMO_DATABASE_URLL")silently falls through to “env var unset” — no TypeError, no NameError, no probe failure. The bug surfaces as “DB connection used the cfg default instead of the variant URL” 30 seconds later in a totally different stack frame.Required-vs-optional context.
RECON_GEN_RUN_DIRis optional (sidecar capture);RECON_E2E_USER_ARNis required (browser e2e embed-URL signing);RECON_GEN_DEMO_DATABASE_URLis context- required (set by the runner for non-default variants). Every call site re-implements its own None-handling.Value validation. When
RECON_GEN_CONFIG=/typo/pathis set, the failure shows up asFileNotFoundError5 frames deep insideload_config— the operator has to trace it back to the env var. Better: catch at the boundary with the env var name + description in the error message.
(The historical context above used QS_GEN_* examples — flipped to
the new RECON_GEN_* form during AC.B.3 to match the live registry.)
Locked design (Y.2.gate.b.15.spec): typed EnvVar[T] dataclass
per env var carrying name + description + coercer + optional +
validator. Three operations:
.get_or_none() -> T | None— sidecar shape; absent or empty → None. Validator runs on the coerced value when present..require() -> T— required-context shape; raisesEnvVarRequiredwith the spec’sdescriptionas the operator-actionable hint when absent..serialize(value: T) -> str— set-in-subprocess-env shape; validator runs first (catches set-side bugs like “runner forgot to mkdir before setting RUN_DIR”).
Validation runs in BOTH directions so set-side bugs and get-side
bugs both surface at the boundary with EnvVarInvalid carrying
name + description.
The b.15.lint follow-up adds an AST lint that catches any code
attempting to bypass this registry (bare os.environ.get with a
QS_* literal). Until then, the convention is enforced by
review.
Functions
Return a snapshot of every env-var access made by this process, in order. |
|
|
Return a validator that checks the value matches |
|
Path must exist AND be a directory. |
|
Path must exist AND be a regular file. |
|
Path must exist (file or directory). |
|
Integer must be > 0. |
Clear the access log. |
Classes
|
Spec for one env var. |
Exceptions
|
Base for env-var registry errors. |
|
Raised by |
|
Raised by |
- class recon_gen.common.env_keys.EnvVar(name, description, coercer, optional=True, validator=None, legacy_name=None)[source]
Bases:
GenericSpec for one env var. Frozen so call sites can declare these at module top-level without mutation worry.
- Fields:
name: the env-var name (the string passed to os.environ). description: operator-facing description; surfaces in the
error message when
require()finds the var unset orget_or_none()finds the value invalid.- coercer:
str → T. Examples:Path,int, _bool_coercer,str(identity).- optional: True iff the var is allowed to be absent. Affects
documentation only;
get_or_none()always tolerates absence andrequire()always raises on absence.- validator: optional
T → Nonecallable. Raises ValueError(re-wrapped asEnvVarInvalidby the EnvVar machinery) when the coerced value is unacceptable (path doesn’t exist, int non-positive, etc.). Runs in both directions: get-side AND serialize-side.
- coercer:
- Parameters:
name (str)
description (str)
coercer (Callable[[str], T])
optional (bool)
validator (Callable[[T], None] | None)
legacy_name (str | None)
- coercer: Callable[[str], T]
- description: str
- get_or_none()[source]
Read the env var. Returns None when unset or empty. Coerces + validates when present;
EnvVarInvalidon validator failure.- Return type:
Optional[TypeVar(T)]
- legacy_name: str | None = None
AC.B.3 — deprecated legacy env-var name to fall back to when the canonical
nameis unset. Reads emit a one-shotDeprecationWarningper legacy name (deduped via_legacy_warned). Set during AC.B.3 for theQS_GEN_*/QS_E2E_*→RECON_*rename; the fallback is dropped alongside the PyPI shim drop.
- name: str
- optional: bool = True
- require()[source]
Read the env var. Raises
EnvVarRequiredwhen unset or empty (with the spec’sdescriptionin the error). Coerces + validates when present.- Return type:
TypeVar(T)
- serialize(value)[source]
Convert
valueto a string for placement in a subprocess env dict. Validates before serializing so set-side bugs (“runner forgot to mkdir before setting RUN_DIR”) surface at the same boundary as get-side bugs.- Return type:
str- Parameters:
value (T)
- validator: Callable[[T], None] | None = None
- exception recon_gen.common.env_keys.EnvVarError(spec_name, description, detail)[source]
Bases:
ExceptionBase for env-var registry errors. Carries the spec name + description so the operator-facing message is always actionable.
- Parameters:
spec_name (str)
description (str)
detail (str)
- Return type:
None
- exception recon_gen.common.env_keys.EnvVarInvalid(spec_name, description, detail)[source]
Bases:
EnvVarErrorRaised by
EnvVar.get_or_none()/.require()/.serialize()when the value fails the spec’s validator (e.g., path doesn’t exist, int is non-positive).- Parameters:
spec_name (str)
description (str)
detail (str)
- Return type:
None
- exception recon_gen.common.env_keys.EnvVarRequired(spec_name, description, detail)[source]
Bases:
EnvVarErrorRaised by
EnvVar.require()when the env var is unset (or empty).- Parameters:
spec_name (str)
description (str)
detail (str)
- Return type:
None
- recon_gen.common.env_keys.dump_env_access()[source]
Return a snapshot of every env-var access made by this process, in order. Each entry is
(canonical_name, op)where op is"read_hit"/"read_miss"/"write".Useful for CB.17.d’s strangler-pattern verification: run both the legacy + thin paths through this hook, compare the access lists, surface any env-var that one path uses but the other doesn’t.
The snapshot is shallow-copied so callers can clear / inspect without racing the next access.
- Return type:
list[tuple[str,str]]
- recon_gen.common.env_keys.matches(pattern)[source]
Return a validator that checks the value matches
pattern.- Return type:
Callable[[str],None]- Parameters:
pattern (Pattern[str])
- recon_gen.common.env_keys.must_be_dir(p)[source]
Path must exist AND be a directory.
- Return type:
None- Parameters:
p (Path)
- recon_gen.common.env_keys.must_be_file(p)[source]
Path must exist AND be a regular file.
- Return type:
None- Parameters:
p (Path)
- recon_gen.common.env_keys.must_exist(p)[source]
Path must exist (file or directory).
- Return type:
None- Parameters:
p (Path)