Coverage for scripts/test_handlers.py: 100%
41 statements
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-07 16:54 +0000
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-07 16:54 +0000
1#!/usr/bin/env python3
3import ast
4from pathlib import Path
7EXAMPLES_IMPORT_PREFIX = "async_durable_execution_examples"
8HANDLER_SUFFIX = ".handler"
11def load_test_handlers(test_root: Path) -> set[str]:
12 """Load handler identifiers referenced by the example pytest suite."""
13 handlers: set[str] = set()
14 for path in sorted(test_root.rglob("test_*.py")):
15 handlers.update(load_test_handlers_from_file(path))
16 return handlers
19def load_test_handlers_from_file(path: Path) -> set[str]:
20 """Load handler identifiers from a single pytest file."""
21 tree = ast.parse(path.read_text(), filename=str(path))
22 imported_modules: dict[str, str] = {}
24 for node in ast.walk(tree):
25 if isinstance(node, ast.ImportFrom) and node.module:
26 if not node.module.startswith(EXAMPLES_IMPORT_PREFIX):
27 continue
28 for alias in node.names:
29 # Example: from async_durable_execution_examples.step import step
30 imported_modules[alias.asname or alias.name] = (
31 f"{node.module}.{alias.name}{HANDLER_SUFFIX}"
32 )
34 handlers: set[str] = set()
35 for node in ast.walk(tree):
36 if not isinstance(node, ast.Call):
37 continue
38 if not _is_durable_execution_marker(node.func):
39 continue
41 handler_name = _get_handler_name(node)
42 if not handler_name:
43 continue
45 handler = imported_modules.get(handler_name)
46 if handler:
47 handlers.add(handler)
49 return handlers
52def _is_durable_execution_marker(node: ast.AST) -> bool:
53 return (
54 isinstance(node, ast.Attribute)
55 and node.attr == "durable_execution"
56 and isinstance(node.value, ast.Attribute)
57 and node.value.attr == "mark"
58 and isinstance(node.value.value, ast.Name)
59 and node.value.value.id == "pytest"
60 )
63def _get_handler_name(node: ast.Call) -> str | None:
64 for keyword in node.keywords:
65 if keyword.arg != "handler":
66 continue
67 value = keyword.value
68 if (
69 isinstance(value, ast.Attribute)
70 and value.attr == "handler"
71 and isinstance(value.value, ast.Name)
72 ):
73 return value.value.id
74 return None