66"""
77import importlib
88import importlib .util
9+ import inspect
910import os
1011import sys
1112import types
1213
14+ __all__ = ["attach" , "load" ]
15+
1316
1417def attach (package_name , submodules = None , submod_attrs = None ):
1518 """Attach lazily loaded submodules, functions, or other attributes.
@@ -83,6 +86,24 @@ def __dir__():
8386 return __getattr__ , __dir__ , list (__all__ )
8487
8588
89+ class DelayedImportErrorModule (types .ModuleType ):
90+ def __init__ (self , frame_data , * args , ** kwargs ):
91+ self .__frame_data = frame_data
92+ super ().__init__ (* args , ** kwargs )
93+
94+ def __getattr__ (self , x ):
95+ if x in ("__class__" , "__file__" , "__frame_data" ):
96+ super ().__getattr__ (x )
97+ else :
98+ fd = self .__frame_data
99+ raise ModuleNotFoundError (
100+ f"No module named '{ fd ['spec' ]} '\n \n "
101+ "This error is lazily reported, having originally occured in\n "
102+ f' File { fd ["filename" ]} , line { fd ["lineno" ]} , in { fd ["function" ]} \n \n '
103+ f'----> { "" .join (fd ["code_context" ]).strip ()} '
104+ )
105+
106+
86107def load (fullname , error_on_import = False ):
87108 """Return a lazily imported proxy for a module.
88109
@@ -138,13 +159,18 @@ def myfunc():
138159 if error_on_import :
139160 raise ModuleNotFoundError (f"No module named '{ fullname } '" )
140161 else :
141- spec = importlib .util .spec_from_loader (fullname , loader = None )
142- module = importlib .util .module_from_spec (spec )
143- tmp_loader = importlib .machinery .SourceFileLoader (module , path = None )
144- loader = DelayedImportErrorLoader (tmp_loader )
145- loader .exec_module (module )
146- # dont add to sys.modules. The module wasn't found.
147- return module
162+ try :
163+ parent = inspect .stack ()[1 ]
164+ frame_data = {
165+ "spec" : fullname ,
166+ "filename" : parent .filename ,
167+ "lineno" : parent .lineno ,
168+ "function" : parent .function ,
169+ "code_context" : parent .code_context ,
170+ }
171+ return DelayedImportErrorModule (frame_data , "DelayedImportErrorModule" )
172+ finally :
173+ del parent
148174
149175 module = importlib .util .module_from_spec (spec )
150176 sys .modules [fullname ] = module
@@ -153,20 +179,3 @@ def myfunc():
153179 loader .exec_module (module )
154180
155181 return module
156-
157-
158- class DelayedImportErrorLoader (importlib .util .LazyLoader ):
159- def exec_module (self , module ):
160- super ().exec_module (module )
161- module .__class__ = DelayedImportErrorModule
162-
163-
164- class DelayedImportErrorModule (types .ModuleType ):
165- def __getattribute__ (self , attr ):
166- """Trigger a ModuleNotFoundError upon attribute access"""
167- spec = super ().__getattribute__ ("__spec__" )
168- # allows isinstance and type functions to work without raising error
169- if attr in ["__class__" ]:
170- return super ().__getattribute__ ("__class__" )
171-
172- raise ModuleNotFoundError (f"No module named '{ spec .name } '" )
0 commit comments