Tries to import a module by its canonical name, path, and its parent location. :param module_name: The expected module name, will become the key of `sys.modules`. :param module_path: The file path of the module, for example `/foo/bar/test_demo.py`. If module is
(
module_name: str, module_path: Path, *, insert_modules: bool
)
| 622 | |
| 623 | |
| 624 | def _import_module_using_spec( |
| 625 | module_name: str, module_path: Path, *, insert_modules: bool |
| 626 | ) -> ModuleType | None: |
| 627 | class="st">""" |
| 628 | Tries to import a module by its canonical name, path, and its parent location. |
| 629 | |
| 630 | :param module_name: |
| 631 | The expected module name, will become the key of `sys.modules`. |
| 632 | |
| 633 | :param module_path: |
| 634 | The file path of the module, for example `/foo/bar/test_demo.py`. |
| 635 | If module is a package, pass the path to the `__init__.py` of the package. |
| 636 | If module is a namespace package, pass directory path. |
| 637 | |
| 638 | :param insert_modules: |
| 639 | If True, will call `insert_missing_modules` to create empty intermediate modules |
| 640 | with made-up module names (when importing test files not reachable from `sys.path`). |
| 641 | |
| 642 | Example 1 of parent_module_*: |
| 643 | |
| 644 | module_name: class="st">"a.b.c.demo" |
| 645 | module_path: Path(class="st">"a/b/c/demo.py") |
| 646 | if class="st">"a.b.c" is package (class="st">"a/b/c/__init__.py" exists), then |
| 647 | parent_module_name: class="st">"a.b.c" |
| 648 | parent_module_path: Path(class="st">"a/b/c/__init__.py") |
| 649 | else: |
| 650 | parent_module_name: class="st">"a.b.c" |
| 651 | parent_module_path: Path(class="st">"a/b/c") |
| 652 | |
| 653 | Example 2 of parent_module_*: |
| 654 | |
| 655 | module_name: class="st">"a.b.c" |
| 656 | module_path: Path(class="st">"a/b/c/__init__.py") |
| 657 | if class="st">"a.b" is package (class="st">"a/b/__init__.py" exists), then |
| 658 | parent_module_name: class="st">"a.b" |
| 659 | parent_module_path: Path(class="st">"a/b/__init__.py") |
| 660 | else: |
| 661 | parent_module_name: class="st">"a.b" |
| 662 | parent_module_path: Path(class="st">"a/b/") |
| 663 | class="st">""" |
| 664 | class="cm"># Attempt to import the parent module, seems is our responsibility: |
| 665 | class="cm"># https://github.com/python/cpython/blob/73906d5c908c1e0b73c5436faeff7d93698fc074/Lib/importlib/_bootstrap.py#L1308-L1311 |
| 666 | parent_module_name, _, name = module_name.rpartition(class="st">".") |
| 667 | parent_module: ModuleType | None = None |
| 668 | if parent_module_name: |
| 669 | parent_module = sys.modules.get(parent_module_name) |
| 670 | class="cm"># If the parent_module lacks the `__path__` attribute, AttributeError when finding a submodule's spec, |
| 671 | class="cm"># requiring re-import according to the path. |
| 672 | need_reimport = not hasattr(parent_module, class="st">"__path__") |
| 673 | if parent_module is None or need_reimport: |
| 674 | class="cm"># Get parent_location based on location, get parent_path based on path. |
| 675 | if module_path.name == class="st">"__init__.py": |
| 676 | class="cm"># If the current module is in a package, |
| 677 | class="cm"># need to leave the package first and then enter the parent module. |
| 678 | parent_module_path = module_path.parent.parent |
| 679 | else: |
| 680 | parent_module_path = module_path.parent |
| 681 |