(path: Path, module_qn: str)
| 152 | |
| 153 | |
| 154 | def parse_module(path: Path, module_qn: str) -> tuple[ModuleStubs, list[StarReExport]]: |
| 155 | src = path.read_text(encoding="utf-8", errors="replace") |
| 156 | try: |
| 157 | tree = ast.parse(src) |
| 158 | except SyntaxError: |
| 159 | return ModuleStubs(module_qn=module_qn, classes=[], functions=[]), [] |
| 160 | |
| 161 | classes: list[StubClass] = [] |
| 162 | functions: list[StubFunction] = [] |
| 163 | star_imports: list[StarReExport] = [] |
| 164 | seen_funcs: set[str] = set() |
| 165 | |
| 166 | def walk(body: list[ast.stmt], current_module_qn: str) -> None: |
| 167 | for node in body: |
| 168 | if isinstance(node, ast.ClassDef): |
| 169 | qn = f"{current_module_qn}.{node.name}" |
| 170 | classes.append(StubClass( |
| 171 | qualified_name=qn, |
| 172 | short_name=node.name, |
| 173 | methods=class_methods(node), |
| 174 | bases=base_qns(node, current_module_qn), |
| 175 | )) |
| 176 | elif is_method(node): |
| 177 | name = node.name # type: ignore[attr-defined] |
| 178 | if name.startswith("_") and name not in {"__init__"}: |
| 179 | continue |
| 180 | if name in seen_funcs: |
| 181 | continue |
| 182 | seen_funcs.add(name) |
| 183 | functions.append(StubFunction( |
| 184 | qualified_name=f"{current_module_qn}.{name}", |
| 185 | short_name=name, |
| 186 | module_qn=current_module_qn, |
| 187 | )) |
| 188 | elif isinstance(node, ast.ImportFrom): |
| 189 | # Track `from X import *` for re-export resolution. |
| 190 | if node.module and any(alias.name == "*" for alias in node.names): |
| 191 | star_imports.append(StarReExport(target_module=node.module)) |
| 192 | elif isinstance(node, ast.If): |
| 193 | # Flatten version guards. |
| 194 | walk(node.body, current_module_qn) |
| 195 | walk(node.orelse, current_module_qn) |
| 196 | |
| 197 | walk(tree.body, module_qn) |
| 198 | return ModuleStubs(module_qn=module_qn, classes=classes, functions=functions), star_imports |
| 199 | |
| 200 | |
| 201 | def collect_all_stubs(stdlib_root: Path) -> tuple[dict[str, ModuleStubs], dict[str, list[StarReExport]]]: |
no test coverage detected