shared
_shared
Shared AST primitives for test_quality rules.
Registry-neutral helpers ported from the detect_pyramid_level_v6 and
triage_tautologies_v4 prototypes. Rules in this subpackage import
from here; no @register_rule lives here.
analyze_imports(tree, pkg_prefixes, init_all, pkg_root)
Classify imports + collect IO module names.
Returns (public, internal, modules, has_private, io_module_names,
io_signals).
Source code in packages/axm-audit/src/axm_audit/core/rules/test_quality/_shared.py
canonical_filename(*, symbols_or_tuples, tier, single_binary)
Emit the canonical test_*.py filename for a tier's tuple.
For tier="integration", symbols_or_tuples is an iterable of
first-party symbol names; the top-K=2 (already sorted) are snake-cased
and joined by __ (PEP 8 module name; a single - would break
Python imports). For tier="e2e", symbols_or_tuples is an iterable
of (bin, sub) tuples; the same K=2 rule applies. When single_binary
is not None, the redundant binary prefix is stripped: (axm-audit,
audit) emits test_audit.py; (axm-audit, "") emits the bare
binary test_axm_audit.py.
K=0 yields test_UNKNOWN.py — but the FILE_NAMING rule never emits
K=0 findings (that case is NO_PACKAGE_SYMBOL's concern).
Source code in packages/axm-audit/src/axm_audit/core/rules/test_quality/_shared.py
cli_invocation_tuple(*, test_func, mod_ast, project_scripts)
Count (bin, sub) invocations of declared scripts in test_func.
Recognises subprocess.run(["bin", "sub", ...]) shapes (including
module aliases like python -m pkg ... and indirect list/string
bindings already handled by _argv_from_call). The sub-command is the
first non-flag argv element after the matched binary, or "" when the
bare binary is invoked. Non-package invocations (git init,
shutil.which-resolved plumbing) yield an empty counter.
Source code in packages/axm-audit/src/axm_audit/core/rules/test_quality/_shared.py
collect_pkg_contract_classes(pkg_root)
Contract classes (Protocol/ABC/TypedDict) in pkg_root and sibling packages.
Source code in packages/axm-audit/src/axm_audit/core/rules/test_quality/_shared.py
collect_pkg_public_symbols(pkg_root)
Collect top-level public function, class, and constant names across src/.
Walks every *.py file under {pkg_root}/src and returns the union of
non-underscore names defined at module top level — functions, async
functions, classes, and simple / annotated assignments to Name targets.
Source code in packages/axm-audit/src/axm_audit/core/rules/test_quality/_shared.py
current_level_from_path(test_file, tests_dir)
Map test_file to its pyramid level based on its location.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
test_file
|
Path
|
Test file path to classify. |
required |
tests_dir
|
Path
|
Root tests directory used to compute the relative path. |
required |
Returns:
| Type | Description |
|---|---|
str
|
One of |
str
|
the level cannot be inferred. |
str
|
|
Source code in packages/axm-audit/src/axm_audit/core/rules/test_quality/_shared.py
decorator_has_marker(dec, marker_name)
True when dec is @pytest.mark.<marker_name> (or aliased).
Source code in packages/axm-audit/src/axm_audit/core/rules/test_quality/_shared.py
detect_real_io(tree)
File-scope I/O detection from a parsed test module.
Scans the module for three kinds of evidence that a test performs real I/O:
fixtures listed in _IO_FIXTURES consumed by test_* functions,
dotted calls listed in _IO_CALLS (e.g. subprocess.run,
pathlib.Path.write_text), and CLI runner invocations such as
CliRunner().invoke(...). Attribute-IO inside helpers is intentionally
out of scope — see :func:func_attr_io_transitive.
Returns:
| Type | Description |
|---|---|
bool
|
|
bool
|
signal was found. |
list[str]
|
|
tuple[bool, bool, list[str]]
|
list of evidence strings ( |
tuple[bool, bool, list[str]]
|
|
Source code in packages/axm-audit/src/axm_audit/core/rules/test_quality/_shared.py
extract_mock_targets(func)
Collect dotted mock/patch targets + mock-factory:Mock/... markers.
Source code in packages/axm-audit/src/axm_audit/core/rules/test_quality/_shared.py
file_has_module_marker(tree, marker_name)
True when pytestmark = pytest.mark.<marker_name> is set at module level.
Source code in packages/axm-audit/src/axm_audit/core/rules/test_quality/_shared.py
first_party_symbol_counts(*, test_func, mod_ast, pkg_prefixes)
Count direct references to first-party symbols inside test_func.
Aliases are resolved from the module's imports (from pkg.x import Y
or import pkg.x as y). Per the FILE_NAMING design, only the bare
test body is walked — no helper closure — because tuple emission must
reflect direct usage frequency, not transitive reachability.
Source code in packages/axm-audit/src/axm_audit/core/rules/test_quality/_shared.py
fixture_does_io(fix_name, fixtures, visited, depth)
True if fixture body does real I/O.
Walks the fixture body for attr-IO, _IO_CALLS matches and
transitive tmp_path deps; depth-bounded by _FIXTURE_DEPTH_LIMIT.
Source code in packages/axm-audit/src/axm_audit/core/rules/test_quality/_shared.py
func_attr_io_transitive(func, helpers, max_depth=2)
Attr-IO signals over the function subtree + transitively reachable helpers.
Source code in packages/axm-audit/src/axm_audit/core/rules/test_quality/_shared.py
get_init_all(pkg_root)
Read __all__ from the first src/<pkg>/__init__.py that defines it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pkg_root
|
Path
|
Repository root expected to follow the |
required |
Returns:
| Type | Description |
|---|---|
set[str] | None
|
Set of exported names, or |
set[str] | None
|
declares |
Source code in packages/axm-audit/src/axm_audit/core/rules/test_quality/_shared.py
get_module_all(pkg_root, dotted)
Resolve __all__ for dotted module within the src tree.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pkg_root
|
Path
|
Repository root expected to follow the |
required |
dotted
|
str
|
Dotted module path relative to |
required |
Returns:
| Type | Description |
|---|---|
set[str] | None
|
Set of exported names, or |
set[str] | None
|
not declare |
Source code in packages/axm-audit/src/axm_audit/core/rules/test_quality/_shared.py
get_pkg_prefixes(pkg_root)
Return top-level package directory names under <pkg_root>/src.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pkg_root
|
Path
|
Repository root expected to follow the |
required |
Returns:
| Type | Description |
|---|---|
set[str]
|
Set of directory names directly under |
set[str]
|
entries. Empty when |
Source code in packages/axm-audit/src/axm_audit/core/rules/test_quality/_shared.py
has_in_package_subprocess_invocation(*, call, module_ast, project_scripts)
Return true when call invokes a declared package script.
Source code in packages/axm-audit/src/axm_audit/core/rules/test_quality/_shared.py
is_import_smoke_test(func)
Detect an import + weak-assert smoke test.
Returns True when the function body (docstrings excluded, ≤ 4 stmts)
contains at least one import/from … import … statement and at
least one weak assertion (assert name, assert x is not None,
assert isinstance(...), assert callable(...), or
self.assertIsNotNone(...)), with no statement stronger than these.
Source code in packages/axm-audit/src/axm_audit/core/rules/test_quality/_shared.py
iter_test_files(pkg_root)
Yield (path, ast) for every tests/**/test_*.py.
Skips tests/fixtures/ — by AXM convention that directory holds
static test data (corpora, snapshots, baselines) consumed by real
tests; pytest excludes it from collection via collect_ignore_glob,
and the test_quality rules must do the same so corpus files don't
get flagged as pyramid / naming / tautology offenders.
Source code in packages/axm-audit/src/axm_audit/core/rules/test_quality/_shared.py
iter_test_funcs(tree)
Yield top-level test_* functions and Test*-class test methods.
Source code in packages/axm-audit/src/axm_audit/core/rules/test_quality/_shared.py
load_project_scripts(pkg_root)
Return scripts declared by [project.scripts] in pyproject.toml.
Source code in packages/axm-audit/src/axm_audit/core/rules/test_quality/_shared.py
target_matches_io(target)
Return True when target references a known I/O call or module.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
str
|
Dotted call expression captured from the AST (e.g.
|
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
bool
|
its dotted segments matches the package's I/O catalog; |
bool
|
otherwise (including for empty input). |
Source code in packages/axm-audit/src/axm_audit/core/rules/test_quality/_shared.py
test_invokes_in_package_script(*, test_func, module_ast, project_scripts)
True when the test's closure invokes a declared package script.
Criterion (b) of TEST_QUALITY_NO_PACKAGE_SYMBOL. Recognises
subprocess.run(["script", ...]) (also python -m pkg ...) and
CliRunner().invoke(app, [...]) shapes. The walk uses the same
intra-module closure as criterion (a) so a helper-wrapped invocation
still counts.
Source code in packages/axm-audit/src/axm_audit/core/rules/test_quality/_shared.py
test_invokes_inline_python_script(*, test_func, module_ast, pkg_prefixes)
True when the test drives the package via python -c "<script>".
A black-box e2e idiom: the test spawns a subprocess running an inline
driver script (subprocess.run([sys.executable, "-c", script, ...]))
whose string body imports a first-party package. Criterion (a) misses it
because the first-party imports live inside a string literal (not the
test's AST closure); the declared-script branch of criterion (b) misses
it because the entrypoint is inline code, not a [project.scripts]
entry or python -m pkg. This helper closes that gap, so such a test
counts as exercising the package (criterion (b), subprocess tier).
Source code in packages/axm-audit/src/axm_audit/core/rules/test_quality/_shared.py
test_is_in_lazy_import_context(func, tree_module, test_file)
Detect when the import itself is the system-under-test.
Returns True when the surrounding test file, class, or module
docstring signals that the test exists to verify lazy/optional
imports — in which case bare import statements inside the
test body must not be flagged as smoke tests.
Source code in packages/axm-audit/src/axm_audit/core/rules/test_quality/_shared.py
test_references_first_party(*, test_func, module_ast, pkg_prefixes)
True when test_func exercises a first-party Python symbol.
Criterion (a) of the TEST_QUALITY_NO_PACKAGE_SYMBOL rule. A symbol is "exercised" if it is referenced anywhere in the intra-module closure of test_func (its body + bodies of every top-level helper it calls transitively), or indirectly via a fixture whose return-type annotation or return/yield expression resolves to a first-party alias.
Source code in packages/axm-audit/src/axm_audit/core/rules/test_quality/_shared.py
to_snake_token(name)
Normalize a PascalCase / kebab-case token to snake_case.
Used to compose canonical filenames. Empty input returns empty.
Source code in packages/axm-audit/src/axm_audit/core/rules/test_quality/_shared.py
value_marks_node(value, marker_name)
True when value is pytest.mark.<marker_name> (with or without a
reason argument).
Also matches a list/tuple containing the marker.