-
-
Notifications
You must be signed in to change notification settings - Fork 270
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Attempt "cross-builds" of sdists for foreign platforms. (#2075)
The "cross-building" is in scare-quotes because this is not actually cross-building, it is just attempting a build of the sdist, and, if successful, seeing if the resulting wheel matches the foreign platform. This enables sdist-only projects to be used in foreign platform Pex operations when it turns out the sdist is multi-platform. Closes #2073
- Loading branch information
Showing
20 changed files
with
660 additions
and
450 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# Copyright 2023 Pants project contributors (see CONTRIBUTORS.md). | ||
# Licensed under the Apache License, Version 2.0 (see LICENSE). | ||
|
||
from __future__ import absolute_import | ||
|
||
import json | ||
import os | ||
|
||
|
||
def patch(): | ||
from pip._vendor.packaging import markers # type: ignore[import] | ||
|
||
# N.B.: The following environment variable is used by the Pex runtime to control Pip and must be | ||
# kept in-sync with `__init__.py`. | ||
patched_markers_file = os.environ.pop("_PEX_PATCHED_MARKERS_FILE") | ||
with open(patched_markers_file) as fp: | ||
patched_markers = json.load(fp) | ||
|
||
markers.default_environment = patched_markers.copy |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
# Copyright 2023 Pants project contributors (see CONTRIBUTORS.md). | ||
# Licensed under the Apache License, Version 2.0 (see LICENSE). | ||
|
||
from __future__ import absolute_import | ||
|
||
import json | ||
import os | ||
|
||
# N.B.: The following environment variable is used by the Pex runtime to control Pip and must be | ||
# kept in-sync with `__init__.py`. | ||
with open(os.environ.pop("_PEX_PYTHON_VERSIONS_FILE")) as fp: | ||
PYTHON_FULL_VERSIONS = json.load(fp) | ||
PYTHON_VERSIONS = sorted(set((version[0], version[1]) for version in PYTHON_FULL_VERSIONS)) | ||
|
||
|
||
def patch(): | ||
# The pip-legacy-resolver patch. | ||
from pip._internal.utils import packaging # type: ignore[import] | ||
|
||
if PYTHON_FULL_VERSIONS: | ||
orig_check_requires_python = packaging.check_requires_python | ||
|
||
def check_requires_python(requires_python, *_args, **_kw): | ||
# Ensure any dependency we lock is compatible with the full interpreter range | ||
# specified since we have no way to force Pip to backtrack and follow paths for any | ||
# divergences. Most (all?) true divergences should be covered by forked environment | ||
# markers. | ||
return all( | ||
orig_check_requires_python(requires_python, python_full_version) | ||
for python_full_version in PYTHON_FULL_VERSIONS | ||
) | ||
|
||
packaging.check_requires_python = check_requires_python | ||
else: | ||
packaging.check_requires_python = lambda *_args, **_kw: True | ||
|
||
# The pip-2020-resolver patch. | ||
from pip._internal.resolution.resolvelib.candidates import ( # type: ignore[import] | ||
RequiresPythonCandidate, | ||
) | ||
from pip._internal.resolution.resolvelib.requirements import ( # type: ignore[import] | ||
RequiresPythonRequirement, | ||
) | ||
|
||
if PYTHON_FULL_VERSIONS: | ||
orig_get_candidate_lookup = RequiresPythonRequirement.get_candidate_lookup | ||
orig_is_satisfied_by = RequiresPythonRequirement.is_satisfied_by | ||
|
||
# Ensure we do a proper, but minimal, comparison for Python versions. Previously we | ||
# always tested all `Requires-Python` specifier sets against Python full versions. | ||
# That can be pathologically slow (see: | ||
# https://github.com/pantsbuild/pants/issues/14998); so we avoid using Python full | ||
# versions unless the `Requires-Python` specifier set requires that data. In other | ||
# words: | ||
# | ||
# Need full versions to evaluate properly: | ||
# + Requires-Python: >=3.7.6 | ||
# + Requires-Python: >=3.7,!=3.7.6,<4 | ||
# | ||
# Do not need full versions to evaluate properly: | ||
# + Requires-Python: >=3.7,<4 | ||
# + Requires-Python: ==3.7.* | ||
# + Requires-Python: >=3.6.0 | ||
# | ||
def needs_full_versions(spec): | ||
components = spec.version.split(".", 2) | ||
if len(components) < 3: | ||
return False | ||
major_, minor_, patch = components | ||
if spec.operator in ("<", "<=", ">", ">=") and patch == "0": | ||
return False | ||
return patch != "*" | ||
|
||
def _py_versions(self): | ||
if not hasattr(self, "__py_versions"): | ||
self.__py_versions = ( | ||
version | ||
for version in ( | ||
PYTHON_FULL_VERSIONS | ||
if any(needs_full_versions(spec) for spec in self.specifier) | ||
else PYTHON_VERSIONS | ||
) | ||
if ".".join(map(str, version)) in self.specifier | ||
) | ||
return self.__py_versions | ||
|
||
def get_candidate_lookup(self): | ||
for py_version in self._py_versions(): | ||
delegate = RequiresPythonRequirement( | ||
self.specifier, RequiresPythonCandidate(py_version) | ||
) | ||
candidate_lookup = orig_get_candidate_lookup(delegate) | ||
if candidate_lookup != (None, None): | ||
return candidate_lookup | ||
return None, None | ||
|
||
def is_satisfied_by(self, *_args, **_kw): | ||
# Ensure any dependency we lock is compatible with the full interpreter range | ||
# specified since we have no way to force Pip to backtrack and follow paths for any | ||
# divergences. Most (all?) true divergences should be covered by forked environment | ||
# markers. | ||
return all( | ||
orig_is_satisfied_by(self, RequiresPythonCandidate(py_version)) | ||
for py_version in self._py_versions() | ||
) | ||
|
||
RequiresPythonRequirement._py_versions = _py_versions | ||
RequiresPythonRequirement.get_candidate_lookup = get_candidate_lookup | ||
RequiresPythonRequirement.is_satisfied_by = is_satisfied_by | ||
else: | ||
RequiresPythonRequirement.get_candidate_lookup = lambda self: (self._candidate, None) | ||
RequiresPythonRequirement.is_satisfied_by = lambda *_args, **_kw: True |
Oops, something went wrong.