Skip to content

Commit

Permalink
Move dist-related logic from misc into metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
uranusjr committed Jan 18, 2021
1 parent b666f62 commit 8d40580
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 102 deletions.
13 changes: 13 additions & 0 deletions src/pip/_internal/metadata/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,12 @@ def from_paths(cls, paths):

def get_distribution(self, name):
# type: (str) -> Optional[BaseDistribution]
"""Given a requirement name, return the installed distributions."""
raise NotImplementedError()

def iter_distributions(self):
# type: () -> Iterator[BaseDistribution]
"""Iterate through installed distributions."""
raise NotImplementedError()

def iter_installed_distributions(
Expand All @@ -64,6 +66,17 @@ def iter_installed_distributions(
user_only=False, # type: bool
):
# type: (...) -> Iterator[BaseDistribution]
"""Return a list of installed distributions.
:param local_only: If True (default), only return installations
local to the current virtualenv, if in a virtualenv.
:param skip: An iterable of canonicalized project names to ignore;
defaults to ``stdlib_pkgs``.
:param include_editables: If False, don't report editables.
:param editables_only: If True, only report editables.
:param user_only: If True, only report installations in the user
site directory.
"""
it = self.iter_distributions()
if local_only:
it = (d for d in it if d.local)
Expand Down
34 changes: 31 additions & 3 deletions src/pip/_internal/metadata/pkg_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,42 @@ def from_paths(cls, paths):
# type: (List[str]) -> BaseEnvironment
return cls(pkg_resources.WorkingSet(paths))

def _search_distribution(self, name):
"""Find a distribution matching the ``name`` in the environment.
This searches from *all* distributions available in the environment, to
match the behavior of ``pkg_resources.get_distribution()``.
"""
# type: (str) -> Optional[BaseDistribution]
canonical_name = canonicalize_name(name)
for dist in self.iter_distributions():
if dist.canonical_name == canonical_name:
return dist
return None

def get_distribution(self, name):
# type: (str) -> Optional[BaseDistribution]
req = pkg_resources.Requirement(name)

# Search the distribution by looking through the working set.
dist = self._search_distribution(name)
if dist:
return dist

# If distribution could not be found, call working_set.require to
# update the working set, and try to find the distribution again.
# This might happen for e.g. when you install a package twice, once
# using setup.py develop and again using setup.py install. Now when
# running pip uninstall twice, the package gets removed from the
# working set in the first uninstall, so we have to populate the
# working set again so that pip knows about it and the packages gets
# picked up and is successfully uninstalled the second time too.
try:
dist = self._ws.find(req)
# We didn't pass in any version specifiers, so this can never
# raise pkg_resources.VersionConflict.
self._ws.require(name)
except pkg_resources.DistributionNotFound:
return None
return Distribution(dist)
return self._search_distribution(name)

def iter_distributions(self):
# type: () -> Iterator[BaseDistribution]
Expand Down
120 changes: 21 additions & 99 deletions src/pip/_internal/utils/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
from itertools import filterfalse, tee, zip_longest

from pip._vendor import pkg_resources
from pip._vendor.packaging.utils import canonicalize_name

# NOTE: retrying is not annotated in typeshed as on 2017-07-17, which is
# why we ignore the type on this import.
Expand Down Expand Up @@ -402,86 +401,20 @@ def get_installed_distributions(
paths=None # type: Optional[List[str]]
):
# type: (...) -> List[Distribution]
"""
Return a list of installed Distribution objects.
If ``local_only`` is True (default), only return installations
local to the current virtualenv, if in a virtualenv.
``skip`` argument is an iterable of lower-case project names to
ignore; defaults to stdlib_pkgs
If ``include_editables`` is False, don't report editables.
If ``editables_only`` is True , only report editables.
If ``user_only`` is True , only report installations in the user
site directory.
If ``paths`` is set, only report the distributions present at the
specified list of locations.
"""
if paths:
working_set = pkg_resources.WorkingSet(paths)
else:
working_set = pkg_resources.working_set

if local_only:
local_test = dist_is_local
else:
def local_test(d):
return True

if include_editables:
def editable_test(d):
return True
else:
def editable_test(d):
return not dist_is_editable(d)

if editables_only:
def editables_only_test(d):
return dist_is_editable(d)
else:
def editables_only_test(d):
return True

if user_only:
user_test = dist_in_usersite
else:
def user_test(d):
return True

return [d for d in working_set
if local_test(d) and
d.key not in skip and
editable_test(d) and
editables_only_test(d) and
user_test(d)
]


def _search_distribution(req_name):
# type: (str) -> Optional[Distribution]
"""Find a distribution matching the ``req_name`` in the environment.
This searches from *all* distributions available in the environment, to
match the behavior of ``pkg_resources.get_distribution()``.
"""
# Canonicalize the name before searching in the list of
# installed distributions and also while creating the package
# dictionary to get the Distribution object
req_name = canonicalize_name(req_name)
packages = get_installed_distributions(
local_only=False,
skip=(),
include_editables=True,
editables_only=False,
user_only=False,
paths=None,
"""Return a list of installed Distribution objects.
Left for compatibility until direct pkg_resources uses are refactored out.
"""
from pip._internal.metadata import get_environment
from pip._internal.metadata.pkg_resources import Distribution as _Dist
dists = get_environment(paths).iter_installed_distributions(
local_only=local_only,
skip=skip,
include_editables=include_editables,
editables_only=editables_only,
user_only=user_only,
)
pkg_dict = {canonicalize_name(p.key): p for p in packages}
return pkg_dict.get(req_name)
return [cast(_Dist, dist)._dist for dist in dists]


def get_distribution(req_name):
Expand All @@ -490,26 +423,15 @@ def get_distribution(req_name):
This searches from *all* distributions available in the environment, to
match the behavior of ``pkg_resources.get_distribution()``.
"""
# Search the distribution by looking through the working set
dist = _search_distribution(req_name)

# If distribution could not be found, call working_set.require
# to update the working set, and try to find the distribution
# again.
# This might happen for e.g. when you install a package
# twice, once using setup.py develop and again using setup.py install.
# Now when run pip uninstall twice, the package gets removed
# from the working set in the first uninstall, so we have to populate
# the working set again so that pip knows about it and the packages
# gets picked up and is successfully uninstalled the second time too.
if not dist:
try:
pkg_resources.working_set.require(req_name)
except pkg_resources.DistributionNotFound:
return None
return _search_distribution(req_name)
Left for compatibility until direct pkg_resources uses are refactored out.
"""
from pip._internal.metadata import get_environment
from pip._internal.metadata.pkg_resources import Distribution as _Dist
dist = get_environment().get_distribution(req_name)
if dist is None:
return None
return cast(_Dist, dist)._dist


def egg_link_path(dist):
Expand Down

0 comments on commit 8d40580

Please sign in to comment.