|
13 | 13 | import sys |
14 | 14 | import importlib.machinery # importlib first so we can test #15386 via -m |
15 | 15 | import importlib.util |
| 16 | +import io |
16 | 17 | import types |
17 | | -from pkgutil import read_code, get_importer |
| 18 | +import os |
18 | 19 |
|
19 | 20 | __all__ = [ |
20 | 21 | "run_module", "run_path", |
@@ -131,6 +132,9 @@ def _get_module_details(mod_name, error=ImportError): |
131 | 132 | # importlib, where the latter raises other errors for cases where |
132 | 133 | # pkgutil previously raised ImportError |
133 | 134 | msg = "Error while finding module specification for {!r} ({}: {})" |
| 135 | + if mod_name.endswith(".py"): |
| 136 | + msg += (f". Try using '{mod_name[:-3]}' instead of " |
| 137 | + f"'{mod_name}' as the module name.") |
134 | 138 | raise error(msg.format(mod_name, type(ex).__name__, ex)) from ex |
135 | 139 | if spec is None: |
136 | 140 | raise error("No module named %s" % mod_name) |
@@ -194,9 +198,24 @@ def _run_module_as_main(mod_name, alter_argv=True): |
194 | 198 |
|
195 | 199 | def run_module(mod_name, init_globals=None, |
196 | 200 | run_name=None, alter_sys=False): |
197 | | - """Execute a module's code without importing it |
| 201 | + """Execute a module's code without importing it. |
198 | 202 |
|
199 | | - Returns the resulting top level namespace dictionary |
| 203 | + mod_name -- an absolute module name or package name. |
| 204 | +
|
| 205 | + Optional arguments: |
| 206 | + init_globals -- dictionary used to pre-populate the module’s |
| 207 | + globals dictionary before the code is executed. |
| 208 | +
|
| 209 | + run_name -- if not None, this will be used for setting __name__; |
| 210 | + otherwise, __name__ will be set to mod_name + '__main__' if the |
| 211 | + named module is a package and to just mod_name otherwise. |
| 212 | +
|
| 213 | + alter_sys -- if True, sys.argv[0] is updated with the value of |
| 214 | + __file__ and sys.modules[__name__] is updated with a temporary |
| 215 | + module object for the module being executed. Both are |
| 216 | + restored to their original values before the function returns. |
| 217 | +
|
| 218 | + Returns the resulting module globals dictionary. |
200 | 219 | """ |
201 | 220 | mod_name, mod_spec, code = _get_module_details(mod_name) |
202 | 221 | if run_name is None: |
@@ -228,27 +247,35 @@ def _get_main_module_details(error=ImportError): |
228 | 247 |
|
229 | 248 | def _get_code_from_file(run_name, fname): |
230 | 249 | # Check for a compiled file first |
231 | | - with open(fname, "rb") as f: |
| 250 | + from pkgutil import read_code |
| 251 | + decoded_path = os.path.abspath(os.fsdecode(fname)) |
| 252 | + with io.open_code(decoded_path) as f: |
232 | 253 | code = read_code(f) |
233 | 254 | if code is None: |
234 | 255 | # That didn't work, so try it as normal source code |
235 | | - with open(fname, "rb") as f: |
| 256 | + with io.open_code(decoded_path) as f: |
236 | 257 | code = compile(f.read(), fname, 'exec') |
237 | 258 | return code, fname |
238 | 259 |
|
239 | 260 | def run_path(path_name, init_globals=None, run_name=None): |
240 | | - """Execute code located at the specified filesystem location |
| 261 | + """Execute code located at the specified filesystem location. |
| 262 | +
|
| 263 | + path_name -- filesystem location of a Python script, zipfile, |
| 264 | + or directory containing a top level __main__.py script. |
| 265 | +
|
| 266 | + Optional arguments: |
| 267 | + init_globals -- dictionary used to pre-populate the module’s |
| 268 | + globals dictionary before the code is executed. |
241 | 269 |
|
242 | | - Returns the resulting top level namespace dictionary |
| 270 | + run_name -- if not None, this will be used to set __name__; |
| 271 | + otherwise, '<run_path>' will be used for __name__. |
243 | 272 |
|
244 | | - The file path may refer directly to a Python script (i.e. |
245 | | - one that could be directly executed with execfile) or else |
246 | | - it may refer to a zipfile or directory containing a top |
247 | | - level __main__.py script. |
| 273 | + Returns the resulting module globals dictionary. |
248 | 274 | """ |
249 | 275 | if run_name is None: |
250 | 276 | run_name = "<run_path>" |
251 | 277 | pkg_name = run_name.rpartition(".")[0] |
| 278 | + from pkgutil import get_importer |
252 | 279 | importer = get_importer(path_name) |
253 | 280 | # Trying to avoid importing imp so as to not consume the deprecation warning. |
254 | 281 | is_NullImporter = False |
|
0 commit comments