diff --git a/config/c-code/manylinux-install.sh.j2 b/config/c-code/manylinux-install.sh.j2 index ffcc20e..0f12fd8 100644 --- a/config/c-code/manylinux-install.sh.j2 +++ b/config/c-code/manylinux-install.sh.j2 @@ -29,14 +29,12 @@ yum -y install libffi-devel tox_env_map() { case $1 in +{% for py_version in supported_python_versions %} + *"cp%(py_version)s"*) echo 'py%(py_version)s';; +{% endfor %} {% if with_future_python %} - *"cp313"*) echo 'py313';; + *"cp%(future_python_shortversion)s"*) echo 'py%(future_python_shortversion)s';; {% endif %} - *"cp38"*) echo 'py38';; - *"cp39"*) echo 'py39';; - *"cp310"*) echo 'py310';; - *"cp311"*) echo 'py311';; - *"cp312"*) echo 'py312';; *) echo 'py';; esac } @@ -44,16 +42,17 @@ tox_env_map() { # Compile wheels for PYBIN in /opt/python/*/bin; do if \ +{% for py_version in supported_python_versions %} + [[ "${PYBIN}" == *"cp%(py_version)s/"* ]] {% if py_version != stop_at %}|| \ +{% endif %} +{% endfor %} {% if with_future_python %} - [[ "${PYBIN}" == *"cp313/"* ]] || \ + [[ "${PYBIN}" == *"cp%(future_python_shortversion)s/"* ]] ; then +{% else %} +; then {% endif %} - [[ "${PYBIN}" == *"cp311/"* ]] || \ - [[ "${PYBIN}" == *"cp312/"* ]] || \ - [[ "${PYBIN}" == *"cp38/"* ]] || \ - [[ "${PYBIN}" == *"cp39/"* ]] || \ - [[ "${PYBIN}" == *"cp310/"* ]] ; then {% if with_future_python %} - if [[ "${PYBIN}" == *"cp313/"* ]] ; then + if [[ "${PYBIN}" == *"cp%(future_python_shortversion)s/"* ]] ; then "${PYBIN}/pip" install --pre -e /io/ "${PYBIN}/pip" wheel /io/ --pre -w wheelhouse/ else diff --git a/config/c-code/tests-cache.j2 b/config/c-code/tests-cache.j2 index fa5b99e..c8e03b7 100644 --- a/config/c-code/tests-cache.j2 +++ b/config/c-code/tests-cache.j2 @@ -4,9 +4,7 @@ uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} -{% if with_future_python %} allow-prereleases: true -{% endif %} ### # Caching. # This actually *restores* a cache and schedules a cleanup action diff --git a/config/c-code/tests-strategy.j2 b/config/c-code/tests-strategy.j2 index 5657731..60cc7d6 100644 --- a/config/c-code/tests-strategy.j2 +++ b/config/c-code/tests-strategy.j2 @@ -5,11 +5,9 @@ {% if with_pypy %} - "pypy-%(pypy_version)s" {% endif %} - - "3.8" - - "3.9" - - "3.10" - - "3.11" - - "3.12" +{% for (py_long_version, py_short_version) in supported_python_versions %} + - "%(py_long_version)s" +{% endfor %} {% if with_future_python %} - "%(future_python_version)s" {% endif %} diff --git a/config/c-code/tests.yml.j2 b/config/c-code/tests.yml.j2 index 7a31c83..994eb83 100644 --- a/config/c-code/tests.yml.j2 +++ b/config/c-code/tests.yml.j2 @@ -269,7 +269,7 @@ jobs: {% endif %} run: | pip install -U wheel "setuptools %(setuptools_version_spec)s" - pip install -U coverage + pip install -U coverage[toml] pip install -U 'cffi; platform_python_implementation == "CPython"' # Unzip into src/ so that testrunner can find the .so files # when we ask it to load tests from that directory. This @@ -334,7 +334,7 @@ jobs: - name: Install %(package_name)s run: | pip install -U wheel - pip install -U coverage + pip install -U coverage[toml] pip install -U "`ls dist/%(package_name)s-*.whl`[docs]" - name: Build docs env: diff --git a/config/c-code/tox.ini.j2 b/config/c-code/tox.ini.j2 index b2f2f99..d6e37e0 100644 --- a/config/c-code/tox.ini.j2 +++ b/config/c-code/tox.ini.j2 @@ -6,13 +6,11 @@ minversion = 4.0 envlist = lint - py38,py38-pure - py39,py39-pure - py310,py310-pure - py311,py311-pure - py312,py312-pure +{% for py_version in supported_python_versions %} + py%(py_version)s,py%(py_version)s-pure +{% endfor %} {% if with_future_python %} - py313,py313-pure + py%(future_python_shortversion)s,py%(future_python_shortversion)s-pure {% endif %} {% if with_pypy %} pypy3 diff --git a/config/config-package.py b/config/config-package.py index f07d79d..1844235 100755 --- a/config/config-package.py +++ b/config/config-package.py @@ -23,11 +23,13 @@ from shared.packages import MANYLINUX_I686 from shared.packages import MANYLINUX_PYTHON_VERSION from shared.packages import MANYLINUX_X86_64 +from shared.packages import NEWEST_PYTHON_VERSION from shared.packages import OLDEST_PYTHON_VERSION from shared.packages import PYPY_VERSION from shared.packages import SETUPTOOLS_VERSION_SPEC from shared.packages import get_pyproject_toml_defaults from shared.packages import parse_additional_config +from shared.packages import supported_python_versions from shared.path import change_dir import argparse import collections @@ -37,6 +39,8 @@ import tomlkit +FUTURE_PYTHON_SHORTVERSION = FUTURE_PYTHON_VERSION.replace('.', '') +NEWEST_PYTHON_SHORTVERSION = NEWEST_PYTHON_VERSION.replace('.', '') META_HINT = """\ # Generated from: # https://github.com/zopefoundation/meta/tree/master/config/{config_type}""" @@ -373,6 +377,9 @@ def manylinux_sh(self): self.copy_with_meta( 'manylinux.sh', self.path / '.manylinux.sh', self.config_type) (self.path / '.manylinux.sh').chmod(0o755) + stop_at = None + if not self.with_future_python: + stop_at = NEWEST_PYTHON_SHORTVERSION self.copy_with_meta( 'manylinux-install.sh.j2', self.path / '.manylinux-install.sh', self.config_type, @@ -380,6 +387,10 @@ def manylinux_sh(self): manylinux_install_setup=manylinux_install_setup, manylinux_aarch64_tests=manylinux_aarch64_tests, with_future_python=self.with_future_python, + future_python_shortversion=FUTURE_PYTHON_SHORTVERSION, + supported_python_versions=supported_python_versions( + short_version=True), + stop_at=stop_at, ) (self.path / '.manylinux-install.sh').chmod(0o755) self.add_manylinux = True @@ -462,6 +473,9 @@ def tox(self): with_sphinx_doctests=self.with_sphinx_doctests, docs_deps=docs_deps, setuptools_version_spec=SETUPTOOLS_VERSION_SPEC, + future_python_shortversion=FUTURE_PYTHON_SHORTVERSION, + supported_python_versions=supported_python_versions( + short_version=True), ) def tests_yml(self): @@ -479,6 +493,9 @@ def tests_yml(self): gha_test_commands = self.gh_option('test-commands') require_cffi = self.meta_cfg.get( 'c-code', {}).get('require-cffi', False) + py_version_matrix = [ + x for x in zip(supported_python_versions(short_version=False), + supported_python_versions(short_version=True))] self.copy_with_meta( 'tests.yml.j2', workflows / 'tests.yml', @@ -506,6 +523,8 @@ def tests_yml(self): manylinux_x86_64=MANYLINUX_X86_64, pypy_version=PYPY_VERSION, setuptools_version_spec=SETUPTOOLS_VERSION_SPEC, + future_python_shortversion=FUTURE_PYTHON_SHORTVERSION, + supported_python_versions=py_version_matrix, ) def pre_commit_yml(self): diff --git a/config/default/tests.yml.j2 b/config/default/tests.yml.j2 index a9d191f..70a8b2a 100644 --- a/config/default/tests.yml.j2 +++ b/config/default/tests.yml.j2 @@ -35,23 +35,21 @@ jobs: {% endif %} config: # [Python version, tox env] - - ["3.11", "release-check"] - - ["3.8", "py38"] - - ["3.9", "py39"] - - ["3.10", "py310"] - - ["3.11", "py311"] - - ["3.12", "py312"] + - ["3.11", "release-check"] +{% for (py_long_version, py_short_version) in supported_python_versions %} + - ["%(py_long_version)s", "py%(py_short_version)s"] +{% endfor %} {% if with_future_python %} - - ["%(future_python_version)s", "py313"] + - ["%(future_python_version)s", "py%(future_python_shortversion)s"] {% endif %} {% if with_pypy %} - - ["pypy-3.10", "pypy3"] + - ["pypy-%(pypi_version)s", "pypy3"] {% endif %} {% if with_docs %} - - ["3.11", "docs"] + - ["3.11", "docs"] {% endif %} {% if with_coverage %} - - ["3.11", "coverage"] + - ["3.11", "coverage"] {% endif %} {% for line in gha_additional_config %} %(line)s @@ -60,18 +58,18 @@ jobs: exclude: {% endif %} {% if with_windows %} - - { os: ["windows", "windows-latest"], config: ["3.11", "release-check"] } + - { os: ["windows", "windows-latest"], config: ["3.11", "release-check"] } {% if with_docs %} - - { os: ["windows", "windows-latest"], config: ["3.11", "docs"] } + - { os: ["windows", "windows-latest"], config: ["3.11", "docs"] } {% endif %} - - { os: ["windows", "windows-latest"], config: ["3.11", "coverage"] } + - { os: ["windows", "windows-latest"], config: ["3.11", "coverage"] } {% endif %} {% if with_macos %} - - { os: ["macos", "macos-latest"], config: ["3.11", "release-check"] } + - { os: ["macos", "macos-latest"], config: ["3.11", "release-check"] } {% if with_docs %} - - { os: ["macos", "macos-latest"], config: ["3.11", "docs"] } + - { os: ["macos", "macos-latest"], config: ["3.11", "docs"] } {% endif %} - - { os: ["macos", "macos-latest"], config: ["3.11", "coverage"] } + - { os: ["macos", "macos-latest"], config: ["3.11", "coverage"] } {% endif %} {% for line in gha_additional_exclude %} %(line)s @@ -93,9 +91,7 @@ jobs: uses: actions/setup-python@v5 with: python-version: ${{ matrix.config[0] }} -{% if with_future_python %} allow-prereleases: true -{% endif %} - name: Pip cache uses: actions/cache@v4 with: diff --git a/config/default/tox-envlist.j2 b/config/default/tox-envlist.j2 index b83184e..d5b33d4 100644 --- a/config/default/tox-envlist.j2 +++ b/config/default/tox-envlist.j2 @@ -3,13 +3,11 @@ minversion = 3.18 envlist = release-check lint - py38 - py39 - py310 - py311 - py312 +{% for py_version in supported_python_versions %} + py%(py_version)s +{% endfor %} {% if with_future_python %} - py313 + py%(future_python_shortversion)s {% endif %} {% if with_pypy %} pypy3 diff --git a/config/default/tox-testenv.j2 b/config/default/tox-testenv.j2 index ee9482d..48e2823 100644 --- a/config/default/tox-testenv.j2 +++ b/config/default/tox-testenv.j2 @@ -8,7 +8,7 @@ package = wheel wheel_build_env = .pkg {% endif %} {% if with_future_python %} -pip_pre = py313: true +pip_pre = py%(future_python_shortversion)s: true {% endif %} deps = setuptools %(setuptools_version_spec)s @@ -24,8 +24,6 @@ setenv = %(line)s {% endfor %} {% endif %} - py312: VIRTUALENV_PIP=23.1.2 - py312: PIP_REQUIRE_VIRTUALENV=0 {% if testenv_commands_pre %} commands_pre = {% for line in testenv_commands_pre %} diff --git a/config/requirements.txt b/config/requirements.txt index 86b3dac..759f2c7 100644 --- a/config/requirements.txt +++ b/config/requirements.txt @@ -1,7 +1,8 @@ -check-python-versions==0.21.3 +check-python-versions==0.22.0 Jinja2==3.1.4 -pyupgrade==3.3.2 -tomlkit==0.12.1 -tox==4.8.0 -requests==2.32.0 -zest.releaser==8.0.0 +packaging==24.1 +pyupgrade==3.16.0 +requests==2.32.3 +tomlkit==0.13.2 +tox==4.18.1 +zest.releaser==9.2.0 diff --git a/config/shared/packages.py b/config/shared/packages.py index 2f6897f..b55cbba 100644 --- a/config/shared/packages.py +++ b/config/shared/packages.py @@ -10,6 +10,7 @@ # FOR A PARTICULAR PURPOSE. # ############################################################################## +from packaging.version import parse as parse_version import configparser import itertools import pathlib @@ -19,12 +20,8 @@ ORG = 'zopefoundation' BASE_PATH = pathlib.Path(__file__).parent.parent OLDEST_PYTHON_VERSION = '3.8' -NEWEST_PYTHON_VERSION = '3.12' -SUPPORTED_PYTHON_VERSIONS = [ - f'3.{i}' for i in range(int(OLDEST_PYTHON_VERSION.replace('3.', '')), - int(NEWEST_PYTHON_VERSION.replace('3.', '')) + 1) -] -FUTURE_PYTHON_VERSION = '3.13' +NEWEST_PYTHON_VERSION = '3.13' +FUTURE_PYTHON_VERSION = '3.14' PYPY_VERSION = '3.10' SETUPTOOLS_VERSION_SPEC = '<74' MANYLINUX_PYTHON_VERSION = '3.11' @@ -147,6 +144,31 @@ def parse_additional_config(cfg): return data +def supported_python_versions(short_version=False): + """Create a list containing all supported Python versions + + Uses the configured oldest and newest Python versions to compute a list + containing all versions from oldest to newest that can be iterated over in + the templates. + + Kwargs: + + short_version (bool): + Return short versions like "313" instead of "3.13" + """ + minor_versions = [] + oldest_python = parse_version(OLDEST_PYTHON_VERSION) + newest_python = parse_version(NEWEST_PYTHON_VERSION) + for minor in range(oldest_python.minor, newest_python.minor+1): + minor_versions.append(minor) + supported = [f'{oldest_python.major}.{minor}' for minor in minor_versions] + + if short_version: + return [x.replace('.', '') for x in supported] + + return supported + + def list_packages(path: pathlib.Path) -> list: """List the packages in ``path``. diff --git a/config/toolkit/tox.ini.j2 b/config/toolkit/tox.ini.j2 index c27cbfa..76a6d13 100644 --- a/config/toolkit/tox.ini.j2 +++ b/config/toolkit/tox.ini.j2 @@ -16,8 +16,6 @@ setenv = %(line)s {% endfor %} {% endif %} - py312: VIRTUALENV_PIP=23.1.2 - py312: PIP_REQUIRE_VIRTUALENV=0 commands_pre = {% if testenv_commands_pre %} {% for line in testenv_commands_pre %} diff --git a/config/update-python-support.py b/config/update-python-support.py index b158d0c..398f3f5 100644 --- a/config/update-python-support.py +++ b/config/update-python-support.py @@ -16,7 +16,7 @@ from shared.git import get_branch_name from shared.git import git_branch from shared.packages import OLDEST_PYTHON_VERSION -from shared.packages import SUPPORTED_PYTHON_VERSIONS +from shared.packages import supported_python_versions from shared.path import change_dir import argparse import collections @@ -85,8 +85,8 @@ def get_tox_ini_python_versions(path): current_python_versions = get_tox_ini_python_versions('tox.ini') no_longer_supported = (set(current_python_versions) - - set(SUPPORTED_PYTHON_VERSIONS)) - not_yet_supported = (set(SUPPORTED_PYTHON_VERSIONS) - + set(supported_python_versions())) + not_yet_supported = (set(supported_python_versions()) - set(current_python_versions)) non_interactive_params = [] @@ -97,7 +97,7 @@ def get_tox_ini_python_versions(path): if no_longer_supported or not_yet_supported: call(bin_dir / 'bumpversion', '--feature', *non_interactive_params) - python_versions_args = ['--add=' + ','.join(SUPPORTED_PYTHON_VERSIONS)] + python_versions_args = ['--add=' + ','.join(supported_python_versions())] if no_longer_supported: for version in sorted(list(no_longer_supported)): call(bin_dir / 'addchangelogentry', diff --git a/config/zope-product/tox.ini.j2 b/config/zope-product/tox.ini.j2 index 23ec5cc..45bd35c 100644 --- a/config/zope-product/tox.ini.j2 +++ b/config/zope-product/tox.ini.j2 @@ -16,8 +16,6 @@ setenv = %(line)s {% endfor %} {% endif %} - py312: VIRTUALENV_PIP=23.1.2 - py312: PIP_REQUIRE_VIRTUALENV=0 commands_pre = {% if testenv_commands_pre %} {% for line in testenv_commands_pre %}