refactor: establish standalone application boundary
This commit is contained in:
@@ -83,23 +83,56 @@ def _mapped_paths(text: str) -> dict[str, set[str]]:
|
||||
return paths
|
||||
|
||||
|
||||
def _route_sources() -> list[tuple[Path, str]]:
|
||||
paths = [ROOT / "backend" / "http" / "dispatch.py"]
|
||||
paths.extend(sorted((ROOT / "backend" / "features").glob("*/routes.py")))
|
||||
return [(path, path.read_text(encoding="utf-8")) for path in paths]
|
||||
|
||||
|
||||
def _method_blocks(path: Path, text: str) -> list[tuple[str, str]]:
|
||||
tree = ast.parse(text, filename=str(path))
|
||||
lines = text.splitlines()
|
||||
blocks: list[tuple[str, str]] = []
|
||||
for node in ast.walk(tree):
|
||||
if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
||||
continue
|
||||
method = ""
|
||||
if node.name.startswith("do_") and node.name[3:] in {"GET", "POST", "DELETE"}:
|
||||
method = node.name[3:]
|
||||
else:
|
||||
suffix = node.name.rsplit("_", 1)[-1].upper()
|
||||
if node.name.startswith("_handle_") and suffix in {"GET", "POST", "DELETE"}:
|
||||
method = suffix
|
||||
if method:
|
||||
blocks.append((method, "\n".join(lines[node.lineno - 1 : node.end_lineno])))
|
||||
return blocks
|
||||
|
||||
|
||||
def build() -> dict:
|
||||
text = (ROOT / "backend" / "application.py").read_text(encoding="utf-8")
|
||||
mapped_paths = _mapped_paths(text)
|
||||
method_matches = list(re.finditer(r"^ def do_(GET|POST|DELETE)\(", text, re.MULTILINE))
|
||||
routes = []
|
||||
for index, match in enumerate(method_matches):
|
||||
method = match.group(1)
|
||||
end = method_matches[index + 1].start() if index + 1 < len(method_matches) else len(text)
|
||||
block = text[match.start():end]
|
||||
exact_paths = set(re.findall(r'parsed\.path\s*==\s*"(/api/[^"]+)"', block))
|
||||
exact_paths.update(mapped_paths[method])
|
||||
patterns = set(
|
||||
re.findall(
|
||||
r're\.(?:fullmatch|match)\(\s*r?["\']([^"\']*?/api/[^"\']+)["\']\s*,\s*parsed\.path',
|
||||
block,
|
||||
sources = _route_sources()
|
||||
mapped_paths = {method: set() for method in ("GET", "POST", "DELETE")}
|
||||
for _, text in sources:
|
||||
discovered = _mapped_paths(text)
|
||||
for method in mapped_paths:
|
||||
mapped_paths[method].update(discovered[method])
|
||||
|
||||
discovered_routes = {method: {"exact": set(), "patterns": set()} for method in mapped_paths}
|
||||
for path, text in sources:
|
||||
for method, block in _method_blocks(path, text):
|
||||
discovered_routes[method]["exact"].update(
|
||||
re.findall(r'parsed\.path\s*==\s*"(/api/[^"]+)"', block)
|
||||
)
|
||||
)
|
||||
discovered_routes[method]["patterns"].update(
|
||||
re.findall(
|
||||
r're\.(?:fullmatch|match)\(\s*r?["\']([^"\']*?/api/[^"\']+)["\']\s*,\s*parsed\.path',
|
||||
block,
|
||||
)
|
||||
)
|
||||
|
||||
routes = []
|
||||
for method in ("GET", "POST", "DELETE"):
|
||||
exact_paths = discovered_routes[method]["exact"] | mapped_paths[method]
|
||||
patterns = discovered_routes[method]["patterns"]
|
||||
for path in sorted(exact_paths):
|
||||
routes.append(
|
||||
{"method": method, "path": path, "match": "exact", "feature": _owner(path), "access": _role(method, path)}
|
||||
@@ -110,7 +143,11 @@ def build() -> dict:
|
||||
{"method": method, "path": normalized, "match": "regex", "feature": _owner(normalized), "access": _role(method, normalized)}
|
||||
)
|
||||
routes.sort(key=lambda item: (item["path"], item["method"], item["match"]))
|
||||
return {"schema_version": 1, "generated_from": "server.py", "routes": routes}
|
||||
return {
|
||||
"schema_version": 1,
|
||||
"generated_from": "server.py",
|
||||
"routes": routes,
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
|
||||
Reference in New Issue
Block a user