Skip to content

Commit

Permalink
Support for the importlib.metadata metadata implementation
Browse files Browse the repository at this point in the history
enabled by pip 22.1 by default on Python 3.11 (or later)
  • Loading branch information
richafrank committed May 31, 2022
1 parent e2d0fae commit 9b29e2f
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 8 deletions.
42 changes: 41 additions & 1 deletion piptools/_compat/pip_compat.py
@@ -1,5 +1,5 @@
import optparse
from typing import Iterator, Optional
from typing import Callable, Iterator, Optional, cast

import pip
from pip._internal.index.package_finder import PackageFinder
Expand All @@ -8,13 +8,17 @@
from pip._internal.req import parse_requirements as _parse_requirements
from pip._internal.req.constructors import install_req_from_parsed_requirement
from pip._vendor.packaging.version import parse as parse_version
from pip._vendor.pkg_resources import Requirement

PIP_VERSION = tuple(map(int, parse_version(pip.__version__).base_version.split(".")))


__all__ = [
"get_build_tracker",
"update_env_context_manager",
"dist_requires",
"uses_pkg_resources",
"Distribution",
]


Expand Down Expand Up @@ -42,3 +46,39 @@ def parse_requirements(
get_build_tracker,
update_env_context_manager,
)


# The Distribution interface has changed between pkg_resources and
# importlib.metadata, so this compat layer allows for a consistent access
# pattern. In pip 22.1, importlib.metdata became the default on Python 3.11
# (and later), but is overrideable. `select_backend` returns what's being used.


def _uses_pkg_resources() -> bool:

if PIP_VERSION[:2] < (22, 1):
return True
else:
from pip._internal.metadata import select_backend
from pip._internal.metadata.pkg_resources import Distribution as _Dist

return select_backend().Distribution is _Dist


uses_pkg_resources = _uses_pkg_resources()

if uses_pkg_resources:
from operator import methodcaller

from pip._vendor.pkg_resources import Distribution

dist_requires = cast(
Callable[[Distribution], Iterator[Requirement]], methodcaller("requires")
)
else:
from pip._internal.metadata import select_backend

Distribution = select_backend().Distribution

def dist_requires(dist: "Distribution") -> Iterator[Requirement]:
return map(Requirement, dist.requires or ())
5 changes: 2 additions & 3 deletions piptools/scripts/sync.py
Expand Up @@ -10,10 +10,10 @@
from pip._internal.commands.install import InstallCommand
from pip._internal.index.package_finder import PackageFinder
from pip._internal.metadata import get_environment
from pip._vendor.pkg_resources import Distribution

from .. import sync
from .._compat import IS_CLICK_VER_8_PLUS, parse_requirements
from .._compat.pip_compat import Distribution
from ..exceptions import PipToolsError
from ..logging import log
from ..repositories import PyPIRepository
Expand Down Expand Up @@ -275,12 +275,11 @@ def _get_installed_distributions(
paths: Optional[List[str]] = None,
) -> List[Distribution]:
"""Return a list of installed Distribution objects."""
from pip._internal.metadata.pkg_resources import Distribution as _Dist

env = get_environment(paths)
dists = env.iter_installed_distributions(
local_only=local_only,
user_only=user_only,
skip=[],
)
return [cast(_Dist, dist)._dist for dist in dists]
return [cast(Distribution, dist)._dist for dist in dists]
4 changes: 2 additions & 2 deletions piptools/sync.py
Expand Up @@ -19,8 +19,8 @@
from pip._internal.commands.freeze import DEV_PKGS
from pip._internal.req import InstallRequirement
from pip._internal.utils.compat import stdlib_pkgs
from pip._vendor.pkg_resources import Distribution

from ._compat.pip_compat import Distribution, dist_requires
from .exceptions import IncompatibleRequirements
from .logging import log
from .utils import (
Expand Down Expand Up @@ -69,7 +69,7 @@ def dependency_tree(

dependencies.add(key)

for dep_specifier in v.requires():
for dep_specifier in dist_requires(v):
dep_name = key_from_req(dep_specifier)
if dep_name in installed_keys:
dep = installed_keys[dep_name]
Expand Down
16 changes: 14 additions & 2 deletions tests/conftest.py
Expand Up @@ -19,6 +19,7 @@
from pip._vendor.packaging.version import Version
from pip._vendor.pkg_resources import Requirement

from piptools._compat.pip_compat import uses_pkg_resources
from piptools.cache import DependencyCache
from piptools.exceptions import NoCandidateFound
from piptools.repositories import PyPIRepository
Expand Down Expand Up @@ -106,6 +107,7 @@ class FakeInstalledDistribution:
def __init__(self, line, deps=None):
if deps is None:
deps = []
self.dep_strs = deps
self.deps = [Requirement.parse(d) for d in deps]

self.req = Requirement.parse(line)
Expand All @@ -115,8 +117,18 @@ def __init__(self, line, deps=None):

self.version = line.split("==")[1]

def requires(self):
return self.deps
# The Distribution interface has changed between pkg_resources and
# importlib.metadata.
if uses_pkg_resources:

def requires(self):
return self.deps

else:

@property
def requires(self):
return self.dep_strs


def pytest_collection_modifyitems(config, items):
Expand Down

0 comments on commit 9b29e2f

Please sign in to comment.