Refines the result of `extract_imports` to remove subfolders and get a proper list of module filenames: if a file as an import `from utils import Foo, Bar`, with `utils` being a subfolder containing many files, this will traverse the `utils` init file to check where those dependencies c
(module_fname: str, cache: Dict[str, List[str]] = None)
| 558 | |
| 559 | |
| 560 | def get_module_dependencies(module_fname: str, cache: Dict[str, List[str]] = None) -> List[str]: |
| 561 | """ |
| 562 | Refines the result of `extract_imports` to remove subfolders and get a proper list of module filenames: if a file |
| 563 | as an import `from utils import Foo, Bar`, with `utils` being a subfolder containing many files, this will traverse |
| 564 | the `utils` init file to check where those dependencies come from: for instance the files utils/foo.py and utils/bar.py. |
| 565 | |
| 566 | Warning: This presupposes that all intermediate inits are properly built (with imports from the respective |
| 567 | submodules) and work better if objects are defined in submodules and not the intermediate init (otherwise the |
| 568 | intermediate init is added, and inits usually have a lot of dependencies). |
| 569 | |
| 570 | Args: |
| 571 | module_fname (`str`): |
| 572 | The name of the file of the module where we want to look at the imports (given relative to the root of |
| 573 | the repo). |
| 574 | cache (Dictionary `str` to `List[str]`, *optional*): |
| 575 | To speed up this function if it was previously called on `module_fname`, the cache of all previously |
| 576 | computed results. |
| 577 | |
| 578 | Returns: |
| 579 | `List[str]`: The list of module filenames imported in the input `module_fname` (with submodule imports refined). |
| 580 | """ |
| 581 | dependencies = [] |
| 582 | imported_modules = extract_imports(module_fname, cache=cache) |
| 583 | # The while loop is to recursively traverse all inits we may encounter: we will add things as we go. |
| 584 | while len(imported_modules) > 0: |
| 585 | new_modules = [] |
| 586 | for module, imports in imported_modules: |
| 587 | # If we end up in an __init__ we are often not actually importing from this init (except in the case where |
| 588 | # the object is fully defined in the __init__) |
| 589 | if module.endswith("__init__.py"): |
| 590 | # So we get the imports from that init then try to find where our objects come from. |
| 591 | new_imported_modules = extract_imports(module, cache=cache) |
| 592 | for new_module, new_imports in new_imported_modules: |
| 593 | if any(i in new_imports for i in imports): |
| 594 | if new_module not in dependencies: |
| 595 | new_modules.append((new_module, [i for i in new_imports if i in imports])) |
| 596 | imports = [i for i in imports if i not in new_imports] |
| 597 | if len(imports) > 0: |
| 598 | # If there are any objects lefts, they may be a submodule |
| 599 | path_to_module = PATH_TO_REPO / module.replace("__init__.py", "") |
| 600 | dependencies.extend( |
| 601 | [ |
| 602 | os.path.join(module.replace("__init__.py", ""), f"{i}.py") |
| 603 | for i in imports |
| 604 | if (path_to_module / f"{i}.py").is_file() |
| 605 | ] |
| 606 | ) |
| 607 | imports = [i for i in imports if not (path_to_module / f"{i}.py").is_file()] |
| 608 | if len(imports) > 0: |
| 609 | # Then if there are still objects left, they are fully defined in the init, so we keep it as a |
| 610 | # dependency. |
| 611 | dependencies.append(module) |
| 612 | else: |
| 613 | dependencies.append(module) |
| 614 | |
| 615 | imported_modules = new_modules |
| 616 | |
| 617 | return dependencies |
no test coverage detected
searching dependent graphs…