From 4daf43a85955d288ea865027404520bca9372740 Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Sat, 7 Dec 2024 19:55:55 -0500 Subject: [PATCH 1/4] feat: Include abiflags in version dectection (enabling 3.13t) --- src/tox_gh_actions/plugin.py | 19 +++++++++++-------- tests/test_plugin.py | 13 ++++++++++++- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/tox_gh_actions/plugin.py b/src/tox_gh_actions/plugin.py index 65693d8..1fcbc9a 100644 --- a/src/tox_gh_actions/plugin.py +++ b/src/tox_gh_actions/plugin.py @@ -164,24 +164,27 @@ def get_python_version_keys() -> List[str]: Examples: - CPython 3.8.z => [3.8, 3] - - PyPy 3.6 (v7.3.z) => [pypy-3.6, pypy-3, pypy3] + - PyPy 3.6 (v7.3.z) => [pypy-3.6, pypy-3] - Pyston based on Python CPython 3.8.8 (v2.2) => [pyston-3.8, pyston-3] + - CPython 3.13.z (free-threading build) => [3.13t, 3.13, 3] """ - major_version = str(sys.version_info[0]) - major_minor_version = ".".join([str(i) for i in sys.version_info[:2]]) + major, minor = sys.version_info[:2] if "PyPy" in sys.version: return [ - "pypy-" + major_minor_version, - "pypy-" + major_version, + f"pypy-{major}.{minor}", + f"pypy-{major}", ] elif hasattr(sys, "pyston_version_info"): # Pyston return [ - "pyston-" + major_minor_version, - "pyston-" + major_version, + f"pyston-{major}.{minor}", + f"pyston-{major}", ] else: # Assume this is running on CPython - return [major_minor_version, major_version] + ret = [f"{major}.{minor}", f"{major}"] + if sys.abiflags: + ret.insert(0, f"{major}.{minor}{sys.abiflags}") + return ret def is_running_on_actions() -> bool: diff --git a/tests/test_plugin.py b/tests/test_plugin.py index d841d6a..0c276fd 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -247,29 +247,40 @@ def test_get_envlist_from_factors( @pytest.mark.parametrize( - "version,info,expected", + "version,info,abiflags,expected", [ ( "3.8.1 (default, Jan 22 2020, 06:38:00) \n[GCC 9.2.0]", (3, 8, 1, "final", 0), + "", ["3.8", "3"], ), ( "3.6.9 (1608da62bfc7, Dec 23 2019, 10:50:04)\n" "[PyPy 7.3.0 with GCC 7.3.1 20180303 (Red Hat 7.3.1-5)]", (3, 6, 9, "final", 0), + "", ["pypy-3.6", "pypy-3"], ), + ( + "3.13.0 experimental free-threading build (main, Oct 16 2024, 03:26:14) " + "[Clang 18.1.8 ]\n", + (3, 13, 0, "final", 0), + "t", + ["3.13t", "3.13", "3"], + ), ], ) def test_get_version_keys( mocker: MockerFixture, version: str, info: Tuple[int, int, int, str, int], + abiflags: str, expected: List[str], ) -> None: mocker.patch("tox_gh_actions.plugin.sys.version", version) mocker.patch("tox_gh_actions.plugin.sys.version_info", info) + mocker.patch("tox_gh_actions.plugin.sys.abiflags", abiflags) assert plugin.get_python_version_keys() == expected From 926ec74580b7868317240edd6f249c49a7df2761 Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Sun, 8 Dec 2024 10:33:54 -0500 Subject: [PATCH 2/4] feat: Detect abiflags in Windows, construct all permutations --- src/tox_gh_actions/plugin.py | 56 ++++++++++++++++++++++++++++++++---- tests/test_plugin.py | 7 +++++ 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/src/tox_gh_actions/plugin.py b/src/tox_gh_actions/plugin.py index 1fcbc9a..e47ae1c 100644 --- a/src/tox_gh_actions/plugin.py +++ b/src/tox_gh_actions/plugin.py @@ -1,9 +1,10 @@ -from itertools import product +from itertools import permutations, product from logging import getLogger import os import sys -from typing import Any, Dict, Iterable, List +import sysconfig +from typing import Any, Dict, Iterable, Iterator, List, Tuple from tox.config.cli.parser import Parsed from tox.config.loader.memory import MemoryLoader @@ -159,6 +160,47 @@ def get_envlist_from_factors( return result +def get_abiflags() -> str: + """Return ABI flags as a string of character codes. + + POSIX builds provide sys.abiflags. This function constructs + an equivalent character code sequence for Windows by + parsing the EXT_SUFFIX configuration variable. + """ + try: + return sys.abiflags + except AttributeError: # Windows + ext_suffix = sysconfig.get_config_var("EXT_SUFFIX") + if not ext_suffix: + return "" + + # Looks like [_d].cp314[t]-win_amd64.pyd + prefix, cp3, suffix = ext_suffix.partition(".cp3") + if not cp3: # Before Python 3.10, hard-coded as ".pyd" + return "" + + abiflags = "" + if prefix == "_d": + abiflags += "d" + + if suffix.split("-", 1).endswith("t"): + abiflags += "t" + + return abiflags + + +def permuted_combinations(seq: str) -> Iterator[Tuple[str, ...]]: + """Generate all combinations of any length and ordering. + + >>> list(permuted_combinations("t")) + [(), ('t',)] + >>> list(permuted_combinations("td")) + [(), ('t',), ('d',), ('t', 'd'), ('d', 't')] + """ + for n in range(len(seq) + 1): + yield from permutations(seq, n) + + def get_python_version_keys() -> List[str]: """Get Python version in string for getting factors from gh-action's config @@ -181,10 +223,12 @@ def get_python_version_keys() -> List[str]: ] else: # Assume this is running on CPython - ret = [f"{major}.{minor}", f"{major}"] - if sys.abiflags: - ret.insert(0, f"{major}.{minor}{sys.abiflags}") - return ret + ret = [f"{major}"] + ret.extend( + f"{major}.{minor}{''.join(flags)}" + for flags in permuted_combinations(get_abiflags()) + ) + return sorted(ret, reverse=True) def is_running_on_actions() -> bool: diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 0c276fd..e21d636 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -269,6 +269,13 @@ def test_get_envlist_from_factors( "t", ["3.13t", "3.13", "3"], ), + ( + "3.13.0 experimental free-threading build (main, Oct 16 2024, 03:26:14) " + "[Clang 18.1.8 ]\n", + (3, 13, 0, "final", 0), + "td", + ["3.13td", "3.13t", "3.13dt", "3.13d", "3.13", "3"], + ), ], ) def test_get_version_keys( From b2827506f87f414cd0d996f647370a61493ed6b8 Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Sun, 8 Dec 2024 11:00:20 -0500 Subject: [PATCH 3/4] chore(tox): Enable 3.12 and 3.13 tests in tox --- setup.cfg | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 0144d7c..423d4cc 100644 --- a/setup.cfg +++ b/setup.cfg @@ -81,7 +81,7 @@ envlist = black flake8 mypy - {py37,py38,py39,py310,py311,pypy3}-toxlatest + {py37,py38,py39,py310,py311,py312,py313,pypy3}-toxlatest [gh-actions] python = @@ -91,6 +91,7 @@ python = 3.10: py310 3.11: py311, black, flake8, mypy 3.12: py312 + 3.13: py313 pypy-3: pypy3 [testenv] From da853f9044588b724e96c52c73784bb167921664 Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Sun, 8 Dec 2024 11:06:36 -0500 Subject: [PATCH 4/4] fix: Index split string --- src/tox_gh_actions/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tox_gh_actions/plugin.py b/src/tox_gh_actions/plugin.py index e47ae1c..7e89dfc 100644 --- a/src/tox_gh_actions/plugin.py +++ b/src/tox_gh_actions/plugin.py @@ -183,7 +183,7 @@ def get_abiflags() -> str: if prefix == "_d": abiflags += "d" - if suffix.split("-", 1).endswith("t"): + if suffix.split("-", 1)[0].endswith("t"): abiflags += "t" return abiflags