https://github.com/python/cpython/commit/8bea6c411d65cd987616b4ecdb86373e4f21f1c6
commit: 8bea6c411d65cd987616b4ecdb86373e4f21f1c6
branch: main
author: Victor Stinner <[email protected]>
committer: vstinner <[email protected]>
date: 2024-03-21T16:07:00Z
summary:

gh-115754: Add Py_GetConstant() function (#116883)

Add Py_GetConstant() and Py_GetConstantBorrowed() functions.

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.

Add _testlimitedcapi/object.c and test_capi/test_object.py to test
Py_GetConstant() and Py_GetConstantBorrowed() functions.

files:
A Lib/test/test_capi/test_object.py
A Misc/NEWS.d/next/C API/2024-03-15-23-55-24.gh-issue-115754.xnzc__.rst
A Misc/NEWS.d/next/C API/2024-03-15-23-57-33.gh-issue-115754.zLdv82.rst
A Modules/_testlimitedcapi/object.c
M Doc/c-api/object.rst
M Doc/data/stable_abi.dat
M Doc/whatsnew/3.13.rst
M Include/boolobject.h
M Include/internal/pycore_object.h
M Include/object.h
M Include/sliceobject.h
M Lib/test/test_stable_abi_ctypes.py
M Misc/stable_abi.toml
M Modules/Setup.stdlib.in
M Modules/_testlimitedcapi.c
M Modules/_testlimitedcapi/parts.h
M Objects/object.c
M PC/python3dll.c
M PCbuild/_testlimitedcapi.vcxproj
M PCbuild/_testlimitedcapi.vcxproj.filters
M Python/pylifecycle.c
M Tools/c-analyzer/cpython/ignored.tsv

diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst
index 12476412799a4f..ba454db9117504 100644
--- a/Doc/c-api/object.rst
+++ b/Doc/c-api/object.rst
@@ -6,6 +6,55 @@ Object Protocol
 ===============
 
 
+.. c:function:: PyObject* Py_GetConstant(unsigned int constant_id)
+
+   Get a :term:`strong reference` to a constant.
+
+   Set an exception and return ``NULL`` if *constant_id* is invalid.
+
+   *constant_id* must be one of these constant identifiers:
+
+   .. c:namespace:: NULL
+
+   ========================================  =====  =========================
+   Constant Identifier                       Value  Returned object
+   ========================================  =====  =========================
+   .. c:macro:: Py_CONSTANT_NONE             ``0``  :py:data:`None`
+   .. c:macro:: Py_CONSTANT_FALSE            ``1``  :py:data:`False`
+   .. c:macro:: Py_CONSTANT_TRUE             ``2``  :py:data:`True`
+   .. c:macro:: Py_CONSTANT_ELLIPSIS         ``3``  :py:data:`Ellipsis`
+   .. c:macro:: Py_CONSTANT_NOT_IMPLEMENTED  ``4``  :py:data:`NotImplemented`
+   .. c:macro:: Py_CONSTANT_ZERO             ``5``  ``0``
+   .. c:macro:: Py_CONSTANT_ONE              ``6``  ``1``
+   .. c:macro:: Py_CONSTANT_EMPTY_STR        ``7``  ``''``
+   .. c:macro:: Py_CONSTANT_EMPTY_BYTES      ``8``  ``b''``
+   .. c:macro:: Py_CONSTANT_EMPTY_TUPLE      ``9``  ``()``
+   ========================================  =====  =========================
+
+   Numeric values are only given for projects which cannot use the constant
+   identifiers.
+
+
+   .. versionadded:: 3.13
+
+   .. impl-detail::
+
+      In CPython, all of these constants are :term:`immortal`.
+
+
+.. c:function:: PyObject* Py_GetConstantBorrowed(unsigned int constant_id)
+
+   Similar to :c:func:`Py_GetConstant`, but return a :term:`borrowed
+   reference`.
+
+   This function is primarily intended for backwards compatibility:
+   using :c:func:`Py_GetConstant` is recommended for new code.
+
+   The reference is borrowed from the interpreter, and is valid until the
+   interpreter finalization.
+   .. versionadded:: 3.13
+
+
 .. c:var:: PyObject* Py_NotImplemented
 
    The ``NotImplemented`` singleton, used to signal that an operation is
diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat
index 9d0ad3d036dac3..565f134290802a 100644
--- a/Doc/data/stable_abi.dat
+++ b/Doc/data/stable_abi.dat
@@ -838,6 +838,8 @@ function,Py_GenericAlias,3.9,,
 var,Py_GenericAliasType,3.9,,
 function,Py_GetBuildInfo,3.2,,
 function,Py_GetCompiler,3.2,,
+function,Py_GetConstant,3.13,,
+function,Py_GetConstantBorrowed,3.13,,
 function,Py_GetCopyright,3.2,,
 function,Py_GetExecPrefix,3.2,,
 function,Py_GetPath,3.2,,
diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst
index 40e2e6a8e03be9..caadf4a3d68fb2 100644
--- a/Doc/whatsnew/3.13.rst
+++ b/Doc/whatsnew/3.13.rst
@@ -1731,6 +1731,11 @@ New Features
   more information.
   (Contributed by Victor Stinner in :gh:`111696`.)
 
+* Add :c:func:`Py_GetConstant` and :c:func:`Py_GetConstantBorrowed` functions
+  to get constants. For example, ``Py_GetConstant(Py_CONSTANT_ZERO)`` returns a
+  :term:`strong reference` to the constant zero.
+  (Contributed by Victor Stinner in :gh:`115754`.)
+
 
 Porting to Python 3.13
 ----------------------
diff --git a/Include/boolobject.h b/Include/boolobject.h
index 19aef5b1b87c6a..3037e61bbf6d0c 100644
--- a/Include/boolobject.h
+++ b/Include/boolobject.h
@@ -18,8 +18,13 @@ PyAPI_DATA(PyLongObject) _Py_FalseStruct;
 PyAPI_DATA(PyLongObject) _Py_TrueStruct;
 
 /* Use these macros */
-#define Py_False _PyObject_CAST(&_Py_FalseStruct)
-#define Py_True _PyObject_CAST(&_Py_TrueStruct)
+#if defined(Py_LIMITED_API) && Py_LIMITED_API+0 >= 0x030D0000
+#  define Py_False Py_GetConstantBorrowed(Py_CONSTANT_FALSE)
+#  define Py_True Py_GetConstantBorrowed(Py_CONSTANT_TRUE)
+#else
+#  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/internal/pycore_object.h b/Include/internal/pycore_object.h
index 759ec4d17b5eb4..13fe543133f11e 100644
--- a/Include/internal/pycore_object.h
+++ b/Include/internal/pycore_object.h
@@ -716,6 +716,8 @@ PyAPI_DATA(PyTypeObject) _PyNotImplemented_Type;
 // Export for the stable ABI.
 PyAPI_DATA(int) _Py_SwappedOp[];
 
+extern void _Py_GetConstant_Init(void);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/Include/object.h b/Include/object.h
index b0c0dba06ca139..67a5e514c421c3 100644
--- a/Include/object.h
+++ b/Include/object.h
@@ -1068,12 +1068,34 @@ static inline PyObject* _Py_XNewRef(PyObject *obj)
 #endif
 
 
+#define Py_CONSTANT_NONE 0
+#define Py_CONSTANT_FALSE 1
+#define Py_CONSTANT_TRUE 2
+#define Py_CONSTANT_ELLIPSIS 3
+#define Py_CONSTANT_NOT_IMPLEMENTED 4
+#define Py_CONSTANT_ZERO 5
+#define Py_CONSTANT_ONE 6
+#define Py_CONSTANT_EMPTY_STR 7
+#define Py_CONSTANT_EMPTY_BYTES 8
+#define Py_CONSTANT_EMPTY_TUPLE 9
+
+#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030d0000
+PyAPI_FUNC(PyObject*) Py_GetConstant(unsigned int constant_id);
+PyAPI_FUNC(PyObject*) Py_GetConstantBorrowed(unsigned int constant_id);
+#endif
+
+
 /*
 _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)
+
+#if defined(Py_LIMITED_API) && Py_LIMITED_API+0 >= 0x030D0000
+#  define Py_None Py_GetConstantBorrowed(Py_CONSTANT_NONE)
+#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);
@@ -1087,7 +1109,12 @@ 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)
+
+#if defined(Py_LIMITED_API) && Py_LIMITED_API+0 >= 0x030D0000
+#  define Py_NotImplemented Py_GetConstantBorrowed(Py_CONSTANT_NOT_IMPLEMENTED)
+#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..35e2ea254ca80a 100644
--- a/Include/sliceobject.h
+++ b/Include/sliceobject.h
@@ -8,7 +8,11 @@ extern "C" {
 
 PyAPI_DATA(PyObject) _Py_EllipsisObject; /* Don't use this directly */
 
-#define Py_Ellipsis (&_Py_EllipsisObject)
+#if defined(Py_LIMITED_API) && Py_LIMITED_API+0 >= 0x030D0000
+#  define Py_Ellipsis Py_GetConstantBorrowed(Py_CONSTANT_ELLIPSIS)
+#else
+#  define Py_Ellipsis (&_Py_EllipsisObject)
+#endif
 
 /* Slice object interface */
 
diff --git a/Lib/test/test_capi/test_object.py 
b/Lib/test/test_capi/test_object.py
new file mode 100644
index 00000000000000..c80e9b653789ad
--- /dev/null
+++ b/Lib/test/test_capi/test_object.py
@@ -0,0 +1,54 @@
+import enum
+import unittest
+from test.support import import_helper
+
+_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
+
+
+class Constant(enum.IntEnum):
+    Py_CONSTANT_NONE = 0
+    Py_CONSTANT_FALSE = 1
+    Py_CONSTANT_TRUE = 2
+    Py_CONSTANT_ELLIPSIS = 3
+    Py_CONSTANT_NOT_IMPLEMENTED = 4
+    Py_CONSTANT_ZERO = 5
+    Py_CONSTANT_ONE = 6
+    Py_CONSTANT_EMPTY_STR = 7
+    Py_CONSTANT_EMPTY_BYTES = 8
+    Py_CONSTANT_EMPTY_TUPLE = 9
+
+    INVALID_CONSTANT = Py_CONSTANT_EMPTY_TUPLE + 1
+
+
+class CAPITest(unittest.TestCase):
+    def check_get_constant(self, get_constant):
+        self.assertIs(get_constant(Constant.Py_CONSTANT_NONE), None)
+        self.assertIs(get_constant(Constant.Py_CONSTANT_FALSE), False)
+        self.assertIs(get_constant(Constant.Py_CONSTANT_TRUE), True)
+        self.assertIs(get_constant(Constant.Py_CONSTANT_ELLIPSIS), Ellipsis)
+        self.assertIs(get_constant(Constant.Py_CONSTANT_NOT_IMPLEMENTED), 
NotImplemented)
+
+        for constant_id, constant_type, value in (
+            (Constant.Py_CONSTANT_ZERO, int, 0),
+            (Constant.Py_CONSTANT_ONE, int, 1),
+            (Constant.Py_CONSTANT_EMPTY_STR, str, ""),
+            (Constant.Py_CONSTANT_EMPTY_BYTES, bytes, b""),
+            (Constant.Py_CONSTANT_EMPTY_TUPLE, tuple, ()),
+        ):
+            with self.subTest(constant_id=constant_id):
+                obj = get_constant(constant_id)
+                self.assertEqual(type(obj), constant_type, obj)
+                self.assertEqual(obj, value)
+
+        with self.assertRaises(SystemError):
+            get_constant(Constant.INVALID_CONSTANT)
+
+    def test_get_constant(self):
+        self.check_get_constant(_testlimitedcapi.get_constant)
+
+    def test_get_constant_borrowed(self):
+        self.check_get_constant(_testlimitedcapi.get_constant_borrowed)
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/Lib/test/test_stable_abi_ctypes.py 
b/Lib/test/test_stable_abi_ctypes.py
index 117c27d27b38dc..0e304853d399aa 100644
--- a/Lib/test/test_stable_abi_ctypes.py
+++ b/Lib/test/test_stable_abi_ctypes.py
@@ -856,6 +856,8 @@ def test_windows_feature_macros(self):
     "Py_GetArgcArgv",
     "Py_GetBuildInfo",
     "Py_GetCompiler",
+    "Py_GetConstant",
+    "Py_GetConstantBorrowed",
     "Py_GetCopyright",
     "Py_GetExecPrefix",
     "Py_GetPath",
diff --git a/Misc/NEWS.d/next/C 
API/2024-03-15-23-55-24.gh-issue-115754.xnzc__.rst b/Misc/NEWS.d/next/C 
API/2024-03-15-23-55-24.gh-issue-115754.xnzc__.rst
new file mode 100644
index 00000000000000..d76c98ee54056d
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2024-03-15-23-55-24.gh-issue-115754.xnzc__.rst     
@@ -0,0 +1,3 @@
+Add :c:func:`Py_GetConstant` and :c:func:`Py_GetConstantBorrowed` functions to
+get constants. For example, ``Py_GetConstant(Py_CONSTANT_ZERO)`` returns a
+:term:`strong reference` to the constant zero. Patch by Victor Stinner.
diff --git a/Misc/NEWS.d/next/C 
API/2024-03-15-23-57-33.gh-issue-115754.zLdv82.rst b/Misc/NEWS.d/next/C 
API/2024-03-15-23-57-33.gh-issue-115754.zLdv82.rst
new file mode 100644
index 00000000000000..feff0c0897eae1
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2024-03-15-23-57-33.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 c68adf8db079f9..6d6e484b0b6c67 100644
--- a/Misc/stable_abi.toml
+++ b/Misc/stable_abi.toml
@@ -2500,3 +2500,7 @@
     added = '3.13'
 [function.PyType_GetModuleName]
     added = '3.13'
+[function.Py_GetConstant]
+    added = '3.13'
+[function.Py_GetConstantBorrowed]
+    added = '3.13'
diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in
index 1b7ad0e5d95a13..09d6f3b2bb7e8d 100644
--- a/Modules/Setup.stdlib.in
+++ b/Modules/Setup.stdlib.in
@@ -163,7 +163,7 @@
 @MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
 @MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c 
_testinternalcapi/test_lock.c _testinternalcapi/pytime.c 
_testinternalcapi/set.c _testinternalcapi/test_critical_sections.c
 @MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c 
_testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c 
_testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c 
_testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c 
_testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c 
_testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c 
_testcapi/buffer.c _testcapi/pyatomic.c _testcapi/file.c _testcapi/codec.c 
_testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c
-@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c 
_testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c 
_testlimitedcapi/bytes.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c 
_testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c 
_testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/pyos.c 
_testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/unicode.c 
_testlimitedcapi/vectorcall_limited.c
+@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c 
_testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c 
_testlimitedcapi/bytes.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c 
_testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c 
_testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c 
_testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c 
_testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c
 @MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
 @MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c
 
diff --git a/Modules/_testlimitedcapi.c b/Modules/_testlimitedcapi.c
index bfd41070eedd55..598071fe0ddbad 100644
--- a/Modules/_testlimitedcapi.c
+++ b/Modules/_testlimitedcapi.c
@@ -53,6 +53,9 @@ PyInit__testlimitedcapi(void)
     if (_PyTestLimitedCAPI_Init_Long(mod) < 0) {
         return NULL;
     }
+    if (_PyTestLimitedCAPI_Init_Object(mod) < 0) {
+        return NULL;
+    }
     if (_PyTestLimitedCAPI_Init_PyOS(mod) < 0) {
         return NULL;
     }
diff --git a/Modules/_testlimitedcapi/object.c 
b/Modules/_testlimitedcapi/object.c
new file mode 100644
index 00000000000000..6e438c811d6e98
--- /dev/null
+++ b/Modules/_testlimitedcapi/object.c
@@ -0,0 +1,80 @@
+// Need limited C API version 3.13 for Py_GetConstant()
+#include "pyconfig.h"   // Py_GIL_DISABLED
+#if !defined(Py_GIL_DISABLED) && !defined(Py_LIMITED_API )
+#  define Py_LIMITED_API 0x030d0000
+#endif
+
+#include "parts.h"
+#include "util.h"
+
+
+/* Test Py_GetConstant() */
+static PyObject *
+get_constant(PyObject *Py_UNUSED(module), PyObject *args)
+{
+    int constant_id;
+    if (!PyArg_ParseTuple(args, "i", &constant_id)) {
+        return NULL;
+    }
+
+    PyObject *obj = Py_GetConstant(constant_id);
+    if (obj == NULL) {
+        assert(PyErr_Occurred());
+        return NULL;
+    }
+    return obj;
+}
+
+
+/* Test Py_GetConstantBorrowed() */
+static PyObject *
+get_constant_borrowed(PyObject *Py_UNUSED(module), PyObject *args)
+{
+    int constant_id;
+    if (!PyArg_ParseTuple(args, "i", &constant_id)) {
+        return NULL;
+    }
+
+    PyObject *obj = Py_GetConstantBorrowed(constant_id);
+    if (obj == NULL) {
+        assert(PyErr_Occurred());
+        return NULL;
+    }
+    return Py_NewRef(obj);
+}
+
+
+/* Test constants */
+static PyObject *
+test_constants(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
+{
+    // Test that implementation of constants in the limited C API:
+    // check that the C code compiles.
+    //
+    // Test also that constants and Py_GetConstant() return the same
+    // objects.
+    assert(Py_None == Py_GetConstant(Py_CONSTANT_NONE));
+    assert(Py_False == Py_GetConstant(Py_CONSTANT_FALSE));
+    assert(Py_True == Py_GetConstant(Py_CONSTANT_TRUE));
+    assert(Py_Ellipsis == Py_GetConstant(Py_CONSTANT_ELLIPSIS));
+    assert(Py_NotImplemented == Py_GetConstant(Py_CONSTANT_NOT_IMPLEMENTED));
+    // Other constants are tested in test_capi.test_object
+    Py_RETURN_NONE;
+}
+
+static PyMethodDef test_methods[] = {
+    {"get_constant", get_constant, METH_VARARGS},
+    {"get_constant_borrowed", get_constant_borrowed, METH_VARARGS},
+    {"test_constants", test_constants, METH_NOARGS},
+    {NULL},
+};
+
+int
+_PyTestLimitedCAPI_Init_Object(PyObject *m)
+{
+    if (PyModule_AddFunctions(m, test_methods) < 0) {
+        return -1;
+    }
+
+    return 0;
+}
diff --git a/Modules/_testlimitedcapi/parts.h b/Modules/_testlimitedcapi/parts.h
index 4b65912489661b..d91f174cd31eed 100644
--- a/Modules/_testlimitedcapi/parts.h
+++ b/Modules/_testlimitedcapi/parts.h
@@ -29,6 +29,7 @@ int _PyTestLimitedCAPI_Init_Complex(PyObject *module);
 int _PyTestLimitedCAPI_Init_Dict(PyObject *module);
 int _PyTestLimitedCAPI_Init_Float(PyObject *module);
 int _PyTestLimitedCAPI_Init_HeaptypeRelative(PyObject *module);
+int _PyTestLimitedCAPI_Init_Object(PyObject *module);
 int _PyTestLimitedCAPI_Init_List(PyObject *module);
 int _PyTestLimitedCAPI_Init_Long(PyObject *module);
 int _PyTestLimitedCAPI_Init_PyOS(PyObject *module);
diff --git a/Objects/object.c b/Objects/object.c
index fcb8cf481657e5..0d03292c9115cd 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -14,6 +14,7 @@
 #include "pycore_memoryobject.h"  // _PyManagedBuffer_Type
 #include "pycore_namespace.h"     // _PyNamespace_Type
 #include "pycore_object.h"        // PyAPI_DATA() _Py_SwappedOp definition
+#include "pycore_long.h"          // _PyLong_GetZero()
 #include "pycore_optimizer.h"     // _PyUOpExecutor_Type, 
_PyUOpOptimizer_Type, ...
 #include "pycore_pyerrors.h"      // _PyErr_Occurred()
 #include "pycore_pymem.h"         // _PyMem_IsPtrFreed()
@@ -2991,3 +2992,53 @@ _Py_SetRefcnt(PyObject *ob, Py_ssize_t refcnt)
 {
     Py_SET_REFCNT(ob, refcnt);
 }
+
+
+static PyObject* constants[] = {
+    &_Py_NoneStruct,                   // Py_CONSTANT_NONE
+    (PyObject*)(&_Py_FalseStruct),     // Py_CONSTANT_FALSE
+    (PyObject*)(&_Py_TrueStruct),      // Py_CONSTANT_TRUE
+    &_Py_EllipsisObject,               // Py_CONSTANT_ELLIPSIS
+    &_Py_NotImplementedStruct,         // Py_CONSTANT_NOT_IMPLEMENTED
+    NULL,  // Py_CONSTANT_ZERO
+    NULL,  // Py_CONSTANT_ONE
+    NULL,  // Py_CONSTANT_EMPTY_STR
+    NULL,  // Py_CONSTANT_EMPTY_BYTES
+    NULL,  // Py_CONSTANT_EMPTY_TUPLE
+};
+
+void
+_Py_GetConstant_Init(void)
+{
+    constants[Py_CONSTANT_ZERO] = _PyLong_GetZero();
+    constants[Py_CONSTANT_ONE] = _PyLong_GetOne();
+    constants[Py_CONSTANT_EMPTY_STR] = PyUnicode_New(0, 0);
+    constants[Py_CONSTANT_EMPTY_BYTES] = PyBytes_FromStringAndSize(NULL, 0);
+    constants[Py_CONSTANT_EMPTY_TUPLE] = PyTuple_New(0);
+#ifndef NDEBUG
+    for (size_t i=0; i < Py_ARRAY_LENGTH(constants); i++) {
+        assert(constants[i] != NULL);
+        assert(_Py_IsImmortal(constants[i]));
+    }
+#endif
+}
+
+PyObject*
+Py_GetConstant(unsigned int constant_id)
+{
+    if (constant_id < Py_ARRAY_LENGTH(constants)) {
+        return constants[constant_id];
+    }
+    else {
+        PyErr_BadInternalCall();
+        return NULL;
+    }
+}
+
+
+PyObject*
+Py_GetConstantBorrowed(unsigned int constant_id)
+{
+    // All constants are immortal
+    return Py_GetConstant(constant_id);
+}
diff --git a/PC/python3dll.c b/PC/python3dll.c
index dbfa3f23bb586d..147bfad44c3741 100755
--- a/PC/python3dll.c
+++ b/PC/python3dll.c
@@ -55,6 +55,8 @@ EXPORT_FUNC(Py_GenericAlias)
 EXPORT_FUNC(Py_GetArgcArgv)
 EXPORT_FUNC(Py_GetBuildInfo)
 EXPORT_FUNC(Py_GetCompiler)
+EXPORT_FUNC(Py_GetConstant)
+EXPORT_FUNC(Py_GetConstantBorrowed)
 EXPORT_FUNC(Py_GetCopyright)
 EXPORT_FUNC(Py_GetExecPrefix)
 EXPORT_FUNC(Py_GetPath)
diff --git a/PCbuild/_testlimitedcapi.vcxproj b/PCbuild/_testlimitedcapi.vcxproj
index 58a8bcbdbce4e8..252039d93103bd 100644
--- a/PCbuild/_testlimitedcapi.vcxproj
+++ b/PCbuild/_testlimitedcapi.vcxproj
@@ -103,6 +103,7 @@
     <ClCompile Include="..\Modules\_testlimitedcapi\heaptype_relative.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\list.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\long.c" />
+    <ClCompile Include="..\Modules\_testlimitedcapi\object.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\pyos.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\set.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\sys.c" />
diff --git a/PCbuild/_testlimitedcapi.vcxproj.filters 
b/PCbuild/_testlimitedcapi.vcxproj.filters
index e203edaf123e8d..7efbb0acf8f960 100644
--- a/PCbuild/_testlimitedcapi.vcxproj.filters
+++ b/PCbuild/_testlimitedcapi.vcxproj.filters
@@ -18,6 +18,7 @@
     <ClCompile Include="..\Modules\_testlimitedcapi\heaptype_relative.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\list.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\long.c" />
+    <ClCompile Include="..\Modules\_testlimitedcapi\object.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\pyos.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\set.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\sys.c" />
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index 4bd01ad23e573f..683534d342f437 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -687,6 +687,10 @@ pycore_init_global_objects(PyInterpreterState *interp)
 
     _PyUnicode_InitState(interp);
 
+    if (_Py_IsMainInterpreter(interp)) {
+        _Py_GetConstant_Init();
+    }
+
     return _PyStatus_OK();
 }
 
diff --git a/Tools/c-analyzer/cpython/ignored.tsv 
b/Tools/c-analyzer/cpython/ignored.tsv
index 0024e2683052c8..965346b9b04a32 100644
--- a/Tools/c-analyzer/cpython/ignored.tsv
+++ b/Tools/c-analyzer/cpython/ignored.tsv
@@ -742,3 +742,4 @@ Modules/_sqlite/module.c    -       _sqlite3module  -
 Modules/clinic/md5module.c.h   _md5_md5        _keywords       -
 Modules/clinic/grpmodule.c.h   grp_getgrgid    _keywords       -
 Modules/clinic/grpmodule.c.h   grp_getgrnam    _keywords       -
+Objects/object.c       -       constants       static PyObject*[]

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to