From decb25e8f0051cafcbe7d38171371cf47185165b Mon Sep 17 00:00:00 2001 From: Priyanshu Singh Date: Mon, 26 Jan 2026 17:43:49 +0530 Subject: [PATCH 1/8] gh-144128: Fix crash in array.fromlist with reentrant __index__ (#144138) Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Peter Bierma Co-authored-by: Victor Stinner --- Lib/test/test_array.py | 17 +++++++++++++++ ...-01-22-10-18-17.gh-issue-144128.akwY06.rst | 2 ++ Modules/arraymodule.c | 21 +++++++++++++------ 3 files changed, 34 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-01-22-10-18-17.gh-issue-144128.akwY06.rst diff --git a/Lib/test/test_array.py b/Lib/test/test_array.py index 83b3c978da3581..b49df029f0326f 100755 --- a/Lib/test/test_array.py +++ b/Lib/test/test_array.py @@ -67,6 +67,23 @@ def test_empty(self): a += a self.assertEqual(len(a), 0) + def test_fromlist_reentrant_index_mutation(self): + + class Evil: + def __init__(self, lst): + self.lst = lst + def __index__(self): + self.lst.clear() + return "not an int" + + for typecode in ('I', 'L', 'Q'): + with self.subTest(typecode=typecode): + lst = [] + lst.append(Evil(lst)) + a = array.array(typecode) + with self.assertRaises(TypeError): + a.fromlist(lst) + # Machine format codes. # diff --git a/Misc/NEWS.d/next/Library/2026-01-22-10-18-17.gh-issue-144128.akwY06.rst b/Misc/NEWS.d/next/Library/2026-01-22-10-18-17.gh-issue-144128.akwY06.rst new file mode 100644 index 00000000000000..4010695aec980d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-01-22-10-18-17.gh-issue-144128.akwY06.rst @@ -0,0 +1,2 @@ +Fix a crash in :meth:`array.array.fromlist` when an element's :meth:`~object.__index__` method mutates +the input list during conversion. diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index 729e085c19f006..5769a796b18902 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -408,10 +408,13 @@ II_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) int do_decref = 0; /* if nb_int was called */ if (!PyLong_Check(v)) { - v = _PyNumber_Index(v); - if (NULL == v) { + Py_INCREF(v); + PyObject *res = _PyNumber_Index(v); + Py_DECREF(v); + if (NULL == res) { return -1; } + v = res; do_decref = 1; } x = PyLong_AsUnsignedLong(v); @@ -468,10 +471,13 @@ LL_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) int do_decref = 0; /* if nb_int was called */ if (!PyLong_Check(v)) { - v = _PyNumber_Index(v); - if (NULL == v) { + Py_INCREF(v); + PyObject *res = _PyNumber_Index(v); + Py_DECREF(v); + if (NULL == res) { return -1; } + v = res; do_decref = 1; } x = PyLong_AsUnsignedLong(v); @@ -521,10 +527,13 @@ QQ_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) int do_decref = 0; /* if nb_int was called */ if (!PyLong_Check(v)) { - v = _PyNumber_Index(v); - if (NULL == v) { + Py_INCREF(v); + PyObject *res = _PyNumber_Index(v); + Py_DECREF(v); + if (NULL == res) { return -1; } + v = res; do_decref = 1; } x = PyLong_AsUnsignedLongLong(v); From 8f459255eba2b6639f1912e5c5e318a7cdafada1 Mon Sep 17 00:00:00 2001 From: VanshAgarwal24036 <148854295+VanshAgarwal24036@users.noreply.github.com> Date: Mon, 26 Jan 2026 17:57:42 +0530 Subject: [PATCH 2/8] gh-144100: Fix crash for POINTER(str) used in ctypes argtypes (#144108) Co-authored-by: Victor Stinner --- Lib/test/test_ctypes/test_pointers.py | 10 ++++++++++ .../2026-01-21-19-39-07.gh-issue-144100.hLMZ8Y.rst | 3 +++ Modules/_ctypes/_ctypes.c | 8 +++++++- 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2026-01-21-19-39-07.gh-issue-144100.hLMZ8Y.rst diff --git a/Lib/test/test_ctypes/test_pointers.py b/Lib/test/test_ctypes/test_pointers.py index a8d243a45de0f4..771cc8fbe0ec93 100644 --- a/Lib/test/test_ctypes/test_pointers.py +++ b/Lib/test/test_ctypes/test_pointers.py @@ -403,6 +403,16 @@ class Cls(Structure): self.assertEqual(len(ws_typ), 0, ws_typ) self.assertEqual(len(ws_ptr), 0, ws_ptr) + def test_pointer_proto_missing_argtypes_error(self): + class BadType(ctypes._Pointer): + # _type_ is intentionally missing + pass + + func = ctypes.pythonapi.Py_GetVersion + func.argtypes = (BadType,) + + with self.assertRaises(ctypes.ArgumentError): + func(object()) class PointerTypeCacheTestCase(unittest.TestCase): # dummy tests to check warnings and base behavior diff --git a/Misc/NEWS.d/next/Library/2026-01-21-19-39-07.gh-issue-144100.hLMZ8Y.rst b/Misc/NEWS.d/next/Library/2026-01-21-19-39-07.gh-issue-144100.hLMZ8Y.rst new file mode 100644 index 00000000000000..7093b753141fb8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-01-21-19-39-07.gh-issue-144100.hLMZ8Y.rst @@ -0,0 +1,3 @@ +Fixed a crash in ctypes when using a deprecated ``POINTER(str)`` type in +``argtypes``. Instead of aborting, ctypes now raises a proper Python +exception when the pointer target type is unresolved. diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 563e95a762599b..6d9193af657e2a 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -1419,7 +1419,13 @@ PyCPointerType_from_param_impl(PyObject *type, PyTypeObject *cls, /* If we expect POINTER(), but receive a instance, accept it by calling byref(). */ - assert(typeinfo->proto); + if (typeinfo->proto == NULL) { + PyErr_SetString( + PyExc_TypeError, + "cannot convert argument: POINTER _type_ type is not set" + ); + return NULL; + } switch (PyObject_IsInstance(value, typeinfo->proto)) { case 1: Py_INCREF(value); /* _byref steals a refcount */ From 923d9d2ac23b583a7fb66e2867d8dcc07fec9a40 Mon Sep 17 00:00:00 2001 From: Yashraj Date: Mon, 26 Jan 2026 19:08:23 +0530 Subject: [PATCH 3/8] gh-143928: Remove outdated comparison between pickle and marshal regarding recursion (#144025) --- Doc/library/pickle.rst | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/Doc/library/pickle.rst b/Doc/library/pickle.rst index 3a9b66ec7e7088..7b0d979d61a36c 100644 --- a/Doc/library/pickle.rst +++ b/Doc/library/pickle.rst @@ -56,19 +56,6 @@ files. The :mod:`pickle` module differs from :mod:`marshal` in several significant ways: -* The :mod:`pickle` module keeps track of the objects it has already serialized, - so that later references to the same object won't be serialized again. - :mod:`marshal` doesn't do this. - - This has implications both for recursive objects and object sharing. Recursive - objects are objects that contain references to themselves. These are not - handled by marshal, and in fact, attempting to marshal recursive objects will - crash your Python interpreter. Object sharing happens when there are multiple - references to the same object in different places in the object hierarchy being - serialized. :mod:`pickle` stores such objects only once, and ensures that all - other references point to the master copy. Shared objects remain shared, which - can be very important for mutable objects. - * :mod:`marshal` cannot be used to serialize user-defined classes and their instances. :mod:`pickle` can save and restore class instances transparently, however the class definition must be importable and live in the same module as From 1f55caf97e0906f2b8b693b01d3d6073df8187c1 Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Mon, 26 Jan 2026 22:26:55 +0800 Subject: [PATCH 4/8] gh-144212: Add `image/jxl` to `mimetypes` (GH-144213) --- Doc/whatsnew/3.15.rst | 1 + Lib/mimetypes.py | 1 + Lib/test/test_mimetypes.py | 1 + .../next/Library/2026-01-24-23-11-17.gh-issue-144212.IXqVL8.rst | 1 + 4 files changed, 4 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-01-24-23-11-17.gh-issue-144212.IXqVL8.rst diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index c412f48606c045..aec6b1ceea37cf 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -606,6 +606,7 @@ mimetypes * Add ``application/dicom`` MIME type for ``.dcm`` extension. (Contributed by Benedikt Johannes in :gh:`144217`.) * Add ``application/node`` MIME type for ``.cjs`` extension. (Contributed by John Franey in :gh:`140937`.) * Add ``application/toml``. (Contributed by Gil Forcada in :gh:`139959`.) +* Add ``image/jxl``. (Contributed by Foolbar in :gh:`144213`.) * Rename ``application/x-texinfo`` to ``application/texinfo``. (Contributed by Charlie Lin in :gh:`140165`.) * Changed the MIME type for ``.ai`` files to ``application/pdf``. diff --git a/Lib/mimetypes.py b/Lib/mimetypes.py index 361f924b55da1e..fc916c470a0110 100644 --- a/Lib/mimetypes.py +++ b/Lib/mimetypes.py @@ -609,6 +609,7 @@ def _default_mime_types(): '.jpeg' : 'image/jpeg', '.jpm' : 'image/jpm', '.jpx' : 'image/jpx', + '.jxl' : 'image/jxl', '.heic' : 'image/heic', '.heif' : 'image/heif', '.png' : 'image/png', diff --git a/Lib/test/test_mimetypes.py b/Lib/test/test_mimetypes.py index 32adc59359108f..fe7584f1f9d3b3 100644 --- a/Lib/test/test_mimetypes.py +++ b/Lib/test/test_mimetypes.py @@ -271,6 +271,7 @@ def check_extensions(): ("image/jp2", ".jp2"), ("image/jpeg", ".jpg"), ("image/jpm", ".jpm"), + ("image/jxl", ".jxl"), ("image/t38", ".t38"), ("image/tiff", ".tiff"), ("image/tiff-fx", ".tfx"), diff --git a/Misc/NEWS.d/next/Library/2026-01-24-23-11-17.gh-issue-144212.IXqVL8.rst b/Misc/NEWS.d/next/Library/2026-01-24-23-11-17.gh-issue-144212.IXqVL8.rst new file mode 100644 index 00000000000000..be77fb345adae3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-01-24-23-11-17.gh-issue-144212.IXqVL8.rst @@ -0,0 +1 @@ +Mime type ``image/jxl`` is now supported by :mod:`mimetypes`. From 19de10d3d8605a290492e4fb3871e12638b0f7bb Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Mon, 26 Jan 2026 15:45:17 +0000 Subject: [PATCH 5/8] gh-140557: Force alignment of empty `bytearray` and `array.array` buffers (GH-140559) This ensures the buffers used by the empty `bytearray` and `array.array` are aligned the same as a pointer returned by the allocator. This is a more convenient default for interop with other languages that have stricter requirements of type-safe buffers (e.g. Rust's `&[T]` type) even when empty. --- Lib/test/test_buffer.py | 35 +++++++++++++++++++ Misc/ACKS | 1 + ...-10-24-17-30-51.gh-issue-140557.X2GETk.rst | 2 ++ ...-01-07-11-57-59.gh-issue-140557.3P6-nW.rst | 2 ++ Modules/_testcapi/buffer.c | 24 +++++++++++++ Modules/arraymodule.c | 2 +- 6 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-24-17-30-51.gh-issue-140557.X2GETk.rst create mode 100644 Misc/NEWS.d/next/Library/2026-01-07-11-57-59.gh-issue-140557.3P6-nW.rst diff --git a/Lib/test/test_buffer.py b/Lib/test/test_buffer.py index 19582e757161fc..ab65a44bda6e7e 100644 --- a/Lib/test/test_buffer.py +++ b/Lib/test/test_buffer.py @@ -4447,6 +4447,41 @@ def test_bytearray_release_buffer_read_flag(self): with self.assertRaises(SystemError): obj.__buffer__(inspect.BufferFlags.WRITE) + @support.cpython_only + @unittest.skipIf(_testcapi is None, "requires _testcapi") + def test_bytearray_alignment(self): + # gh-140557: pointer alignment of buffers including empty allocation + # should be at least to `size_t`. + align = struct.calcsize("N") + cases = [ + bytearray(), + bytearray(1), + bytearray(b"0123456789abcdef"), + bytearray(16), + ] + ptrs = [_testcapi.buffer_pointer_as_int(array) for array in cases] + self.assertEqual([ptr % align for ptr in ptrs], [0]*len(ptrs)) + + @support.cpython_only + @unittest.skipIf(_testcapi is None, "requires _testcapi") + def test_array_alignment(self): + # gh-140557: pointer alignment of buffers including empty allocation + # should match the maximum array alignment. + align = max(struct.calcsize(fmt) for fmt in ARRAY) + cases = [array.array(fmt) for fmt in ARRAY] + # Empty arrays + self.assertEqual( + [_testcapi.buffer_pointer_as_int(case) % align for case in cases], + [0] * len(cases), + ) + for case in cases: + case.append(0) + # Allocated arrays + self.assertEqual( + [_testcapi.buffer_pointer_as_int(case) % align for case in cases], + [0] * len(cases), + ) + @support.cpython_only def test_pybuffer_size_from_format(self): # basic tests diff --git a/Misc/ACKS b/Misc/ACKS index feb16a62792e7f..3fe9dcfcc9e7d0 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1151,6 +1151,7 @@ Per Lindqvist Eric Lindvall Gregor Lingl Everett Lipman +Jake Lishman Mirko Liss Alexander Liu Hui Liu diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-24-17-30-51.gh-issue-140557.X2GETk.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-24-17-30-51.gh-issue-140557.X2GETk.rst new file mode 100644 index 00000000000000..d584279a0901b0 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-24-17-30-51.gh-issue-140557.X2GETk.rst @@ -0,0 +1,2 @@ +:class:`bytearray` buffers now have the same alignment +when empty as when allocated. Unaligned buffers can still be created by slicing. diff --git a/Misc/NEWS.d/next/Library/2026-01-07-11-57-59.gh-issue-140557.3P6-nW.rst b/Misc/NEWS.d/next/Library/2026-01-07-11-57-59.gh-issue-140557.3P6-nW.rst new file mode 100644 index 00000000000000..997ad592bbaafd --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-01-07-11-57-59.gh-issue-140557.3P6-nW.rst @@ -0,0 +1,2 @@ +:class:`array.array` buffers now have the same alignment when empty as when +allocated. Unaligned buffers can still be created by slicing. diff --git a/Modules/_testcapi/buffer.c b/Modules/_testcapi/buffer.c index e63d4179824529..48393a3dd530c6 100644 --- a/Modules/_testcapi/buffer.c +++ b/Modules/_testcapi/buffer.c @@ -98,6 +98,27 @@ static PyTypeObject testBufType = { .tp_members = testbuf_members }; +/* Get the pointer from a buffer-supporting object as a PyLong. + * + * Used to test alignment properties. */ +static PyObject * +buffer_pointer_as_int(PyObject *Py_UNUSED(module), PyObject *obj) +{ + PyObject *out; + Py_buffer view; + if (PyObject_GetBuffer(obj, &view, PyBUF_SIMPLE) != 0) { + return NULL; + } + out = PyLong_FromVoidPtr(view.buf); + PyBuffer_Release(&view); + return out; +} + +static PyMethodDef test_methods[] = { + {"buffer_pointer_as_int", buffer_pointer_as_int, METH_O}, + {NULL}, +}; + int _PyTestCapi_Init_Buffer(PyObject *m) { if (PyType_Ready(&testBufType) < 0) { @@ -106,6 +127,9 @@ _PyTestCapi_Init_Buffer(PyObject *m) { if (PyModule_AddObjectRef(m, "testBuf", (PyObject *)&testBufType)) { return -1; } + if (PyModule_AddFunctions(m, test_methods) < 0) { + return -1; + } return 0; } diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index 5769a796b18902..22ec3c31fb3ee1 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -2664,7 +2664,7 @@ array_ass_subscr(PyObject *op, PyObject *item, PyObject *value) } } -static const void *emptybuf = ""; +static const _Py_ALIGNED_DEF(ALIGNOF_MAX_ALIGN_T, char) emptybuf[] = ""; static int From 04d497c284ac933488cc747b3f7082beab300848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20S=C5=82awecki?= Date: Mon, 26 Jan 2026 16:50:27 +0100 Subject: [PATCH 6/8] gh-142119: Clarify context manager protocol documentation on `ContextVar.set` and `Token` (GH-143694) --- Doc/library/contextvars.rst | 38 +++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/Doc/library/contextvars.rst b/Doc/library/contextvars.rst index b218468a084db1..60376e730cb102 100644 --- a/Doc/library/contextvars.rst +++ b/Doc/library/contextvars.rst @@ -77,6 +77,32 @@ Context Variables to restore the variable to its previous value via the :meth:`ContextVar.reset` method. + For convenience, the token object can be used as a context manager + to avoid calling :meth:`ContextVar.reset` manually:: + + var = ContextVar('var', default='default value') + + with var.set('new value'): + assert var.get() == 'new value' + + assert var.get() == 'default value' + + It is a shorthand for:: + + var = ContextVar('var', default='default value') + + token = var.set('new value') + try: + assert var.get() == 'new value' + finally: + var.reset(token) + + assert var.get() == 'default value' + + .. versionadded:: 3.14 + + Added support for using tokens as context managers. + .. method:: reset(token) Reset the context variable to the value it had before the @@ -101,16 +127,8 @@ Context Variables the value of the variable to what it was before the corresponding *set*. - The token supports :ref:`context manager protocol ` - to restore the corresponding context variable value at the exit from - :keyword:`with` block:: - - var = ContextVar('var', default='default value') - - with var.set('new value'): - assert var.get() == 'new value' - - assert var.get() == 'default value' + Tokens support the :ref:`context manager protocol ` + to automatically reset context variables. See :meth:`ContextVar.set`. .. versionadded:: 3.14 From 933540e33217474abee3e1b53dec28ad927b6311 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 26 Jan 2026 17:15:47 +0100 Subject: [PATCH 7/8] gh-101888: Add function.__builtins__ to ref documentation (#144174) --- Doc/reference/datamodel.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 1bfe6b7375bcf7..f784d963f9d3e1 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -546,6 +546,7 @@ Special read-only attributes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. index:: + single: __builtins__ (function attribute) single: __closure__ (function attribute) single: __globals__ (function attribute) pair: global; namespace @@ -556,6 +557,12 @@ Special read-only attributes * - Attribute - Meaning + * - .. attribute:: function.__builtins__ + - A reference to the :class:`dictionary ` that holds the function's + builtins namespace. + + .. versionadded:: 3.10 + * - .. attribute:: function.__globals__ - A reference to the :class:`dictionary ` that holds the function's :ref:`global variables ` -- the global namespace of the module From 9181d776daf87f0e4e2ce02c08f162150fdf7d79 Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" <69878+youknowone@users.noreply.github.com> Date: Tue, 27 Jan 2026 01:40:56 +0900 Subject: [PATCH 8/8] 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 --- Lib/test/test_ctypes/test_incomplete.py | 25 ++++++++++- ...-12-19-11-30-31.gh-issue-142966.PzGiv2.rst | 1 + Modules/_ctypes/_ctypes.c | 43 ++++++++----------- 3 files changed, 44 insertions(+), 25 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-12-19-11-30-31.gh-issue-142966.PzGiv2.rst 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 "&" + 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;