[Python-checkins] bpo-43688: Support the limited C API in debug mode (GH-25131)

vstinner webhook-mailer at python.org
Fri Apr 2 09:45:45 EDT 2021


https://github.com/python/cpython/commit/3359cab038968935b40344fad7c30d211f9692e4
commit: 3359cab038968935b40344fad7c30d211f9692e4
branch: master
author: Victor Stinner <vstinner at python.org>
committer: vstinner <vstinner at python.org>
date: 2021-04-02T15:45:37+02:00
summary:

bpo-43688: Support the limited C API in debug mode (GH-25131)

The limited C API is now supported if Python is built in debug mode
(if the Py_DEBUG macro is defined). In the limited C API, the
Py_INCREF() and Py_DECREF() functions are now implemented as opaque
function calls, rather than accessing directly the PyObject.ob_refcnt
member, if Python is built in debug mode and the Py_LIMITED_API macro
targets Python 3.10 or newer. It became possible to support the
limited C API in debug mode because the PyObject structure is the
same in release and debug mode since Python 3.8 (see bpo-36465).

The limited C API is still not supported in the --with-trace-refs
special build (Py_TRACE_REFS macro).

files:
A Misc/NEWS.d/next/C API/2021-04-01-09-10-42.bpo-43688.G4gs6k.rst
M Doc/whatsnew/3.10.rst
M Include/object.h
M Objects/object.c
M PC/python3dll.c
M setup.py

diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst
index a8ff56745c758..b84bcf93edb1e 100644
--- a/Doc/whatsnew/3.10.rst
+++ b/Doc/whatsnew/3.10.rst
@@ -1315,6 +1315,18 @@ New Features
   to simulate.
   (Contributed by Antoine Pitrou in :issue:`43356`.)
 
+* The limited C API is now supported if Python is built in debug mode (if the
+  ``Py_DEBUG`` macro is defined). In the limited C API, the :c:func:`Py_INCREF`
+  and :c:func:`Py_DECREF` functions are now implemented as opaque function
+  calls, rather than accessing directly the :c:member:`PyObject.ob_refcnt`
+  member, if Python is built in debug mode and the ``Py_LIMITED_API`` macro
+  targets Python 3.10 or newer. It became possible to support the limited C API
+  in debug mode because the :c:type:`PyObject` structure is the same in release
+  and debug mode since Python 3.8 (see :issue:`36465`).
+
+  The limited C API is still not supported in the ``--with-trace-refs`` special
+  build (``Py_TRACE_REFS`` macro).
+  (Contributed by Victor Stinner in :issue:`43688`.)
 
 Porting to Python 3.10
 ----------------------
diff --git a/Include/object.h b/Include/object.h
index 14545839341f4..3138db0a0a3e4 100644
--- a/Include/object.h
+++ b/Include/object.h
@@ -54,11 +54,11 @@ whose size is determined when the object is allocated.
 
 /* Py_DEBUG implies Py_REF_DEBUG. */
 #if defined(Py_DEBUG) && !defined(Py_REF_DEBUG)
-#define Py_REF_DEBUG
+#  define Py_REF_DEBUG
 #endif
 
-#if defined(Py_LIMITED_API) && defined(Py_REF_DEBUG)
-#error Py_LIMITED_API is incompatible with Py_DEBUG, Py_TRACE_REFS, and Py_REF_DEBUG
+#if defined(Py_LIMITED_API) && defined(Py_TRACE_REFS)
+#  error Py_LIMITED_API is incompatible with Py_TRACE_REFS
 #endif
 
 /* PyTypeObject structure is defined in cpython/object.h.
@@ -74,8 +74,8 @@ typedef struct _typeobject PyTypeObject;
 #define _PyObject_EXTRA_INIT 0, 0,
 
 #else
-#define _PyObject_HEAD_EXTRA
-#define _PyObject_EXTRA_INIT
+#  define _PyObject_HEAD_EXTRA
+#  define _PyObject_EXTRA_INIT
 #endif
 
 /* PyObject_HEAD defines the initial segment of every PyObject. */
@@ -427,21 +427,46 @@ PyAPI_FUNC(void) _Py_NegativeRefcount(const char *filename, int lineno,
 
 PyAPI_FUNC(void) _Py_Dealloc(PyObject *);
 
+/*
+These are provided as conveniences to Python runtime embedders, so that
+they can have object code that is not dependent on Python compilation flags.
+*/
+PyAPI_FUNC(void) Py_IncRef(PyObject *);
+PyAPI_FUNC(void) Py_DecRef(PyObject *);
+
+// Similar to Py_IncRef() and Py_DecRef() but the argument must be non-NULL.
+// Private functions used by Py_INCREF() and Py_DECREF().
+PyAPI_FUNC(void) _Py_IncRef(PyObject *);
+PyAPI_FUNC(void) _Py_DecRef(PyObject *);
+
 static inline void _Py_INCREF(PyObject *op)
 {
+#if defined(Py_REF_DEBUG) && defined(Py_LIMITED_API) && Py_LIMITED_API+0 >= 0x030A0000
+    // Stable ABI for Python 3.10 built in debug mode.
+    _Py_IncRef(op);
+#else
+    // Non-limited C API and limited C API for Python 3.9 and older access
+    // directly PyObject.ob_refcnt.
 #ifdef Py_REF_DEBUG
     _Py_RefTotal++;
 #endif
     op->ob_refcnt++;
+#endif
 }
 #define Py_INCREF(op) _Py_INCREF(_PyObject_CAST(op))
 
 static inline void _Py_DECREF(
-#ifdef Py_REF_DEBUG
+#if defined(Py_REF_DEBUG) && !(defined(Py_LIMITED_API) && Py_LIMITED_API+0 >= 0x030A0000)
     const char *filename, int lineno,
 #endif
     PyObject *op)
 {
+#if defined(Py_REF_DEBUG) && defined(Py_LIMITED_API) && Py_LIMITED_API+0 >= 0x030A0000
+    // Stable ABI for Python 3.10 built in debug mode.
+    _Py_DecRef(op);
+#else
+    // Non-limited C API and limited C API for Python 3.9 and older access
+    // directly PyObject.ob_refcnt.
 #ifdef Py_REF_DEBUG
     _Py_RefTotal--;
 #endif
@@ -455,8 +480,9 @@ static inline void _Py_DECREF(
     else {
         _Py_Dealloc(op);
     }
+#endif
 }
-#ifdef Py_REF_DEBUG
+#if defined(Py_REF_DEBUG) && !(defined(Py_LIMITED_API) && Py_LIMITED_API+0 >= 0x030A0000)
 #  define Py_DECREF(op) _Py_DECREF(__FILE__, __LINE__, _PyObject_CAST(op))
 #else
 #  define Py_DECREF(op) _Py_DECREF(_PyObject_CAST(op))
@@ -525,13 +551,6 @@ static inline void _Py_XDECREF(PyObject *op)
 
 #define Py_XDECREF(op) _Py_XDECREF(_PyObject_CAST(op))
 
-/*
-These are provided as conveniences to Python runtime embedders, so that
-they can have object code that is not dependent on Python compilation flags.
-*/
-PyAPI_FUNC(void) Py_IncRef(PyObject *);
-PyAPI_FUNC(void) Py_DecRef(PyObject *);
-
 // Create a new strong reference to an object:
 // increment the reference count of the object and return the object.
 PyAPI_FUNC(PyObject*) Py_NewRef(PyObject *obj);
diff --git a/Misc/NEWS.d/next/C API/2021-04-01-09-10-42.bpo-43688.G4gs6k.rst b/Misc/NEWS.d/next/C API/2021-04-01-09-10-42.bpo-43688.G4gs6k.rst
new file mode 100644
index 0000000000000..6b1d44ee17686
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2021-04-01-09-10-42.bpo-43688.G4gs6k.rst	
@@ -0,0 +1,13 @@
+The limited C API is now supported if Python is built in debug mode (if the
+``Py_DEBUG`` macro is defined). In the limited C API, the :c:func:`Py_INCREF`
+and :c:func:`Py_DECREF` functions are now implemented as opaque function calls,
+rather than accessing directly the :c:member:`PyObject.ob_refcnt` member, if
+Python is built in debug mode and the ``Py_LIMITED_API`` macro targets Python
+3.10 or newer. It became possible to support the limited C API in debug mode
+because the :c:type:`PyObject` structure is the same in release and debug mode
+since Python 3.8 (see :issue:`36465`).
+
+The limited C API is still not supported in the ``--with-trace-refs`` special
+build (``Py_TRACE_REFS`` macro).
+
+Patch by Victor Stinner.
diff --git a/Objects/object.c b/Objects/object.c
index 0a8621b3503b3..0845966c5fe9d 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -13,6 +13,11 @@
 #include "frameobject.h"
 #include "interpreteridobject.h"
 
+#ifdef Py_LIMITED_API
+   // Prevent recursive call _Py_IncRef() <=> Py_INCREF()
+#  error "Py_LIMITED_API macro must not be defined"
+#endif
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -136,6 +141,18 @@ Py_DecRef(PyObject *o)
     Py_XDECREF(o);
 }
 
+void
+_Py_IncRef(PyObject *o)
+{
+    Py_INCREF(o);
+}
+
+void
+_Py_DecRef(PyObject *o)
+{
+    Py_DECREF(o);
+}
+
 PyObject *
 PyObject_Init(PyObject *op, PyTypeObject *tp)
 {
diff --git a/PC/python3dll.c b/PC/python3dll.c
index 1567ac159168a..b5735e81e81ae 100644
--- a/PC/python3dll.c
+++ b/PC/python3dll.c
@@ -36,6 +36,8 @@ EXPORT_FUNC(_PyTrash_deposit_object)
 EXPORT_FUNC(_PyTrash_destroy_chain)
 EXPORT_FUNC(_PyTrash_thread_deposit_object)
 EXPORT_FUNC(_PyTrash_thread_destroy_chain)
+EXPORT_FUNC(_Py_IncRef)
+EXPORT_FUNC(_Py_DecRef)
 EXPORT_FUNC(Py_AddPendingCall)
 EXPORT_FUNC(Py_AtExit)
 EXPORT_FUNC(Py_BuildValue)
diff --git a/setup.py b/setup.py
index 75bd16375063a..3b4e7ae70a4ec 100644
--- a/setup.py
+++ b/setup.py
@@ -1864,17 +1864,11 @@ def detect_modules(self):
 ##         # Uncomment these lines if you want to play with xxmodule.c
 ##         self.add(Extension('xx', ['xxmodule.c']))
 
-        if 'd' not in sysconfig.get_config_var('ABIFLAGS'):
-            # Non-debug mode: Build xxlimited with limited API
-            self.add(Extension('xxlimited', ['xxlimited.c'],
-                               define_macros=[('Py_LIMITED_API', '0x030a0000')]))
-            self.add(Extension('xxlimited_35', ['xxlimited_35.c'],
-                               define_macros=[('Py_LIMITED_API', '0x03050000')]))
-        else:
-            # Debug mode: Build xxlimited with the full API
-            # (which is compatible with the limited one)
-            self.add(Extension('xxlimited', ['xxlimited.c']))
-            self.add(Extension('xxlimited_35', ['xxlimited_35.c']))
+        # Limited C API
+        self.add(Extension('xxlimited', ['xxlimited.c'],
+                           define_macros=[('Py_LIMITED_API', '0x030a0000')]))
+        self.add(Extension('xxlimited_35', ['xxlimited_35.c'],
+                           define_macros=[('Py_LIMITED_API', '0x03050000')]))
 
     def detect_tkinter_fromenv(self):
         # Build _tkinter using the Tcl/Tk locations specified by



More information about the Python-checkins mailing list