-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathgenerate_route_manifest.py
More file actions
160 lines (140 loc) · 5.34 KB
/
Copy pathgenerate_route_manifest.py
File metadata and controls
160 lines (140 loc) · 5.34 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
"""
CLI to generate a manifest of the FastAPI router paths present in both the instrument
server and backend server to enable lookup of the URLs based on function name.
"""
import contextlib
import importlib
import inspect
import io
import pkgutil
from argparse import ArgumentParser
from pathlib import Path
from types import ModuleType
from typing import Any
import yaml
from fastapi import APIRouter
import murfey
from murfey.cli import PrettierDumper
def find_routers(name: str) -> dict[str, APIRouter]:
def _extract_routers_from_module(module: ModuleType):
routers = {}
for name, obj in inspect.getmembers(module):
if isinstance(obj, APIRouter):
module_path = module.__name__
key = f"{module_path}.{name}"
routers[key] = obj
return routers
routers = {}
# Silence output during import and only return messages if imports fail
buffer = io.StringIO()
with contextlib.redirect_stdout(buffer), contextlib.redirect_stderr(buffer):
# Import the module or package
try:
root = importlib.import_module(name)
except Exception as e:
captured_logs = buffer.getvalue().strip()
message = f"Cannot import '{name}': {e}"
if captured_logs:
message += f"\n--- Captured output ---\n{captured_logs}"
raise ImportError(message) from e
# If it's a package, walk through submodules and extract routers from each
if hasattr(root, "__path__"):
module_list = pkgutil.walk_packages(root.__path__, prefix=name + ".")
for _, module_name, _ in module_list:
try:
module = importlib.import_module(module_name)
except Exception as e:
captured_logs = buffer.getvalue().strip()
message = f"Cannot import '{name}': {e}"
if captured_logs:
message += f"\n--- Captured output ---\n{captured_logs}"
raise ImportError(message) from e
routers.update(_extract_routers_from_module(module))
# Extract directly from single module
else:
routers.update(_extract_routers_from_module(root))
return routers
def get_route_manifest(routers: dict[str, APIRouter]):
manifest = {}
for router_name, router in routers.items():
routes = []
for route in router.routes:
path_params = []
for param in route.dependant.path_params:
param_type = param.type_ if param.type_ is not None else Any
param_info = {
"name": param.name if hasattr(param, "name") else "",
"type": (
param_type.__name__
if hasattr(param_type, "__name__")
else str(param_type)
),
}
path_params.append(param_info)
for route_dependency in route.dependant.dependencies:
for param in route_dependency.path_params:
param_type = param.type_ if param.type_ is not None else Any
param_info = {
"name": param.name if hasattr(param, "name") else "",
"type": (
param_type.__name__
if hasattr(param_type, "__name__")
else str(param_type)
),
}
path_params.append(param_info)
route_info = {
"path": route.path if hasattr(route, "path") else "",
"function": route.name if hasattr(route, "name") else "",
"path_params": path_params,
"methods": list(route.methods) if hasattr(route, "methods") else [],
}
routes.append(route_info)
manifest[router_name] = routes
return manifest
def run():
# Set up additional args
parser = ArgumentParser()
parser.add_argument(
"--debug",
action="store_true",
default=False,
help=("Outputs the modules being inspected when creating the route manifest"),
)
args = parser.parse_args()
# Find routers
print("Finding routers...")
routers = {
**find_routers("murfey.instrument_server.api"),
**find_routers("murfey.server.api"),
}
# Generate the manifest
print("Extracting route information")
manifest = get_route_manifest(routers)
# Verify
if args.debug:
for router_name, routes in manifest.items():
print(f"Routes found in {router_name!r}")
for route in routes:
for key, value in route.items():
print(f"\t{key}: {value}")
print()
# Save the manifest
murfey_dir = Path(murfey.__path__[0])
manifest_file = murfey_dir / "util" / "route_manifest.yaml"
with open(manifest_file, "w") as file:
yaml.dump(
manifest,
file,
Dumper=PrettierDumper,
default_flow_style=False,
sort_keys=False,
indent=2,
)
print(
"Route manifest for instrument and backend servers saved to "
f"{str(manifest_file)!r}"
)
exit()
if __name__ == "__main__":
run()