Skip to content

Commit 1c91acd

Browse files
committed
gh-151631: Fix traceback when parent package is blocked in sys.modules
_find_incompatible_extension_module() must not import a parent package blocked via sys.modules[parent] = None when formatting ModuleNotFoundError.
1 parent 1b9fe5c commit 1c91acd

3 files changed

Lines changed: 33 additions & 1 deletion

File tree

Lib/test/test_traceback.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5375,6 +5375,30 @@ def test_incompatible_extension_modules_hint(self):
53755375
hint = f'Although a module with this name was found for a different Python version ({incompatible_module}).'
53765376
self.assertIn(hint, stderr.decode())
53775377

5378+
def test_module_not_found_with_blocked_parent(self):
5379+
# gh-151631: formatting a ModuleNotFoundError for a submodule must not
5380+
# raise when the parent package is blocked via sys.modules[parent]=None.
5381+
sys.modules['pkg'] = None
5382+
self.addCleanup(sys.modules.pop, 'pkg', None)
5383+
with self.assertRaises(ModuleNotFoundError) as cm:
5384+
from pkg.mod import name # noqa: F401
5385+
exc = cm.exception
5386+
5387+
self.assertIsNone(
5388+
traceback._find_incompatible_extension_module('pkg.mod'))
5389+
te = traceback.TracebackException(
5390+
type(exc), exc, exc.__traceback__)
5391+
formatted = ''.join(te.format())
5392+
self.assertIn("No module named 'pkg.mod'", formatted)
5393+
5394+
formatted2 = ''.join(traceback.format_exception(exc))
5395+
self.assertIn("No module named 'pkg.mod'", formatted2)
5396+
5397+
import logging
5398+
with self.assertLogs('test_traceback', level='ERROR') as cm_logs:
5399+
logging.getLogger('test_traceback').exception('fail', exc_info=exc)
5400+
self.assertIn("No module named 'pkg.mod'", cm_logs.output[0])
5401+
53785402

53795403
class TestColorizedTraceback(unittest.TestCase):
53805404
maxDiff = None

Lib/traceback.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2104,7 +2104,12 @@ def _find_incompatible_extension_module(module_name):
21042104

21052105
parent, _, child = module_name.rpartition('.')
21062106
if parent:
2107-
traversable = importlib.resources.files(parent)
2107+
try:
2108+
traversable = importlib.resources.files(parent)
2109+
except ImportError:
2110+
# gh-151631: importlib.resources.files() imports the parent
2111+
# package, which fails when sys.modules[parent] is None.
2112+
return
21082113
else:
21092114
traversable = importlib.resources.readers.MultiplexedPath(
21102115
*map(pathlib.Path, filter(os.path.isdir, sys.path))
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix :func:`traceback.format_exception` raising :exc:`ModuleNotFoundError` when
2+
formatting an existing :exc:`ModuleNotFoundError` for a submodule whose parent
3+
package is blocked via ``sys.modules[parent] = None``.

0 commit comments

Comments
 (0)