GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Install the finder only after the module is initialized by jaraco · Pull Request #556 · python/importlib_metadata

Conversation

@jaraco

@jaraco jaraco commented Oct 31, 2026

Copy link
Copy Markdown
Member

install appended a MetadataPathFinder to sys.meta_path while the module body was still executing, 36 lines before PathDistribution is bound. PathDistribution is the only global the installed finder has yet to see — Prepared, FastPath and DistributionFinder all precede it — which is why it is specifically this name that fails.

Anything walking sys.meta_path in that window resolves the finder against incomplete globals:

  File ".../importlib/metadata/__init__.py", line 486, in <genexpr>
    resolver(context) for resolver in cls._discover_resolvers()
  File ".../importlib_metadata/__init__.py", line 1054, in find_distributions
    return map(PathDistribution, found)
NameError: name 'PathDistribution' is not defined

Two conditions reach it:

  • Another thread calling entry_points(). No module lock applies, because that thread imports nothing — it only iterates sys.meta_path.
  • An import aborted in the window, as an async exception (a signal handler, a soft time limit, KeyboardInterrupt) can do. The module is dropped from sys.modules but the half-built finder stays installed for the life of the process, install has already deleted PathFinder.find_distributions so the orphan is the only provider, and even a later successful import does not displace it.

The second explains why #525 wasn't reproducible: the import that fails is not the import that reports, and consumers of a backport conventionally swallow its ImportError.

Installing at the end of the module, rather than just after PathDistribution, states the invariant the finder actually depends on, so a later reordering can't silently reopen the window.

The stdlib copy is unaffected — install is stripped there and PathFinder.find_distributions imports MetadataPathFinder lazily — so this needs no CPython-side change and stays clear of python/cpython#160387.

Test

FinderInstallTests snapshots the module globals at the moment the finder is published and asserts PathDistribution is already bound. Without the fix:

E       AssertionError: assert 'PathDistribution' in {'Any', 'Callable', 'Distribution', 'DistributionFinder', 'EntryPoint', 'EntryPoints', ...}

Closes #525.

🤖 Generated with Claude Code

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@jaraco @claude
Install the finder only after the module is initialized.
27909fe
`install` appended a `MetadataPathFinder` to `sys.meta_path` while the
module body was still executing, 36 lines before `PathDistribution` is
bound, and `PathDistribution` is the only global the installed finder has
yet to see (`Prepared`, `FastPath` and `DistributionFinder` all precede
it). Anything walking `sys.meta_path` in that window resolves it against
incomplete globals and raises `NameError: name 'PathDistribution' is not
defined`.

Two conditions reach the window. Another thread calling `entry_points()`
takes no module lock, as it imports nothing and only iterates
`sys.meta_path`. And an import aborted in the window, as an async
exception such as a signal handler or a soft time limit can do, leaves
the half-built finder installed for the life of the process: the module
is dropped from `sys.modules`, `install` has already deleted
`PathFinder.find_distributions` so the orphan is the only provider, and
even a later successful import does not displace it.

Installing at the end of the module, rather than just after
`PathDistribution`, states the invariant the finder depends on and so
survives any later reordering.

The stdlib copy is unaffected, where `install` is stripped and
`PathFinder.find_distributions` imports `MetadataPathFinder` lazily.

Closes #525.

Co-Authored-By: Claude Opus 5 <[email protected]>
jaraco
jaraco commented Nov 7, 2026
View reviewed changes
Comment thread importlib_metadata/__init__.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

jaraco
jaraco commented Nov 7, 2026
View reviewed changes
Comment thread tests/test_main.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

jaraco and others added 2 commits November 7, 2026 14:34
@jaraco @claude
Carry the rationale in install, not at the call site.
c52e108
The comment explaining why the call sits at the end of the module was
four more lines for the CPython port to strip. `install` is itself
removed there, so its docstring carries the constraint for free.

Co-Authored-By: Claude Opus 5 <[email protected]>
@jaraco @claude
Move the install expectation to tests/compat.
579d2e9
`install` has no counterpart in the stdlib, which reaches
`MetadataPathFinder` through its own `PathFinder` and publishes no finder
of its own, so the expectation belongs beside the other backport-specific
tests rather than among those test_main holds for both codebases.

Co-Authored-By: Claude Opus 5 <[email protected]>
jaraco
jaraco commented Nov 8, 2026
View reviewed changes
Comment thread importlib_metadata/_compat/__init__.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

@jaraco @claude
Stop returning the class from install.
fa2b269
Nothing consumes the return value now that the call is a statement, and
withholding it makes the decorator form bind None rather than quietly
restoring the window it was moved to close.

Co-Authored-By: Claude Opus 5 <[email protected]>
@jaraco
jaraco merged commit 1d5a2ce into main Nov 8, 2026
34 of 36 checks passed

Uh oh!

There was an error while loading. Please reload this page.

@jaraco
jaraco deleted the bugfix/525-install-after-init branch November 8, 2026 16:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

No reviews

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

NameError: name 'PathDistribution' is not defined

Uh oh!

There was an error while loading. Please reload this page.

1 participant

@jaraco
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.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.