gh-127337: Fix importlib.resources for legacy ResourceReader loaders - #154919
Open
pikammmmm wants to merge 1 commit into
Open
gh-127337: Fix importlib.resources for legacy ResourceReader loaders#154919pikammmmm wants to merge 1 commit into
pikammmmm wants to merge 1 commit into
Conversation
…aders CompatibilityFiles.SpecPath, .ChildPath and .OrphanPath each defined joinpath() with a single required argument, but importlib.resources.abc.Traversable declares joinpath(*descendants) and documents that passing no descendants returns self. Since 3.13 the functional API is implemented on top of files(anchor).joinpath(*path_names), so any call that passes no path names -- contents(anchor), is_resource(anchor) -- raised TypeError for a package whose loader provides only a legacy ResourceReader. On 3.12, contents(anchor) returned the resource names. Passing several descendants at once now gives the same result as applying them one at a time, which is the equivalence the Traversable documentation relies on for implementations predating the multi-argument protocol.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
CompatibilityFiles.SpecPath,.ChildPathand.OrphanPatheach definedjoinpath()with a single required argument, butimportlib.resources.abc.Traversabledeclaresjoinpath(*descendants)and documents that passing no descendants returnsself.Since 3.13 the functional API is implemented on top of
files(anchor).joinpath(*path_names), so any call that passes no path names reachesjoinpath()with no arguments and raisesTypeErrorfor a package whose loader provides only a legacyResourceReader:contents(FakeModule)['NAME']TypeError['NAME']is_resource(FakeModule)nameTypeErrorFalse(measured with the reproducer from the issue; 3.12 predates
_functional.py, whose one-name-per-call predecessor never routed throughjoinpath())This is the fix @jaraco sketched in the issue, extended to
ChildPathandOrphanPathas he suggested it might need to be. It also fixes the latent multi-argument case, broken ever since the protocol gained*descendantsin 3.11 without_adapters.pybeing updated — as @FFY00 diagnosed, "theCompatibilityFilesimplementation is bad, it can only ever take one argument, and now it needs to be able to take any number of arguments."One deviation from the sketch: it used
'/'.join(descendants)forSpecPath, which would makefiles.joinpath('a', 'b')a singleChildPathnameda/b, whilefiles / 'a' / 'b'is anOrphanPath('a', 'b'). TheTraversable.joinpathdocs present the chained single-argument form as the compatibility equivalent of the multi-argument form for implementations that predate it, so this PR applies descendants one at a time and the two forms agree. That also avoids'/'.joinraisingTypeErroron aPathLikesegment which single-argumentjoinpath()accepts today.CompatibilityFilesstill does not meaningfully supportos.PathLikesegments; that is unchanged here and out of scope.Tests
Seven new tests in
test_compatibilty_files.py: zero-argumentjoinpath()on all three path classes (with and without a reader), the multi-argument/chained equivalence, andcontents()/is_resource()through the functional API._adapters.pyreverted, all seven new tests fail and the 16 pre-existing tests in the file still pass.test_importlib.resourcespass (1 skipped).Lib/on a matching installed interpreter — which yields 88 unrelated pre-existing failures intest_importlib.metadata/frozen/extension. That set is identical with and without this patch, and none of them are inresources.Sibling fix for the same protocol violation in
simple.py: #145263.@FFY00 — @jaraco assigned this to you in December 2024; happy to close this if you would rather take it yourself.