Coverage for /Users/davegaeddert/Development/dropseed/plain/plain/plain/utils/module_loading.py: 39%
36 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-10-16 22:03 -0500
« prev ^ index » next coverage.py v7.6.1, created at 2024-10-16 22:03 -0500
1import os
2import sys
3from importlib import import_module
4from importlib.util import find_spec as importlib_find
7def cached_import(module_path, class_name):
8 # Check whether module is loaded and fully initialized.
9 if not (
10 (module := sys.modules.get(module_path))
11 and (spec := getattr(module, "__spec__", None))
12 and getattr(spec, "_initializing", False) is False
13 ):
14 module = import_module(module_path)
15 return getattr(module, class_name)
18def import_string(dotted_path):
19 """
20 Import a dotted module path and return the attribute/class designated by the
21 last name in the path. Raise ImportError if the import failed.
22 """
23 try:
24 module_path, class_name = dotted_path.rsplit(".", 1)
25 except ValueError as err:
26 raise ImportError("%s doesn't look like a module path" % dotted_path) from err
28 try:
29 return cached_import(module_path, class_name)
30 except AttributeError as err:
31 raise ImportError(
32 f'Module "{module_path}" does not define a "{class_name}" attribute/class'
33 ) from err
36def module_has_submodule(package, module_name):
37 """See if 'module' is in 'package'."""
38 try:
39 package_name = package.__name__
40 package_path = package.__path__
41 except AttributeError:
42 # package isn't a package.
43 return False
45 full_module_name = package_name + "." + module_name
46 try:
47 return importlib_find(full_module_name, package_path) is not None
48 except ModuleNotFoundError:
49 # When module_name is an invalid dotted path, Python raises
50 # ModuleNotFoundError.
51 return False
54def module_dir(module):
55 """
56 Find the name of the directory that contains a module, if possible.
58 Raise ValueError otherwise, e.g. for namespace packages that are split
59 over several directories.
60 """
61 # Convert to list because __path__ may not support indexing.
62 paths = list(getattr(module, "__path__", []))
63 if len(paths) == 1:
64 return paths[0]
65 else:
66 filename = getattr(module, "__file__", None)
67 if filename is not None:
68 return os.path.dirname(filename)
69 raise ValueError("Cannot determine directory containing %s" % module)