Skip to content

gh-115754: Get singletons via function calls #116572

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Doc/whatsnew/3.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
----------------------
Expand Down
13 changes: 10 additions & 3 deletions Include/boolobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
16 changes: 14 additions & 2 deletions Include/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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
Expand Down
7 changes: 6 additions & 1 deletion Include/sliceobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */

Expand Down
5 changes: 5 additions & 0 deletions Lib/test/test_stable_abi_ctypes.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -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.
15 changes: 15 additions & 0 deletions Misc/stable_abi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2496,3 +2496,18 @@
[typedef.PyCFunctionFastWithKeywords]
added = '3.13'
# "abi-only" since 3.10. (Same story as PyCFunctionFast.)
[function._Py_GetNone]
added = '3.13'
abi_only = true
[function._Py_GetFalse]
added = '3.13'
abi_only = true
[function._Py_GetTrue]
added = '3.13'
abi_only = true
[function._Py_GetEllipsis]
added = '3.13'
abi_only = true
[function._Py_GetNotImplemented]
added = '3.13'
abi_only = true
11 changes: 9 additions & 2 deletions Objects/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -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); }
8 changes: 8 additions & 0 deletions Objects/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -2920,6 +2920,8 @@ PyObject_GET_WEAKREFS_LISTPTR(PyObject *op)
}


// -- Stable ABI ------------------------------------------------------------

#undef Py_NewRef
#undef Py_XNewRef

Expand Down Expand Up @@ -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; }
6 changes: 6 additions & 0 deletions Objects/sliceobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -721,3 +721,9 @@ PyTypeObject PySlice_Type = {
0, /* tp_alloc */
slice_new, /* tp_new */
};


// -- Stable ABI ------------------------------------------------------------

PyObject* _Py_GetEllipsis(void)
{ return &_Py_EllipsisObject; }
5 changes: 5 additions & 0 deletions PC/python3dll.c

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.