Skip to content

Commit cf7d7f8

Browse files
committed
Remove get_installed_distributions usages
The function itself is kept for now because it's currently used to test the pip.metadata subpackage...
1 parent 23be323 commit cf7d7f8

File tree

8 files changed

+79
-60
lines changed

8 files changed

+79
-60
lines changed

src/pip/_internal/cli/autocompletion.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
from pip._internal.cli.main_parser import create_main_parser
1111
from pip._internal.commands import commands_dict, create_command
12-
from pip._internal.utils.misc import get_installed_distributions
12+
from pip._internal.metadata import get_default_environment
1313

1414

1515
def autocomplete() -> None:
@@ -45,11 +45,13 @@ def autocomplete() -> None:
4545
"uninstall",
4646
]
4747
if should_list_installed:
48+
env = get_default_environment()
4849
lc = current.lower()
4950
installed = [
50-
dist.key
51-
for dist in get_installed_distributions(local_only=True)
52-
if dist.key.startswith(lc) and dist.key not in cwords[1:]
51+
dist.canonical_name
52+
for dist in env.iter_installed_distributions(local_only=True)
53+
if dist.canonical_name.startswith(lc)
54+
and dist.canonical_name not in cwords[1:]
5355
]
5456
# if there are no dists installed, fall back to option completion
5557
if installed:

src/pip/_internal/commands/list.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ def get_not_required(self, packages, options):
199199
dep_keys = {
200200
canonicalize_name(dep.name)
201201
for dist in packages
202-
for dep in dist.iter_dependencies()
202+
for dep in (dist.iter_dependencies() or ())
203203
}
204204

205205
# Create a set to remove duplicate packages, and cast it to a list

src/pip/_internal/metadata/base.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525

2626
if TYPE_CHECKING:
2727
from typing import Protocol
28+
29+
from pip._vendor.packaging.utils import NormalizedName
2830
else:
2931
Protocol = object
3032

@@ -59,7 +61,7 @@ def location(self) -> Optional[str]:
5961
raise NotImplementedError()
6062

6163
@property
62-
def canonical_name(self) -> str:
64+
def canonical_name(self) -> "NormalizedName":
6365
raise NotImplementedError()
6466

6567
@property
@@ -108,6 +110,10 @@ def local(self) -> bool:
108110
def in_usersite(self) -> bool:
109111
raise NotImplementedError()
110112

113+
@property
114+
def in_site_packages(self) -> bool:
115+
raise NotImplementedError()
116+
111117
def read_text(self, name: str) -> str:
112118
"""Read a file in the .dist-info (or .egg-info) directory.
113119

src/pip/_internal/metadata/pkg_resources.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
import email.message
22
import logging
33
import zipfile
4-
from typing import Collection, Iterable, Iterator, List, NamedTuple, Optional
4+
from typing import (
5+
TYPE_CHECKING,
6+
Collection,
7+
Iterable,
8+
Iterator,
9+
List,
10+
NamedTuple,
11+
Optional,
12+
)
513

614
from pip._vendor import pkg_resources
715
from pip._vendor.packaging.requirements import Requirement
@@ -14,6 +22,9 @@
1422

1523
from .base import BaseDistribution, BaseEntryPoint, BaseEnvironment, DistributionVersion
1624

25+
if TYPE_CHECKING:
26+
from pip._vendor.packaging.utils import NormalizedName
27+
1728
logger = logging.getLogger(__name__)
1829

1930

@@ -38,7 +49,7 @@ def location(self) -> Optional[str]:
3849
return self._dist.location
3950

4051
@property
41-
def canonical_name(self) -> str:
52+
def canonical_name(self) -> "NormalizedName":
4253
return canonicalize_name(self._dist.project_name)
4354

4455
@property
@@ -61,6 +72,10 @@ def local(self) -> bool:
6172
def in_usersite(self) -> bool:
6273
return misc.dist_in_usersite(self._dist)
6374

75+
@property
76+
def in_site_packages(self) -> bool:
77+
return misc.dist_in_site_packages(self._dist)
78+
6479
def read_text(self, name: str) -> str:
6580
if not self._dist.has_metadata(name):
6681
raise FileNotFoundError(name)

src/pip/_internal/operations/check.py

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,49 +2,50 @@
22
"""
33

44
import logging
5-
from collections import namedtuple
6-
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Set, Tuple
5+
from typing import TYPE_CHECKING, Callable, Dict, List, NamedTuple, Optional, Set, Tuple
76

7+
from pip._vendor.packaging.requirements import Requirement
88
from pip._vendor.packaging.utils import canonicalize_name
9-
from pip._vendor.pkg_resources import RequirementParseError
109

1110
from pip._internal.distributions import make_distribution_for_install_requirement
11+
from pip._internal.metadata import get_default_environment
12+
from pip._internal.metadata.base import DistributionVersion
1213
from pip._internal.req.req_install import InstallRequirement
13-
from pip._internal.utils.misc import get_installed_distributions
1414

1515
if TYPE_CHECKING:
1616
from pip._vendor.packaging.utils import NormalizedName
1717

1818
logger = logging.getLogger(__name__)
1919

20+
21+
class PackageDetails(NamedTuple):
22+
version: DistributionVersion
23+
dependencies: List[Requirement]
24+
25+
2026
# Shorthands
21-
PackageSet = Dict['NormalizedName', 'PackageDetails']
22-
Missing = Tuple[str, Any]
23-
Conflicting = Tuple[str, str, Any]
27+
PackageSet = Dict['NormalizedName', PackageDetails]
28+
Missing = Tuple['NormalizedName', Requirement]
29+
Conflicting = Tuple['NormalizedName', DistributionVersion, Requirement]
2430

2531
MissingDict = Dict['NormalizedName', List[Missing]]
2632
ConflictingDict = Dict['NormalizedName', List[Conflicting]]
2733
CheckResult = Tuple[MissingDict, ConflictingDict]
2834
ConflictDetails = Tuple[PackageSet, CheckResult]
2935

30-
PackageDetails = namedtuple('PackageDetails', ['version', 'requires'])
31-
32-
33-
def create_package_set_from_installed(**kwargs: Any) -> Tuple["PackageSet", bool]:
34-
"""Converts a list of distributions into a PackageSet.
35-
"""
36-
# Default to using all packages installed on the system
37-
if kwargs == {}:
38-
kwargs = {"local_only": False, "skip": ()}
3936

37+
def create_package_set_from_installed() -> Tuple[PackageSet, bool]:
38+
"""Converts a list of distributions into a PackageSet."""
4039
package_set = {}
4140
problems = False
42-
for dist in get_installed_distributions(**kwargs):
43-
name = canonicalize_name(dist.project_name)
41+
env = get_default_environment()
42+
for dist in env.iter_installed_distributions(local_only=False, skip=()):
43+
name = dist.canonical_name
4444
try:
45-
package_set[name] = PackageDetails(dist.version, dist.requires())
46-
except (OSError, RequirementParseError) as e:
47-
# Don't crash on unreadable or broken metadata
45+
dependencies = list(dist.iter_dependencies())
46+
package_set[name] = PackageDetails(dist.version, dependencies)
47+
except (OSError, ValueError) as e:
48+
# Don't crash on unreadable or broken metadata.
4849
logger.warning("Error parsing requirements for %s: %s", name, e)
4950
problems = True
5051
return package_set, problems
@@ -69,8 +70,8 @@ def check_package_set(package_set, should_ignore=None):
6970
if should_ignore and should_ignore(package_name):
7071
continue
7172

72-
for req in package_detail.requires:
73-
name = canonicalize_name(req.project_name)
73+
for req in package_detail.dependencies:
74+
name = canonicalize_name(req.name)
7475

7576
# Check if it's missing
7677
if name not in package_set:
@@ -82,7 +83,7 @@ def check_package_set(package_set, should_ignore=None):
8283
continue
8384

8485
# Check if there's a conflict
85-
version = package_set[name].version # type: str
86+
version = package_set[name].version
8687
if not req.specifier.contains(version, prereleases=True):
8788
conflicting_deps.add((name, version, req))
8889

@@ -129,8 +130,11 @@ def _simulate_installation_of(to_install, package_set):
129130
dist = abstract_dist.get_pkg_resources_distribution()
130131

131132
assert dist is not None
132-
name = canonicalize_name(dist.key)
133-
package_set[name] = PackageDetails(dist.version, dist.requires())
133+
name = dist.canonical_name
134+
package_set[name] = PackageDetails(
135+
dist.version,
136+
list(dist.iter_dependencies()),
137+
)
134138

135139
installed.add(name)
136140

@@ -145,7 +149,7 @@ def _create_whitelist(would_be_installed, package_set):
145149
if package_name in packages_affected:
146150
continue
147151

148-
for req in package_set[package_name].requires:
152+
for req in package_set[package_name].dependencies:
149153
if canonicalize_name(req.name) in packages_affected:
150154
packages_affected.add(package_name)
151155
break

src/pip/_internal/resolution/resolvelib/factory.py

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
from pip._vendor.packaging.requirements import Requirement as PackagingRequirement
2323
from pip._vendor.packaging.specifiers import SpecifierSet
2424
from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
25-
from pip._vendor.pkg_resources import Distribution
2625
from pip._vendor.resolvelib import ResolutionImpossible
2726

2827
from pip._internal.cache import CacheEntry, WheelCache
@@ -35,6 +34,7 @@
3534
UnsupportedWheel,
3635
)
3736
from pip._internal.index.package_finder import PackageFinder
37+
from pip._internal.metadata import BaseDistribution, get_default_environment
3838
from pip._internal.models.link import Link
3939
from pip._internal.models.wheel import Wheel
4040
from pip._internal.operations.prepare import RequirementPreparer
@@ -46,11 +46,6 @@
4646
from pip._internal.resolution.base import InstallRequirementProvider
4747
from pip._internal.utils.compatibility_tags import get_supported
4848
from pip._internal.utils.hashes import Hashes
49-
from pip._internal.utils.misc import (
50-
dist_in_site_packages,
51-
dist_in_usersite,
52-
get_installed_distributions,
53-
)
5449
from pip._internal.utils.virtualenv import running_under_virtualenv
5550

5651
from .base import Candidate, CandidateVersion, Constraint, Requirement
@@ -122,9 +117,10 @@ def __init__(
122117
] = {}
123118

124119
if not ignore_installed:
120+
env = get_default_environment()
125121
self._installed_dists = {
126-
canonicalize_name(dist.project_name): dist
127-
for dist in get_installed_distributions(local_only=False)
122+
dist.canonical_name: dist
123+
for dist in env.iter_installed_distributions(local_only=False)
128124
}
129125
else:
130126
self._installed_dists = {}
@@ -155,15 +151,15 @@ def _make_extras_candidate(
155151

156152
def _make_candidate_from_dist(
157153
self,
158-
dist: Distribution,
154+
dist: BaseDistribution,
159155
extras: FrozenSet[str],
160156
template: InstallRequirement,
161157
) -> Candidate:
162158
try:
163-
base = self._installed_candidate_cache[dist.key]
159+
base = self._installed_candidate_cache[dist.canonical_name]
164160
except KeyError:
165161
base = AlreadyInstalledCandidate(dist, template, factory=self)
166-
self._installed_candidate_cache[dist.key] = base
162+
self._installed_candidate_cache[dist.canonical_name] = base
167163
if not extras:
168164
return base
169165
return self._make_extras_candidate(base, extras)
@@ -520,7 +516,7 @@ def get_wheel_cache_entry(
520516
supported_tags=get_supported(),
521517
)
522518

523-
def get_dist_to_uninstall(self, candidate: Candidate) -> Optional[Distribution]:
519+
def get_dist_to_uninstall(self, candidate: Candidate) -> Optional[BaseDistribution]:
524520
# TODO: Are there more cases this needs to return True? Editable?
525521
dist = self._installed_dists.get(candidate.project_name)
526522
if dist is None: # Not installed, no uninstallation required.
@@ -533,21 +529,19 @@ def get_dist_to_uninstall(self, candidate: Candidate) -> Optional[Distribution]:
533529
return dist
534530

535531
# We're installing into user site. Remove the user site installation.
536-
if dist_in_usersite(dist):
532+
if dist.in_usersite:
537533
return dist
538534

539535
# We're installing into user site, but the installed incompatible
540536
# package is in global site. We can't uninstall that, and would let
541537
# the new user installation to "shadow" it. But shadowing won't work
542538
# in virtual environments, so we error out.
543-
if running_under_virtualenv() and dist_in_site_packages(dist):
544-
raise InstallationError(
545-
"Will not install to the user site because it will "
546-
"lack sys.path precedence to {} in {}".format(
547-
dist.project_name,
548-
dist.location,
549-
)
539+
if running_under_virtualenv() and dist.in_site_packages:
540+
message = (
541+
f"Will not install to the user site because it will lack "
542+
f"sys.path precedence to {dist.raw_name} in {dist.location}"
550543
)
544+
raise InstallationError(message)
551545
return None
552546

553547
def _report_requires_python_error(

src/pip/_internal/resolution/resolvelib/resolver.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple, cast
55

66
from pip._vendor.packaging.utils import canonicalize_name
7-
from pip._vendor.packaging.version import parse as parse_version
87
from pip._vendor.resolvelib import BaseReporter, ResolutionImpossible
98
from pip._vendor.resolvelib import Resolver as RLResolver
109
from pip._vendor.resolvelib.structs import DirectedGraph
@@ -22,7 +21,6 @@
2221
)
2322
from pip._internal.utils.deprecation import deprecated
2423
from pip._internal.utils.filetypes import is_archive_file
25-
from pip._internal.utils.misc import dist_is_editable
2624

2725
from .base import Candidate, Requirement
2826
from .factory import Factory
@@ -119,10 +117,10 @@ def resolve(
119117
elif self.factory.force_reinstall:
120118
# The --force-reinstall flag is set -- reinstall.
121119
ireq.should_reinstall = True
122-
elif parse_version(installed_dist.version) != candidate.version:
120+
elif installed_dist.version != candidate.version:
123121
# The installation is different in version -- reinstall.
124122
ireq.should_reinstall = True
125-
elif candidate.is_editable or dist_is_editable(installed_dist):
123+
elif candidate.is_editable or installed_dist.editable:
126124
# The incoming distribution is editable, or different in
127125
# editable-ness to installation -- reinstall.
128126
ireq.should_reinstall = True

tests/unit/test_metadata.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from unittest.mock import patch
22

3-
from pip._internal.models.direct_url import DIRECT_URL_METADATA_NAME, ArchiveInfo
43
from pip._internal.metadata import BaseDistribution
4+
from pip._internal.models.direct_url import DIRECT_URL_METADATA_NAME, ArchiveInfo
55

66

77
@patch.object(BaseDistribution, "read_text", side_effect=FileNotFoundError)

0 commit comments

Comments
 (0)