Coverage for /Users/davegaeddert/Developer/dropseed/plain/plain/plain/utils/module_loading.py: 22%

36 statements  

« prev     ^ index     » next       coverage.py v7.6.9, created at 2024-12-23 11:16 -0600

1import os 

2import sys 

3from importlib import import_module 

4from importlib.util import find_spec as importlib_find 

5 

6 

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) 

16 

17 

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(f"{dotted_path} doesn't look like a module path") from err 

27 

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 

34 

35 

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 

44 

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 

52 

53 

54def module_dir(module): 

55 """ 

56 Find the name of the directory that contains a module, if possible. 

57 

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(f"Cannot determine directory containing {module}")