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]