Skip to content

Commit 37a202e

Browse files
Deduplicate AST function source extraction in architecture guards
Co-authored-by: Shri Sukhani <shrisukhani@users.noreply.github.com>
1 parent 163b9c2 commit 37a202e

9 files changed

+51
-148
lines changed

tests/ast_function_source_utils.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import ast
2+
from pathlib import Path
3+
4+
5+
def collect_function_sources(module_path: str) -> dict[str, str]:
6+
module_text = Path(module_path).read_text(encoding="utf-8")
7+
module_ast = ast.parse(module_text)
8+
function_sources: dict[str, str] = {}
9+
for node in module_ast.body:
10+
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
11+
function_source = ast.get_source_segment(module_text, node)
12+
if function_source is not None:
13+
function_sources[node.name] = function_source
14+
return function_sources
Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
import ast
2-
from pathlib import Path
3-
41
import pytest
52

3+
from tests.ast_function_source_utils import collect_function_sources
4+
65
pytestmark = pytest.mark.architecture
76

87

9-
MODULE_PATH = Path("hyperbrowser/client/managers/job_request_utils.py")
8+
MODULE_PATH = "hyperbrowser/client/managers/job_request_utils.py"
109

1110
FUNCTION_ROUTE_BUILDER_EXPECTATIONS = {
1211
"get_job_status": "build_job_status_route(",
@@ -17,21 +16,8 @@
1716
"put_job_action_async": "build_job_action_route(",
1817
}
1918

20-
21-
def _collect_function_sources() -> dict[str, str]:
22-
module_text = MODULE_PATH.read_text(encoding="utf-8")
23-
module_ast = ast.parse(module_text)
24-
function_sources: dict[str, str] = {}
25-
for node in module_ast.body:
26-
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
27-
function_source = ast.get_source_segment(module_text, node)
28-
if function_source is not None:
29-
function_sources[node.name] = function_source
30-
return function_sources
31-
32-
3319
def test_job_request_wrappers_use_expected_route_builders():
34-
function_sources = _collect_function_sources()
20+
function_sources = collect_function_sources(MODULE_PATH)
3521
for function_name, route_builder_call in FUNCTION_ROUTE_BUILDER_EXPECTATIONS.items():
3622
function_source = function_sources[function_name]
3723
assert route_builder_call in function_source

tests/test_job_request_wrapper_internal_reuse.py

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
import ast
2-
from pathlib import Path
3-
41
import pytest
52

3+
from tests.ast_function_source_utils import collect_function_sources
4+
65
pytestmark = pytest.mark.architecture
76

87

9-
MODULE_PATH = Path("hyperbrowser/client/managers/job_request_utils.py")
8+
MODULE_PATH = "hyperbrowser/client/managers/job_request_utils.py"
109

1110
SYNC_WRAPPER_TO_MODEL_HELPER = {
1211
"start_job": "post_model_request(",
@@ -22,21 +21,8 @@
2221
"put_job_action_async": "put_model_request_async(",
2322
}
2423

25-
26-
def _collect_function_sources() -> dict[str, str]:
27-
module_text = MODULE_PATH.read_text(encoding="utf-8")
28-
module_ast = ast.parse(module_text)
29-
function_sources: dict[str, str] = {}
30-
for node in module_ast.body:
31-
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
32-
function_source = ast.get_source_segment(module_text, node)
33-
if function_source is not None:
34-
function_sources[node.name] = function_source
35-
return function_sources
36-
37-
3824
def test_sync_job_request_wrappers_delegate_to_model_helpers():
39-
function_sources = _collect_function_sources()
25+
function_sources = collect_function_sources(MODULE_PATH)
4026
for wrapper_name, helper_call in SYNC_WRAPPER_TO_MODEL_HELPER.items():
4127
wrapper_source = function_sources[wrapper_name]
4228
assert helper_call in wrapper_source
@@ -45,7 +31,7 @@ def test_sync_job_request_wrappers_delegate_to_model_helpers():
4531

4632

4733
def test_async_job_request_wrappers_delegate_to_model_helpers():
48-
function_sources = _collect_function_sources()
34+
function_sources = collect_function_sources(MODULE_PATH)
4935
for wrapper_name, helper_call in ASYNC_WRAPPER_TO_MODEL_HELPER.items():
5036
wrapper_source = function_sources[wrapper_name]
5137
assert helper_call in wrapper_source

tests/test_model_request_function_transport_boundary.py

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
import ast
2-
from pathlib import Path
3-
41
import pytest
52

3+
from tests.ast_function_source_utils import collect_function_sources
4+
65
pytestmark = pytest.mark.architecture
76

87

9-
MODULE_PATH = Path("hyperbrowser/client/managers/model_request_utils.py")
8+
MODULE_PATH = "hyperbrowser/client/managers/model_request_utils.py"
109

1110
PARSED_WRAPPER_FUNCTIONS = (
1211
"post_model_request",
@@ -34,28 +33,15 @@
3433
"post_model_response_data_to_endpoint_async",
3534
)
3635

37-
38-
def _collect_function_sources() -> dict[str, str]:
39-
module_text = MODULE_PATH.read_text(encoding="utf-8")
40-
module_ast = ast.parse(module_text)
41-
function_sources: dict[str, str] = {}
42-
for node in module_ast.body:
43-
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
44-
function_source = ast.get_source_segment(module_text, node)
45-
if function_source is not None:
46-
function_sources[node.name] = function_source
47-
return function_sources
48-
49-
5036
def test_parsed_model_request_wrappers_do_not_call_transport_directly():
51-
function_sources = _collect_function_sources()
37+
function_sources = collect_function_sources(MODULE_PATH)
5238
for function_name in PARSED_WRAPPER_FUNCTIONS:
5339
function_source = function_sources[function_name]
5440
assert "client.transport." not in function_source
5541

5642

5743
def test_raw_model_request_helpers_are_transport_boundary():
58-
function_sources = _collect_function_sources()
44+
function_sources = collect_function_sources(MODULE_PATH)
5945
for function_name in RAW_HELPER_FUNCTIONS:
6046
function_source = function_sources[function_name]
6147
assert "client.transport." in function_source

tests/test_model_request_wrapper_internal_reuse.py

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
import ast
2-
from pathlib import Path
3-
41
import pytest
52

3+
from tests.ast_function_source_utils import collect_function_sources
4+
65
pytestmark = pytest.mark.architecture
76

87

9-
MODULE_PATH = Path("hyperbrowser/client/managers/model_request_utils.py")
8+
MODULE_PATH = "hyperbrowser/client/managers/model_request_utils.py"
109

1110
SYNC_PARSED_WRAPPER_TO_RAW_HELPER = {
1211
"post_model_request": "post_model_response_data(",
@@ -24,29 +23,16 @@
2423
"post_model_request_to_endpoint_async": "post_model_response_data_to_endpoint_async(",
2524
}
2625

27-
28-
def _collect_function_sources() -> dict[str, str]:
29-
module_text = MODULE_PATH.read_text(encoding="utf-8")
30-
module_ast = ast.parse(module_text)
31-
function_sources: dict[str, str] = {}
32-
for node in module_ast.body:
33-
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
34-
function_source = ast.get_source_segment(module_text, node)
35-
if function_source is not None:
36-
function_sources[node.name] = function_source
37-
return function_sources
38-
39-
4026
def test_sync_parsed_wrappers_delegate_to_raw_helpers():
41-
function_sources = _collect_function_sources()
27+
function_sources = collect_function_sources(MODULE_PATH)
4228
for wrapper_name, raw_helper_call in SYNC_PARSED_WRAPPER_TO_RAW_HELPER.items():
4329
wrapper_source = function_sources[wrapper_name]
4430
assert raw_helper_call in wrapper_source
4531
assert "client.transport." not in wrapper_source
4632

4733

4834
def test_async_parsed_wrappers_delegate_to_raw_helpers():
49-
function_sources = _collect_function_sources()
35+
function_sources = collect_function_sources(MODULE_PATH)
5036
for wrapper_name, raw_helper_call in ASYNC_PARSED_WRAPPER_TO_RAW_HELPER.items():
5137
wrapper_source = function_sources[wrapper_name]
5238
assert raw_helper_call in wrapper_source

tests/test_request_wrapper_internal_reuse.py

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import ast
2-
from pathlib import Path
3-
41
import pytest
52

3+
from tests.ast_function_source_utils import collect_function_sources
4+
65
pytestmark = pytest.mark.architecture
76

87

@@ -41,22 +40,9 @@
4140
},
4241
}
4342

44-
45-
def _collect_module_function_sources(module_path: str) -> dict[str, str]:
46-
module_text = Path(module_path).read_text(encoding="utf-8")
47-
module_ast = ast.parse(module_text)
48-
function_sources: dict[str, str] = {}
49-
for node in module_ast.body:
50-
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
51-
function_source = ast.get_source_segment(module_text, node)
52-
if function_source is not None:
53-
function_sources[node.name] = function_source
54-
return function_sources
55-
56-
5743
def test_request_wrappers_delegate_to_expected_shared_helpers():
5844
for module_path, wrapper_expectations in MODULE_WRAPPER_EXPECTATIONS.items():
59-
function_sources = _collect_module_function_sources(module_path)
45+
function_sources = collect_function_sources(module_path)
6046
for wrapper_name, expected_markers in wrapper_expectations.items():
6147
wrapper_source = function_sources[wrapper_name]
6248
for expected_marker in expected_markers:
Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,20 @@
1-
import ast
2-
from pathlib import Path
3-
41
import pytest
52

6-
pytestmark = pytest.mark.architecture
7-
3+
from tests.ast_function_source_utils import collect_function_sources
84

9-
MODULE_PATH = Path("hyperbrowser/client/managers/session_request_utils.py")
5+
pytestmark = pytest.mark.architecture
106

117

12-
def _collect_function_sources() -> dict[str, str]:
13-
module_text = MODULE_PATH.read_text(encoding="utf-8")
14-
module_ast = ast.parse(module_text)
15-
function_sources: dict[str, str] = {}
16-
for node in module_ast.body:
17-
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
18-
function_source = ast.get_source_segment(module_text, node)
19-
if function_source is not None:
20-
function_sources[node.name] = function_source
21-
return function_sources
8+
MODULE_PATH = "hyperbrowser/client/managers/session_request_utils.py"
229

2310

2411
def test_sync_session_recordings_wrapper_enforces_follow_redirects():
25-
function_source = _collect_function_sources()["get_session_recordings"]
12+
function_source = collect_function_sources(MODULE_PATH)["get_session_recordings"]
2613
assert "get_session_resource(" in function_source
2714
assert "follow_redirects=True" in function_source
2815

2916

3017
def test_async_session_recordings_wrapper_enforces_follow_redirects():
31-
function_source = _collect_function_sources()["get_session_recordings_async"]
18+
function_source = collect_function_sources(MODULE_PATH)["get_session_recordings_async"]
3219
assert "get_session_resource_async(" in function_source
3320
assert "follow_redirects=True" in function_source

tests/test_session_request_wrapper_internal_reuse.py

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
import ast
2-
from pathlib import Path
3-
41
import pytest
52

3+
from tests.ast_function_source_utils import collect_function_sources
4+
65
pytestmark = pytest.mark.architecture
76

87

9-
MODULE_PATH = Path("hyperbrowser/client/managers/session_request_utils.py")
8+
MODULE_PATH = "hyperbrowser/client/managers/session_request_utils.py"
109

1110
SYNC_PARSED_WRAPPER_TO_RESOURCE_HELPER = {
1211
"post_session_model": "post_session_resource(",
@@ -22,29 +21,16 @@
2221
"get_session_recordings_async": "get_session_resource_async(",
2322
}
2423

25-
26-
def _collect_function_sources() -> dict[str, str]:
27-
module_text = MODULE_PATH.read_text(encoding="utf-8")
28-
module_ast = ast.parse(module_text)
29-
function_sources: dict[str, str] = {}
30-
for node in module_ast.body:
31-
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
32-
function_source = ast.get_source_segment(module_text, node)
33-
if function_source is not None:
34-
function_sources[node.name] = function_source
35-
return function_sources
36-
37-
3824
def test_sync_parsed_session_wrappers_delegate_to_resource_helpers():
39-
function_sources = _collect_function_sources()
25+
function_sources = collect_function_sources(MODULE_PATH)
4026
for wrapper_name, helper_call in SYNC_PARSED_WRAPPER_TO_RESOURCE_HELPER.items():
4127
wrapper_source = function_sources[wrapper_name]
4228
assert helper_call in wrapper_source
4329
assert "client.transport." not in wrapper_source
4430

4531

4632
def test_async_parsed_session_wrappers_delegate_to_resource_helpers():
47-
function_sources = _collect_function_sources()
33+
function_sources = collect_function_sources(MODULE_PATH)
4834
for wrapper_name, helper_call in ASYNC_PARSED_WRAPPER_TO_RESOURCE_HELPER.items():
4935
wrapper_source = function_sources[wrapper_name]
5036
assert helper_call in wrapper_source

tests/test_session_resource_wrapper_internal_reuse.py

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
import ast
2-
from pathlib import Path
3-
41
import pytest
52

3+
from tests.ast_function_source_utils import collect_function_sources
4+
65
pytestmark = pytest.mark.architecture
76

87

9-
MODULE_PATH = Path("hyperbrowser/client/managers/session_request_utils.py")
8+
MODULE_PATH = "hyperbrowser/client/managers/session_request_utils.py"
109

1110
SYNC_RESOURCE_WRAPPER_TO_MODEL_HELPER = {
1211
"post_session_resource": "post_model_response_data(",
@@ -20,29 +19,16 @@
2019
"put_session_resource_async": "put_model_response_data_async(",
2120
}
2221

23-
24-
def _collect_function_sources() -> dict[str, str]:
25-
module_text = MODULE_PATH.read_text(encoding="utf-8")
26-
module_ast = ast.parse(module_text)
27-
function_sources: dict[str, str] = {}
28-
for node in module_ast.body:
29-
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
30-
function_source = ast.get_source_segment(module_text, node)
31-
if function_source is not None:
32-
function_sources[node.name] = function_source
33-
return function_sources
34-
35-
3622
def test_sync_session_resource_wrappers_delegate_to_model_helpers():
37-
function_sources = _collect_function_sources()
23+
function_sources = collect_function_sources(MODULE_PATH)
3824
for wrapper_name, helper_call in SYNC_RESOURCE_WRAPPER_TO_MODEL_HELPER.items():
3925
wrapper_source = function_sources[wrapper_name]
4026
assert helper_call in wrapper_source
4127
assert "client.transport." not in wrapper_source
4228

4329

4430
def test_async_session_resource_wrappers_delegate_to_model_helpers():
45-
function_sources = _collect_function_sources()
31+
function_sources = collect_function_sources(MODULE_PATH)
4632
for wrapper_name, helper_call in ASYNC_RESOURCE_WRAPPER_TO_MODEL_HELPER.items():
4733
wrapper_source = function_sources[wrapper_name]
4834
assert helper_call in wrapper_source

0 commit comments

Comments
 (0)