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 671fcf88c75af9..d3e83bf98ea3e0 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 729e085c19f006..1129314067e15e 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -2655,7 +2655,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