For each module with `from X import *`, copy X's classes/functions into the current module under that module's QN. Iterates to a fixed point to handle re-export chains.
(modules: dict[str, ModuleStubs],
star_imports: dict[str, list[StarReExport]])
| 250 | |
| 251 | |
| 252 | def resolve_reexports(modules: dict[str, ModuleStubs], |
| 253 | star_imports: dict[str, list[StarReExport]]) -> None: |
| 254 | """For each module with `from X import *`, copy X's classes/functions |
| 255 | into the current module under that module's QN. Iterates to a fixed |
| 256 | point to handle re-export chains. """ |
| 257 | changed = True |
| 258 | iter_count = 0 |
| 259 | while changed and iter_count < 8: |
| 260 | changed = False |
| 261 | iter_count += 1 |
| 262 | for mod_qn, stars in star_imports.items(): |
| 263 | ms = modules[mod_qn] |
| 264 | for star in stars: |
| 265 | target = modules.get(star.target_module) |
| 266 | if not target: |
| 267 | continue |
| 268 | # Copy target's classes/functions under mod_qn |
| 269 | existing_class_names = {c.short_name for c in ms.classes} |
| 270 | existing_func_names = {f.short_name for f in ms.functions} |
| 271 | for c in target.classes: |
| 272 | if c.short_name in existing_class_names: |
| 273 | continue |
| 274 | ms.classes.append(StubClass( |
| 275 | qualified_name=f"{mod_qn}.{c.short_name}", |
| 276 | short_name=c.short_name, |
| 277 | methods=list(c.methods), |
| 278 | bases=list(c.bases), |
| 279 | )) |
| 280 | existing_class_names.add(c.short_name) |
| 281 | changed = True |
| 282 | for f in target.functions: |
| 283 | if f.short_name in existing_func_names: |
| 284 | continue |
| 285 | ms.functions.append(StubFunction( |
| 286 | qualified_name=f"{mod_qn}.{f.short_name}", |
| 287 | short_name=f.short_name, |
| 288 | module_qn=mod_qn, |
| 289 | )) |
| 290 | existing_func_names.add(f.short_name) |
| 291 | changed = True |
| 292 | |
| 293 | |
| 294 | def iter_module_stubs(stdlib_root: Path) -> Iterable[ModuleStubs]: |
no test coverage detected