-
Notifications
You must be signed in to change notification settings - Fork 49
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve interop with importlib.metadata
#195
Changes from all commits
ff71ab6
282b873
ca5c5a5
455b77f
d68f4d9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -106,6 +106,18 @@ def find_spec(self, fullname, _path, _target=None): | |
|
||
return spec | ||
|
||
def find_distributions(self, context=None): | ||
# Delayed import: Python 3.7 does not contain importlib.metadata | ||
# If this method is being called it must be because | ||
# `importlib.metadata`/`importlib_metadata` is available. | ||
try: | ||
from importlib_metadata import DistributionFinder, MetadataPathFinder | ||
except ImportError: | ||
from importlib.metadata import DistributionFinder, MetadataPathFinder | ||
|
||
context = DistributionFinder.Context(path=self.backend_path) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this line be in an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure if the passed context will ever contain the My intuition is that we can simply discard the given context, and other finders will handle it. Footnotes
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, I see, it defaults to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed, this looks right to me. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jaraco is there any other optional part that needs to be implemented from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is all that's needed for |
||
return MetadataPathFinder.find_distributions(context=context) | ||
|
||
|
||
def _supported_features(): | ||
"""Return the list of options features supported by the backend. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
Name: _test_bootstrap | ||
Version: 0.0.1 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[_test_backend.importlib_metadata] | ||
hello = world |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
from importlib.metadata import distribution | ||
|
||
|
||
def get_requires_for_build_sdist(config_settings): | ||
dist = distribution("_test_bootstrap") # discovered in backend-path | ||
ep = next(iter(dist.entry_points)) | ||
return [ep.group, ep.name, ep.value] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
[build-system] | ||
build-backend = 'intree_backend' | ||
backend-path = ['backend'] |
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.
Should we only define this if importlib.metadata is available?
I'm mildly concerned about importing
importlib_metadata
from here since pip would need to modify its vendored copy of pyproject-hooks to not import an installed module (we've had issues due to attempting to do this in the past, usually resulting in breakage of pip because the user somehow ended up with a bad copy of said package in their environment and the user needing to understand the nuances + details of how stuff works to fix their environment).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.
I agree with @pradyunsg here. Pip would need to patch this out.
Can we not simply do a version check? Or even just prefer the stdlib version always? That way, pip (which only supports Python 3.8+) would never pick up
importlib_metadata
, avoiding any problems. Is there any justification for usingimportlib_metadata
in preference to the stdlib implementation, when the latter is present?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.
IIRC,
importlib.metadata
was added to the stdlib, but then had significant API changes in Python 3.10, andimportlib_metadata
made those changes available to previous Python versions. So various tools using it prefer the backport over the stdlib module.I also don't really like the idea that this will do different things depending on whether a separate package is installed or not. Maybe we should pretend that
importlib.metadata
was only added from Python 3.10 and do a version check?(I remember this backporting causing issues before as well, though I don't recall which project that was in. I think for most stdlib modules doing backports like this would have been fine, but because
importlib.metadata
defines an interface between third party packages, the packages using it need to agree on whether they use the backport or not. With the benefit of hindsight, I think once it was in the stdlib, backports should have stopped, so everyone just used the stdlib version, even if that meant we had to wait longer to use new APIs.)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.
Yeah, unfortunately
importlib_metadata
is not 100% backward compatible withimportlib.metadata
.In general, I find that the following works relatively fine:
importlib_metadata.DistributionFinder, MetadataPathFinder
to implementfind_distrubitions
, but then usingimportlib.metadata
high-level APIsAnd the following does not work:
importlib.metadata.DistributionFinder, MetadataPathFinder
to implementfind_distrubitions
, but then usingimportlib_metadata
high-level APIs(Of course that depends on the exact functionalities being used by all the other packages in the virtual environment)
That is the main reason why I preferred this order.
But as discussed in #195 (comment), I have no personal problem in exchanging the order in the
try...except...
if that is a better approach. Or even just usingimportlib.metadata
all the time in Python 3.8+. Please let me know and I will proceed accordingly.I believe that preferring the
stdlib
version always (or not attempting to useimportlib_metadata
at all) will have the same limitations.An anecdote about that:
Think about abravalheri/setuptools#12 and the patch necessary to make it work. What happens there is that the
except
branch is executed andimportlib.metadata
is always used.However setuptools uses its own vendored version of
importlib_metadata
high-level API which causes the incompatibility documented in python/importlib_metadata#486. The good side is that, ifsetuptools
was not forced to vendor dependencies and useimportlib_metadata
directly, no patch would be necessary.In fact any package using
importlib_metadata
is always subject to these problems as mentioned by Thomas in #195 (comment).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.
That's a problem for
importlib_metadata
/importlib.metadata
IMO. But it's been discussed elsewhere and I don't want to reiterate old arguments here.For this PR, the main point is that we do not want a situation where pip's behaviour can change as a result of the user installing
importlib_metadata
(whether it's a pristine copy, or some patched nightmare that a distro, in their wisdom, has created). That's something we try very hard to avoid in pip, because it has caused significant issues for us in the past - it's at the root of why our policy on debundling is basically "please don't, but if you must, it's your problem to deal with".Pip doesn't support Python 3.7, so I'm happy with any solution that only uses the stdlib
importlib.metadata
for Python 3.8+. At worst, it'll be pip patching this library when we vendor it, so nothing has to change here. But I'd expect other users of this library, likebuild
, to have similar issues, and so I think it's worth discussing a solution that isn't pip-specific. But if the conclusion is "we don't want to bother about that", then that's OK - pip will patch it.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.
I am happy to go with
importlib.metadata
always. That was more similar to my initial implementation.I believe that
build
also uses the same approach of preferringimportlib_metadata
and falling back toimportlib.metadata
(for Python < 3.10.2). In this sense, the current implementation of this PR may be more compatible withbuild
.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.
I will open a separate PR with that approach so the maintainers can choose which one they prefer and close the other one: #199