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 — Prefer valid distributions when collecting entry points by jaraco · Pull Request #553 · python/importlib_metadata

Conversation

@jaraco

@jaraco jaraco commented Oct 23, 2026 •
edited
Loading

Uh oh!

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

Copy link
Copy Markdown
Member

Closes #534.

Built on the #550 revert (#552), now on main.

The fix

entry_points() fed distributions() straight to _unique(), which keeps the first distribution seen for each normalized name. Where an unclean upgrade leaves an empty foo-1.0.dist-info beside the valid foo-1.1.dist-info, the empty one could be listed first, and with it the valid distribution's entry points disappeared entirely.

_prefer_valid already existed for exactly this preference, used by from_name. Applying it ahead of _unique in entry_points() is the whole of the fix.

Detecting metadata rather than loading it

_prefer_valid decided validity by consulting metadata, which runs the whole METADATA file through the email parser and then discards the result. Applied to every distribution rather than to one, that is the cost @jaraco anticipated on the issue — measured, entry_points() went from 1.53 msec to 13.4 msec, 8.7x.

So PathDistribution grows a _has_metadata that detects the metadata file without opening it, and that is the preference's predicate. The remaining 29% on entry_points() buys a large improvement in retrieval by name, which no longer parses the metadata of the distribution it returns twice:

benchmark before after
entry_points() 1.53 msec 2.03 msec (+29%)
discovery 138 µsec 15.5 µsec (−89%)
cached distribution 138 µsec 15.6 µsec (−89%)
uncached distribution 236 µsec 107 µsec (−55%)

Distributions that aren't ours

localize.dist passes a provider's own Distribution subclass through unadapted (#486), so it has no _has_metadata and test_conforming_provider_is_not_warned_about caught that directly.

Nothing on IDistribution is as cheap for those: locate_file returns a SimplePath, which does have exists(), but it resolves against the package root rather than the metadata, so locate_file('METADATA') names site-packages/METADATA. Every other member loads the metadata or parses it. So Distribution._has_metadata() reads, through a _metadata_text that metadata shares so neither names the files twice, and PathDistribution overrides it with the detection above. The predicate is _safe_has_metadata, which falls back for a distribution supplying neither helper.

That turns out to fix something pre-existing. Asking such a distribution for metadata raises importlib.metadata.MetadataNotFound, a different class from this package's, so it escaped ExceptionTrap(MetadataNotFound) on main and propagated out of from_name instead of marking the distribution invalid. test_dist_without_metadata_is_deferred covers it.

Tests

  • test_entry_points_from_valid_dist — fails before the fix with assert set() == {'ep'}. It brackets the valid distribution between two invalid ones, as test_valid_dists_preferred does, so that no file system lists it first.
  • test_invalid_dists_deferred — the invalid distributions are moved to the end, not dropped.
  • test_dist_without_metadata_is_deferred — the foreign-MetadataNotFound case above.

tox passes on each commit separately: 260 passed, 1 skipped, 41 subtests; mypy and ruff clean.

🤖 Generated with Claude Code

Sorry, something went wrong.

Uh oh!

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

All reactions
jaraco
jaraco commented Oct 23, 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 Oct 23, 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 force-pushed the bugfix/534-entry-points-prefer-valid branch from 64141d1 to 6a39aee Compare October 23, 2026 22:25
jaraco
jaraco commented Oct 23, 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 Oct 23, 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 Oct 23, 2026
View reviewed changes
Comment thread importlib_metadata/__init__.py Resolved

Uh oh!

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

@binggao1230 binggao1230 mentioned this pull request Oct 23, 2026
Prefer valid distributions when collecting entry points #536
Closed
@jaraco
jaraco force-pushed the bugfix/534-entry-points-prefer-valid branch 3 times, most recently from f8fa1c2 to 86e2d3a Compare October 23, 2026 23:02
jaraco
jaraco commented Oct 23, 2026
View reviewed changes
Comment thread importlib_metadata/__init__.py Outdated
return distribution(distribution_name).version


def _metadata_text(dist: IDistribution) -> str | None:

@jaraco jaraco Oct 23, 2026 •
edited
Loading

Uh oh!

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

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Can this be an instance method?

Sorry, something went wrong.

Uh oh!

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

All reactions

@jaraco jaraco Oct 23, 2026

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Yes — _metadata_text is now Distribution._metadata_text(self), and metadata calls self._metadata_text().

Sorry, something went wrong.

Uh oh!

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

All reactions
jaraco
jaraco commented Oct 23, 2026
View reviewed changes
Comment thread importlib_metadata/__init__.py Outdated
"""
with suppress(AttributeError):
return dist._has_metadata # type: ignore[attr-defined]
return bool(_metadata_text(dist))

@jaraco jaraco Oct 23, 2026

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

What about instead having Distribution._has_metadata(), supplying the default behavior, have it overridden in PathDistribution, then this function is _safe_has_metadata, which suppresses the AttributeError for IDistributions not derived from Distribution?

Sorry, something went wrong.

Uh oh!

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

All reactions

@jaraco jaraco Oct 23, 2026

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Done, that reads better. Distribution._has_metadata() supplies the default, PathDistribution overrides it with the detection, and the predicate is _safe_has_metadata.

One wrinkle worth knowing: the fallback cannot be Distribution._has_metadata(dist), because its body is self._metadata_text(), which dispatches on the instance again and a foreign distribution has no _metadata_text either — test_conforming_provider_is_not_warned_about failed on exactly that. So the fallback is bool(Distribution._metadata_text(dist)), unbound, which needs nothing of the distribution beyond read_text.

Sorry, something went wrong.

Uh oh!

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

All reactions
@jaraco
jaraco force-pushed the bugfix/534-entry-points-prefer-valid branch from 86e2d3a to eba46e3 Compare October 23, 2026 23:21
@jaraco

jaraco commented Oct 23, 2026

Copy link
Copy Markdown
Member Author

I think this is ready to go. I'm going to sleep on it before merging.

All reactions

Sorry, something went wrong.

Uh oh!

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

@jaraco

jaraco commented Oct 23, 2026

Copy link
Copy Markdown
Member Author

On second thought, let's get it out; we can iterate later if needed.

All reactions

Sorry, something went wrong.

Uh oh!

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

jaraco and others added 2 commits October 23, 2026 16:31
@jaraco @claude
Detect the presence of metadata without reading it.
e52be78
`_prefer_valid` decided validity by consulting `metadata`, which runs the
whole METADATA file through the email parser only for the result to be
discarded. Give `Distribution` a `_has_metadata` that reads no further than
the text, through a `_metadata_text` that `metadata` shares so neither names
the files twice, and override it in `PathDistribution` to detect the
metadata file without opening it at all. An old-style egg-info file, which
is itself the metadata, is detected with `is_file`; a metadata directory
exists whether or not it holds anything, so its own presence says nothing.

The predicate is `_safe_has_metadata`, since a provider's own `Distribution`
subclass derives from `importlib.metadata` and is passed through unadapted
(#486), so it supplies neither helper. Reading its metadata rather than
asking it for `metadata` also closes a gap: the `MetadataNotFound` such a
distribution raises is a different class from this package's, so it escaped
the preference rather than marking the distribution invalid.

Co-Authored-By: Claude Opus 5 <[email protected]>
@jaraco @claude
Prefer valid distributions when collecting entry points.
0aa24f3
Where an unclean upgrade leaves an empty metadata directory beside the valid
one, `_unique` kept whichever the file system happened to list first, so the
entry points of the valid distribution could go missing entirely.

Closes #534.

Co-Authored-By: Claude Opus 5 <[email protected]>
@jaraco
jaraco force-pushed the bugfix/534-entry-points-prefer-valid branch from eba46e3 to 0aa24f3 Compare October 23, 2026 23:32
@jaraco
jaraco merged commit 5d253ae into main Oct 23, 2026
30 of 32 checks passed

Uh oh!

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

@jaraco
jaraco deleted the bugfix/534-entry-points-prefer-valid branch October 23, 2026 23:41
@rossburton

rossburton commented Oct 26, 2026

Copy link
Copy Markdown

Semi-related question: how often is importlib-metadata synced into python?

All reactions

Sorry, something went wrong.

Uh oh!

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

@jaraco jaraco mentioned this pull request Oct 31, 2026
gh-160386: Apply changes from importlib_metadata 9.1 python/cpython#160387
Merged
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.

entry_points doesn't handle empty .dist-info files well

Uh oh!

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

2 participants

@jaraco @rossburton
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.