https://github.com/python/cpython/commit/9181d776daf87f0e4e2ce02c08f162150fdf7d79
commit: 9181d776daf87f0e4e2ce02c08f162150fdf7d79
branch: main
author: Jeong, YunWon <[email protected]>
committer: encukou <[email protected]>
date: 2026-01-26T17:40:56+01:00
summary:

gh-142966: Make ctypes.POINTER.set_type also reset format (GH-142967)

Make the deprecated set_type method resets the format, using the
same code as in type initialization.

Implementation note: this was done in PyCPointerType_init 
after calling PyCPointerType_SetProto, but was forgotten
after in PyCPointerType_set_type_impl's call to
PyCPointerType_SetProto.
With this change, setting the format is conceptually part of
setting proto (i.e. the pointed-to type).

Co-authored-by: AN Long <[email protected]>

files:
A Misc/NEWS.d/next/Library/2025-12-19-11-30-31.gh-issue-142966.PzGiv2.rst
M Lib/test/test_ctypes/test_incomplete.py
M Modules/_ctypes/_ctypes.c

diff --git a/Lib/test/test_ctypes/test_incomplete.py 
b/Lib/test/test_ctypes/test_incomplete.py
index 3189fcd1bd1330..8f6316d6fba61a 100644
--- a/Lib/test/test_ctypes/test_incomplete.py
+++ b/Lib/test/test_ctypes/test_incomplete.py
@@ -1,6 +1,6 @@
 import ctypes
 import unittest
-from ctypes import Structure, POINTER, pointer, c_char_p
+from ctypes import Structure, POINTER, pointer, c_char_p, c_int
 
 # String-based "incomplete pointers" were implemented in ctypes 0.6.3 (2003, 
when
 # ctypes was an external project). They made obsolete by the current
@@ -50,6 +50,29 @@ class cell(Structure):
         lpcell.set_type(cell)
         self.assertIs(POINTER(cell), lpcell)
 
+    def test_set_type_updates_format(self):
+        # gh-142966: set_type should update StgInfo.format
+        # to match the element type's format
+        with self.assertWarns(DeprecationWarning):
+            lp = POINTER("node")
+
+        class node(Structure):
+            _fields_ = [("value", c_int)]
+
+        # Get the expected format before set_type
+        node_format = memoryview(node()).format
+        expected_format = "&" + node_format
+
+        lp.set_type(node)
+
+        # Create instance to check format via memoryview
+        n = node(42)
+        p = lp(n)
+        actual_format = memoryview(p).format
+
+        # After set_type, the pointer's format should be "&<element_format>"
+        self.assertEqual(actual_format, expected_format)
+
 
 if __name__ == '__main__':
     unittest.main()
diff --git 
a/Misc/NEWS.d/next/Library/2025-12-19-11-30-31.gh-issue-142966.PzGiv2.rst 
b/Misc/NEWS.d/next/Library/2025-12-19-11-30-31.gh-issue-142966.PzGiv2.rst
new file mode 100644
index 00000000000000..92ea407c6b456e
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-12-19-11-30-31.gh-issue-142966.PzGiv2.rst
@@ -0,0 +1 @@
+Fix :func:`!ctypes.POINTER.set_type` not updating the format string to match 
the type.
diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c
index 6d9193af657e2a..2c691c3766fc4d 100644
--- a/Modules/_ctypes/_ctypes.c
+++ b/Modules/_ctypes/_ctypes.c
@@ -1258,11 +1258,30 @@ PyCPointerType_SetProto(ctypes_state *st, PyObject 
*self, StgInfo *stginfo, PyOb
         return -1;
     }
     Py_XSETREF(stginfo->proto, Py_NewRef(proto));
+
+    // Set the format string for the pointer type based on element type.
+    // If info->format is NULL, this is a pointer to an incomplete type.
+    // We create a generic format string 'pointer to bytes' in this case.
+    char *new_format = NULL;
     STGINFO_LOCK(info);
     if (info->pointer_type == NULL) {
         Py_XSETREF(info->pointer_type, Py_NewRef(self));
     }
+    const char *current_format = info->format ? info->format : "B";
+    if (info->shape != NULL) {
+        // pointer to an array: the shape needs to be prefixed
+        new_format = _ctypes_alloc_format_string_with_shape(
+            info->ndim, info->shape, "&", current_format);
+    } else {
+        new_format = _ctypes_alloc_format_string("&", current_format);
+    }
+    PyMem_Free(stginfo->format);
+    stginfo->format = new_format;
     STGINFO_UNLOCK();
+
+    if (new_format == NULL) {
+        return -1;
+    }
     return 0;
 }
 
@@ -1314,35 +1333,11 @@ PyCPointerType_init(PyObject *self, PyObject *args, 
PyObject *kwds)
         return -1;
     }
     if (proto) {
-        const char *current_format;
         if (PyCPointerType_SetProto(st, self, stginfo, proto) < 0) {
             Py_DECREF(proto);
             return -1;
         }
-        StgInfo *iteminfo;
-        if (PyStgInfo_FromType(st, proto, &iteminfo) < 0) {
-            Py_DECREF(proto);
-            return -1;
-        }
-        /* PyCPointerType_SetProto has verified proto has a stginfo. */
-        assert(iteminfo);
-        /* If iteminfo->format is NULL, then this is a pointer to an
-           incomplete type.  We create a generic format string
-           'pointer to bytes' in this case.  XXX Better would be to
-           fix the format string later...
-        */
-        current_format = iteminfo->format ? iteminfo->format : "B";
-        if (iteminfo->shape != NULL) {
-            /* pointer to an array: the shape needs to be prefixed */
-            stginfo->format = _ctypes_alloc_format_string_with_shape(
-                iteminfo->ndim, iteminfo->shape, "&", current_format);
-        } else {
-            stginfo->format = _ctypes_alloc_format_string("&", current_format);
-        }
         Py_DECREF(proto);
-        if (stginfo->format == NULL) {
-            return -1;
-        }
     }
 
     return 0;

_______________________________________________
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