-
Notifications
You must be signed in to change notification settings - Fork 159
Support PEP 600 platform tags #234
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9c04077
4b35d78
3bf13b0
ca17b79
337a948
9657532
1bba618
5840438
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -150,13 +150,23 @@ class DependencyBuilder(object): | |
| packager. | ||
| """ | ||
|
|
||
| _MANYLINUX_COMPATIBLE_PLATFORM = { | ||
| "any", | ||
| "linux_x86_64", | ||
| "manylinux1_x86_64", | ||
| "manylinux2010_x86_64", | ||
| "manylinux2014_x86_64", | ||
| _ADDITIONAL_COMPATIBLE_PLATFORM = {"any", "linux_x86_64"} | ||
| _MANYLINUX_LEGACY_MAP = { | ||
| "manylinux1_x86_64": "manylinux_2_5_x86_64", | ||
| "manylinux2010_x86_64": "manylinux_2_12_x86_64", | ||
| "manylinux2014_x86_64": "manylinux_2_17_x86_64", | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| } | ||
| # Mapping of abi to glibc version in Lambda runtime. | ||
| _RUNTIME_GLIBC = { | ||
| "cp27mu": (2, 17), | ||
| "cp36m": (2, 17), | ||
| "cp37m": (2, 17), | ||
| "cp38": (2, 26), | ||
| } | ||
|
Comment on lines
+159
to
+165
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we add a reference here? |
||
| # Fallback version if we're on an unknown python version | ||
| # not in _RUNTIME_GLIBC. | ||
| # Unlikely to hit this case. | ||
| _DEFAULT_GLIBC = (2, 17) | ||
| _COMPATIBLE_PACKAGE_ALLOWLIST = {"sqlalchemy"} | ||
|
|
||
| def __init__(self, osutils, runtime, pip_runner=None): | ||
|
|
@@ -341,30 +351,62 @@ def _categorize_wheel_files(self, directory): | |
|
|
||
| def _is_compatible_wheel_filename(self, filename): | ||
| wheel = filename[:-4] | ||
| implementation, abi, platform = wheel.split("-")[-3:] | ||
| # Verify platform is compatible | ||
| if platform not in self._MANYLINUX_COMPATIBLE_PLATFORM: | ||
| return False | ||
|
|
||
| lambda_runtime_abi = get_lambda_abi(self.runtime) | ||
| for implementation, abi, platform in self._iter_all_compatibility_tags(wheel): | ||
| if not self._is_compatible_platform_tag(lambda_runtime_abi, platform): | ||
| continue | ||
|
|
||
| # Verify that the ABI is compatible with lambda. Either none or the | ||
| # correct type for the python version cp27mu for py27 and cp36m for | ||
| # py36. | ||
| if abi == "none": | ||
| return True | ||
| prefix_version = implementation[:3] | ||
| if prefix_version == "cp3": | ||
| # Deploying python 3 function which means we need cp36m abi | ||
| # We can also accept abi3 which is the CPython 3 Stable ABI and | ||
| # will work on any version of python 3. | ||
| if abi == lambda_runtime_abi or abi == "abi3": | ||
| return True | ||
| elif prefix_version == "cp2": | ||
| # Deploying to python 2 function which means we need cp27mu abi | ||
| if abi == "cp27mu": | ||
| return True | ||
| # Don't know what we have but it didn't pass compatibility tests. | ||
| return False | ||
|
|
||
| # Verify that the ABI is compatible with lambda. Either none or the | ||
| # correct type for the python version cp27mu for py27 and cp36m for | ||
| # py36. | ||
| if abi == "none": | ||
| def _is_compatible_platform_tag(self, expected_abi, platform): | ||
| """ | ||
| Verify if a platform tag is compatible based on PEP 600 | ||
| https://www.python.org/dev/peps/pep-0600/#specification | ||
|
|
||
| In addition to checking the tag pattern, we also need to verify the glibc version | ||
| """ | ||
| if platform in self._ADDITIONAL_COMPATIBLE_PLATFORM: | ||
| return True | ||
| prefix_version = implementation[:3] | ||
| if prefix_version == "cp3": | ||
| # Deploying python 3 function which means we need cp36m abi | ||
| # We can also accept abi3 which is the CPython 3 Stable ABI and | ||
| # will work on any version of python 3. | ||
| return abi == lambda_runtime_abi or abi == "abi3" | ||
| elif prefix_version == "cp2": | ||
| # Deploying to python 2 function which means we need cp27mu abi | ||
| return abi == "cp27mu" | ||
| # Don't know what we have but it didn't pass compatibility tests. | ||
| elif platform.startswith("manylinux"): | ||
| perennial_tag = self._MANYLINUX_LEGACY_MAP.get(platform, platform) | ||
| m = re.match("manylinux_([0-9]+)_([0-9]+)_(.*)", perennial_tag) | ||
| if m is None: | ||
| return False | ||
| tag_major, tag_minor = [int(x) for x in m.groups()[:2]] | ||
| runtime_major, runtime_minor = self._RUNTIME_GLIBC.get(expected_abi, self._DEFAULT_GLIBC) | ||
| if (tag_major, tag_minor) <= (runtime_major, runtime_minor): | ||
| # glibc version is compatible with Lambda Runtime | ||
| return True | ||
| return False | ||
|
|
||
| def _iter_all_compatibility_tags(self, wheel): | ||
| """ | ||
| Generates all possible combination of tag sets as described in PEP 425 | ||
| https://www.python.org/dev/peps/pep-0425/#compressed-tag-sets | ||
| """ | ||
| implementation_tag, abi_tag, platform_tag = wheel.split("-")[-3:] | ||
| for implementation in implementation_tag.split("."): | ||
| for abi in abi_tag.split("."): | ||
| for platform in platform_tag.split("."): | ||
| yield (implementation, abi, platform) | ||
|
|
||
| def _apply_wheel_allowlist(self, compatible_wheels, incompatible_wheels): | ||
| compatible_wheels = set(compatible_wheels) | ||
| actual_incompatible_wheels = set() | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -449,6 +449,48 @@ def test_can_get_whls_mixed_compat(self, tmpdir, osutils, pip_runner): | |
| for req in reqs: | ||
| assert req in installed_packages | ||
|
|
||
| def test_can_support_pep_600_tags(self, tmpdir, osutils, pip_runner): | ||
| reqs = ["foo"] | ||
| pip, runner = pip_runner | ||
| appdir, builder = self._make_appdir_and_dependency_builder(reqs, tmpdir, runner) | ||
| requirements_file = os.path.join(appdir, "requirements.txt") | ||
| pip.packages_to_download( | ||
| expected_args=["-r", requirements_file, "--dest", mock.ANY, "--exists-action", "i"], | ||
| packages=[ | ||
| "foo-1.2-cp36-cp36m-manylinux_2_12_x86_64.whl", | ||
| ], | ||
| ) | ||
|
|
||
| site_packages = os.path.join(appdir, ".chalice.", "site-packages") | ||
| with osutils.tempdir() as scratch_dir: | ||
| builder.build_site_packages(requirements_file, site_packages, scratch_dir) | ||
| installed_packages = os.listdir(site_packages) | ||
|
|
||
| pip.validate() | ||
| for req in reqs: | ||
| assert req in installed_packages | ||
|
|
||
| def test_can_support_compressed_tags(self, tmpdir, osutils, pip_runner): | ||
| reqs = ["foo"] | ||
| pip, runner = pip_runner | ||
| appdir, builder = self._make_appdir_and_dependency_builder(reqs, tmpdir, runner) | ||
| requirements_file = os.path.join(appdir, "requirements.txt") | ||
| pip.packages_to_download( | ||
| expected_args=["-r", requirements_file, "--dest", mock.ANY, "--exists-action", "i"], | ||
| packages=[ | ||
| "foo-1.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Can you double check whether this is sorted?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (and |
||
| ], | ||
| ) | ||
|
|
||
| site_packages = os.path.join(appdir, ".chalice.", "site-packages") | ||
| with osutils.tempdir() as scratch_dir: | ||
| builder.build_site_packages(requirements_file, site_packages, scratch_dir) | ||
| installed_packages = os.listdir(site_packages) | ||
|
|
||
| pip.validate() | ||
| for req in reqs: | ||
| assert req in installed_packages | ||
|
|
||
| def test_can_get_py27_whls(self, tmpdir, osutils, pip_runner): | ||
| reqs = ["foo", "bar", "baz"] | ||
| pip, runner = pip_runner | ||
|
|
@@ -539,6 +581,36 @@ def test_does_fail_on_python_1_whl(self, tmpdir, osutils, pip_runner): | |
| assert missing_packages[0].identifier == "baz==1.5" | ||
| assert len(installed_packages) == 0 | ||
|
|
||
| def test_does_fail_on_pep_600_tag_with_unsupported_glibc_version(self, tmpdir, osutils, pip_runner): | ||
| reqs = ["foo", "bar", "baz", "qux"] | ||
| pip, runner = pip_runner | ||
| appdir, builder = self._make_appdir_and_dependency_builder(reqs, tmpdir, runner) | ||
| requirements_file = os.path.join(appdir, "requirements.txt") | ||
| pip.packages_to_download( | ||
| expected_args=["-r", requirements_file, "--dest", mock.ANY, "--exists-action", "i"], | ||
| packages=[ | ||
| "foo-1.2-cp36-cp36m-manylinux_2_12_x86_64.whl", | ||
| "bar-1.2-cp36-cp36m-manylinux_2_999_x86_64.whl", | ||
| "baz-1.2-cp36-cp36m-manylinux_3_12_x86_64.whl", | ||
| "qux-1.2-cp36-cp36m-manylinux_3_999_x86_64.whl", | ||
| ], | ||
| ) | ||
|
|
||
| site_packages = os.path.join(appdir, ".chalice.", "site-packages") | ||
| with osutils.tempdir() as scratch_dir: | ||
| with pytest.raises(MissingDependencyError) as e: | ||
| builder.build_site_packages(requirements_file, site_packages, scratch_dir) | ||
| installed_packages = os.listdir(site_packages) | ||
|
|
||
| missing_packages = list(e.value.missing) | ||
| pip.validate() | ||
| assert len(missing_packages) == 3 | ||
| missing_package_identifies = [package.identifier for package in missing_packages] | ||
| assert "bar==1.2" in missing_package_identifies | ||
| assert "baz==1.2" in missing_package_identifies | ||
| assert "qux==1.2" in missing_package_identifies | ||
| assert len(installed_packages) == 1 | ||
|
|
||
| def test_can_replace_incompat_whl(self, tmpdir, osutils, pip_runner): | ||
| reqs = ["foo", "bar"] | ||
| pip, runner = pip_runner | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,3 @@ | ||
| numpy==1.15.4; python_version == '2.7' | ||
| numpy==1.17.4; python_version >= '3.6' | ||
| numpy==1.17.4; python_version == '3.6' | ||
| numpy==1.20.3; python_version >= '3.7' |

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this trying to solve the same problem as this? aws/aws-sam-cli#2646
If so maybe we should keep them consistent
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems not the same problem. However, we might need to update the go dep integration test cases to be future-proof of future go version (>=1.17).
https://blog.golang.org/go116-module-changes