Commit Graph
28 Commits
Author SHA1 Message Date
Jeong, YunWon c603eb7c9a Implement PEP 688 and rework the buffer protocol around managed exports (#8523)
* Implement PEP 688 __buffer__ and __release_buffer__

A Python class could not export a buffer: the slot machinery had no
bf_getbuffer or bf_releasebuffer, and every consumer acquired buffers as
PyBUF_FULL_RO through a module of PyBUF_* constants.

Add both slots. PyBuffer::release now runs a Python __release_buffer__
before the exporter's own release, once per acquisition, which PyBuffer
tracks with an `acquired` flag that clones do not inherit. An export made
by a Python __buffer__ is held by a _buffer_wrapper payload that counts its
exports and drops the returned memoryview with the last one, and the view
handed to __release_buffer__ is a _buffer_window that owns no export, so
releasing it inside the hook is inert instead of re-entering it.

Replace the PyBUF_* constants with a BufferFlags bitflags type whose
composite requests are supersets of the simpler ones, so `contains` answers
the REQ_* questions, and pass the request to PyBuffer::from_object. Each
consumer now asks for what its counterpart asks for: y* arguments for
SIMPLE, w* for WRITABLE, BytesIO.write for CONTIG_RO, bytes(), bytearray()
and memoryview() for FULL_RO. memoryview checks the request in
memory_getbuf, and array.array and mmap.mmap expose __release_buffer__.

Test buffer support with PyObject::check_buffer (PyObject_CheckBuffer)
instead of attempting an acquisition, so an exception raised by __buffer__
is no longer reported as the object not being bytes-like, and a __buffer__
with side effects runs once. PyBytesInner becomes a y* conversion as a
result: bytes and bytearray methods no longer accept iterables of ints, and
find, index, count and __contains__ take the arguments
parse_args_finds_byte and bytes_contains describe.

A view exports its start offset in the descriptor rather than in its
window, which fixes a panic when collecting from a negative-stride view.

BytesIO.write rechecks closed after acquiring its buffer, which __buffer__
can close in between.

Assisted-by: Claude Code:claude-opus-5

* Rework the buffer protocol around managed exports and view offsets

Give `PyBuffer` the `_PyManagedBufferObject` shape: one `bf_getbuffer`
acquisition is shared by every handle taken from it, cloning takes another
share instead of re-acquiring, and the exporter's release runs once when the
last share goes away. Remove `retain`, the unsafe `drop_without_release`, the
three `impl Drop`s and the `ManuallyDrop` that stood in for this. Add
`abort_acquisition` so a failed request does not run `bf_releasebuffer`.

Move the view start into `BufferDescriptor::offset`, the `Py_buffer.buf`
analogue, and drop the separate `start` fields on `PyMemoryView` and
`PyBufferWrapper`. Slicing goes through `SaturatedSlice::adjust_indices_start`,
which reproduces `PySlice_AdjustIndices` and keeps the adjusted start.

Fix `zip_eq` to take its contiguous fast path only when both last dimensions
are contiguous, and make `for_each_segment` and `zip_eq` handle zero-length and
zero-dimensional views.

Add `BufferDescriptor::projected` so a request without `PyBUF_ND`,
`PyBUF_STRIDES` or `PyBUF_FORMAT` receives a correspondingly reduced
descriptor, and reject a request without `PyBUF_INDIRECT` against an exporter
that has suboffsets.

Copy the source first in `memoryview` slice assignment when both sides reach
the same root exporter.

Hold the export across the resize in `bytearray.extend`, take `y*` in
`marshal.loads`, stop probing the buffer protocol in `FsPath`, rewrite `ord`
over the concrete string types, fold `array`'s buffer slot into one
`slot_as_buffer`, take `w*`/`y*` in `_overlapped`, and thread the new `offset`
field through the `_ctypes` descriptors.

Assisted-by: Claude

* Re-check a memoryview after a conversion and tighten cast and release

`pack_single` and `unpack_single` addressed the buffer with a position taken
before `__index__` ran, so releasing the view from that conversion read or wrote
outside the exporter's storage, panicking when it had also shrunk. Check the
released flag again once the conversion is done, as `CHECK_RELEASED_AGAIN` does.

Reject a cast to a format that is not a single native format character with an
optional `@` in front of it. An empty format reached a division by its item size
of zero. get_native_fmtchar

Report a second `__release_buffer__` on the same view as a `ValueError` rather
than accepting it, and check that the view belongs to the object first; the
silent case is a view that exports nothing. wrap_releasebuffer

Compare against another memoryview by reading its view where it lies instead of
acquiring a buffer from it, so the restricted view handed to
`__release_buffer__` compares equal rather than unequal in one direction only.
memory_richcompare

Name the type in the unraisable an exception from `__release_buffer__` reports,
as `releasebuffer_call_python` does, instead of reporting the exporter object.

Assisted-by: Claude

* Re-check the destination of a memoryview slice assignment

Acquiring the source runs `__buffer__`, which can release the destination view,
so check the released flag again once the source is in hand and before the
structures are compared, as `copy_single` does.

Build the sliced destination as a view that counts as no export, the way a
`Py_buffer dest = *view` copy does. Holding one kept the exporter unresizable
for the length of the assignment, so a source that released the view and then
resized the exporter met a `BufferError` instead of the assignment reporting the
released view.

Assisted-by: Claude

* Answer the memoryview contiguity getsets with the layout

`f_contiguous` reported the row-major answer for one dimension and False
for any more, so a view laid out both ways, such as one of shape (1, 8),
reported False while `check_buffer_request` accepted a `F_CONTIGUOUS`
request on it. `contiguous` answered row-major order alone.

Move the Fortran-order test from `PyMemoryView` to `BufferDescriptor`,
next to the row-major one it differs from only in iteration order, and
answer all three getsets with it.

Assisted-by: Claude
2026-08-16 21:32:29 +09:00
Yubin Kim 79f7da3b47 bytes: support keyword arguments in hex() (#8312)
* bytes: support keyword arguments in hex()

* bytes: unmark test_memoryview_hex_separator
2026-07-22 20:31:31 +09:00
Shahar Naveh 292f66e051 Update some tests to 3.14.5 (#7909)
* Align patches for `test_class.py`

* Update more tests to 3.14.5

* Update `test_math`

* `test_hash.py`

* Update `test_grammar.py`

* Fix failing test

* Restore `test_grammar.py`
2026-05-19 13:31:33 +09:00
CPython Developers 7ef9e5ebff Update test_memoryview from v3.14.3 2026-02-10 22:27:46 +09:00
Jeong YunWon 3073947a8b mark failing test_memoryview tests 2022-08-15 04:24:58 +09:00
CPython Developers b5dcd61b58 Update test_memoryview from CPython 3.10.6 2022-08-15 04:24:58 +09:00
CPython developers c7a5b960be Update test_memoryview from CPython 3.10.5 2022-07-18 04:07:35 +09:00
Dean Li 6f98288e84 test: use import_helper 2021-11-29 21:03:02 +08:00
Noa e51cb82ff0 Unskip tests 2021-11-15 16:08:46 -06:00
Steve ShiandJeong YunWon 742ea0c4fa Reimpl Buffer Protocol and memoryview support ndarray with shape, stride and suboffset (#3340)
* Fix buffer protocol and memoryview

* Fix io and array the right way to use buffer protocol

* fix rebase

* fix memoryview and introduce VecBuffer

* fix deadlock

* fix memoryview avoid double release buffer

* impl ndim buffer support

* first implement nd array for buffer and memoryview

* fix slice adjust_indices

* fix adjust_indices introduct SaturatedSliceIterator

* fix memoryview cmp

* fix adjust_indices positive_order

* fix bug mark passed test

* fix clippy

* fix set_item_slice deadlock, optimize buffer

* Delete @test_113974_tmp

* Remove vec_buffer_type

* rusty names

* impl memoryview multi index

* add comments

Co-authored-by: Jeong YunWon <jeong@youknowone.org>
2021-11-13 07:17:11 +02:00
Padraic Fanning e25941a5f8 Unskip test(s) in test_memoryview 2021-02-25 21:46:00 -05:00
Padraic Fanning 5996bafae0 Explain test_gc skip 2021-02-07 15:49:42 -05:00
Noah f428fcc692 unskip tests 2021-01-25 18:28:18 -06:00
Kangzhi Shi 44048f60cb Implement memoryview cast 2020-12-27 19:03:05 +02:00
hyperbora 928fbcda50 fix memoryview compare 2020-12-07 21:58:54 +09:00
Jeong YunWon b230045677 Merge pull request #2299 from qingshi163/memoryview_hash
Implement memoryview hash
2020-10-25 13:51:09 +09:00
Kangzhi Shi b38d278f91 memoryview constructor args 2020-10-22 16:45:30 +02:00
Kangzhi Shi 101d8c8dc1 memoryview hash 2020-10-22 13:17:54 +02:00
Kangzhi Shi ce097617f7 fix memoryview is not pickleable 2020-10-17 13:35:29 +02:00
Kangzhi Shi 6058d653a8 memoryview hex 2020-10-10 17:01:00 +09:00
Kangzhi Shi b48c5a2b52 BytesIO getbuffer; no more hack for pickle.py 2020-10-10 17:01:00 +09:00
Kangzhi Shi 3fe8a30ca0 bytesinner from pytuple 2020-10-10 17:01:00 +09:00
Kangzhi Shi 275b727e9d memoryview __setitem__ 2020-10-10 17:01:00 +09:00
Kangzhi Shi cf3572385c Fix more to pass unittest 2020-10-10 17:00:57 +09:00
Kangzhi Shi 499d99794c Implement Buffer Protocol 2020-10-10 16:57:19 +09:00
Noah b245712949 Fix some 32-bit only oom tests + others 2020-06-06 17:14:32 -05:00
Jeong YunWon b9f2b87758 Mark failing tests from test_memoryview.py 2020-03-08 05:53:30 +09:00
CPython dev 4a329bdc65 Lib/test/test_memoryview.py from CPython 3.8.2 2020-03-08 05:53:30 +09:00