1
1
# (c) 2014-2023 The mqttwarn developers
2
2
import functools
3
- import hashlib
4
- import imp
3
+ import importlib . machinery
4
+ import importlib . util
5
5
import json
6
6
import logging
7
7
import os
15
15
import pkg_resources
16
16
from six import string_types
17
17
18
+ if t .TYPE_CHECKING :
19
+ from importlib ._bootstrap_external import FileLoader
20
+
18
21
logger = logging .getLogger (__name__ )
19
22
20
23
@@ -132,15 +135,20 @@ def load_module_from_file(path: str) -> types.ModuleType:
132
135
:param path:
133
136
:return:
134
137
"""
135
- try :
136
- fp = open (path , "rb" )
137
- digest = hashlib .md5 (path .encode ("utf-8" )).hexdigest ()
138
- return imp .load_source (digest , path , fp ) # type: ignore[arg-type]
139
- finally :
140
- try :
141
- fp .close ()
142
- except :
143
- pass
138
+ name = Path (path ).stem
139
+ loader : "FileLoader"
140
+ if path .endswith (".py" ):
141
+ loader = importlib .machinery .SourceFileLoader (fullname = name , path = path )
142
+ elif path .endswith (".pyc" ):
143
+ loader = importlib .machinery .SourcelessFileLoader (fullname = name , path = path )
144
+ else :
145
+ raise ImportError (f"Loading file failed (only .py and .pyc): { path } " )
146
+ spec = importlib .util .spec_from_loader (loader .name , loader )
147
+ if spec is None :
148
+ raise ModuleNotFoundError (f"Failed loading module from file: { path } " )
149
+ mod = importlib .util .module_from_spec (spec )
150
+ loader .exec_module (mod )
151
+ return mod
144
152
145
153
146
154
def load_module_by_name (name : str ) -> types .ModuleType :
@@ -150,11 +158,11 @@ def load_module_by_name(name: str) -> types.ModuleType:
150
158
:param name:
151
159
:return:
152
160
"""
153
- module = import_module (name )
161
+ module = import_symbol (name )
154
162
return module
155
163
156
164
157
- def import_module (name : str , path : t .Optional [t . List [ str ] ] = None ) -> types .ModuleType :
165
+ def import_symbol (name : str , parent : t .Optional [types . ModuleType ] = None ) -> types .ModuleType :
158
166
"""
159
167
Derived from `import_from_dotted_path`:
160
168
https://chase-seibert.github.io/blog/2014/04/23/python-imp-examples.html
@@ -168,16 +176,38 @@ def import_module(name: str, path: t.Optional[t.List[str]] = None) -> types.Modu
168
176
next_module = name
169
177
remaining_names = None
170
178
171
- fp , pathname , description = imp .find_module (next_module , path )
172
- module = imp .load_module (next_module , fp , pathname , description ) # type: ignore[arg-type]
179
+ parent_name = None
180
+ next_module_real = next_module
181
+
182
+ if parent is not None :
183
+ next_module_real = "." + next_module
184
+ parent_name = parent .__name__
185
+ try :
186
+ spec = importlib .util .find_spec (next_module_real , parent_name )
187
+ except (AttributeError , ModuleNotFoundError ):
188
+ module = parent
189
+ if module is None or module .__loader__ is None :
190
+ raise ImportError (f"Symbol not found: { name } " )
191
+ if hasattr (module , next_module ):
192
+ return getattr (module , next_module )
193
+ else :
194
+ raise ImportError (f"Symbol not found: { name } , module={ module } " )
195
+
196
+ if spec is None :
197
+ msg = f"Symbol not found: { name } "
198
+ if parent is not None :
199
+ msg += f", module={ parent .__name__ } "
200
+ raise ImportError (msg )
201
+ module = importlib .util .module_from_spec (spec )
202
+
203
+ # Actually load the module.
204
+ loader : FileLoader = module .__loader__
205
+ loader .exec_module (module )
173
206
174
207
if remaining_names is None :
175
208
return module
176
209
177
- if hasattr (module , remaining_names ):
178
- return getattr (module , remaining_names )
179
- else :
180
- return import_module (remaining_names , path = list (module .__path__ ))
210
+ return import_symbol (remaining_names , parent = module )
181
211
182
212
183
213
def load_functions (filepath : t .Optional [str ] = None ) -> t .Optional [types .ModuleType ]:
@@ -188,17 +218,7 @@ def load_functions(filepath: t.Optional[str] = None) -> t.Optional[types.ModuleT
188
218
if not os .path .isfile (filepath ):
189
219
raise IOError ("'{}' not found" .format (filepath ))
190
220
191
- mod_name , file_ext = os .path .splitext (os .path .split (filepath )[- 1 ])
192
-
193
- if file_ext .lower () == ".py" :
194
- py_mod = imp .load_source (mod_name , filepath )
195
-
196
- elif file_ext .lower () == ".pyc" :
197
- py_mod = imp .load_compiled (mod_name , filepath )
198
-
199
- else :
200
- raise ValueError ("'{}' does not have the .py or .pyc extension" .format (filepath ))
201
-
221
+ py_mod = load_module_from_file (filepath )
202
222
return py_mod
203
223
204
224
0 commit comments