Skip to content

gh-157144: Let unittest discovery and regrtest find tests inside archives on sys.path - #157145

Draft
gpshead wants to merge 3 commits into
python:mainfrom
gpshead:regrtest-nonfs-test-packages
Draft

gh-157144: Let unittest discovery and regrtest find tests inside archives on sys.path#157145
gpshead wants to merge 3 commits into
python:mainfrom
gpshead:regrtest-nonfs-test-packages

Conversation

@gpshead

@gpshead gpshead commented Sep 8, 2026

Copy link
Copy Markdown
Member

When the test package lives inside an archive on sys.path (a zipped standard library, a zipapp), the test suite cannot run:

  • unittest.TestLoader.discover() requires a real directory and raises "Start directory is not importable", which breaks every test package whose load_tests() uses test.support.load_package_tests().
  • libregrtest.findtests() calls os.listdir() on it, which raises NotADirectoryError (FileNotFoundError on Windows).

Fix this in unittest rather than in regrtest: when the start directory is not on the file system but a path hook claims it, discover() lists it through the archive's path entry finder with pkgutil.iter_modules(), which also distinguishes modules from packages. Module naming, importing and the load_tests protocol already work on such paths. findtests() gets the same pkgutil fallback for the top-level listing, keyed on os.path.isdir().

(thank you Claude Fable 5.1)

When the standard library is a zip archive on sys.path, the test package
is not a directory: findtests() failed with NotADirectoryError from
os.listdir() and load_package_tests() failed with "Start directory is
not importable" from unittest discovery. Fall back to listing the test
modules with pkgutil.iter_modules() through the import system.
Comment thread Lib/test/support/__init__.py Outdated
Comment on lines +204 to +211
def _load_package_tests_from_import_system(pkg_dir, loader, pattern,
package=None):
"""Load the tests of a package which is not on the file system.

Mimic unittest discovery for a package that unittest cannot walk,
e.g. a package inside a zip archive, by asking the import system for
its modules.
"""

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we want to support this at all, this is probably too targeted. why not fix unittest discovery itself instead of just doing this in regrtest?

… regrtest

Make unittest.TestLoader.discover() work when the start directory is
inside an archive on sys.path, such as a zip file: the archive's path
entry finder lists the entries through pkgutil.iter_modules() and tells
modules and packages apart, and the rest of discovery (module naming,
importing, the load_tests protocol) already works on such paths.  With
that, test.support.load_package_tests() needs no special case and goes
back to plain discovery.

libregrtest's findtests() keeps a small fallback for listing the top
level test directory, now keyed on os.path.isdir() rather than on the
exception os.listdir() raises, which is NotADirectoryError on POSIX but
FileNotFoundError on Windows.
@gpshead gpshead changed the title gh-157144: Let regrtest find tests that are not on the file system gh-157144: Let unittest discovery and regrtest find tests inside archives on sys.path Sep 8, 2026
@read-the-docs-community

read-the-docs-community Bot commented Sep 8, 2026

Copy link
Copy Markdown

Documentation build overview

📚 cpython-previews | 🛠️ Build #34442024 | 📁 Comparing d3f5935 against main (c8da735)

  🔍 Preview build  

2 files changed
± library/unittest.html
± whatsnew/changelog.html

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

drop this one, the unittest news entry is sufficient.

Comment thread Lib/unittest/loader.py Outdated

Loads tests from a single file, or a directories' __init__.py when
passed the directory.
passed the directory. *kind* is 'module' or 'package' for an entry

@gpshead gpshead Sep 8, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets use something like defined module level _CONSTANTS rather than a inline magic values. make their names clearly indicate when something refers to an archive member or not and do not use None to indicate anything.

Address review: describe what _find_tests() hands _find_test_path()
with module-level constants (_FILESYSTEM_ENTRY, _FILESYSTEM_MODULE,
_FILESYSTEM_PACKAGE, _ARCHIVE_MODULE, _ARCHIVE_PACKAGE) instead of
inline strings and None, and split the archive helper into a predicate
and a listing function so that neither returns None to mean anything.

Drop the regrtest NEWS entry; the unittest one covers the change.
@serhiy-storchaka
serhiy-storchaka self-requested a review September 9, 2026 14:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant