From bcfc1fda7036d6a0bf59e61a04b78346f765c6b6 Mon Sep 17 00:00:00 2001 From: Stefano Rivera Date: Sun, 11 Aug 2024 10:43:45 +0200 Subject: [PATCH 1/9] gh-122931 Allow stable API extensions to include a multiarch tuple in the filename This permits stable ABI extensions for multiple architectures to be co-installed into the same directory, without clashing with each other, the same way (non-stable ABI) regular extensions can. It is listed below the current .abi3 suffix because setuptools will select the first suffix containing .abi3, as the target filename. We do this to protect older Python versions predating this patch. --- Doc/whatsnew/3.16.rst | 9 +++++++++ .../C_API/2024-08-12-09-48-04.gh-issue-122931.QwHc2l.rst | 2 ++ Python/dynload_shlib.c | 6 ++++++ configure | 4 ++++ configure.ac | 2 ++ pyconfig.h.in | 3 +++ 6 files changed, 26 insertions(+) create mode 100644 Misc/NEWS.d/next/C_API/2024-08-12-09-48-04.gh-issue-122931.QwHc2l.rst diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst index e6565b186a598c..ff0365a94ae99d 100644 --- a/Doc/whatsnew/3.16.rst +++ b/Doc/whatsnew/3.16.rst @@ -81,6 +81,15 @@ Other language changes they reference) alive indefinitely. (Contributed by Łukasz Langa in :gh:`102960`.) +* Stable ABI extensions may now include a multiarch tuple in the + filename, e.g. ``foo.abi3-x86-64-linux-gnu.so``. + This permits stable ABI extensions for multiple architectures to be + co-installed into the same directory, without clashing with each + other, as regular dynamic extensions do. Build tools will not generate + these multiarch tagged filenames, by default, while still supporting + older Python versions that don't recognize these filenames. + (Contributed by Stefano Rivera in :gh:`122931`.) + New modules =========== diff --git a/Misc/NEWS.d/next/C_API/2024-08-12-09-48-04.gh-issue-122931.QwHc2l.rst b/Misc/NEWS.d/next/C_API/2024-08-12-09-48-04.gh-issue-122931.QwHc2l.rst new file mode 100644 index 00000000000000..d1caad2e98482d --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2024-08-12-09-48-04.gh-issue-122931.QwHc2l.rst @@ -0,0 +1,2 @@ +Allow importing stable ABI C extensions that include a multiarch tuple +in their filename, e.g. ``foo.abi3-x86-64-linux-gnu.so``. diff --git a/Python/dynload_shlib.c b/Python/dynload_shlib.c index 0ff88ad330fd09..e2dc8f9e0fc780 100644 --- a/Python/dynload_shlib.c +++ b/Python/dynload_shlib.c @@ -47,8 +47,14 @@ const char *_PyImport_DynLoadFiletab[] = { #endif #ifndef Py_GIL_DISABLED ".abi" PYTHON_ABI_STRING ".so", +#ifdef SOABI_PLATFORM + ".abi" PYTHON_ABI_STRING "-" SOABI_PLATFORM ".so", +#endif /* SOABI_PLATFORM */ #endif /* Py_GIL_DISABLED */ ".abi" PYTHON_ABI_STRING "t.so", +#ifdef SOABI_PLATFORM + ".abi" PYTHON_ABI_STRING "t-" SOABI_PLATFORM ".so", +#endif /* SOABI_PLATFORM */ ".so", #endif /* __CYGWIN__ */ NULL, diff --git a/configure b/configure index c8494891087d04..ba3bfaed7c4772 100755 --- a/configure +++ b/configure @@ -7266,6 +7266,10 @@ case $ac_sys_system in #( ;; esac + +printf "%s\n" "#define SOABI_PLATFORM \"${SOABI_PLATFORM}\"" >>confdefs.h + + if test x$MULTIARCH != x; then MULTIARCH_CPPFLAGS="-DMULTIARCH=\\\"$MULTIARCH\\\"" fi diff --git a/configure.ac b/configure.ac index ad8280349466e0..2d6672e3231f59 100644 --- a/configure.ac +++ b/configure.ac @@ -1212,6 +1212,8 @@ AS_CASE([$ac_sys_system], [SOABI_PLATFORM=$PLATFORM_TRIPLET] ) +AC_DEFINE_UNQUOTED([SOABI_PLATFORM], ["${SOABI_PLATFORM}"], [Platform tag, used in binary module extension filenames.]) + if test x$MULTIARCH != x; then MULTIARCH_CPPFLAGS="-DMULTIARCH=\\\"$MULTIARCH\\\"" fi diff --git a/pyconfig.h.in b/pyconfig.h.in index 2f9b2140d9f191..90cd5cfff9bb2e 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -1938,6 +1938,9 @@ /* The size of '_Bool', as computed by sizeof. */ #undef SIZEOF__BOOL +/* Platform tag, used in binary module extension filenames. */ +#undef SOABI_PLATFORM + /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS From 8d565694a9a18325a0a7885d512d96fa69a2724a Mon Sep 17 00:00:00 2001 From: Stefano Rivera Date: Mon, 18 May 2026 17:23:07 -0700 Subject: [PATCH 2/9] Order the multiarch-tagged extensions first --- Doc/whatsnew/3.16.rst | 6 +++--- Python/dynload_shlib.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst index ff0365a94ae99d..332c0ce00e50f5 100644 --- a/Doc/whatsnew/3.16.rst +++ b/Doc/whatsnew/3.16.rst @@ -85,9 +85,9 @@ Other language changes filename, e.g. ``foo.abi3-x86-64-linux-gnu.so``. This permits stable ABI extensions for multiple architectures to be co-installed into the same directory, without clashing with each - other, as regular dynamic extensions do. Build tools will not generate - these multiarch tagged filenames, by default, while still supporting - older Python versions that don't recognize these filenames. + other, as regular dynamic extensions do. Build may tools will generate + these multiarch tagged filenames, by default, when targeting + compatibility with at least Python 3.15. (Contributed by Stefano Rivera in :gh:`122931`.) diff --git a/Python/dynload_shlib.c b/Python/dynload_shlib.c index e2dc8f9e0fc780..f7f7c922920085 100644 --- a/Python/dynload_shlib.c +++ b/Python/dynload_shlib.c @@ -46,15 +46,15 @@ const char *_PyImport_DynLoadFiletab[] = { "." ALT_SOABI ".so", #endif #ifndef Py_GIL_DISABLED - ".abi" PYTHON_ABI_STRING ".so", #ifdef SOABI_PLATFORM ".abi" PYTHON_ABI_STRING "-" SOABI_PLATFORM ".so", #endif /* SOABI_PLATFORM */ + ".abi" PYTHON_ABI_STRING ".so", #endif /* Py_GIL_DISABLED */ - ".abi" PYTHON_ABI_STRING "t.so", #ifdef SOABI_PLATFORM ".abi" PYTHON_ABI_STRING "t-" SOABI_PLATFORM ".so", #endif /* SOABI_PLATFORM */ + ".abi" PYTHON_ABI_STRING "t.so", ".so", #endif /* __CYGWIN__ */ NULL, From f5d73675c2deb25746aeb662298710852cb80bae Mon Sep 17 00:00:00 2001 From: Stefano Rivera Date: Tue, 19 May 2026 23:49:42 -0400 Subject: [PATCH 3/9] Test for presence of multiarch extension suffixes --- Lib/test/test_importlib/extension/test_finder.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_importlib/extension/test_finder.py b/Lib/test/test_importlib/extension/test_finder.py index dc77fa78a203fd..da7f31d98e4bd4 100644 --- a/Lib/test/test_importlib/extension/test_finder.py +++ b/Lib/test/test_importlib/extension/test_finder.py @@ -5,6 +5,7 @@ import unittest import sys +import sysconfig class FinderTests(abc.FinderTests): @@ -68,11 +69,22 @@ def test_abi3_extension_suffixes(self): pass else: if Py_GIL_DISABLED: - self.assertNotIn(".abi3.so", suffixes) + self.assertFalse(any(".abi3" in suffix) for suffix in suffixes) else: self.assertIn(".abi3.so", suffixes) self.assertIn(".abi3t.so", suffixes) + @unittest.skipIf( + not sysconfig.get_config_var("SOABI_PLATFORM").strip('"'), + "Linux-only test" + ) + def test_multiarch_abi3_extension_suffixes(self): + suffixes = self.machinery.EXTENSION_SUFFIXES + platform = sysconfig.get_config_var("SOABI_PLATFORM").strip('"') + if Py_GIL_DISABLED: + self.assertIn(f".abi3-{platform}.so", suffixes) + self.assertIn(f".abi3t-{platform}.so", suffixes) + (Frozen_FinderTests, Source_FinderTests From e79e570b1bc0c2f4df24bd1b07cd997a92c28be8 Mon Sep 17 00:00:00 2001 From: Stefano Rivera Date: Wed, 20 May 2026 11:10:16 -0400 Subject: [PATCH 4/9] Fix editing error in NEWS and improve clarity --- Doc/whatsnew/3.16.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst index 332c0ce00e50f5..7529d60d2f455c 100644 --- a/Doc/whatsnew/3.16.rst +++ b/Doc/whatsnew/3.16.rst @@ -85,7 +85,7 @@ Other language changes filename, e.g. ``foo.abi3-x86-64-linux-gnu.so``. This permits stable ABI extensions for multiple architectures to be co-installed into the same directory, without clashing with each - other, as regular dynamic extensions do. Build may tools will generate + other, as regular dynamic extensions do. Build tools will generate these multiarch tagged filenames, by default, when targeting compatibility with at least Python 3.15. (Contributed by Stefano Rivera in :gh:`122931`.) From 46e746d3ec36b9742b5f3ea52bff634450240660 Mon Sep 17 00:00:00 2001 From: Stefano Rivera Date: Wed, 20 May 2026 11:41:42 -0400 Subject: [PATCH 5/9] Fix up the test --- Lib/test/test_importlib/extension/test_finder.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_importlib/extension/test_finder.py b/Lib/test/test_importlib/extension/test_finder.py index da7f31d98e4bd4..686f502bf5b368 100644 --- a/Lib/test/test_importlib/extension/test_finder.py +++ b/Lib/test/test_importlib/extension/test_finder.py @@ -69,19 +69,21 @@ def test_abi3_extension_suffixes(self): pass else: if Py_GIL_DISABLED: - self.assertFalse(any(".abi3" in suffix) for suffix in suffixes) + self.assertNotIn(".abi3.so", suffixes) else: self.assertIn(".abi3.so", suffixes) self.assertIn(".abi3t.so", suffixes) @unittest.skipIf( - not sysconfig.get_config_var("SOABI_PLATFORM").strip('"'), + not (sysconfig.get_config_var("SOABI_PLATFORM") or "").strip('"'), "Linux-only test" ) def test_multiarch_abi3_extension_suffixes(self): suffixes = self.machinery.EXTENSION_SUFFIXES platform = sysconfig.get_config_var("SOABI_PLATFORM").strip('"') if Py_GIL_DISABLED: + self.assertNotIn(f".abi3-{platform}.so", suffixes) + else: self.assertIn(f".abi3-{platform}.so", suffixes) self.assertIn(f".abi3t-{platform}.so", suffixes) From 47116a4c5c82324efe5146860ba9e44ff4929ce0 Mon Sep 17 00:00:00 2001 From: Stefano Rivera Date: Fri, 26 Jun 2026 08:08:56 -0400 Subject: [PATCH 6/9] Only target abi3t (for expediency) --- Doc/whatsnew/3.16.rst | 5 +++-- .../test_importlib/extension/test_finder.py | 19 +++++-------------- ...-08-12-09-48-04.gh-issue-122931.QwHc2l.rst | 3 +-- Python/dynload_shlib.c | 6 ++---- 4 files changed, 11 insertions(+), 22 deletions(-) diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst index 7529d60d2f455c..c2c9375e50dff6 100644 --- a/Doc/whatsnew/3.16.rst +++ b/Doc/whatsnew/3.16.rst @@ -81,8 +81,9 @@ Other language changes they reference) alive indefinitely. (Contributed by Łukasz Langa in :gh:`102960`.) -* Stable ABI extensions may now include a multiarch tuple in the - filename, e.g. ``foo.abi3-x86-64-linux-gnu.so``. +* Stable ABI extensions (with free-threaded compatibility) may now + include a multiarch tuple in the filename, e.g. + ``foo.abi3t-x86-64-linux-gnu.so``. This permits stable ABI extensions for multiple architectures to be co-installed into the same directory, without clashing with each other, as regular dynamic extensions do. Build tools will generate diff --git a/Lib/test/test_importlib/extension/test_finder.py b/Lib/test/test_importlib/extension/test_finder.py index 686f502bf5b368..c460ff01d9f0d2 100644 --- a/Lib/test/test_importlib/extension/test_finder.py +++ b/Lib/test/test_importlib/extension/test_finder.py @@ -62,6 +62,7 @@ def test_failure(self): def test_abi3_extension_suffixes(self): suffixes = self.machinery.EXTENSION_SUFFIXES + platform = sysconfig.get_config_var("SOABI_PLATFORM") if 'win32' in sys.platform: # Either "_d.pyd" or ".pyd" must be in suffixes self.assertTrue({"_d.pyd", ".pyd"}.intersection(suffixes)) @@ -72,20 +73,10 @@ def test_abi3_extension_suffixes(self): self.assertNotIn(".abi3.so", suffixes) else: self.assertIn(".abi3.so", suffixes) - self.assertIn(".abi3t.so", suffixes) - - @unittest.skipIf( - not (sysconfig.get_config_var("SOABI_PLATFORM") or "").strip('"'), - "Linux-only test" - ) - def test_multiarch_abi3_extension_suffixes(self): - suffixes = self.machinery.EXTENSION_SUFFIXES - platform = sysconfig.get_config_var("SOABI_PLATFORM").strip('"') - if Py_GIL_DISABLED: - self.assertNotIn(f".abi3-{platform}.so", suffixes) - else: - self.assertIn(f".abi3-{platform}.so", suffixes) - self.assertIn(f".abi3t-{platform}.so", suffixes) + if platform: + self.assertIn(f".abi3t-{platform}.so", suffixes) + else: + self.assertIn(".abi3t.so", suffixes) (Frozen_FinderTests, diff --git a/Misc/NEWS.d/next/C_API/2024-08-12-09-48-04.gh-issue-122931.QwHc2l.rst b/Misc/NEWS.d/next/C_API/2024-08-12-09-48-04.gh-issue-122931.QwHc2l.rst index d1caad2e98482d..734547c86dbfbd 100644 --- a/Misc/NEWS.d/next/C_API/2024-08-12-09-48-04.gh-issue-122931.QwHc2l.rst +++ b/Misc/NEWS.d/next/C_API/2024-08-12-09-48-04.gh-issue-122931.QwHc2l.rst @@ -1,2 +1 @@ -Allow importing stable ABI C extensions that include a multiarch tuple -in their filename, e.g. ``foo.abi3-x86-64-linux-gnu.so``. +Linux: Include multiarch tuples in ``abi3t`` stable ABI C extension filenames, e.g. ``foo.abi3t-x86-64-linux-gnu.so``. diff --git a/Python/dynload_shlib.c b/Python/dynload_shlib.c index f7f7c922920085..a60cd1850c7df1 100644 --- a/Python/dynload_shlib.c +++ b/Python/dynload_shlib.c @@ -46,15 +46,13 @@ const char *_PyImport_DynLoadFiletab[] = { "." ALT_SOABI ".so", #endif #ifndef Py_GIL_DISABLED -#ifdef SOABI_PLATFORM - ".abi" PYTHON_ABI_STRING "-" SOABI_PLATFORM ".so", -#endif /* SOABI_PLATFORM */ ".abi" PYTHON_ABI_STRING ".so", #endif /* Py_GIL_DISABLED */ #ifdef SOABI_PLATFORM ".abi" PYTHON_ABI_STRING "t-" SOABI_PLATFORM ".so", -#endif /* SOABI_PLATFORM */ +#else ".abi" PYTHON_ABI_STRING "t.so", +#endif /* SOABI_PLATFORM */ ".so", #endif /* __CYGWIN__ */ NULL, From b4a638976aa4f25d9549cffdb10cad36a38292fd Mon Sep 17 00:00:00 2001 From: Stefano Rivera Date: Sat, 18 Jul 2026 11:40:45 -0300 Subject: [PATCH 7/9] Keep .abi3t.so, as requested by the SC --- Lib/test/test_importlib/extension/test_finder.py | 3 +-- .../next/C_API/2024-08-12-09-48-04.gh-issue-122931.QwHc2l.rst | 2 +- Python/dynload_shlib.c | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_importlib/extension/test_finder.py b/Lib/test/test_importlib/extension/test_finder.py index c460ff01d9f0d2..5050c7afc8428b 100644 --- a/Lib/test/test_importlib/extension/test_finder.py +++ b/Lib/test/test_importlib/extension/test_finder.py @@ -75,8 +75,7 @@ def test_abi3_extension_suffixes(self): self.assertIn(".abi3.so", suffixes) if platform: self.assertIn(f".abi3t-{platform}.so", suffixes) - else: - self.assertIn(".abi3t.so", suffixes) + self.assertIn(".abi3t.so", suffixes) (Frozen_FinderTests, diff --git a/Misc/NEWS.d/next/C_API/2024-08-12-09-48-04.gh-issue-122931.QwHc2l.rst b/Misc/NEWS.d/next/C_API/2024-08-12-09-48-04.gh-issue-122931.QwHc2l.rst index 734547c86dbfbd..4ff31a66f8562f 100644 --- a/Misc/NEWS.d/next/C_API/2024-08-12-09-48-04.gh-issue-122931.QwHc2l.rst +++ b/Misc/NEWS.d/next/C_API/2024-08-12-09-48-04.gh-issue-122931.QwHc2l.rst @@ -1 +1 @@ -Linux: Include multiarch tuples in ``abi3t`` stable ABI C extension filenames, e.g. ``foo.abi3t-x86-64-linux-gnu.so``. +Linux: Include multiarch tuples in ``abi3t`` stable ABI C extension filenames, e.g. ``foo.abi3t-x86-64-linux-gnu.so``, by default. diff --git a/Python/dynload_shlib.c b/Python/dynload_shlib.c index a60cd1850c7df1..234e94accd685d 100644 --- a/Python/dynload_shlib.c +++ b/Python/dynload_shlib.c @@ -50,9 +50,8 @@ const char *_PyImport_DynLoadFiletab[] = { #endif /* Py_GIL_DISABLED */ #ifdef SOABI_PLATFORM ".abi" PYTHON_ABI_STRING "t-" SOABI_PLATFORM ".so", -#else - ".abi" PYTHON_ABI_STRING "t.so", #endif /* SOABI_PLATFORM */ + ".abi" PYTHON_ABI_STRING "t.so", ".so", #endif /* __CYGWIN__ */ NULL, From 71ff85567ea86ca7c0e584017715cc819817c19a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sat, 18 Jul 2026 18:44:14 +0200 Subject: [PATCH 8/9] Add SOABI_PLATFORM to test.pythoninfo Add ALT_SOABI and SOABI_PLATFORM to test.pythoninfo. Add also EXTENSION_SUFFIXES of importlib.machinery. --- Lib/test/pythoninfo.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Lib/test/pythoninfo.py b/Lib/test/pythoninfo.py index 3227b91bd82a86..a870b3e5243869 100644 --- a/Lib/test/pythoninfo.py +++ b/Lib/test/pythoninfo.py @@ -570,6 +570,7 @@ def collect_sysconfig(info_add): for name in ( 'ABIFLAGS', + 'ALT_SOABI', 'ANDROID_API_LEVEL', 'CC', 'CCSHARED', @@ -595,6 +596,7 @@ def collect_sysconfig(info_add): 'Py_REMOTE_DEBUG', 'SHELL', 'SOABI', + 'SOABI_PLATFORM', 'TEST_MODULES', 'VAPTH', 'abs_builddir', @@ -1316,6 +1318,12 @@ def collect_system(info_add): info_add('system.hardware', hardware) +def collect_importlib(info_add): + import importlib.machinery + info_add('importlib.extension_suffixes', + importlib.machinery.EXTENSION_SUFFIXES) + + def collect_info(info): error = False info_add = info.add @@ -1358,6 +1366,7 @@ def collect_info(info): collect_zstd, collect_libregrtest_utils, collect_system, + collect_importlib, # Collecting from tests should be last as they have side effects. collect_test_socket, From 5736916b8ceec4c49240d8a775afa65f472aadc6 Mon Sep 17 00:00:00 2001 From: Stefano Rivera Date: Sat, 18 Jul 2026 16:19:59 -0300 Subject: [PATCH 9/9] Only define SOABI_PLATFORM if it is set --- configure | 2 ++ configure.ac | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/configure b/configure index ba3bfaed7c4772..2041a15b1a1486 100755 --- a/configure +++ b/configure @@ -7266,9 +7266,11 @@ case $ac_sys_system in #( ;; esac +if test x$SOABI_PLATFORM != x; then printf "%s\n" "#define SOABI_PLATFORM \"${SOABI_PLATFORM}\"" >>confdefs.h +fi if test x$MULTIARCH != x; then MULTIARCH_CPPFLAGS="-DMULTIARCH=\\\"$MULTIARCH\\\"" diff --git a/configure.ac b/configure.ac index 2d6672e3231f59..c30d98af28aa07 100644 --- a/configure.ac +++ b/configure.ac @@ -1212,7 +1212,9 @@ AS_CASE([$ac_sys_system], [SOABI_PLATFORM=$PLATFORM_TRIPLET] ) -AC_DEFINE_UNQUOTED([SOABI_PLATFORM], ["${SOABI_PLATFORM}"], [Platform tag, used in binary module extension filenames.]) +if test x$SOABI_PLATFORM != x; then + AC_DEFINE_UNQUOTED([SOABI_PLATFORM], ["${SOABI_PLATFORM}"], [Platform tag, used in binary module extension filenames.]) +fi if test x$MULTIARCH != x; then MULTIARCH_CPPFLAGS="-DMULTIARCH=\\\"$MULTIARCH\\\""