File tree Expand file tree Collapse file tree 5 files changed +60
-1
lines changed Expand file tree Collapse file tree 5 files changed +60
-1
lines changed Original file line number Diff line number Diff line change @@ -412,6 +412,23 @@ managed by Python. The user is responsible for managing the lifetime of the
412
412
buffer. Using a ``memoryview `` created in this way after deleting the buffer in
413
413
C++ side results in undefined behavior.
414
414
415
+ To prevent undefined behavior, you can call the ``release `` function on a
416
+ ``memoryview ``. After ``release `` is called, any further operation on the view
417
+ will raise a ``ValueError ``. For short lived buffers, consider using
418
+ ``memoryview_scoped_release `` to release the memoryview:
419
+
420
+ .. code-block :: cpp
421
+
422
+ {
423
+ auto view = py::memoryview::from_memory(buffer, size);
424
+ py::memoryview_scoped_release release(view);
425
+
426
+ some_function(view);
427
+ }
428
+
429
+ // operations on the memoryview after this scope exits will raise a
430
+ // ValueError exception
431
+
415
432
We can also use ``memoryview::from_memory `` for a simple 1D contiguous buffer:
416
433
417
434
.. code-block :: cpp
Original file line number Diff line number Diff line change @@ -2047,6 +2047,21 @@ class gil_scoped_acquire { };
2047
2047
class gil_scoped_release { };
2048
2048
#endif
2049
2049
2050
+ #if PY_VERSION_HEX >= 0x03020000
2051
+ // / Release the underlying buffer exposed by the memoryview object when this
2052
+ // / object goes out of scope. Any further operation on the view raises a
2053
+ // / ValueError.
2054
+ // /
2055
+ // / Only available in Python 3.2+
2056
+ class memoryview_scoped_release {
2057
+ public:
2058
+ explicit memoryview_scoped_release (memoryview view) : m_view(view) {}
2059
+ ~memoryview_scoped_release () { m_view.attr (" release" )(); }
2060
+ private:
2061
+ memoryview m_view;
2062
+ };
2063
+ #endif
2064
+
2050
2065
error_already_set::~error_already_set () {
2051
2066
if (m_type) {
2052
2067
gil_scoped_acquire gil;
Original file line number Diff line number Diff line change @@ -1364,7 +1364,8 @@ class memoryview : public object {
1364
1364
This method is meant for providing a ``memoryview`` for C/C++ buffer not
1365
1365
managed by Python. The caller is responsible for managing the lifetime
1366
1366
of ``ptr`` and ``format``, which MUST outlive the memoryview constructed
1367
- here.
1367
+ here. Consider using ``memoryview_scoped_release`` to manage the lifetime
1368
+ for short-lived memoryview objects.
1368
1369
1369
1370
See also: Python C API documentation for `PyMemoryView_FromBuffer`_.
1370
1371
@@ -1418,6 +1419,8 @@ class memoryview : public object {
1418
1419
This method is meant for providing a ``memoryview`` for C/C++ buffer not
1419
1420
managed by Python. The caller is responsible for managing the lifetime
1420
1421
of ``mem``, which MUST outlive the memoryview constructed here.
1422
+ Consider using ``memoryview_scoped_release`` to manage the lifetime
1423
+ for short-lived memoryview objects.
1421
1424
1422
1425
This method is not available in Python 2.
1423
1426
Original file line number Diff line number Diff line change @@ -367,4 +367,14 @@ TEST_SUBMODULE(pytypes, m) {
367
367
buf, static_cast <ssize_t >(strlen (buf)));
368
368
});
369
369
#endif
370
+
371
+ #if PY_VERSION_HEX >= 0x03020000
372
+ m.def (" test_memoryview_scoped_release" , [](py::function f) {
373
+ const char * buf = " \x42 " ;
374
+ auto view = py::memoryview::from_memory (buf, 1 );
375
+ py::memoryview_scoped_release release (view);
376
+ f (view);
377
+ });
378
+ #endif
379
+
370
380
}
Original file line number Diff line number Diff line change @@ -345,3 +345,17 @@ def test_memoryview_from_memory():
345
345
assert isinstance (view , memoryview )
346
346
assert view .format == 'B'
347
347
assert bytes (view ) == b'\xff \xe1 \xab \x37 '
348
+
349
+
350
+ @pytest .mark .skipif (sys .version_info < (3 , 2 ), reason = 'API not available' )
351
+ def test_memoryview_scoped_release ():
352
+ class C :
353
+ def fn (self , view ):
354
+ self .view = view
355
+ assert bytes (view ) == b'\x42 '
356
+ c = C ()
357
+ m .test_memoryview_scoped_release (c .fn )
358
+ assert hasattr (c , 'view' )
359
+ with pytest .raises (ValueError ):
360
+ bytes (c .view )
361
+
You can’t perform that action at this time.
0 commit comments