Extract helpers
extract_helpers
Post-pipeline polish: collapse duplicated test helpers.
After SPLIT and MERGE leave anvil's shared_helpers="duplicate" copies
of helpers (gold_project, _make_result, …) in every post-move file, this
module collapses them into a single tests/<tier>/_helpers.py (for
pure helpers) or the tier's conftest.py (for @pytest.fixture).
Three layers:
extract_shared_helpers— top-level entry; iterates_onceuntil fixed-point, deduplicating ambiguous-fixture warnings across iterations.extract_shared_helpers_once/_in_tier— single-pass extraction across all canonical tiers.load_or_create_*/strip_def_*— file-level mutation helpers, libcst-based.
extract_shared_helpers(project_path)
Iterate extract_shared_helpers_once until fixed-point.
A single pass cannot catch every duplicate: promoting helper A can
expose helper B as duplicate (e.g. A's body referenced B locally,
so B looked non-shared until A moved out). Loop until no further
extraction happens. Capped at _EXTRACT_MAX_ITERS to fail loud
on a buggy fixed-point.
ambiguous fixture messages are re-emitted on every iteration
(the same fixtures stay ambiguous forever) — collapse them so the
operator sees each one exactly once.
Source code in packages/axm-audit/src/axm_audit/core/fix/extract_helpers.py
extract_shared_helpers_in_tier(project_path, tier_dir)
Process a single tier. Splitting per-tier keeps imports local.
Source code in packages/axm-audit/src/axm_audit/core/fix/extract_helpers.py
extract_shared_helpers_once(project_path)
Promote helpers duplicated across a tier into tests/<tier>/_helpers.py.
Source code in packages/axm-audit/src/axm_audit/core/fix/extract_helpers.py
strip_def_and_inject_import(file, name, helpers_module, project_path)
Remove the top-level def of name from file and import it instead.
Source code in packages/axm-audit/src/axm_audit/core/fix/extract_helpers.py
strip_def_only(file, name)
Remove the top-level def of name from file without injecting an import.
Used for fixtures whose new home (conftest.py) is auto-discovered
by pytest — no import would be valid syntactically (it would shadow
the injected fixture parameter) and none is needed.