From a5e6e52b7f22a059c8e0d9cdbd840f2df2ba6154 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 21 Feb 2024 10:52:08 +0100 Subject: [PATCH] gh-115754: Export Py_None, Py_False, Py_True as symbols In the limited C API and the stable ABI, implement Py_None, Py_False and Py_True constants are symbols. --- Include/boolobject.h | 8 +++++++- Include/object.h | 7 +++++++ Lib/test/test_stable_abi_ctypes.py | 10 ++++++++++ .../2024-02-21-10-54-27.gh-issue-115754.RSL-q0.rst | 6 ++++++ Objects/boolobject.c | 7 +++++++ Objects/object.c | 5 +++++ 6 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/C API/2024-02-21-10-54-27.gh-issue-115754.RSL-q0.rst diff --git a/Include/boolobject.h b/Include/boolobject.h index 19aef5b1b87c6ae..2a739812fa278bd 100644 --- a/Include/boolobject.h +++ b/Include/boolobject.h @@ -17,9 +17,15 @@ extern "C" { PyAPI_DATA(PyLongObject) _Py_FalseStruct; PyAPI_DATA(PyLongObject) _Py_TrueStruct; -/* Use these macros */ +// Export symbols in the stable ABI +PyAPI_DATA(PyLongObject*) Py_False; +PyAPI_DATA(PyLongObject*) Py_True; + +#ifndef Py_LIMITED_API +// Implement Py_False and Py_True as macros in the non-limited C API #define Py_False _PyObject_CAST(&_Py_FalseStruct) #define Py_True _PyObject_CAST(&_Py_TrueStruct) +#endif // 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 05187fe5dc4f20d..71af3cfb08b6c90 100644 --- a/Include/object.h +++ b/Include/object.h @@ -1070,7 +1070,14 @@ _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 */ + +// Export the symbol in the stable ABI +PyAPI_DATA(PyObject*) Py_None; + +#ifndef Py_LIMITED_API +// Implement Py_None as a macro in the non-limited C API #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); diff --git a/Lib/test/test_stable_abi_ctypes.py b/Lib/test/test_stable_abi_ctypes.py index 8bd373976426ef3..7747413e9550325 100644 --- a/Lib/test/test_stable_abi_ctypes.py +++ b/Lib/test/test_stable_abi_ctypes.py @@ -40,6 +40,16 @@ def test_windows_feature_macros(self): with self.subTest(name): self.assertEqual(feature_macros[name], value) + def test_constants(self): + process = ctypes_test.pythonapi + def get_object(name): + return ctypes_test.py_object.in_dll(process, name).value + + self.assertIs(get_object("Py_None"), None) + self.assertIs(get_object("Py_False"), False) + self.assertIs(get_object("Py_True"), True) + + SYMBOL_NAMES = ( "PyAIter_Check", diff --git a/Misc/NEWS.d/next/C API/2024-02-21-10-54-27.gh-issue-115754.RSL-q0.rst b/Misc/NEWS.d/next/C API/2024-02-21-10-54-27.gh-issue-115754.RSL-q0.rst new file mode 100644 index 000000000000000..f10a0d2a08f0eda --- /dev/null +++ b/Misc/NEWS.d/next/C API/2024-02-21-10-54-27.gh-issue-115754.RSL-q0.rst @@ -0,0 +1,6 @@ +In the limited C API and the stable ABI, implement ``Py_None``, ``Py_False`` +and ``Py_True`` constants are symbols, rather than implementing them as +macros. So they can be loaded by dlopen/dlsym in an embedded in Python, +rather than having to reimplement these macros manually. In the non-limited +C API, these constants are still implemented as macros. Patch by Victor +Stinner. diff --git a/Objects/boolobject.c b/Objects/boolobject.c index fb48dcbeca78503..c05f89812165476 100644 --- a/Objects/boolobject.c +++ b/Objects/boolobject.c @@ -225,3 +225,10 @@ struct _longobject _Py_TrueStruct = { { 1 } } }; + +// Stable ABI: export symbols + +#undef Py_False +#undef Py_True +PyLongObject *Py_False = _Py_CAST(PyLongObject*, &_Py_FalseStruct); +PyLongObject *Py_True = _Py_CAST(PyLongObject*, &_Py_TrueStruct); diff --git a/Objects/object.c b/Objects/object.c index 23eab8288a41e8b..3fdcb5320d4f39b 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -2970,3 +2970,8 @@ _Py_SetRefcnt(PyObject *ob, Py_ssize_t refcnt) { Py_SET_REFCNT(ob, refcnt); } + +#undef Py_None + +// Export the symbol in the stable ABI +PyObject *Py_None = &_Py_NoneStruct;