Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7dda421

Browse files
committed
Issue #14285: Do not catch exceptions initializing any ancestor package
The previous fix only handled the case of the parent package of __main__ failing to initialize. Also make the "Error while finding spec" formatting slightly more appealing, and document and test that the module name must be absolute.
1 parent a29eb08 commit 7dda421

5 files changed

Lines changed: 31 additions & 6 deletions

File tree

Doc/library/runpy.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ The :mod:`runpy` module provides two functions:
3636
import mechanism (refer to :pep:`302` for details) and then executed in a
3737
fresh module namespace.
3838

39-
If the supplied module name refers to a package rather than a normal
39+
The *mod_name* argument should be an absolute module name.
40+
If the module name refers to a package rather than a normal
4041
module, then that package is imported and the ``__main__`` submodule within
4142
that package is then executed and the resulting module globals dictionary
4243
returned.

Doc/using/cmdline.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ source.
7777
the :mod:`__main__` module.
7878

7979
Since the argument is a *module* name, you must not give a file extension
80-
(``.py``). The ``module-name`` should be a valid Python module name, but
80+
(``.py``). The module name should be a valid absolute Python module name, but
8181
the implementation may not always enforce this (e.g. it may allow you to
8282
use a name that includes a hyphen).
8383

Lib/runpy.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,24 +100,38 @@ def _run_module_code(code, init_globals=None,
100100

101101
# Helper to get the loader, code and filename for a module
102102
def _get_module_details(mod_name, error=ImportError):
103+
if mod_name.startswith("."):
104+
raise error("Relative module names not supported")
105+
pkg_name, _, _ = mod_name.rpartition(".")
106+
if pkg_name:
107+
# Try importing the parent to avoid catching initialization errors
108+
try:
109+
__import__(pkg_name)
110+
except ImportError as e:
111+
# If the parent or higher ancestor package is missing, let the
112+
# error be raised by find_spec() below and then be caught. But do
113+
# not allow other errors to be caught.
114+
if e.name is None or (e.name != pkg_name and
115+
not pkg_name.startswith(e.name + ".")):
116+
raise
117+
103118
try:
104119
spec = importlib.util.find_spec(mod_name)
105120
except (ImportError, AttributeError, TypeError, ValueError) as ex:
106121
# This hack fixes an impedance mismatch between pkgutil and
107122
# importlib, where the latter raises other errors for cases where
108123
# pkgutil previously raised ImportError
109124
msg = "Error while finding spec for {!r} ({}: {})"
110-
raise error(msg.format(mod_name, type(ex), ex)) from ex
125+
raise error(msg.format(mod_name, type(ex).__name__, ex)) from ex
111126
if spec is None:
112127
raise error("No module named %s" % mod_name)
113128
if spec.submodule_search_locations is not None:
114129
if mod_name == "__main__" or mod_name.endswith(".__main__"):
115130
raise error("Cannot use package as __main__ module")
116-
__import__(mod_name) # Do not catch exceptions initializing package
117131
try:
118132
pkg_main_name = mod_name + ".__main__"
119-
return _get_module_details(pkg_main_name)
120-
except ImportError as e:
133+
return _get_module_details(pkg_main_name, error)
134+
except error as e:
121135
raise error(("%s; %r is a package and cannot " +
122136
"be directly executed") %(e, mod_name))
123137
loader = spec.loader

Lib/test/test_cmd_line_script.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,7 @@ def test_dash_m_errors(self):
433433
('importlib', br'No module named.*'
434434
br'is a package and cannot be directly executed'),
435435
('importlib.nonexistant', br'No module named'),
436+
('.unittest', br'Relative module names not supported'),
436437
)
437438
for name, regex in tests:
438439
with self.subTest(name):

Lib/test/test_runpy.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,8 +197,11 @@ def test_invalid_names(self):
197197
self.expect_import_error("sys.imp.eric")
198198
self.expect_import_error("os.path.half")
199199
self.expect_import_error("a.bee")
200+
# Relative names not allowed
200201
self.expect_import_error(".howard")
201202
self.expect_import_error("..eaten")
203+
self.expect_import_error(".test_runpy")
204+
self.expect_import_error(".unittest")
202205
# Package without __main__.py
203206
self.expect_import_error("multiprocessing")
204207

@@ -460,6 +463,12 @@ def test_run_package_init_exceptions(self):
460463
self.assertNotIn("finding spec", format(err))
461464
else:
462465
self.fail("Nothing raised; expected {}".format(name))
466+
try:
467+
run_module(mod_name + ".submodule")
468+
except exception as err:
469+
self.assertNotIn("finding spec", format(err))
470+
else:
471+
self.fail("Nothing raised; expected {}".format(name))
463472

464473
def test_run_package_in_namespace_package(self):
465474
for depth in range(1, 4):

0 commit comments

Comments
 (0)