1
1
import importlib
2
2
import importlib .util
3
+ import types
3
4
import os
4
5
import sys
5
6
@@ -78,7 +79,7 @@ def __dir__():
78
79
return __getattr__ , __dir__ , list (__all__ )
79
80
80
81
81
- def load (fullname ):
82
+ def load (fullname , error_on_import = False ):
82
83
"""Return a lazily imported proxy for a module.
83
84
84
85
We often see the following pattern::
@@ -112,6 +113,9 @@ def myfunc():
112
113
113
114
sp = lazy.load('scipy') # import scipy as sp
114
115
spla = lazy.load('scipy.linalg') # import scipy.linalg as spla
116
+ error_on_import : bool
117
+ Whether to postpone raising import errors until the module is accessed.
118
+ If set to `True`, import errors are raised as soon as `load` is called.
115
119
116
120
Returns
117
121
-------
@@ -127,7 +131,16 @@ def myfunc():
127
131
128
132
spec = importlib .util .find_spec (fullname )
129
133
if spec is None :
130
- raise ModuleNotFoundError (f"No module name '{ fullname } '" )
134
+ if error_on_import :
135
+ raise ModuleNotFoundError (f"No module named '{ fullname } '" )
136
+ else :
137
+ spec = importlib .util .spec_from_loader (fullname , loader = None )
138
+ module = importlib .util .module_from_spec (spec )
139
+ tmp_loader = importlib .machinery .SourceFileLoader (module , path = None )
140
+ loader = DelayedImportErrorLoader (tmp_loader )
141
+ loader .exec_module (module )
142
+ # dont add to sys.modules. The module wasn't found.
143
+ return module
131
144
132
145
module = importlib .util .module_from_spec (spec )
133
146
sys .modules [fullname ] = module
@@ -136,3 +149,22 @@ def myfunc():
136
149
loader .exec_module (module )
137
150
138
151
return module
152
+
153
+
154
+ class DelayedImportErrorLoader (importlib .util .LazyLoader ):
155
+ def exec_module (self , module ):
156
+ super ().exec_module (module )
157
+ module .__class__ = DelayedImportErrorModule
158
+
159
+
160
+ class DelayedImportErrorModule (types .ModuleType ):
161
+ def __getattribute__ (self , attr ):
162
+ """Trigger a ModuleNotFoundError upon attribute access"""
163
+ spec = super ().__getattribute__ ("__spec__" )
164
+ # allows isinstance and type functions to work without raising error
165
+ if attr in ["__class__" ]:
166
+ return super ().__getattribute__ ("__class__" )
167
+
168
+ raise ModuleNotFoundError (
169
+ f"No module named '{ spec .name } '"
170
+ )
0 commit comments