Skip to content

Commit b345823

Browse files
committed
Warn when legacy versions and specifiers are resolved
Also warn in pip check. ...
1 parent b0a8317 commit b345823

File tree

7 files changed

+80
-0
lines changed

7 files changed

+80
-0
lines changed

news/12063.removal.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Deprecate legacy version and version specifiers that don't conform to `PEP 440
2+
<https://peps.python.org/pep-0440/>`_

src/pip/_internal/commands/check.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from pip._internal.operations.check import (
88
check_package_set,
99
create_package_set_from_installed,
10+
warn_legacy_versions_and_specifiers,
1011
)
1112
from pip._internal.utils.misc import write_output
1213

@@ -21,6 +22,7 @@ class CheckCommand(Command):
2122

2223
def run(self, options: Values, args: List[str]) -> int:
2324
package_set, parsing_probs = create_package_set_from_installed()
25+
warn_legacy_versions_and_specifiers(package_set)
2426
missing, conflicting = check_package_set(package_set)
2527

2628
for project_name in missing:

src/pip/_internal/commands/download.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ def run(self, options: Values, args: List[str]) -> int:
130130
self.trace_basic_info(finder)
131131

132132
requirement_set = resolver.resolve(reqs, check_supported_wheels=True)
133+
requirement_set.warn_legacy_versions_and_specifiers()
133134

134135
downloaded: List[str] = []
135136
for req in requirement_set.requirements.values():

src/pip/_internal/commands/install.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,9 @@ def run(self, options: Values, args: List[str]) -> int:
387387
json.dump(report.to_dict(), f, indent=2, ensure_ascii=False)
388388

389389
if options.dry_run:
390+
# In non dry-run mode, the legacy versions and specifiers check
391+
# will be done as part of conflict detection.
392+
requirement_set.warn_legacy_versions_and_specifiers()
390393
would_install_items = sorted(
391394
(r.metadata["name"], r.metadata["version"])
392395
for r in requirement_set.requirements_to_install

src/pip/_internal/commands/wheel.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ def run(self, options: Values, args: List[str]) -> int:
145145
self.trace_basic_info(finder)
146146

147147
requirement_set = resolver.resolve(reqs, check_supported_wheels=True)
148+
requirement_set.warn_legacy_versions_and_specifiers()
148149

149150
reqs_to_build: List[InstallRequirement] = []
150151
for req in requirement_set.requirements.values():

src/pip/_internal/operations/check.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55
from typing import Callable, Dict, List, NamedTuple, Optional, Set, Tuple
66

77
from pip._vendor.packaging.requirements import Requirement
8+
from pip._vendor.packaging.specifiers import LegacySpecifier
89
from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
10+
from pip._vendor.packaging.version import LegacyVersion
911

1012
from pip._internal.distributions import make_distribution_for_install_requirement
1113
from pip._internal.metadata import get_default_environment
1214
from pip._internal.metadata.base import DistributionVersion
1315
from pip._internal.req.req_install import InstallRequirement
16+
from pip._internal.utils.deprecation import deprecated
1417

1518
logger = logging.getLogger(__name__)
1619

@@ -57,6 +60,8 @@ def check_package_set(
5760
package name and returns a boolean.
5861
"""
5962

63+
warn_legacy_versions_and_specifiers(package_set)
64+
6065
missing = {}
6166
conflicting = {}
6267

@@ -147,3 +152,34 @@ def _create_whitelist(
147152
break
148153

149154
return packages_affected
155+
156+
157+
def warn_legacy_versions_and_specifiers(package_set: PackageSet) -> None:
158+
for project_name, package_details in package_set.items():
159+
if isinstance(package_details.version, LegacyVersion):
160+
deprecated(
161+
reason=(
162+
f"{project_name} {package_details.version} "
163+
f"has a non-standard version number."
164+
),
165+
replacement=(
166+
f"to upgrade to a newer version of {project_name} "
167+
f"or contact the author to suggest that they "
168+
f"release a version with a conforming version number"
169+
),
170+
gone_in="23.3",
171+
)
172+
for dep in package_details.dependencies:
173+
if any(isinstance(spec, LegacySpecifier) for spec in dep.specifier):
174+
deprecated(
175+
reason=(
176+
f"{project_name} {package_details.version} "
177+
f"has a non-standard dependency specifier {dep}."
178+
),
179+
replacement=(
180+
f"to upgrade to a newer version of {project_name} "
181+
f"or contact the author to suggest that they "
182+
f"release a version with a conforming dependency specifiers"
183+
),
184+
gone_in="23.3",
185+
)

src/pip/_internal/req/req_set.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22
from collections import OrderedDict
33
from typing import Dict, List
44

5+
from pip._vendor.packaging.specifiers import LegacySpecifier
56
from pip._vendor.packaging.utils import canonicalize_name
7+
from pip._vendor.packaging.version import LegacyVersion
68

79
from pip._internal.req.req_install import InstallRequirement
10+
from pip._internal.utils.deprecation import deprecated
811

912
logger = logging.getLogger(__name__)
1013

@@ -80,3 +83,35 @@ def requirements_to_install(self) -> List[InstallRequirement]:
8083
for install_req in self.all_requirements
8184
if not install_req.constraint and not install_req.satisfied_by
8285
]
86+
87+
def warn_legacy_versions_and_specifiers(self) -> None:
88+
for req in self.requirements_to_install:
89+
version = req.get_dist().version
90+
if isinstance(version, LegacyVersion):
91+
deprecated(
92+
reason=(
93+
f"pip has selected the non standard version {version} "
94+
f"of {req}. In the future this version will be "
95+
f"ignored as it isn't standard compliant."
96+
),
97+
replacement=(
98+
"set or update constraints to select another version "
99+
"or contact the package author to fix the version number"
100+
),
101+
gone_in="23.3",
102+
)
103+
for dep in req.get_dist().iter_dependencies():
104+
if isinstance(dep.specifier, LegacySpecifier):
105+
deprecated(
106+
reason=(
107+
f"pip has selected {req} {version} which has non "
108+
f"standard dependency specifier {dep.specifier}. "
109+
f"In the future this version of {req} will be "
110+
f"ignored as it isn't standard compliant."
111+
),
112+
replacement=(
113+
"set or update constraints to select another version "
114+
"or contact the package author to fix the version number"
115+
),
116+
gone_in="23.3",
117+
)

0 commit comments

Comments
 (0)