Warn when legacy versions and specifiers are resolved

Also warn in pip check.

...
This commit is contained in:
Stéphane Bidoul 2023-04-10 15:59:59 +02:00
parent 9f72cd0269
commit 782cff7e01
7 changed files with 84 additions and 0 deletions

2
news/12063.removal.rst Normal file
View File

@ -0,0 +1,2 @@
Deprecate legacy version and version specifiers that don't conform to `PEP 440
<https://peps.python.org/pep-0440/>`_

View File

@ -7,6 +7,7 @@ from pip._internal.cli.status_codes import ERROR, SUCCESS
from pip._internal.operations.check import (
check_package_set,
create_package_set_from_installed,
warn_legacy_versions_and_specifiers,
)
from pip._internal.utils.misc import write_output
@ -21,6 +22,7 @@ class CheckCommand(Command):
def run(self, options: Values, args: List[str]) -> int:
package_set, parsing_probs = create_package_set_from_installed()
warn_legacy_versions_and_specifiers(package_set)
missing, conflicting = check_package_set(package_set)
for project_name in missing:

View File

@ -130,6 +130,7 @@ class DownloadCommand(RequirementCommand):
self.trace_basic_info(finder)
requirement_set = resolver.resolve(reqs, check_supported_wheels=True)
requirement_set.warn_legacy_versions_and_specifiers()
downloaded: List[str] = []
for req in requirement_set.requirements.values():

View File

@ -387,6 +387,9 @@ class InstallCommand(RequirementCommand):
json.dump(report.to_dict(), f, indent=2, ensure_ascii=False)
if options.dry_run:
# In non dry-run mode, the legacy versions and specifiers check
# will be done as part of conflict detection.
requirement_set.warn_legacy_versions_and_specifiers()
would_install_items = sorted(
(r.metadata["name"], r.metadata["version"])
for r in requirement_set.requirements_to_install

View File

@ -145,6 +145,7 @@ class WheelCommand(RequirementCommand):
self.trace_basic_info(finder)
requirement_set = resolver.resolve(reqs, check_supported_wheels=True)
requirement_set.warn_legacy_versions_and_specifiers()
reqs_to_build: List[InstallRequirement] = []
for req in requirement_set.requirements.values():

View File

@ -5,12 +5,15 @@ import logging
from typing import Callable, Dict, List, NamedTuple, Optional, Set, Tuple
from pip._vendor.packaging.requirements import Requirement
from pip._vendor.packaging.specifiers import LegacySpecifier
from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
from pip._vendor.packaging.version import LegacyVersion
from pip._internal.distributions import make_distribution_for_install_requirement
from pip._internal.metadata import get_default_environment
from pip._internal.metadata.base import DistributionVersion
from pip._internal.req.req_install import InstallRequirement
from pip._internal.utils.deprecation import deprecated
logger = logging.getLogger(__name__)
@ -57,6 +60,8 @@ def check_package_set(
package name and returns a boolean.
"""
warn_legacy_versions_and_specifiers(package_set)
missing = {}
conflicting = {}
@ -147,3 +152,36 @@ def _create_whitelist(
break
return packages_affected
def warn_legacy_versions_and_specifiers(package_set: PackageSet) -> None:
for project_name, package_details in package_set.items():
if isinstance(package_details.version, LegacyVersion):
deprecated(
reason=(
f"{project_name} {package_details.version} "
f"has a non-standard version number."
),
replacement=(
f"to upgrade to a newer version of {project_name} "
f"or contact the author to suggest that they "
f"release a version with a conforming version number"
),
issue=12063,
gone_in="23.3",
)
for dep in package_details.dependencies:
if any(isinstance(spec, LegacySpecifier) for spec in dep.specifier):
deprecated(
reason=(
f"{project_name} {package_details.version} "
f"has a non-standard dependency specifier {dep}."
),
replacement=(
f"to upgrade to a newer version of {project_name} "
f"or contact the author to suggest that they "
f"release a version with a conforming dependency specifiers"
),
issue=12063,
gone_in="23.3",
)

View File

@ -2,9 +2,12 @@ import logging
from collections import OrderedDict
from typing import Dict, List
from pip._vendor.packaging.specifiers import LegacySpecifier
from pip._vendor.packaging.utils import canonicalize_name
from pip._vendor.packaging.version import LegacyVersion
from pip._internal.req.req_install import InstallRequirement
from pip._internal.utils.deprecation import deprecated
logger = logging.getLogger(__name__)
@ -80,3 +83,37 @@ class RequirementSet:
for install_req in self.all_requirements
if not install_req.constraint and not install_req.satisfied_by
]
def warn_legacy_versions_and_specifiers(self) -> None:
for req in self.requirements_to_install:
version = req.get_dist().version
if isinstance(version, LegacyVersion):
deprecated(
reason=(
f"pip has selected the non standard version {version} "
f"of {req}. In the future this version will be "
f"ignored as it isn't standard compliant."
),
replacement=(
"set or update constraints to select another version "
"or contact the package author to fix the version number"
),
issue=12063,
gone_in="23.3",
)
for dep in req.get_dist().iter_dependencies():
if any(isinstance(spec, LegacySpecifier) for spec in dep.specifier):
deprecated(
reason=(
f"pip has selected {req} {version} which has non "
f"standard dependency specifier {dep}. "
f"In the future this version of {req} will be "
f"ignored as it isn't standard compliant."
),
replacement=(
"set or update constraints to select another version "
"or contact the package author to fix the version number"
),
issue=12063,
gone_in="23.3",
)