@@ -99,7 +99,7 @@ class InvalidLicenseExpression(Exception): # type: ignore[no-redef]
9999_MESON_ARGS_KEYS = ['dist' , 'setup' , 'compile' , 'install' ]
100100
101101_SUFFIXES = importlib .machinery .all_suffixes ()
102- _EXTENSION_SUFFIX_REGEX = re .compile (r'^[^.]+\.(?:(?P<abi>[^.]+)\.)?(?:so|pyd|dll)$' )
102+ _EXTENSION_SUFFIX_REGEX = re .compile (r'^[^.]+(?P<suffix> \.(?:(?P<abi>[^.]+)\.)?(?:so|pyd|dll) )$' )
103103assert all (re .match (_EXTENSION_SUFFIX_REGEX , f'foo{ x } ' ) for x in importlib .machinery .EXTENSION_SUFFIXES )
104104
105105# Map Meson installation path placeholders to wheel installation paths.
@@ -332,6 +332,29 @@ def _is_native(file: Path) -> bool:
332332 return f .read (4 ) == b'\x7f ELF' # ELF
333333
334334
335+ def _get_abi3_suffix () -> str | None :
336+ """Get the filename suffix identifying stable ABI extension modules."""
337+
338+ # EXTENSION_SUFFIXES is ordered from the more to the less specific ABI.
339+ # On Python 3.15 or later, this resolves to ".abi3t-${platform}" for
340+ # free-threaded builds and to ".abi3-${platform}" on regular builds.
341+ # Python 3.15 and later also accepts the generic ".abi3t" or ".abi3"
342+ # suffixes, but we ignore this for the moment.
343+ #
344+ # Older Python versions do not support a stable ABI for free-threaded
345+ # build, and do not support a platform-specific filename suffix, and this
346+ # resolves to simply ".abi3".
347+ #
348+ # On Windows, Python does not use a dedicated filename suffix for stable
349+ # ABI extension modules.
350+ for suffix in importlib .machinery .EXTENSION_SUFFIXES :
351+ if suffix .startswith ('.abi3' ):
352+ return suffix
353+ if suffix == '.pyd' :
354+ return suffix
355+ return None
356+
357+
335358@dataclasses .dataclass
336359class _WheelBuilder ():
337360 """Helper class to build wheels from projects."""
@@ -429,12 +452,11 @@ def _stable_abi(self) -> Optional[str]:
429452 # not use the stable ABI filename suffix and wheels should not
430453 # be tagged with the abi3 tag.
431454 if self ._limited_api and '__pypy__' not in sys .builtin_module_names :
432- # On free-threaded Python 3.15.0b2+ , we expect to be
455+ # On free-threaded Python 3.15 and later , we expect to be
433456 # building 'abi3t' wheels for the time being. In the future
434457 # we will want an option to target 'abi3t' from GIL-enabled
435458 # Python too.
436- abi3t = bool (sysconfig .get_config_var ('Py_GIL_DISABLED' )) and sys .version_info >= (3 , 15 )
437- expected_abi = 'abi3t' if abi3t else 'abi3'
459+ abi3 = _get_abi3_suffix ()
438460
439461 # Verify stable ABI compatibility: examine files installed
440462 # in {platlib} that look like extension modules, and raise
@@ -443,12 +465,11 @@ def _stable_abi(self) -> Optional[str]:
443465 for entry in self ._manifest ['platlib' ]:
444466 match = _EXTENSION_SUFFIX_REGEX .match (entry .dst .name )
445467 if match :
446- abi = match .group ('abi' )
447- if abi is not None and abi != expected_abi :
468+ if match .group ('abi' ) is not None and match .group ('suffix' ) != abi3 :
448469 raise BuildError (
449470 f'The package declares compatibility with Python limited API but extension '
450471 f'module { os .fspath (entry .dst )!r} is tagged for a specific Python version.' )
451- return 'abi3.abi3t' if abi3t else 'abi3'
472+ return 'abi3.abi3t' if abi3 . startswith ( '. abi3t' ) else 'abi3'
452473 return None
453474
454475 def _install_path (self , wheel_file : mesonpy ._wheelfile .WheelFile , origin : Path , destination : pathlib .Path ) -> None :
0 commit comments