Skip to content

Commit ad7f93a

Browse files
committed
Move dist-related logic from misc into metadata
1 parent 02651df commit ad7f93a

File tree

3 files changed

+65
-102
lines changed

3 files changed

+65
-102
lines changed

src/pip/_internal/metadata/base.py

+13
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,12 @@ def from_paths(cls, paths):
4949

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

5455
def iter_distributions(self):
5556
# type: () -> Iterator[BaseDistribution]
57+
"""Iterate through installed distributions."""
5658
raise NotImplementedError()
5759

5860
def iter_installed_distributions(
@@ -64,6 +66,17 @@ def iter_installed_distributions(
6466
user_only=False, # type: bool
6567
):
6668
# type: (...) -> Iterator[BaseDistribution]
69+
"""Return a list of installed distributions.
70+
71+
:param local_only: If True (default), only return installations
72+
local to the current virtualenv, if in a virtualenv.
73+
:param skip: An iterable of canonicalized project names to ignore;
74+
defaults to ``stdlib_pkgs``.
75+
:param include_editables: If False, don't report editables.
76+
:param editables_only: If True, only report editables.
77+
:param user_only: If True, only report installations in the user
78+
site directory.
79+
"""
6780
it = self.iter_distributions()
6881
if local_only:
6982
it = (d for d in it if d.local)

src/pip/_internal/metadata/pkg_resources.py

+31-3
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,42 @@ def from_paths(cls, paths):
6161
# type: (List[str]) -> BaseEnvironment
6262
return cls(pkg_resources.WorkingSet(paths))
6363

64+
def _search_distribution(self, name):
65+
"""Find a distribution matching the ``name`` in the environment.
66+
67+
This searches from *all* distributions available in the environment, to
68+
match the behavior of ``pkg_resources.get_distribution()``.
69+
"""
70+
# type: (str) -> Optional[BaseDistribution]
71+
canonical_name = canonicalize_name(name)
72+
for dist in self.iter_distributions():
73+
if dist.canonical_name == canonical_name:
74+
return dist
75+
return None
76+
6477
def get_distribution(self, name):
6578
# type: (str) -> Optional[BaseDistribution]
66-
req = pkg_resources.Requirement(name)
79+
80+
# Search the distribution by looking through the working set.
81+
dist = self._search_distribution(name)
82+
if dist:
83+
return dist
84+
85+
# If distribution could not be found, call working_set.require to
86+
# update the working set, and try to find the distribution again.
87+
# This might happen for e.g. when you install a package twice, once
88+
# using setup.py develop and again using setup.py install. Now when
89+
# running pip uninstall twice, the package gets removed from the
90+
# working set in the first uninstall, so we have to populate the
91+
# working set again so that pip knows about it and the packages gets
92+
# picked up and is successfully uninstalled the second time too.
6793
try:
68-
dist = self._ws.find(req)
94+
# We didn't pass in any version specifiers, so this can never
95+
# raise pkg_resources.VersionConflict.
96+
self._ws.require(name)
6997
except pkg_resources.DistributionNotFound:
7098
return None
71-
return Distribution(dist)
99+
return self._search_distribution(name)
72100

73101
def iter_distributions(self):
74102
# type: () -> Iterator[BaseDistribution]

src/pip/_internal/utils/misc.py

+21-99
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
from itertools import filterfalse, tee, zip_longest
1919

2020
from pip._vendor import pkg_resources
21-
from pip._vendor.packaging.utils import canonicalize_name
2221

2322
# NOTE: retrying is not annotated in typeshed as on 2017-07-17, which is
2423
# why we ignore the type on this import.
@@ -402,86 +401,20 @@ def get_installed_distributions(
402401
paths=None # type: Optional[List[str]]
403402
):
404403
# type: (...) -> List[Distribution]
405-
"""
406-
Return a list of installed Distribution objects.
407-
408-
If ``local_only`` is True (default), only return installations
409-
local to the current virtualenv, if in a virtualenv.
410-
411-
``skip`` argument is an iterable of lower-case project names to
412-
ignore; defaults to stdlib_pkgs
413-
414-
If ``include_editables`` is False, don't report editables.
415-
416-
If ``editables_only`` is True , only report editables.
417-
418-
If ``user_only`` is True , only report installations in the user
419-
site directory.
420-
421-
If ``paths`` is set, only report the distributions present at the
422-
specified list of locations.
423-
"""
424-
if paths:
425-
working_set = pkg_resources.WorkingSet(paths)
426-
else:
427-
working_set = pkg_resources.working_set
428-
429-
if local_only:
430-
local_test = dist_is_local
431-
else:
432-
def local_test(d):
433-
return True
434-
435-
if include_editables:
436-
def editable_test(d):
437-
return True
438-
else:
439-
def editable_test(d):
440-
return not dist_is_editable(d)
441-
442-
if editables_only:
443-
def editables_only_test(d):
444-
return dist_is_editable(d)
445-
else:
446-
def editables_only_test(d):
447-
return True
448-
449-
if user_only:
450-
user_test = dist_in_usersite
451-
else:
452-
def user_test(d):
453-
return True
454-
455-
return [d for d in working_set
456-
if local_test(d) and
457-
d.key not in skip and
458-
editable_test(d) and
459-
editables_only_test(d) and
460-
user_test(d)
461-
]
462-
463-
464-
def _search_distribution(req_name):
465-
# type: (str) -> Optional[Distribution]
466-
"""Find a distribution matching the ``req_name`` in the environment.
467-
468-
This searches from *all* distributions available in the environment, to
469-
match the behavior of ``pkg_resources.get_distribution()``.
470-
"""
471-
# Canonicalize the name before searching in the list of
472-
# installed distributions and also while creating the package
473-
# dictionary to get the Distribution object
474-
req_name = canonicalize_name(req_name)
475-
packages = get_installed_distributions(
476-
local_only=False,
477-
skip=(),
478-
include_editables=True,
479-
editables_only=False,
480-
user_only=False,
481-
paths=None,
404+
"""Return a list of installed Distribution objects.
405+
406+
Left for compatibility until direct pkg_resources uses are refactored out.
407+
"""
408+
from pip._internal.metadata import get_environment
409+
from pip._internal.metadata.pkg_resources import Distribution as _Dist
410+
dists = get_environment(paths).iter_installed_distributions(
411+
local_only=local_only,
412+
skip=skip,
413+
include_editables=include_editables,
414+
editables_only=editables_only,
415+
user_only=user_only,
482416
)
483-
pkg_dict = {canonicalize_name(p.key): p for p in packages}
484-
return pkg_dict.get(req_name)
417+
return [cast(_Dist, dist)._dist for dist in dists]
485418

486419

487420
def get_distribution(req_name):
@@ -490,26 +423,15 @@ def get_distribution(req_name):
490423
491424
This searches from *all* distributions available in the environment, to
492425
match the behavior of ``pkg_resources.get_distribution()``.
493-
"""
494426
495-
# Search the distribution by looking through the working set
496-
dist = _search_distribution(req_name)
497-
498-
# If distribution could not be found, call working_set.require
499-
# to update the working set, and try to find the distribution
500-
# again.
501-
# This might happen for e.g. when you install a package
502-
# twice, once using setup.py develop and again using setup.py install.
503-
# Now when run pip uninstall twice, the package gets removed
504-
# from the working set in the first uninstall, so we have to populate
505-
# the working set again so that pip knows about it and the packages
506-
# gets picked up and is successfully uninstalled the second time too.
507-
if not dist:
508-
try:
509-
pkg_resources.working_set.require(req_name)
510-
except pkg_resources.DistributionNotFound:
511-
return None
512-
return _search_distribution(req_name)
427+
Left for compatibility until direct pkg_resources uses are refactored out.
428+
"""
429+
from pip._internal.metadata import get_environment
430+
from pip._internal.metadata.pkg_resources import Distribution as _Dist
431+
dist = get_environment().get_distribution(req_name)
432+
if dist is None:
433+
return None
434+
return cast(_Dist, dist)._dist
513435

514436

515437
def egg_link_path(dist):

0 commit comments

Comments
 (0)