diff --git a/Lib/pkgutil.py b/Lib/pkgutil.py index 8772a66791a3c9..aff09593c5a9a6 100644 --- a/Lib/pkgutil.py +++ b/Lib/pkgutil.py @@ -77,11 +77,13 @@ def seen(p, m={}): except ImportError: if onerror is not None: onerror(info.name) - except Exception: + except Exception as e: if onerror is not None: onerror(info.name) else: - raise + from unittest import SkipTest + if not isinstance(e, SkipTest): + raise else: path = getattr(sys.modules[info.name], '__path__', None) or [] diff --git a/Lib/test/test_pkgutil.py b/Lib/test/test_pkgutil.py index d4faaaeca00457..f20dcfcb5d6f0b 100644 --- a/Lib/test/test_pkgutil.py +++ b/Lib/test/test_pkgutil.py @@ -354,6 +354,26 @@ def test_name_resolution_import_rebinding2(self): self.assertEqual(pkgutil.resolve_name('package4.submodule:attr'), 'submodule') self.assertEqual(pkgutil.resolve_name('package4:submodule.attr'), 'submodule') + def test_walkpackages_skips_unittest_skiptest(self): + skip_pkg = 'aaa_walkpackages_skiptest' + skip_pkg_dir = os.path.join(self.dirname, skip_pkg) + os.mkdir(skip_pkg_dir) + with open(os.path.join(skip_pkg_dir, '__init__.py'), 'w', encoding='utf-8') as f: + f.write('import unittest\nraise unittest.SkipTest("skip me")\n') + + ok_pkg = 'bbb_walkpackages_ok' + ok_pkg_dir = os.path.join(self.dirname, ok_pkg) + os.mkdir(ok_pkg_dir) + with open(os.path.join(ok_pkg_dir, '__init__.py'), 'w', encoding='utf-8') as f: + f.write('') + with open(os.path.join(ok_pkg_dir, 'mod.py'), 'w', encoding='utf-8') as f: + f.write('') + + actual = [m.name for m in pkgutil.walk_packages([self.dirname])] + self.assertEqual(actual, [skip_pkg, ok_pkg, f'{ok_pkg}.mod']) + + sys.modules.pop(skip_pkg, None) + sys.modules.pop(ok_pkg, None) class PkgutilPEP302Tests(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2026-04-23-14-16-58.gh-issue-131684.Qcwzzh.rst b/Misc/NEWS.d/next/Library/2026-04-23-14-16-58.gh-issue-131684.Qcwzzh.rst new file mode 100644 index 00000000000000..8e491606ddb6fb --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-04-23-14-16-58.gh-issue-131684.Qcwzzh.rst @@ -0,0 +1 @@ +pkgutil.walk_packages() now ignores unittest.SkipTest by default when no onerror callback is provided, allowing iteration to continue.