Import and return a module from the given path, which can be a file (a module) or a directory (a package). The import mechanism used is controlled by the `mode` parameter: * `mode == ImportMode.prepend`: the directory containing the module (or package, taking `__init__.py` files
(
p: Union[str, "os.PathLike[str]"],
*,
mode: Union[str, ImportMode] = ImportMode.prepend,
root: Path,
)
| 452 | |
| 453 | |
| 454 | def import_path( |
| 455 | p: Union[str, "os.PathLike[str]"], |
| 456 | *, |
| 457 | mode: Union[str, ImportMode] = ImportMode.prepend, |
| 458 | root: Path, |
| 459 | ) -> ModuleType: |
| 460 | """Import and return a module from the given path, which can be a file (a module) or |
| 461 | a directory (a package). |
| 462 | |
| 463 | The import mechanism used is controlled by the `mode` parameter: |
| 464 | |
| 465 | * `mode == ImportMode.prepend`: the directory containing the module (or package, taking |
| 466 | `__init__.py` files into account) will be put at the *start* of `sys.path` before |
| 467 | being imported with `__import__. |
| 468 | |
| 469 | * `mode == ImportMode.append`: same as `prepend`, but the directory will be appended |
| 470 | to the end of `sys.path`, if not already in `sys.path`. |
| 471 | |
| 472 | * `mode == ImportMode.importlib`: uses more fine control mechanisms provided by `importlib` |
| 473 | to import the module, which avoids having to use `__import__` and muck with `sys.path` |
| 474 | at all. It effectively allows having same-named test modules in different places. |
| 475 | |
| 476 | :param root: |
| 477 | Used as an anchor when mode == ImportMode.importlib to obtain |
| 478 | a unique name for the module being imported so it can safely be stored |
| 479 | into ``sys.modules``. |
| 480 | |
| 481 | :raises ImportPathMismatchError: |
| 482 | If after importing the given `path` and the module `__file__` |
| 483 | are different. Only raised in `prepend` and `append` modes. |
| 484 | """ |
| 485 | mode = ImportMode(mode) |
| 486 | |
| 487 | path = Path(p) |
| 488 | |
| 489 | if not path.exists(): |
| 490 | raise ImportError(path) |
| 491 | |
| 492 | if mode is ImportMode.importlib: |
| 493 | module_name = module_name_from_path(path, root) |
| 494 | |
| 495 | for meta_importer in sys.meta_path: |
| 496 | spec = meta_importer.find_spec(module_name, [str(path.parent)]) |
| 497 | if spec is not None: |
| 498 | break |
| 499 | else: |
| 500 | spec = importlib.util.spec_from_file_location(module_name, str(path)) |
| 501 | |
| 502 | if spec is None: |
| 503 | raise ImportError(f"Can't find module {module_name} at location {path}") |
| 504 | mod = importlib.util.module_from_spec(spec) |
| 505 | sys.modules[module_name] = mod |
| 506 | spec.loader.exec_module(mod) # type: ignore[union-attr] |
| 507 | insert_missing_modules(sys.modules, module_name) |
| 508 | return mod |
| 509 | |
| 510 | pkg_path = resolve_package_path(path) |
| 511 | if pkg_path is not None: |