diff env/lib/python3.9/site-packages/pip/_internal/wheel_builder.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/env/lib/python3.9/site-packages/pip/_internal/wheel_builder.py	Mon Mar 22 18:12:50 2021 +0000
@@ -0,0 +1,365 @@
+"""Orchestrator for building wheels from InstallRequirements.
+"""
+
+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
+from pip._internal.utils.misc import ensure_dir, hash_file, is_wheel_installed
+from pip._internal.utils.setuptools_build import make_setuptools_clean_args
+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:
+    from typing import Any, Callable, Iterable, List, Optional, Tuple
+
+    from pip._internal.cache import WheelCache
+    from pip._internal.req.req_install import InstallRequirement
+
+    BinaryAllowedPredicate = Callable[[InstallRequirement], bool]
+    BuildResult = Tuple[List[InstallRequirement], List[InstallRequirement]]
+
+logger = logging.getLogger(__name__)
+
+_egg_info_re = re.compile(r'([a-z0-9_.]+)-([a-z0-9_.!+-]+)', re.IGNORECASE)
+
+
+def _contains_egg_info(s):
+    # type: (str) -> bool
+    """Determine whether the string looks like an egg_info.
+
+    :param s: The string to parse. E.g. foo-2.1
+    """
+    return bool(_egg_info_re.search(s))
+
+
+def _should_build(
+    req,  # type: InstallRequirement
+    need_wheel,  # type: bool
+    check_binary_allowed,  # type: BinaryAllowedPredicate
+):
+    # type: (...) -> bool
+    """Return whether an InstallRequirement should be built into a wheel."""
+    if req.constraint:
+        # never build requirements that are merely constraints
+        return False
+    if req.is_wheel:
+        if need_wheel:
+            logger.info(
+                'Skipping %s, due to already being wheel.', req.name,
+            )
+        return False
+
+    if need_wheel:
+        # i.e. pip wheel, not pip install
+        return True
+
+    # From this point, this concerns the pip install command only
+    # (need_wheel=False).
+
+    if req.editable or not req.source_dir:
+        return False
+
+    if req.use_pep517:
+        return True
+
+    if not check_binary_allowed(req):
+        logger.info(
+            "Skipping wheel build for %s, due to binaries "
+            "being disabled for it.", req.name,
+        )
+        return False
+
+    if not is_wheel_installed():
+        # we don't build legacy requirements if wheel is not installed
+        logger.info(
+            "Using legacy 'setup.py install' for %s, "
+            "since package 'wheel' is not installed.", req.name,
+        )
+        return False
+
+    return True
+
+
+def should_build_for_wheel_command(
+    req,  # type: InstallRequirement
+):
+    # type: (...) -> bool
+    return _should_build(
+        req, need_wheel=True, check_binary_allowed=_always_true
+    )
+
+
+def should_build_for_install_command(
+    req,  # type: InstallRequirement
+    check_binary_allowed,  # type: BinaryAllowedPredicate
+):
+    # type: (...) -> bool
+    return _should_build(
+        req, need_wheel=False, check_binary_allowed=check_binary_allowed
+    )
+
+
+def _should_cache(
+    req,  # type: InstallRequirement
+):
+    # type: (...) -> Optional[bool]
+    """
+    Return whether a built InstallRequirement can be stored in the persistent
+    wheel cache, assuming the wheel cache is available, and _should_build()
+    has determined a wheel needs to be built.
+    """
+    if req.editable or not req.source_dir:
+        # never cache editable requirements
+        return False
+
+    if req.link and req.link.is_vcs:
+        # VCS checkout. Do not cache
+        # unless it points to an immutable commit hash.
+        assert not req.editable
+        assert req.source_dir
+        vcs_backend = vcs.get_backend_for_scheme(req.link.scheme)
+        assert vcs_backend
+        if vcs_backend.is_immutable_rev_checkout(req.link.url, req.source_dir):
+            return True
+        return False
+
+    assert req.link
+    base, ext = req.link.splitext()
+    if _contains_egg_info(base):
+        return True
+
+    # Otherwise, do not cache.
+    return False
+
+
+def _get_cache_dir(
+    req,  # type: InstallRequirement
+    wheel_cache,  # type: WheelCache
+):
+    # type: (...) -> str
+    """Return the persistent or temporary cache directory where the built
+    wheel need to be stored.
+    """
+    cache_available = bool(wheel_cache.cache_dir)
+    assert req.link
+    if cache_available and _should_cache(req):
+        cache_dir = wheel_cache.get_path_for_link(req.link)
+    else:
+        cache_dir = wheel_cache.get_ephem_path_for_link(req.link)
+    return cache_dir
+
+
+def _always_true(_):
+    # type: (Any) -> bool
+    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]
+):
+    # type: (...) -> Optional[str]
+    """Build one wheel.
+
+    :return: The filename of the built wheel, or None if the build failed.
+    """
+    try:
+        ensure_dir(output_dir)
+    except OSError as e:
+        logger.warning(
+            "Building wheel for %s failed: %s",
+            req.name, e,
+        )
+        return None
+
+    # Install build deps into temporary directory (PEP 518)
+    with req.build_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(
+    req,  # type: InstallRequirement
+    output_dir,  # type: str
+    build_options,  # type: List[str]
+    global_options,  # type: List[str]
+):
+    # type: (...) -> Optional[str]
+    with TempDirectory(kind="wheel") as temp_dir:
+        assert req.name
+        if req.use_pep517:
+            assert req.metadata_directory
+            wheel_path = build_wheel_pep517(
+                name=req.name,
+                backend=req.pep517_backend,
+                metadata_directory=req.metadata_directory,
+                build_options=build_options,
+                tempd=temp_dir.path,
+            )
+        else:
+            wheel_path = build_wheel_legacy(
+                name=req.name,
+                setup_py_path=req.setup_py_path,
+                source_dir=req.unpacked_source_directory,
+                global_options=global_options,
+                build_options=build_options,
+                tempd=temp_dir.path,
+            )
+
+        if wheel_path is not None:
+            wheel_name = os.path.basename(wheel_path)
+            dest_path = os.path.join(output_dir, wheel_name)
+            try:
+                wheel_hash, length = hash_file(wheel_path)
+                shutil.move(wheel_path, dest_path)
+                logger.info('Created wheel for %s: '
+                            'filename=%s size=%d sha256=%s',
+                            req.name, wheel_name, length,
+                            wheel_hash.hexdigest())
+                logger.info('Stored in directory: %s', output_dir)
+                return dest_path
+            except Exception as e:
+                logger.warning(
+                    "Building wheel for %s failed: %s",
+                    req.name, e,
+                )
+        # Ignore return, we can't do anything else useful.
+        if not req.use_pep517:
+            _clean_one_legacy(req, global_options)
+        return None
+
+
+def _clean_one_legacy(req, global_options):
+    # type: (InstallRequirement, List[str]) -> bool
+    clean_args = make_setuptools_clean_args(
+        req.setup_py_path,
+        global_options=global_options,
+    )
+
+    logger.info('Running setup.py clean for %s', req.name)
+    try:
+        call_subprocess(clean_args, cwd=req.source_dir)
+        return True
+    except Exception:
+        logger.error('Failed cleaning build dir for %s', req.name)
+        return False
+
+
+def build(
+    requirements,  # type: Iterable[InstallRequirement]
+    wheel_cache,  # type: WheelCache
+    verify,  # type: bool
+    build_options,  # type: List[str]
+    global_options,  # type: List[str]
+):
+    # type: (...) -> BuildResult
+    """Build wheels.
+
+    :return: The list of InstallRequirement that succeeded to build and
+        the list of InstallRequirement that failed to build.
+    """
+    if not requirements:
+        return [], []
+
+    # Build the wheels.
+    logger.info(
+        'Building wheels for collected packages: %s',
+        ', '.join(req.name for req in requirements),  # type: ignore
+    )
+
+    with indent_log():
+        build_successes, build_failures = [], []
+        for req in requirements:
+            cache_dir = _get_cache_dir(req, wheel_cache)
+            wheel_file = _build_one(
+                req, cache_dir, verify, build_options, global_options
+            )
+            if wheel_file:
+                # Update the link for this.
+                req.link = Link(path_to_url(wheel_file))
+                req.local_file_path = req.link.file_path
+                assert req.link.is_wheel
+                build_successes.append(req)
+            else:
+                build_failures.append(req)
+
+    # notify success/failure
+    if build_successes:
+        logger.info(
+            'Successfully built %s',
+            ' '.join([req.name for req in build_successes]),  # type: ignore
+        )
+    if build_failures:
+        logger.info(
+            'Failed to build %s',
+            ' '.join([req.name for req in build_failures]),  # type: ignore
+        )
+    # Return a list of requirements that failed to build
+    return build_successes, build_failures