https://github.com/python/cpython/commit/19de10d3d8605a290492e4fb3871e12638b0f7bb
commit: 19de10d3d8605a290492e4fb3871e12638b0f7bb
branch: main
author: Jake Lishman <[email protected]>
committer: encukou <[email protected]>
date: 2026-01-26T16:45:17+01:00
summary:

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.

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2025-10-24-17-30-51.gh-issue-140557.X2GETk.rst
A Misc/NEWS.d/next/Library/2026-01-07-11-57-59.gh-issue-140557.3P6-nW.rst
M Lib/test/test_buffer.py
M Misc/ACKS
M Modules/_testcapi/buffer.c
M Modules/arraymodule.c

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

_______________________________________________
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