Skip to content

Commit e9dbc5e

Browse files
committed
Add PyListWriter_FromBuffer() function
Add internal _PyMemoryView_ToListWriter() function.
1 parent 8c7ec58 commit e9dbc5e

8 files changed

Lines changed: 112 additions & 1 deletion

File tree

‎Doc/c-api/list.rst‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,3 +329,12 @@ On Free Threading, it's more efficient than the ``PyList`` C API to create a
329329
330330
On success, return ``0``.
331331
On error, set an exception, leave the writer unchanged, and return ``-1``.
332+
333+
.. c:function:: int PyListWriter_FromBuffer(PyListWriter *writer, PyBuffer *view)
334+
335+
Add values from a buffer.
336+
337+
*buffer* must have 1 dimension.
338+
339+
On success, return ``0``.
340+
On error, set an exception, leave the writer unchanged, and return ``-1``.

‎Doc/whatsnew/3.16.rst‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1324,6 +1324,7 @@ New features
13241324
* :c:func:`PyListWriter_ExtendArray`
13251325
* :c:func:`PyListWriter_ExtendArraySteal`
13261326
* :c:func:`PyListWriter_FromFormat`
1327+
* :c:func:`PyListWriter_FromBuffer`
13271328

13281329
(Contributed by Victor Stinner in :gh:`158660`.)
13291330

‎Include/cpython/listobject.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,6 @@ PyAPI_FUNC(int) PyListWriter_FromFormat(
8181
PyListWriter *writer,
8282
const char *format,
8383
...);
84+
PyAPI_FUNC(int) PyListWriter_FromBuffer(
85+
PyListWriter *writer,
86+
Py_buffer *buf);

‎Lib/test/test_capi/test_list.py‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import array
12
import threading
23
import unittest
34
from collections import UserList
@@ -460,7 +461,7 @@ def test_extend_array_steal(self):
460461
# Test PyListWriter_ExtendArraySteal()
461462
self.check_extend('extend_array_steal')
462463

463-
def test_build_value(self):
464+
def test_from_format(self):
464465
# Test PyListWriter_FromFormat()
465466

466467
# Allocate a small array on the stack (1 item)
@@ -483,6 +484,17 @@ def test_build_value(self):
483484
writer.append("after")
484485
self.list_equal(writer.finish(), ["before", "after"])
485486

487+
def test_from_buffer(self):
488+
# Test PyListWriter_FromBuffer()
489+
writer = self.create_writer(0)
490+
writer.from_buffer(memoryview(b'abc'))
491+
self.list_equal(writer.finish(), [97, 98, 99])
492+
493+
numbers = array.array('d', [1.5, 3.0, 5.0])
494+
writer = self.create_writer(0)
495+
writer.from_buffer(memoryview(numbers))
496+
self.list_equal(writer.finish(), [1.5, 3.0, 5.0])
497+
486498
def test_empty(self):
487499
list_get_allocated = _testcapi._list_get_allocated
488500
empty_tuple = ()

‎Misc/NEWS.d/next/C_API/2026-10-21-16-35-42.gh-issue-158660.KvixNp.rst‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@ created list. Add functions:
1212
* :c:func:`PyListWriter_ExtendArray`
1313
* :c:func:`PyListWriter_ExtendArraySteal`
1414
* :c:func:`PyListWriter_FromFormat`
15+
* :c:func:`PyListWriter_FromBuffer`
1516

1617
Patch by Victor Stinner.

‎Modules/_testcapi/list.c‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,30 @@ writer_from_format_error(PyObject *self_raw, PyObject *Py_UNUSED(args))
365365
}
366366

367367

368+
// Test PyListWriter_FromBuffer() error
369+
static PyObject*
370+
writer_from_buffer(PyObject *self_raw, PyObject *obj)
371+
{
372+
WriterObject *self = (WriterObject *)self_raw;
373+
if (writer_check(self) < 0) {
374+
return NULL;
375+
}
376+
377+
Py_buffer view;
378+
int flags = PyBUF_FORMAT | PyBUF_ND | PyBUF_STRIDES;
379+
if (PyObject_GetBuffer(obj, &view, flags) < 0) {
380+
return NULL;
381+
}
382+
383+
int res = PyListWriter_FromBuffer(self->writer, &view);
384+
PyBuffer_Release(&view);
385+
if (res < 0) {
386+
return NULL;
387+
}
388+
Py_RETURN_NONE;
389+
}
390+
391+
368392
// Test PyListWriter_Finish()
369393
static PyObject*
370394
writer_finish(PyObject *self_raw, PyObject *Py_UNUSED(args))
@@ -404,6 +428,7 @@ static PyMethodDef writer_methods[] = {
404428
{"from_format_i", _PyCFunction_CAST(writer_from_format_i), METH_O},
405429
{"from_format_i16", _PyCFunction_CAST(writer_from_format_i16), METH_O},
406430
{"from_format_error", _PyCFunction_CAST(writer_from_format_error), METH_NOARGS},
431+
{"from_buffer", _PyCFunction_CAST(writer_from_buffer), METH_O},
407432
{"finish", _PyCFunction_CAST(writer_finish), METH_NOARGS},
408433
{"discard", _PyCFunction_CAST(writer_discard), METH_NOARGS},
409434
{NULL, NULL} /* sentinel */

‎Objects/listobject.c‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4706,3 +4706,25 @@ PyListWriter_FromFormat(PyListWriter *writer, const char *format, ...)
47064706
}
47074707
return res;
47084708
}
4709+
4710+
4711+
int
4712+
PyListWriter_FromBuffer(PyListWriter *writer, Py_buffer *buf)
4713+
{
4714+
extern int _PyMemoryView_ToListWriter(PyObject *self,
4715+
PyListWriter *writer);
4716+
4717+
if (buf->ndim != 1) {
4718+
PyErr_SetString(PyExc_ValueError, "require a buffer of 1 dimension");
4719+
return -1;
4720+
}
4721+
4722+
PyObject *mview = PyMemoryView_FromBuffer(buf);
4723+
if (mview == NULL) {
4724+
return -1;
4725+
}
4726+
4727+
int res = _PyMemoryView_ToListWriter(mview, writer);
4728+
Py_DECREF(mview);
4729+
return res;
4730+
}

‎Objects/memoryobject.c‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2303,6 +2303,24 @@ tolist_base(PyMemoryViewObject *self, const char *ptr, const Py_ssize_t *shape,
23032303
return lst;
23042304
}
23052305

2306+
static int
2307+
tolistwriter(PyListWriter *writer, PyMemoryViewObject *self, const char *ptr,
2308+
const Py_ssize_t *shape, const Py_ssize_t *strides,
2309+
const Py_ssize_t *suboffsets, const char *fmt)
2310+
{
2311+
for (Py_ssize_t i = 0; i < shape[0]; ptr+=strides[0], i++) {
2312+
const char *xptr = ADJUST_PTR(ptr, suboffsets, 0);
2313+
PyObject *item = unpack_single(self, xptr, fmt);
2314+
if (item == NULL) {
2315+
return -1;
2316+
}
2317+
if (PyListWriter_AppendSteal(writer, item) < 0) {
2318+
return -1;
2319+
}
2320+
}
2321+
return 0;
2322+
}
2323+
23062324
/* Unpack a multi-dimensional array into a nested list.
23072325
Assumption: ndim >= 1. */
23082326
static PyObject *
@@ -2374,6 +2392,26 @@ memoryview_tolist_impl(PyMemoryViewObject *self)
23742392
}
23752393
}
23762394

2395+
2396+
int
2397+
_PyMemoryView_ToListWriter(PyObject *mview, PyListWriter *writer)
2398+
{
2399+
assert(PyMemoryView_Check(mview));
2400+
PyMemoryViewObject *self = (PyMemoryViewObject *)mview;
2401+
const Py_buffer *view = &self->view;
2402+
assert(view->ndim == 1);
2403+
2404+
const char *fmt = adjust_fmt(view);
2405+
if (fmt == NULL) {
2406+
return -1;
2407+
}
2408+
2409+
return tolistwriter(writer, self, view->buf, view->shape,
2410+
view->strides, view->suboffsets,
2411+
fmt);
2412+
}
2413+
2414+
23772415
/*[clinic input]
23782416
memoryview.tobytes
23792417

0 commit comments

Comments
 (0)