feat: requires-python (#536)
* feat: requires-python refactor: updating to current proposal fix: add tests and fix a few issues docs: Update mostly from @joerick docs: update README from readthedocs * refactor: pull out config file reading * fix: always assume highest Python patch version * refactor: try new design * refactor: function and bump MyPy to 0.800 * fix: tighten AST to call only, add docs * fix: address review points from @joerick
This commit is contained in:
@@ -1,11 +1,8 @@
|
||||
import pytest
|
||||
import toml
|
||||
|
||||
from cibuildwheel.util import resources_dir
|
||||
|
||||
Version = pytest.importorskip("packaging.version").Version
|
||||
from packaging.version import Version
|
||||
|
||||
from cibuildwheel.extra import InlineArrayDictEncoder # noqa: E402
|
||||
from cibuildwheel.util import resources_dir
|
||||
|
||||
|
||||
def test_compare_configs():
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from packaging.specifiers import SpecifierSet
|
||||
|
||||
from cibuildwheel.util import BuildSelector
|
||||
|
||||
|
||||
@@ -69,3 +71,36 @@ def test_build_braces():
|
||||
assert build_selector('cp37-manylinux1_x86_64')
|
||||
assert not build_selector('cp38-manylinux1_x86_64')
|
||||
assert not build_selector('cp39-manylinux1_x86_64')
|
||||
|
||||
|
||||
def test_build_limited_python():
|
||||
build_selector = BuildSelector(build_config="*", skip_config="", requires_python=SpecifierSet(">=3.6"))
|
||||
|
||||
assert not build_selector('cp27-manylinux1_x86_64')
|
||||
assert build_selector('cp36-manylinux1_x86_64')
|
||||
assert build_selector('cp37-manylinux1_x86_64')
|
||||
assert not build_selector('cp27-manylinux1_i686')
|
||||
assert build_selector('cp36-manylinux1_i686')
|
||||
assert build_selector('cp37-manylinux1_i686')
|
||||
assert not build_selector('cp27-win32')
|
||||
assert build_selector('cp36-win32')
|
||||
assert build_selector('cp37-win32')
|
||||
assert not build_selector('pp27-win32')
|
||||
assert build_selector('pp36-win32')
|
||||
assert build_selector('pp37-win32')
|
||||
|
||||
|
||||
def test_build_limited_python_partial():
|
||||
build_selector = BuildSelector(build_config="*", skip_config="", requires_python=SpecifierSet(">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"))
|
||||
|
||||
assert build_selector('cp27-manylinux1_x86_64')
|
||||
assert not build_selector('cp35-manylinux1_x86_64')
|
||||
assert build_selector('cp36-manylinux1_x86_64')
|
||||
|
||||
|
||||
def test_build_limited_python_patch():
|
||||
build_selector = BuildSelector(build_config="*", skip_config="", requires_python=SpecifierSet(">=2.7.9"))
|
||||
|
||||
assert build_selector('cp27-manylinux1_x86_64')
|
||||
assert build_selector('cp36-manylinux1_x86_64')
|
||||
assert build_selector('cp37-manylinux1_x86_64')
|
||||
|
||||
@@ -51,8 +51,8 @@ def test_build_selector(platform, intercepted_build_args, monkeypatch, allow_emp
|
||||
|
||||
intercepted_build_selector = intercepted_build_args.args[0].build_selector
|
||||
assert isinstance(intercepted_build_selector, BuildSelector)
|
||||
assert intercepted_build_selector('build-this')
|
||||
assert not intercepted_build_selector('skip-that')
|
||||
assert intercepted_build_selector('build24-this')
|
||||
assert not intercepted_build_selector('skip65-that')
|
||||
# This unit test is just testing the options of 'main'
|
||||
# Unit tests for BuildSelector are in build_selector_test.py
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
from conftest import MOCK_PACKAGE_DIR # noqa: I100
|
||||
|
||||
from cibuildwheel.__main__ import main
|
||||
from cibuildwheel.util import Architecture
|
||||
from cibuildwheel.architecture import Architecture
|
||||
|
||||
from .conftest import MOCK_PACKAGE_DIR
|
||||
|
||||
|
||||
def test_unknown_platform_non_ci(monkeypatch, capsys):
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
import pytest
|
||||
from packaging.specifiers import SpecifierSet
|
||||
|
||||
from cibuildwheel.__main__ import main
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True, scope="function")
|
||||
def fake_package_dir(monkeypatch, tmp_path):
|
||||
'''
|
||||
Set up a fake project
|
||||
'''
|
||||
|
||||
local_path = tmp_path / "tmp_project"
|
||||
local_path.mkdir()
|
||||
|
||||
local_path.joinpath("setup.py").touch()
|
||||
|
||||
monkeypatch.setattr(sys, 'argv', ['cibuildwheel', str(local_path)])
|
||||
|
||||
return local_path
|
||||
|
||||
|
||||
def test_no_override(platform, monkeypatch, intercepted_build_args):
|
||||
|
||||
main()
|
||||
|
||||
intercepted_build_selector = intercepted_build_args.args[0].build_selector
|
||||
|
||||
assert intercepted_build_selector('cp39-win32')
|
||||
assert intercepted_build_selector('cp36-win32')
|
||||
|
||||
assert intercepted_build_selector.requires_python is None
|
||||
|
||||
|
||||
def test_override_env(platform, monkeypatch, intercepted_build_args):
|
||||
monkeypatch.setenv('CIBW_PROJECT_REQUIRES_PYTHON', '>=3.8')
|
||||
|
||||
main()
|
||||
|
||||
intercepted_build_selector = intercepted_build_args.args[0].build_selector
|
||||
|
||||
assert intercepted_build_selector.requires_python == SpecifierSet(">=3.8")
|
||||
|
||||
assert intercepted_build_selector('cp39-win32')
|
||||
assert not intercepted_build_selector('cp36-win32')
|
||||
|
||||
|
||||
def test_override_setup_cfg(platform, monkeypatch, intercepted_build_args, fake_package_dir):
|
||||
|
||||
fake_package_dir.joinpath("setup.cfg").write_text(textwrap.dedent("""
|
||||
[options]
|
||||
python_requires = >=3.8
|
||||
"""))
|
||||
|
||||
main()
|
||||
|
||||
intercepted_build_selector = intercepted_build_args.args[0].build_selector
|
||||
|
||||
assert intercepted_build_selector.requires_python == SpecifierSet(">=3.8")
|
||||
|
||||
assert intercepted_build_selector('cp39-win32')
|
||||
assert not intercepted_build_selector('cp36-win32')
|
||||
|
||||
|
||||
def test_override_pyproject_toml(platform, monkeypatch, intercepted_build_args, fake_package_dir):
|
||||
|
||||
fake_package_dir.joinpath("pyproject.toml").write_text(textwrap.dedent("""
|
||||
[project]
|
||||
requires-python = ">=3.8"
|
||||
"""))
|
||||
|
||||
main()
|
||||
|
||||
intercepted_build_selector = intercepted_build_args.args[0].build_selector
|
||||
|
||||
assert intercepted_build_selector.requires_python == SpecifierSet(">=3.8")
|
||||
|
||||
assert intercepted_build_selector('cp39-win32')
|
||||
assert not intercepted_build_selector('cp36-win32')
|
||||
|
||||
|
||||
def test_override_setup_py_simple(platform, monkeypatch, intercepted_build_args, fake_package_dir):
|
||||
|
||||
fake_package_dir.joinpath("setup.py").write_text(textwrap.dedent("""
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name = "other",
|
||||
python_requires = ">=3.7",
|
||||
)
|
||||
"""))
|
||||
|
||||
main()
|
||||
|
||||
intercepted_build_selector = intercepted_build_args.args[0].build_selector
|
||||
|
||||
assert intercepted_build_selector.requires_python == SpecifierSet(">=3.7")
|
||||
|
||||
assert intercepted_build_selector('cp39-win32')
|
||||
assert not intercepted_build_selector('cp36-win32')
|
||||
@@ -0,0 +1,142 @@
|
||||
from textwrap import dedent
|
||||
|
||||
from cibuildwheel.projectfiles import get_requires_python_str, setup_py_python_requires
|
||||
|
||||
|
||||
def test_read_setup_py_simple(tmp_path):
|
||||
with open(tmp_path / "setup.py", "w") as f:
|
||||
f.write(dedent("""
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name = "hello",
|
||||
other = 23,
|
||||
example = ["item", "other"],
|
||||
python_requires = "1.23",
|
||||
)
|
||||
"""))
|
||||
|
||||
assert setup_py_python_requires(tmp_path.joinpath("setup.py").read_text()) == "1.23"
|
||||
assert get_requires_python_str(tmp_path) == "1.23"
|
||||
|
||||
|
||||
def test_read_setup_py_full(tmp_path):
|
||||
with open(tmp_path / "setup.py", "w") as f:
|
||||
f.write(dedent("""
|
||||
import setuptools
|
||||
|
||||
setuptools.randomfunc()
|
||||
|
||||
setuptools.setup(
|
||||
name = "hello",
|
||||
other = 23,
|
||||
example = ["item", "other"],
|
||||
python_requires = "1.24",
|
||||
)
|
||||
"""))
|
||||
|
||||
assert setup_py_python_requires(tmp_path.joinpath("setup.py").read_text()) == "1.24"
|
||||
assert get_requires_python_str(tmp_path) == "1.24"
|
||||
|
||||
|
||||
def test_read_setup_py_assign(tmp_path):
|
||||
with open(tmp_path / "setup.py", "w") as f:
|
||||
f.write(dedent("""
|
||||
from setuptools import setup
|
||||
|
||||
REQUIRES = "3.21"
|
||||
|
||||
setuptools.setup(
|
||||
name = "hello",
|
||||
other = 23,
|
||||
example = ["item", "other"],
|
||||
python_requires = REQUIRES,
|
||||
)
|
||||
"""))
|
||||
|
||||
assert setup_py_python_requires(tmp_path.joinpath("setup.py").read_text()) is None
|
||||
assert get_requires_python_str(tmp_path) is None
|
||||
|
||||
|
||||
def test_read_setup_py_None(tmp_path):
|
||||
with open(tmp_path / "setup.py", "w") as f:
|
||||
f.write(dedent("""
|
||||
from setuptools import setup
|
||||
|
||||
REQUIRES = None
|
||||
|
||||
setuptools.setup(
|
||||
name = "hello",
|
||||
other = 23,
|
||||
example = ["item", "other"],
|
||||
python_requires = None,
|
||||
)
|
||||
"""))
|
||||
|
||||
assert setup_py_python_requires(tmp_path.joinpath("setup.py").read_text()) is None
|
||||
assert get_requires_python_str(tmp_path) is None
|
||||
|
||||
|
||||
def test_read_setup_py_empty(tmp_path):
|
||||
with open(tmp_path / "setup.py", "w") as f:
|
||||
f.write(dedent("""
|
||||
from setuptools import setup
|
||||
|
||||
REQUIRES = "3.21"
|
||||
|
||||
setuptools.setup(
|
||||
name = "hello",
|
||||
other = 23,
|
||||
example = ["item", "other"],
|
||||
)
|
||||
"""))
|
||||
|
||||
assert setup_py_python_requires(tmp_path.joinpath("setup.py").read_text()) is None
|
||||
assert get_requires_python_str(tmp_path) is None
|
||||
|
||||
|
||||
def test_read_setup_cfg(tmp_path):
|
||||
with open(tmp_path / "setup.cfg", "w") as f:
|
||||
f.write(dedent("""
|
||||
[options]
|
||||
python_requires = 1.234
|
||||
[metadata]
|
||||
something = other
|
||||
"""))
|
||||
|
||||
assert get_requires_python_str(tmp_path) == "1.234"
|
||||
|
||||
|
||||
def test_read_setup_cfg_empty(tmp_path):
|
||||
with open(tmp_path / "setup.cfg", "w") as f:
|
||||
f.write(dedent("""
|
||||
[options]
|
||||
other = 1.234
|
||||
[metadata]
|
||||
something = other
|
||||
"""))
|
||||
|
||||
assert get_requires_python_str(tmp_path) is None
|
||||
|
||||
|
||||
def test_read_pyproject_toml(tmp_path):
|
||||
with open(tmp_path / "pyproject.toml", "w") as f:
|
||||
f.write(dedent("""
|
||||
[project]
|
||||
requires-python = "1.654"
|
||||
|
||||
[tool.cibuildwheel]
|
||||
something = "other"
|
||||
"""))
|
||||
|
||||
assert get_requires_python_str(tmp_path) == "1.654"
|
||||
|
||||
|
||||
def test_read_pyproject_toml_empty(tmp_path):
|
||||
with open(tmp_path / "pyproject.toml", "w") as f:
|
||||
f.write(dedent("""
|
||||
[project]
|
||||
other = 1.234
|
||||
"""))
|
||||
|
||||
assert get_requires_python_str(tmp_path) is None
|
||||
Reference in New Issue
Block a user