Cst rewrite
cst_rewrite
libcst write helpers + the import-index cache.
Everything that mutates Python source code lives here. Split into five concerns:
- class flatten (
_flatten_class_to_top_level,_flatten_class_child) - rename (
_rename_name_in_module,_rename_top_level_in_source) - delete (
_delete_function_from_source,_delete_source_if_empty_tests) - statement reorder (
_reorder_module_statements+ ast helpers) Path(__file__).parents[N]depth patch (_patch_file_dunder_depth)- import management — read-side analysis lives in
tests_ast; here we insert, dedupe, backfill, and synthesise missing imports, plus the project-wide import index that keeps backfill O(1) per lookup.
Hybrid I/O: we read with ast (cheap, well-tested for analysis) and
write with libcst (source-fidelity: comments, triple-quoted strings,
blank lines, quote style all preserved — what ast.unparse silently
loses).
backfill_import(module, mapping)
Insert from {mod} import {name} for each (name → mod) in mapping.
The new imports go at the canonical position — after every
from __future__ import … (or at the module top when none exists)
and before the first non-import statement. Names already imported in
module are skipped, so this is idempotent on already-correct sources.
Source code in packages/axm-audit/src/axm_audit/core/fix/cst_rewrite.py
backfill_missing_imports(source, target, project_path=None)
Copy imports from source into target for names target uses but doesn't define.
Falls back to scanning all test files under project_path if the
immediate source doesn't have the import — covers cases where the
original import was lost by an earlier move.
Hybrid: analyse with ast (cheap, well-tested), write with libcst so triple-quoted strings, blank lines, and comments in the target file are preserved byte-for-byte.
Source code in packages/axm-audit/src/axm_audit/core/fix/cst_rewrite.py
dedupe_imports(module)
delete_function(module, func_name)
Drop top-level function func_name from module.
Neighbouring statements (and their attached blank-line spacing) are
preserved by libcst's leading-lines semantics. In-memory counterpart
of :func:_delete_function_from_source.
Source code in packages/axm-audit/src/axm_audit/core/fix/cst_rewrite.py
flatten_class(module, class_name)
Flatten the class_name class into top-level functions.
In-memory variant of :func:_flatten_class_to_top_level. Class-level
pytest marks are propagated onto each promoted method; method-level
decorators are preserved verbatim; the class docstring is dropped.
Source code in packages/axm-audit/src/axm_audit/core/fix/cst_rewrite.py
invalidate_import_index(project_path)
Drop the cached import index for project_path.
patch_file_depth(module, depth_delta=0)
Rewrite Path(__file__).parents[N] literals by depth_delta.
In-memory variant of :func:_patch_file_dunder_depth that targets the
subscript form only — the chained .parent.parent form is left for
the file-level helper. Identity transform when depth_delta is 0 or
the pattern is absent.
Source code in packages/axm-audit/src/axm_audit/core/fix/cst_rewrite.py
rename_function(module, old_name, new_name)
Rename top-level function old_name to new_name across module.
Updates the FunctionDef itself, any Name reference, and any
string-literal argument (e.g. pytest.mark.parametrize("old", …))
that matches old_name. In-memory counterpart of
:func:_rename_name_in_module.
Source code in packages/axm-audit/src/axm_audit/core/fix/cst_rewrite.py
resolve_import_for_symbol(project_path, symbol)
Return the import statement that brings symbol into scope, or None.
Builds (and caches in _PROJECT_IMPORT_INDEX_CACHE) a project-wide
index of top-level FunctionDef / AsyncFunctionDef / ClassDef
definitions across every .py file under project_path. Drop the
cache via :func:invalidate_import_index after mutating the file
tree so the next call rebuilds.