mirror of https://github.com/pypa/pip
Merge pull request #9320 from uranusjr/wheel-check-valid
Verify built wheel contains valid metadata
This commit is contained in:
commit
c7419b2aac
|
@ -0,0 +1,3 @@
|
|||
``pip wheel`` now verifies the built wheel contains valid metadata, and can be
|
||||
installed by a subsequent ``pip install``. This can be disabled with
|
||||
``--no-verify``.
|
|
@ -343,6 +343,7 @@ class InstallCommand(RequirementCommand):
|
|||
_, build_failures = build(
|
||||
reqs_to_build,
|
||||
wheel_cache=wheel_cache,
|
||||
verify=True,
|
||||
build_options=[],
|
||||
global_options=[],
|
||||
)
|
||||
|
|
|
@ -77,6 +77,14 @@ class WheelCommand(RequirementCommand):
|
|||
self.cmd_opts.add_option(cmdoptions.build_dir())
|
||||
self.cmd_opts.add_option(cmdoptions.progress_bar())
|
||||
|
||||
self.cmd_opts.add_option(
|
||||
'--no-verify',
|
||||
dest='no_verify',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help="Don't verify if built wheel is valid.",
|
||||
)
|
||||
|
||||
self.cmd_opts.add_option(
|
||||
'--global-option',
|
||||
dest='global_options',
|
||||
|
@ -162,6 +170,7 @@ class WheelCommand(RequirementCommand):
|
|||
build_successes, build_failures = build(
|
||||
reqs_to_build,
|
||||
wheel_cache=wheel_cache,
|
||||
verify=(not options.no_verify),
|
||||
build_options=options.build_options or [],
|
||||
global_options=options.global_options or [],
|
||||
)
|
||||
|
|
|
@ -5,8 +5,15 @@ import logging
|
|||
import os.path
|
||||
import re
|
||||
import shutil
|
||||
import zipfile
|
||||
|
||||
from pip._vendor.packaging.utils import canonicalize_name, canonicalize_version
|
||||
from pip._vendor.packaging.version import InvalidVersion, Version
|
||||
from pip._vendor.pkg_resources import Distribution
|
||||
|
||||
from pip._internal.exceptions import InvalidWheelFilename, UnsupportedWheel
|
||||
from pip._internal.models.link import Link
|
||||
from pip._internal.models.wheel import Wheel
|
||||
from pip._internal.operations.build.wheel import build_wheel_pep517
|
||||
from pip._internal.operations.build.wheel_legacy import build_wheel_legacy
|
||||
from pip._internal.utils.logging import indent_log
|
||||
|
@ -16,6 +23,7 @@ from pip._internal.utils.subprocess import call_subprocess
|
|||
from pip._internal.utils.temp_dir import TempDirectory
|
||||
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
|
||||
from pip._internal.utils.urls import path_to_url
|
||||
from pip._internal.utils.wheel import pkg_resources_distribution_for_wheel
|
||||
from pip._internal.vcs import vcs
|
||||
|
||||
if MYPY_CHECK_RUNNING:
|
||||
|
@ -163,9 +171,49 @@ def _always_true(_):
|
|||
return True
|
||||
|
||||
|
||||
def _get_metadata_version(dist):
|
||||
# type: (Distribution) -> Optional[Version]
|
||||
for line in dist.get_metadata_lines(dist.PKG_INFO):
|
||||
if line.lower().startswith("metadata-version:"):
|
||||
value = line.split(":", 1)[-1].strip()
|
||||
try:
|
||||
return Version(value)
|
||||
except InvalidVersion:
|
||||
msg = "Invalid Metadata-Version: {}".format(value)
|
||||
raise UnsupportedWheel(msg)
|
||||
raise UnsupportedWheel("Missing Metadata-Version")
|
||||
|
||||
|
||||
def _verify_one(req, wheel_path):
|
||||
# type: (InstallRequirement, str) -> None
|
||||
canonical_name = canonicalize_name(req.name)
|
||||
w = Wheel(os.path.basename(wheel_path))
|
||||
if canonicalize_name(w.name) != canonical_name:
|
||||
raise InvalidWheelFilename(
|
||||
"Wheel has unexpected file name: expected {!r}, "
|
||||
"got {!r}".format(canonical_name, w.name),
|
||||
)
|
||||
with zipfile.ZipFile(wheel_path, allowZip64=True) as zf:
|
||||
dist = pkg_resources_distribution_for_wheel(
|
||||
zf, canonical_name, wheel_path,
|
||||
)
|
||||
if canonicalize_version(dist.version) != canonicalize_version(w.version):
|
||||
raise InvalidWheelFilename(
|
||||
"Wheel has unexpected file name: expected {!r}, "
|
||||
"got {!r}".format(dist.version, w.version),
|
||||
)
|
||||
if (_get_metadata_version(dist) >= Version("1.2")
|
||||
and not isinstance(dist.parsed_version, Version)):
|
||||
raise UnsupportedWheel(
|
||||
"Metadata 1.2 mandates PEP 440 version, "
|
||||
"but {!r} is not".format(dist.version)
|
||||
)
|
||||
|
||||
|
||||
def _build_one(
|
||||
req, # type: InstallRequirement
|
||||
output_dir, # type: str
|
||||
verify, # type: bool
|
||||
build_options, # type: List[str]
|
||||
global_options, # type: List[str]
|
||||
):
|
||||
|
@ -185,9 +233,16 @@ def _build_one(
|
|||
|
||||
# Install build deps into temporary directory (PEP 518)
|
||||
with req.build_env:
|
||||
return _build_one_inside_env(
|
||||
wheel_path = _build_one_inside_env(
|
||||
req, output_dir, build_options, global_options
|
||||
)
|
||||
if wheel_path and verify:
|
||||
try:
|
||||
_verify_one(req, wheel_path)
|
||||
except (InvalidWheelFilename, UnsupportedWheel) as e:
|
||||
logger.warning("Built wheel for %s is invalid: %s", req.name, e)
|
||||
return None
|
||||
return wheel_path
|
||||
|
||||
|
||||
def _build_one_inside_env(
|
||||
|
@ -260,6 +315,7 @@ def _clean_one_legacy(req, global_options):
|
|||
def build(
|
||||
requirements, # type: Iterable[InstallRequirement]
|
||||
wheel_cache, # type: WheelCache
|
||||
verify, # type: bool
|
||||
build_options, # type: List[str]
|
||||
global_options, # type: List[str]
|
||||
):
|
||||
|
@ -283,7 +339,7 @@ def build(
|
|||
for req in requirements:
|
||||
cache_dir = _get_cache_dir(req, wheel_cache)
|
||||
wheel_file = _build_one(
|
||||
req, cache_dir, build_options, global_options
|
||||
req, cache_dir, verify, build_options, global_options
|
||||
)
|
||||
if wheel_file:
|
||||
# Update the link for this.
|
||||
|
|
Loading…
Reference in New Issue