From 880278de17828c54d4f945ad0301e95ba1ab7159 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sun, 10 Mar 2024 21:09:20 +0100 Subject: [PATCH 1/2] gh-115754: Get singletons by function calls in lmited C API 3.13 In the limited C API version 3.13, getting Py_None, Py_False, Py_True, Py_Ellipsis and Py_NotImplemented singletons is now implemented as function calls at the stable ABI level to hide implementation details. Getting these constants still return borrowed references. --- Doc/whatsnew/3.13.rst | 5 +++++ Include/boolobject.h | 13 ++++++++++--- Include/object.h | 16 ++++++++++++++-- Include/sliceobject.h | 7 ++++++- ...024-03-11-09-24-35.gh-issue-115754.zLdv82.rst | 5 +++++ Misc/stable_abi.toml | 10 ++++++++++ Objects/longobject.c | 11 +++++++++-- Objects/object.c | 8 ++++++++ Objects/sliceobject.c | 6 ++++++ 9 files changed, 73 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2024-03-11-09-24-35.gh-issue-115754.zLdv82.rst diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 51939909000960..2b7b84944f4594 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -1645,6 +1645,11 @@ New Features between native integer types and Python :class:`int` objects. (Contributed by Steve Dower in :gh:`111140`.) +* In the limited C API version 3.13, getting ``Py_None``, ``Py_False``, + ``Py_True``, ``Py_Ellipsis`` and ``Py_NotImplemented`` singletons is now + implemented as function calls at the stable ABI level to hide implementation + details. Getting these constants still return borrowed references. + (Contributed by Victor Stinner in :gh:`115754`.) Porting to Python 3.13 ---------------------- diff --git a/Include/boolobject.h b/Include/boolobject.h index 19aef5b1b87c6a..4345337a4d6551 100644 --- a/Include/boolobject.h +++ b/Include/boolobject.h @@ -16,10 +16,17 @@ extern "C" { /* Don't use these directly */ PyAPI_DATA(PyLongObject) _Py_FalseStruct; PyAPI_DATA(PyLongObject) _Py_TrueStruct; +PyAPI_FUNC(PyObject*) _Py_GetFalse(void); +PyAPI_FUNC(PyObject*) _Py_GetTrue(void); + +#if defined(Py_LIMITED_API) && Py_LIMITED_API+0 >= 0x030D0000 +# define Py_False _Py_GetFalse() +# define Py_True _Py_GetTrue() +#else +# define Py_False _PyObject_CAST(&_Py_FalseStruct) +# define Py_True _PyObject_CAST(&_Py_TrueStruct) +#endif -/* Use these macros */ -#define Py_False _PyObject_CAST(&_Py_FalseStruct) -#define Py_True _PyObject_CAST(&_Py_TrueStruct) // Test if an object is the True singleton, the same as "x is True" in Python. PyAPI_FUNC(int) Py_IsTrue(PyObject *x); diff --git a/Include/object.h b/Include/object.h index 05187fe5dc4f20..2d55358673fda0 100644 --- a/Include/object.h +++ b/Include/object.h @@ -1070,7 +1070,13 @@ _Py_NoneStruct is an object of undefined type which can be used in contexts where NULL (nil) is not suitable (since NULL often means 'error'). */ PyAPI_DATA(PyObject) _Py_NoneStruct; /* Don't use this directly */ -#define Py_None (&_Py_NoneStruct) +PyAPI_FUNC(PyObject*) _Py_GetNone(void); + +#if defined(Py_LIMITED_API) && Py_LIMITED_API+0 >= 0x030D0000 +# define Py_None _Py_GetNone() +#else +# define Py_None (&_Py_NoneStruct) +#endif // Test if an object is the None singleton, the same as "x is None" in Python. PyAPI_FUNC(int) Py_IsNone(PyObject *x); @@ -1084,7 +1090,13 @@ Py_NotImplemented is a singleton used to signal that an operation is not implemented for a given type combination. */ PyAPI_DATA(PyObject) _Py_NotImplementedStruct; /* Don't use this directly */ -#define Py_NotImplemented (&_Py_NotImplementedStruct) +PyAPI_FUNC(PyObject*) _Py_GetNotImplemented(void); + +#if defined(Py_LIMITED_API) && Py_LIMITED_API+0 >= 0x030D0000 +# define Py_NotImplemented _Py_GetNotImplemented() +#else +# define Py_NotImplemented (&_Py_NotImplementedStruct) +#endif /* Macro for returning Py_NotImplemented from a function */ #define Py_RETURN_NOTIMPLEMENTED return Py_NotImplemented diff --git a/Include/sliceobject.h b/Include/sliceobject.h index c13863f27c2e63..5ae71dd1469f43 100644 --- a/Include/sliceobject.h +++ b/Include/sliceobject.h @@ -7,8 +7,13 @@ extern "C" { /* The unique ellipsis object "..." */ PyAPI_DATA(PyObject) _Py_EllipsisObject; /* Don't use this directly */ +PyAPI_FUNC(PyObject*) _Py_GetEllipsis(void); -#define Py_Ellipsis (&_Py_EllipsisObject) +#if defined(Py_LIMITED_API) && Py_LIMITED_API+0 >= 0x030D0000 +# define Py_Ellipsis _Py_GetEllipsis() +#else +# define Py_Ellipsis (&_Py_EllipsisObject) +#endif /* Slice object interface */ diff --git a/Misc/NEWS.d/next/C API/2024-03-11-09-24-35.gh-issue-115754.zLdv82.rst b/Misc/NEWS.d/next/C API/2024-03-11-09-24-35.gh-issue-115754.zLdv82.rst new file mode 100644 index 00000000000000..feff0c0897eae1 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2024-03-11-09-24-35.gh-issue-115754.zLdv82.rst @@ -0,0 +1,5 @@ +In the limited C API version 3.13, getting ``Py_None``, ``Py_False``, +``Py_True``, ``Py_Ellipsis`` and ``Py_NotImplemented`` singletons is now +implemented as function calls at the stable ABI level to hide implementation +details. Getting these constants still return borrowed references. Patch by +Victor Stinner. diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml index ca7cf02961571e..8af51c9437b234 100644 --- a/Misc/stable_abi.toml +++ b/Misc/stable_abi.toml @@ -2496,3 +2496,13 @@ [typedef.PyCFunctionFastWithKeywords] added = '3.13' # "abi-only" since 3.10. (Same story as PyCFunctionFast.) +[typedef._Py_GetNone] + added = '3.13' +[typedef._Py_GetFalse] + added = '3.13' +[typedef._Py_GetTrue] + added = '3.13' +[typedef._Py_GetEllipsis] + added = '3.13' +[typedef._Py_GetNotImplemented] + added = '3.13' diff --git a/Objects/longobject.c b/Objects/longobject.c index 2d1c6ad788e281..f8094f412cb24d 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -6592,16 +6592,23 @@ _PyLong_FiniTypes(PyInterpreterState *interp) _PyStructSequence_FiniBuiltin(interp, &Int_InfoType); } -#undef PyUnstable_Long_IsCompact +// -- Stable ABI ------------------------------------------------------------ + +#undef PyUnstable_Long_IsCompact int PyUnstable_Long_IsCompact(const PyLongObject* op) { return _PyLong_IsCompact(op); } #undef PyUnstable_Long_CompactValue - Py_ssize_t PyUnstable_Long_CompactValue(const PyLongObject* op) { return _PyLong_CompactValue(op); } + +PyObject* _Py_GetFalse(void) +{ return _PyObject_CAST(&_Py_FalseStruct); } + +PyObject* _Py_GetTrue(void) +{ return _PyObject_CAST(&_Py_TrueStruct); } diff --git a/Objects/object.c b/Objects/object.c index df14fe0c6fbfec..89afb46202cf48 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -2920,6 +2920,8 @@ PyObject_GET_WEAKREFS_LISTPTR(PyObject *op) } +// -- Stable ABI ------------------------------------------------------------ + #undef Py_NewRef #undef Py_XNewRef @@ -2970,3 +2972,9 @@ _Py_SetRefcnt(PyObject *ob, Py_ssize_t refcnt) { Py_SET_REFCNT(ob, refcnt); } + +PyObject* _Py_GetNone(void) +{ return &_Py_NoneStruct; } + +PyObject* _Py_GetNotImplemented(void) +{ return &_Py_NotImplementedStruct; } diff --git a/Objects/sliceobject.c b/Objects/sliceobject.c index 7333aea91e5648..fd4dbad658d09e 100644 --- a/Objects/sliceobject.c +++ b/Objects/sliceobject.c @@ -721,3 +721,9 @@ PyTypeObject PySlice_Type = { 0, /* tp_alloc */ slice_new, /* tp_new */ }; + + +// -- Stable ABI ------------------------------------------------------------ + +PyObject* _Py_GetEllipsis(void) +{ return &_Py_EllipsisObject; } From 060283f67f7d5511e26c1dd7d4dcbf2c9e3ab3e3 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 11 Mar 2024 13:03:43 +0100 Subject: [PATCH 2/2] Fix the stable ABI --- Lib/test/test_stable_abi_ctypes.py | 5 +++++ Misc/stable_abi.toml | 15 ++++++++++----- PC/python3dll.c | 5 +++++ 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_stable_abi_ctypes.py b/Lib/test/test_stable_abi_ctypes.py index 8bd373976426ef..88b3bf948fb4e0 100644 --- a/Lib/test/test_stable_abi_ctypes.py +++ b/Lib/test/test_stable_abi_ctypes.py @@ -914,6 +914,11 @@ def test_windows_feature_macros(self): "_Py_DecRef", "_Py_EllipsisObject", "_Py_FalseStruct", + "_Py_GetEllipsis", + "_Py_GetFalse", + "_Py_GetNone", + "_Py_GetNotImplemented", + "_Py_GetTrue", "_Py_IncRef", "_Py_NoneStruct", "_Py_NotImplementedStruct", diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml index 8af51c9437b234..5f66dd8c46940a 100644 --- a/Misc/stable_abi.toml +++ b/Misc/stable_abi.toml @@ -2496,13 +2496,18 @@ [typedef.PyCFunctionFastWithKeywords] added = '3.13' # "abi-only" since 3.10. (Same story as PyCFunctionFast.) -[typedef._Py_GetNone] +[function._Py_GetNone] added = '3.13' -[typedef._Py_GetFalse] + abi_only = true +[function._Py_GetFalse] added = '3.13' -[typedef._Py_GetTrue] + abi_only = true +[function._Py_GetTrue] added = '3.13' -[typedef._Py_GetEllipsis] + abi_only = true +[function._Py_GetEllipsis] added = '3.13' -[typedef._Py_GetNotImplemented] + abi_only = true +[function._Py_GetNotImplemented] added = '3.13' + abi_only = true diff --git a/PC/python3dll.c b/PC/python3dll.c index aa6bfe2c4022db..033710d77385b3 100755 --- a/PC/python3dll.c +++ b/PC/python3dll.c @@ -17,6 +17,11 @@ EXPORT_FUNC(_Py_BuildValue_SizeT) EXPORT_FUNC(_Py_CheckRecursiveCall) EXPORT_FUNC(_Py_Dealloc) EXPORT_FUNC(_Py_DecRef) +EXPORT_FUNC(_Py_GetEllipsis) +EXPORT_FUNC(_Py_GetFalse) +EXPORT_FUNC(_Py_GetNone) +EXPORT_FUNC(_Py_GetNotImplemented) +EXPORT_FUNC(_Py_GetTrue) EXPORT_FUNC(_Py_IncRef) EXPORT_FUNC(_Py_NegativeRefcount) EXPORT_FUNC(_Py_SetRefcnt)