From 5825d7684eb3dc40a5b6e577c68279107f72cffc Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Mon, 19 Dec 2022 15:43:22 -0500 Subject: [PATCH] initial commit --- .gitignore | 4 ++++ .pre-commit-config.yaml | 42 +++++++++++++++++++++++++++++++++++++ LICENSE | 19 +++++++++++++++++ README.md | 36 +++++++++++++++++++++++++++++++ _manylinux.py | 17 +++++++++++++++ azure-pipelines.yml | 19 +++++++++++++++++ requirements-dev.txt | 3 +++ setup.cfg | 41 ++++++++++++++++++++++++++++++++++++ setup.py | 4 ++++ tests/__init__.py | 0 tests/manylinux_max_test.py | 27 ++++++++++++++++++++++++ tox.ini | 17 +++++++++++++++ 12 files changed, 229 insertions(+) create mode 100644 .gitignore create mode 100644 .pre-commit-config.yaml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 _manylinux.py create mode 100644 azure-pipelines.yml create mode 100644 requirements-dev.txt create mode 100644 setup.cfg create mode 100644 setup.py create mode 100644 tests/__init__.py create mode 100644 tests/manylinux_max_test.py create mode 100644 tox.ini diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ca1d90e --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.egg-info +*.pyc +/.coverage +/.tox diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..5661f42 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,42 @@ +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: debug-statements + - id: double-quote-string-fixer + - id: name-tests-test + - id: requirements-txt-fixer +- repo: https://github.com/asottile/setup-cfg-fmt + rev: v2.2.0 + hooks: + - id: setup-cfg-fmt +- repo: https://github.com/asottile/reorder_python_imports + rev: v3.9.0 + hooks: + - id: reorder-python-imports + args: [--py37-plus, --add-import, 'from __future__ import annotations'] +- repo: https://github.com/asottile/add-trailing-comma + rev: v2.4.0 + hooks: + - id: add-trailing-comma + args: [--py36-plus] +- repo: https://github.com/asottile/pyupgrade + rev: v3.3.1 + hooks: + - id: pyupgrade + args: [--py37-plus] +- repo: https://github.com/pre-commit/mirrors-autopep8 + rev: v2.0.1 + hooks: + - id: autopep8 +- repo: https://github.com/PyCQA/flake8 + rev: 6.0.0 + hooks: + - id: flake8 +- repo: https://github.com/pre-commit/mirrors-mypy + rev: v0.991 + hooks: + - id: mypy diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..799aeaa --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2022 Anthony Sottile + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..c3a136c --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +[![Build Status](https://dev.azure.com/asottile/asottile/_apis/build/status/asottile.manylinux-max?branchName=main)](https://dev.azure.com/asottile/asottile/_build/latest?definitionId=75&branchName=main) +[![Azure DevOps coverage](https://img.shields.io/azure-devops/coverage/asottile/asottile/75/main.svg)](https://dev.azure.com/asottile/asottile/_build/latest?definitionId=75&branchName=main) +[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/asottile/manylinux-max/main.svg)](https://results.pre-commit.ci/latest/github/asottile/manylinux-max/main) + +manylinux-max +============= + +dynamically cap the version of manylinux when installing from pip + +## installation + +```bash +pip install manylinux-max +``` + +## usage + +install this package before trying to install other packages. + +set the `MANYLINUX_MAX` environment variable to limit the candidate manylinux +versions. + +this is useful (for example) when using `pip install --target` to build a zip +for aws lambda (which has an old libc version -- and likely a different libc +version than your host machine). + +```bash +pip install manylinux-max +MANYLINUX_MAX=2.26 pip install --target src -r requirements.txt +``` + +## example error that you may encounter from aws lambda + +``` +ImportError: /lib64/libc.so.6: version `GLIBC_2.28' not found (required by /var/task/cryptography/hazmat/bindings/_rust.abi3.so) +``` diff --git a/_manylinux.py b/_manylinux.py new file mode 100644 index 0000000..00c4285 --- /dev/null +++ b/_manylinux.py @@ -0,0 +1,17 @@ +from __future__ import annotations + +import os +import re + +PATTERN = re.compile(r'^(\d+).(\d+)$', re.ASCII) + + +def manylinux_compatible(major: int, minor: int, arch: str) -> bool: + var = os.environ.get('MANYLINUX_MAX') + if var is None: + return True + + parsed = PATTERN.match(var) + if parsed is None: + raise ValueError(f'invalid MANYLINUX_MAX, expected `#.##` got `{var}`') + return (major, minor) <= (int(parsed[1]), int(parsed[2])) diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 0000000..c9ae213 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,19 @@ +trigger: + branches: + include: [main, test-me-*] + tags: + include: ['*'] + +resources: + repositories: + - repository: asottile + type: github + endpoint: github + name: asottile/azure-pipeline-templates + ref: refs/tags/v2.4.0 + +jobs: +- template: job--python-tox.yml@asottile + parameters: + toxenvs: [py37, py38, py39] + os: linux diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..8c38c3f --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,3 @@ +covdefaults>=2.1 +coverage +pytest diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..23af094 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,41 @@ +[metadata] +name = manylinux_max +version = 0.0.0 +description = dynamically cap the version of manylinux when installing from pip +long_description = file: README.md +long_description_content_type = text/markdown +url = https://github.com/asottile/manylinux-max +author = Anthony Sottile +author_email = asottile@umich.edu +license = MIT +license_file = LICENSE +classifiers = + License :: OSI Approved :: MIT License + Programming Language :: Python :: 3 + Programming Language :: Python :: 3 :: Only + Programming Language :: Python :: Implementation :: CPython + Programming Language :: Python :: Implementation :: PyPy + +[options] +py_modules = _manylinux +python_requires = >=3.7 + +[bdist_wheel] +universal = True + +[coverage:run] +plugins = covdefaults + +[mypy] +check_untyped_defs = true +disallow_any_generics = true +disallow_incomplete_defs = true +disallow_untyped_defs = true +warn_redundant_casts = true +warn_unused_ignores = true + +[mypy-testing.*] +disallow_untyped_defs = false + +[mypy-tests.*] +disallow_untyped_defs = false diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..3d93aef --- /dev/null +++ b/setup.py @@ -0,0 +1,4 @@ +from __future__ import annotations + +from setuptools import setup +setup() diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/manylinux_max_test.py b/tests/manylinux_max_test.py new file mode 100644 index 0000000..13517b9 --- /dev/null +++ b/tests/manylinux_max_test.py @@ -0,0 +1,27 @@ +from __future__ import annotations + +import os +from unittest import mock + +import pytest + +from _manylinux import manylinux_compatible + + +def test_default_is_allow_all(): + assert manylinux_compatible(2, 28, 'x86_64') is True + + +def test_incompatible_based_on_environment_variable(): + with mock.patch.dict(os.environ, {'MANYLINUX_MAX': '2.26'}): + assert manylinux_compatible(2, 28, 'x86_64') is False + assert manylinux_compatible(2, 26, 'x86_64') is True + + +def test_environment_variable_is_garbage(): + with mock.patch.dict(os.environ, {'MANYLINUX_MAX': 'garbage'}): + with pytest.raises(ValueError) as excinfo: + manylinux_compatible(2, 28, 'x86_64') + + msg, = excinfo.value.args + assert msg == 'invalid MANYLINUX_MAX, expected `#.##` got `garbage`' diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..5f66e9f --- /dev/null +++ b/tox.ini @@ -0,0 +1,17 @@ +[tox] +envlist = py37,py38,pypy3,pre-commit + +[testenv] +deps = -rrequirements-dev.txt +commands = + coverage erase + coverage run -m pytest {posargs:tests} + coverage report + +[testenv:pre-commit] +skip_install = true +deps = pre-commit +commands = pre-commit run --all-files --show-diff-on-failure + +[pep8] +ignore = E265,E501,W504