diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4f837e5a..e34aa83a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -105,6 +105,14 @@ jobs: - os: windows-latest python: '3.14' meson: '@git+https://github.com/mesonbuild/meson.git' + # Test free-threaded builds to verify the correct handling of the + # 'abi3t' stable ABI, see PEP 803. Test on Linux and Windows to cover + # the case where stable ABI extension modules use a dedicated + # filename suffix and the case where they do not. + - os: ubuntu-latest + python: '3.15t' + - os: windows-latest + python: '3.15t' steps: - name: Checkout diff --git a/mesonpy/__init__.py b/mesonpy/__init__.py index 3281bb1d..a12a9fb6 100644 --- a/mesonpy/__init__.py +++ b/mesonpy/__init__.py @@ -99,7 +99,7 @@ class InvalidLicenseExpression(Exception): # type: ignore[no-redef] _MESON_ARGS_KEYS = ['dist', 'setup', 'compile', 'install'] _SUFFIXES = importlib.machinery.all_suffixes() -_EXTENSION_SUFFIX_REGEX = re.compile(r'^[^.]+\.(?:(?P[^.]+)\.)?(?:so|pyd|dll)$') +_EXTENSION_SUFFIX_REGEX = re.compile(r'^[^.]+\.(?:(?P[^.-]+)(?:-[^.]+)?\.)?(?:so|pyd|dll)$') assert all(re.match(_EXTENSION_SUFFIX_REGEX, f'foo{x}') for x in importlib.machinery.EXTENSION_SUFFIXES) # Map Meson installation path placeholders to wheel installation paths. @@ -429,7 +429,7 @@ def _stable_abi(self) -> Optional[str]: # not use the stable ABI filename suffix and wheels should not # be tagged with the abi3 tag. if self._limited_api and '__pypy__' not in sys.builtin_module_names: - # On free-threaded Python 3.15.0b2+, we expect to be + # On free-threaded Python 3.15 or later, we expect to be # building 'abi3t' wheels for the time being. In the future # we will want an option to target 'abi3t' from GIL-enabled # Python too. diff --git a/tests/test_tags.py b/tests/test_tags.py index 88972d11..7292c946 100644 --- a/tests/test_tags.py +++ b/tests/test_tags.py @@ -29,13 +29,22 @@ def get_abi3_suffix(): - # EXTENSION_SUFFIXES are ordered in preference order, and more specific ABI is preferred. - # On free-threaded 3.15+, this will match ".abi3t" as the only supported stable ABI. - # On GIL-enabled 3.15+, it will match plain ".abi3" first, since that ABI is more specific. + """Get the filename suffix identifying stable ABI extension modules.""" + + # EXTENSION_SUFFIXES is ordered from the more to the less specific ABI. + # On Python 3.15 or later, this resolves to ``.abi3t-${arch}`` for + # free-threaded builds and to ``.abi3-${arch}`` on regular builds. + # + # Older Python versions do not support a stable ABI for free-threaded + # builds, and do not support a platform-specific filename suffix, and + # this resolves to simply ``.abi3``. + # + # On Windows, Python does not use a dedicated filename suffix for stable + # ABI extension modules, and this resolves to ``.pyd``. for suffix in importlib.machinery.EXTENSION_SUFFIXES: - if '.abi3' in suffix: # Unix + if suffix.startswith('.abi3'): return suffix - elif suffix == '.pyd': # Windows + if suffix == '.pyd': return suffix