Coverage for src/lazy_imports_lite/_hooks.py: 100%

66 statements  

« prev     ^ index     » next       coverage.py v7.4.1, created at 2024-02-12 09:19 +0100

1import importlib 

2from collections import defaultdict 

3 

4 

5class LazyObject: 

6 __slots__ = ("_lazy_value",) 

7 

8 

9class LazyImportError(BaseException): 

10 def __init__(self, module, package): 

11 self.module = module 

12 self.package = package 

13 

14 def __str__(self): 

15 if self.package is None: 

16 return f"Deferred importing of module '{self.module}' caused an error" 

17 else: 

18 return f"Deferred importing of module '{self.module}' in '{self.package}' caused an error" 

19 

20 

21class ImportFrom(LazyObject): 

22 __slots__ = ("package", "module", "name", "_lazy_value") 

23 

24 def __init__(self, package, module, name): 

25 self.package = package 

26 self.module = module 

27 self.name = name 

28 

29 def __getattr__(self, name): 

30 if name == "_lazy_value": 

31 module = safe_import(self.module, self.package) 

32 try: 

33 attr = getattr(module, self.name) 

34 except AttributeError: 

35 attr = safe_import(self.module + "." + self.name, self.package) 

36 self._lazy_value = attr 

37 return attr 

38 else: 

39 assert False 

40 

41 

42pending_imports = defaultdict(list) 

43imported_modules = set() 

44 

45 

46def safe_import(module, package=None): 

47 try: 

48 return importlib.import_module(module, package) 

49 except LazyImportError: 

50 raise 

51 except: 

52 raise LazyImportError(module, package) 

53 

54 

55class Import(LazyObject): 

56 __slots__ = ("module", "_lazy_value") 

57 

58 def __init__(self, module): 

59 self.module = module 

60 m = self.module.split(".")[0] 

61 

62 if m in imported_modules: 

63 safe_import(self.module) 

64 else: 

65 pending_imports[m].append(module) 

66 

67 def __getattr__(self, name): 

68 if name == "_lazy_value": 

69 m = self.module.split(".")[0] 

70 for pending in pending_imports[m]: 

71 safe_import(pending) 

72 result = safe_import(self.module.split(".")[0]) 

73 imported_modules.add(m) 

74 self._lazy_value = result 

75 return result 

76 else: 

77 assert False 

78 

79 

80class ImportAs(LazyObject): 

81 __slots__ = ("module", "_lazy_value") 

82 

83 def __init__(self, module): 

84 self.module = module 

85 

86 def __getattr__(self, name): 

87 if name == "_lazy_value": 

88 module = safe_import(self.module) 

89 self._lazy_value = module 

90 return module 

91 else: 

92 assert False 

93 

94 

95def make_globals(global_provider): 

96 def g(): 

97 return { 

98 key: value._lazy_value if isinstance(value, LazyObject) else value 

99 for key, value in dict(global_provider()).items() 

100 if key not in ("globals", "__lazy_imports_lite__") 

101 } 

102 

103 return g