Skip to content

Commit 2452324

Browse files
encukoungoldbaum
andauthored
gh-146636: PEP 803: add Py_TARGET_ABI3T and .abi3t.so extension (GH-146637)
- Add Py_TARGET_ABI3T macro. - Add ".abi3t.so" to importlib EXTENSION_SUFFIXES. - Remove ".abi3.so" from importlib EXTENSION_SUFFIXES on Free Threading. - Adjust tests This is part of the implementation for PEP-803. Detailed documentation to come later. Co-authored-by: Nathan Goldbaum <nathan.goldbaum@gmail.com>
1 parent 08c5d3d commit 2452324

File tree

7 files changed

+69
-21
lines changed

7 files changed

+69
-21
lines changed

Doc/c-api/stable.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,16 @@ Contents of the Limited API are :ref:`listed below <limited-api-list>`.
8888
You can also define ``Py_LIMITED_API`` to ``3``. This works the same as
8989
``0x03020000`` (Python 3.2, the version that introduced Limited API).
9090

91+
.. c:macro:: Py_TARGET_ABI3T
92+
93+
Define this macro before including ``Python.h`` to opt in to only use
94+
the Limited API for :term:`free-threaded builds <free-threaded build>`,
95+
and to select the Limited API version.
96+
97+
.. seealso:: :pep:`803`
98+
99+
.. versionadded:: next
100+
91101

92102
.. _stable-abi:
93103

Include/Python.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,6 @@
4747
#endif
4848

4949
#if defined(Py_GIL_DISABLED)
50-
# if defined(Py_LIMITED_API) && !defined(_Py_OPAQUE_PYOBJECT)
51-
# error "Py_LIMITED_API is not currently supported in the free-threaded build"
52-
# endif
53-
5450
# if defined(_MSC_VER)
5551
# include <intrin.h> // __readgsqword()
5652
# endif

Include/patchlevel.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,32 @@
6161
#define PYTHON_ABI_VERSION 3
6262
#define PYTHON_ABI_STRING "3"
6363

64+
65+
/* Stable ABI for free-threaded builds (introduced in PEP 803)
66+
is enabled by one of:
67+
- Py_TARGET_ABI3T, or
68+
- Py_LIMITED_API and Py_GIL_DISABLED.
69+
"Output" macros to be used internally:
70+
- Py_LIMITED_API (defines the subset of API we expose)
71+
- _Py_OPAQUE_PYOBJECT (additionally hides what's ABI-incompatible between
72+
free-threaded & GIL)
73+
(Don't use Py_TARGET_ABI3T directly: it's currently only used to set these
74+
2 macros. It's also available for users' convenience.)
75+
*/
76+
#if defined(Py_LIMITED_API) && defined(Py_GIL_DISABLED) \
77+
&& !defined(Py_TARGET_ABI3T)
78+
# define Py_TARGET_ABI3T Py_LIMITED_API
79+
#endif
80+
#if defined(Py_TARGET_ABI3T)
81+
# define _Py_OPAQUE_PYOBJECT
82+
# if !defined(Py_LIMITED_API)
83+
# define Py_LIMITED_API Py_TARGET_ABI3T
84+
# elif Py_LIMITED_API > Py_TARGET_ABI3T
85+
// if both are defined, use the *lower* version,
86+
// i.e. maximum compatibility
87+
# undef Py_LIMITED_API
88+
# define Py_LIMITED_API Py_TARGET_ABI3T
89+
# endif
90+
#endif
91+
6492
#endif //_Py_PATCHLEVEL_H

Lib/test/test_cext/__init__.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,15 @@ def test_build(self):
3838
self.check_build('_test_cext')
3939

4040
def check_build(self, extension_name, std=None, limited=False,
41-
opaque_pyobject=False):
41+
abi3t=False):
4242
venv_dir = 'env'
4343
with support.setup_venv_with_pip_setuptools(venv_dir) as python_exe:
4444
self._check_build(extension_name, python_exe,
4545
std=std, limited=limited,
46-
opaque_pyobject=opaque_pyobject)
46+
abi3t=abi3t)
4747

4848
def _check_build(self, extension_name, python_exe, std, limited,
49-
opaque_pyobject):
49+
abi3t):
5050
pkg_dir = 'pkg'
5151
os.mkdir(pkg_dir)
5252
shutil.copy(SETUP, os.path.join(pkg_dir, os.path.basename(SETUP)))
@@ -60,8 +60,8 @@ def run_cmd(operation, cmd):
6060
env['CPYTHON_TEST_STD'] = std
6161
if limited:
6262
env['CPYTHON_TEST_LIMITED'] = '1'
63-
if opaque_pyobject:
64-
env['CPYTHON_TEST_OPAQUE_PYOBJECT'] = '1'
63+
if abi3t:
64+
env['CPYTHON_TEST_ABI3T'] = '1'
6565
env['CPYTHON_TEST_EXT_NAME'] = extension_name
6666
env['TEST_INTERNAL_C_API'] = str(int(self.TEST_INTERNAL_C_API))
6767
if support.verbose:
@@ -116,10 +116,9 @@ def test_build_limited_c11(self):
116116
def test_build_c11(self):
117117
self.check_build('_test_c11_cext', std='c11')
118118

119-
def test_build_opaque_pyobject(self):
120-
# Test with _Py_OPAQUE_PYOBJECT
121-
self.check_build('_test_limited_opaque_cext', limited=True,
122-
opaque_pyobject=True)
119+
def test_build_abi3t(self):
120+
# Test with Py_TARGET_ABI3T
121+
self.check_build('_test_abi3t', abi3t=True)
123122

124123
@unittest.skipIf(support.MS_WINDOWS, "MSVC doesn't support /std:c99")
125124
def test_build_c99(self):

Lib/test/test_cext/setup.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ def main():
5959
std = os.environ.get("CPYTHON_TEST_STD", "")
6060
module_name = os.environ["CPYTHON_TEST_EXT_NAME"]
6161
limited = bool(os.environ.get("CPYTHON_TEST_LIMITED", ""))
62-
opaque_pyobject = bool(os.environ.get("CPYTHON_TEST_OPAQUE_PYOBJECT", ""))
62+
abi3t = bool(os.environ.get("CPYTHON_TEST_ABI3T", ""))
6363
internal = bool(int(os.environ.get("TEST_INTERNAL_C_API", "0")))
6464

6565
sources = [SOURCE]
@@ -91,14 +91,12 @@ def main():
9191
# CC env var overrides sysconfig CC variable in setuptools
9292
os.environ['CC'] = cmd
9393

94-
# Define Py_LIMITED_API macro
94+
# Define opt-in macros
9595
if limited:
96-
version = sys.hexversion
97-
cflags.append(f'-DPy_LIMITED_API={version:#x}')
96+
cflags.append(f'-DPy_LIMITED_API={sys.hexversion:#x}')
9897

99-
# Define _Py_OPAQUE_PYOBJECT macro
100-
if opaque_pyobject:
101-
cflags.append(f'-D_Py_OPAQUE_PYOBJECT')
98+
if abi3t:
99+
cflags.append(f'-DPy_TARGET_ABI3T={sys.hexversion:#x}')
102100

103101
if internal:
104102
cflags.append('-DTEST_INTERNAL_C_API=1')

Lib/test/test_importlib/extension/test_finder.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from test.support import is_apple_mobile
1+
from test.support import is_apple_mobile, Py_GIL_DISABLED
22
from test.test_importlib import abc, util
33

44
machinery = util.import_importlib('importlib.machinery')
@@ -59,6 +59,20 @@ def test_module(self):
5959
def test_failure(self):
6060
self.assertIsNone(self.find_spec('asdfjkl;'))
6161

62+
def test_abi3_extension_suffixes(self):
63+
suffixes = self.machinery.EXTENSION_SUFFIXES
64+
if 'win32' in sys.platform:
65+
# Either "_d.pyd" or ".pyd" must be in suffixes
66+
self.assertTrue({"_d.pyd", ".pyd"}.intersection(suffixes))
67+
elif 'cygwin' in sys.platform:
68+
pass
69+
else:
70+
if Py_GIL_DISABLED:
71+
self.assertNotIn(".abi3.so", suffixes)
72+
else:
73+
self.assertIn(".abi3.so", suffixes)
74+
self.assertIn(".abi3t.so", suffixes)
75+
6276

6377
(Frozen_FinderTests,
6478
Source_FinderTests

Python/dynload_shlib.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,10 @@ const char *_PyImport_DynLoadFiletab[] = {
4444
#ifdef ALT_SOABI
4545
"." ALT_SOABI ".so",
4646
#endif
47+
#ifndef Py_GIL_DISABLED
4748
".abi" PYTHON_ABI_STRING ".so",
49+
#endif /* Py_GIL_DISABLED */
50+
".abi" PYTHON_ABI_STRING "t.so",
4851
".so",
4952
#endif /* __CYGWIN__ */
5053
NULL,

0 commit comments

Comments
 (0)