Skip to content

fix pkg_resources import failures w/ py3-only loaders#2918

Merged
jaraco merged 2 commits intopypa:mainfrom
nitzmahone:fix_nonlegacy_loader
Jan 11, 2022
Merged

fix pkg_resources import failures w/ py3-only loaders#2918
jaraco merged 2 commits intopypa:mainfrom
nitzmahone:fix_nonlegacy_loader

Conversation

@nitzmahone
Copy link
Copy Markdown
Contributor

@nitzmahone nitzmahone commented Dec 7, 2021

Summary of changes

#1563 introduced a subtle bug in the fallback code for pkg_resources enumeration. The original code that only used find_module later accounts for a None loader return value (ie, the requested module couldn't be found), but the new code blindly dots into the return from find_spec (which can also be None) to grab its loader. When this occurs, it inadvertently trips the AttributeError that was supposed to be there for py2 loaders that don't implement find_spec, causing find_module to be called even on a py3-native loader. Loaders that still implement the deprecated 2.x methods will (correctly) return None, triggering the old fallback path, all is well. However, new loaders that don't implement find_module will blow up the pkg_resources enumeration with another AttributeError.

The inadvertent trip of the py2 fallback happens a lot even with just the built-in loaders (eg, instrument the fallback exception handler with a print or something, then install straight.plugin in a fresh venv and import pkg_resources)- it works by accident until the builtin loaders start dropping find_module (as is planned for Python 3.11+), at which point it gets ugly fast.

This PR minimizes the try block's surface area to only the necessary loader method call, and defensively checks for a non-empty response from find_spec before trying to grab its loader (otherwise setting the loader to None to restore the previous early exit behavior for modules that can't be loaded.

This one's pretty tricky to add tests for without wiring up a bespoke py3 loader. Happy to add a changelog entry if the fix otherwise looks good...

Pull Request Checklist

@jaraco
Copy link
Copy Markdown
Member

jaraco commented Jan 11, 2022

This looks good. Thanks for reporting it.

My only concern is that someone could "optimize" the change back to the old code and no one would notice. Usually in such a situation, where creating a test is difficult, I'll instead protect the change with a comment or separate function. I'll illustrate that in a subsequent change.

@jaraco
Copy link
Copy Markdown
Member

jaraco commented Jan 11, 2022

On further consideration, after struggling to come up with a clean abstraction, I've decided to proceed with this implementation. Thanks!

@jaraco jaraco merged commit 8fd31fc into pypa:main Jan 11, 2022
blitzy Bot pushed a commit to blitzy-showcase/ansible that referenced this pull request Apr 20, 2026
Fix AttributeError when Python 3 falls back to the legacy find_module()
protocol after find_spec() returns None. On modern Python 3 runtimes
(>=3.4) the cached importlib.machinery.FileFinder does not accept a
'path' keyword argument on find_module(); on Python 3.12+ the method
was removed entirely.

Changes in lib/ansible/utils/collection_loader/_collection_finder.py:

1. Add a try/except/else block importing FileFinder from
   importlib.machinery with a HAS_FILE_FINDER sentinel for graceful
   degradation on runtimes that do not expose FileFinder (e.g., Py2).

2. Flatten _AnsibleCollectionFinder.find_spec() to the early-return-on-
   None idiom. Behavior preserved, control flow simplified.

3. Type-dispatch _AnsiblePathHookFinder.find_module() so that when the
   returned finder is a native FileFinder instance, find_module() is
   called WITHOUT the 'path' keyword argument. Other finder types
   continue to receive 'path=[self._pathctx]' unchanged.
   THIS IS THE CORE BUG FIX.

4. Flatten _AnsiblePathHookFinder.find_spec() to the early-return-on-
   None three-way if/elif/else idiom for structural symmetry with
   find_module().

Refs: ansible/ansible#76448
Refs: pypa/setuptools#2918
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.

2 participants