Source code for imptools.import_path

[docs]def import_path(path, name=None, notfound='error', reload=False): """ Import a module from any path on the filesystem. Usually, this would be achieved by adding the parent directory of the module to `sys.path` or the `PYTHONPATH` environment variable and then importing it normally. However, this pollutes Python's import path, which can lead to accidentally importing the wrong modules. The function `import_path()` avoids this problem by importing a package from a path on the filesystem without modifying the Python import path. The module can be either a directory containing `__init__.py` or a single file. Relative paths are resolved relative to the directory of the source file that calls `import_path()`. ``` import imptools my_module = imptools.import_path('../path/to/my_module') ``` ``` import imptools my_module = imptools.import_path( '../path/to/my_module', # Path to a module directory or single file. notfound='error', # Raise 'error' or 'ignore' if not found. reload=False, # Whether to import if already available. ) import my_module # Import statement also works. ``` Args: path (str or Path): The filesystem path to the module to import. This should be either a directory containing an `__init__.py` file or a single file with `.py` extension. The path can be relative. name (str, optional): A name that is compared to the directory name of `path` and must match it. Useful for relative paths. notfound (str): A string indicating what to do if the module is not found. Values can be `error` to raise an exception or `ignore` to return `None` from this function. reload (bool): A boolean indicating whether to reload the package if it is already loaded. Raises: ModuleNotFoundError: If `path` does not point to a module or if `name` is specified and dose not match the directory name of `path`. Returns: The module or `None` if it was not found. """ assert notfound in ('error', 'ignore') import importlib.util import inspect import pathlib import sys # Resolve relative path. path = pathlib.Path(path).expanduser() if not path.is_absolute(): for info in inspect.stack(): if info.filename == __file__: continue break script = info.filename root = pathlib.Path(script).parent path = (root / path).resolve() # Skip if the module is already loaded. name = name or path.name if name in sys.modules and not reload: return sys.modules[name] # Verify path name against user input. if name and path.name != name: if notfound == 'ignore': return None raise ModuleNotFoundError(f'Path name {path.name} does not match {name}') # Try to find the module. for finder in sys.meta_path: if not hasattr(finder, 'find_spec'): continue spec = finder.find_spec(name, [str(path.parent)]) if spec is not None: break else: if notfound == 'ignore': return None raise ModuleNotFoundError(f'No module named {name}', name=name) # Import the module. module = importlib.util.module_from_spec(spec) sys.modules[name] = module spec.loader.exec_module(module) return module