Skip to content

Commit 7ed73c0

Browse files
Add AST function-source helper import boundary guard
Co-authored-by: Shri Sukhani <shrisukhani@users.noreply.github.com>
1 parent c4ab7d4 commit 7ed73c0

4 files changed

Lines changed: 62 additions & 2 deletions

CONTRIBUTING.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ This runs lint, format checks, compile checks, tests, and package build.
8989
- `tests/test_agent_terminal_status_helper_usage.py` (shared agent terminal-status helper usage enforcement),
9090
- `tests/test_architecture_marker_usage.py` (architecture marker coverage across guard modules),
9191
- `tests/test_ast_function_source_helper_usage.py` (shared AST function-source helper usage enforcement across architecture guard suites),
92+
- `tests/test_ast_function_source_import_boundary.py` (shared AST function-source helper import boundary enforcement across test modules),
9293
- `tests/test_ast_function_source_utils.py` (shared AST function-source helper contract validation),
9394
- `tests/test_binary_file_open_helper_usage.py` (shared binary file open helper usage enforcement),
9495
- `tests/test_browser_use_payload_helper_usage.py` (browser-use payload helper usage enforcement),

tests/test_architecture_marker_usage.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"tests/test_agent_task_read_helper_usage.py",
1818
"tests/test_agent_stop_helper_usage.py",
1919
"tests/test_agent_terminal_status_helper_usage.py",
20+
"tests/test_ast_function_source_import_boundary.py",
2021
"tests/test_ast_function_source_helper_usage.py",
2122
"tests/test_ast_function_source_utils.py",
2223
"tests/test_guardrail_ast_utils.py",

tests/test_ast_function_source_helper_usage.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import ast
12
from pathlib import Path
23

34
import pytest
@@ -21,12 +22,23 @@
2122
"tests/test_web_request_wrapper_internal_reuse.py",
2223
)
2324

25+
def _imports_collect_function_sources(module_text: str) -> bool:
26+
module_ast = ast.parse(module_text)
27+
for node in module_ast.body:
28+
if not isinstance(node, ast.ImportFrom):
29+
continue
30+
if node.module != "tests.ast_function_source_utils":
31+
continue
32+
if any(alias.name == "collect_function_sources" for alias in node.names):
33+
return True
34+
return False
35+
2436

2537
def test_ast_guard_modules_reuse_shared_collect_function_sources_helper():
2638
violating_modules: list[str] = []
2739
for module_path in AST_FUNCTION_SOURCE_GUARD_MODULES:
2840
module_text = Path(module_path).read_text(encoding="utf-8")
29-
if "ast_function_source_utils import collect_function_sources" not in module_text:
41+
if not _imports_collect_function_sources(module_text):
3042
violating_modules.append(module_path)
3143
continue
3244
if "collect_function_sources(" not in module_text:
@@ -49,7 +61,7 @@ def test_ast_guard_inventory_stays_in_sync_with_helper_imports():
4961
if normalized_path in excluded_modules:
5062
continue
5163
module_text = module_path.read_text(encoding="utf-8")
52-
if "ast_function_source_utils import collect_function_sources" not in module_text:
64+
if not _imports_collect_function_sources(module_text):
5365
continue
5466
discovered_modules.append(normalized_path)
5567

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import ast
2+
from pathlib import Path
3+
4+
import pytest
5+
6+
pytestmark = pytest.mark.architecture
7+
8+
9+
EXPECTED_AST_FUNCTION_SOURCE_IMPORTER_MODULES = (
10+
"tests/test_agent_request_wrapper_internal_reuse.py",
11+
"tests/test_ast_function_source_utils.py",
12+
"tests/test_extension_request_function_parse_boundary.py",
13+
"tests/test_job_request_route_builder_internal_reuse.py",
14+
"tests/test_job_request_wrapper_internal_reuse.py",
15+
"tests/test_model_request_function_parse_boundary.py",
16+
"tests/test_model_request_function_transport_boundary.py",
17+
"tests/test_model_request_wrapper_internal_reuse.py",
18+
"tests/test_request_wrapper_internal_reuse.py",
19+
"tests/test_session_recordings_follow_redirects_boundary.py",
20+
"tests/test_session_request_function_parse_boundary.py",
21+
"tests/test_session_request_wrapper_internal_reuse.py",
22+
"tests/test_session_resource_wrapper_internal_reuse.py",
23+
"tests/test_web_request_wrapper_internal_reuse.py",
24+
)
25+
26+
def _imports_collect_function_sources(module_text: str) -> bool:
27+
module_ast = ast.parse(module_text)
28+
for node in module_ast.body:
29+
if not isinstance(node, ast.ImportFrom):
30+
continue
31+
if node.module != "tests.ast_function_source_utils":
32+
continue
33+
if any(alias.name == "collect_function_sources" for alias in node.names):
34+
return True
35+
return False
36+
37+
38+
def test_ast_function_source_helper_imports_are_centralized():
39+
discovered_modules: list[str] = []
40+
for module_path in sorted(Path("tests").glob("test_*.py")):
41+
module_text = module_path.read_text(encoding="utf-8")
42+
if not _imports_collect_function_sources(module_text):
43+
continue
44+
discovered_modules.append(module_path.as_posix())
45+
46+
assert discovered_modules == list(EXPECTED_AST_FUNCTION_SOURCE_IMPORTER_MODULES)

0 commit comments

Comments
 (0)