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 @@ -417,6 +417,23 @@ managed by Python. The user is responsible for managing the lifetime of the
417
417
buffer. Using a ``memoryview `` created in this way after deleting the buffer in
418
418
C++ side results in undefined behavior.
419
419
420
+ To prevent undefined behavior, you can call the ``release `` function on a
421
+ ``memoryview ``. After ``release `` is called, any further operation on the view
422
+ will raise a ``ValueError ``. For short lived buffers, consider using
423
+ ``memoryview_scoped_release `` to release the memoryview:
424
+
425
+ .. code-block :: cpp
426
+
427
+ {
428
+ auto view = py::memoryview::from_memory(buffer, size);
429
+ py::memoryview_scoped_release release(view);
430
+
431
+ some_function(view);
432
+ }
433
+
434
+ // operations on the memoryview after this scope exits will raise a
435
+ // ValueError exception
436
+
420
437
We can also use ``memoryview::from_memory `` for a simple 1D contiguous buffer:
421
438
422
439
.. code-block :: cpp
Original file line number Diff line number Diff line change @@ -2174,6 +2174,21 @@ class gil_scoped_acquire { };
2174
2174
class gil_scoped_release { };
2175
2175
#endif
2176
2176
2177
+ #if PY_VERSION_HEX >= 0x03020000
2178
+ // / Release the underlying buffer exposed by the memoryview object when this
2179
+ // / object goes out of scope. Any further operation on the view raises a
2180
+ // / ValueError.
2181
+ // /
2182
+ // / Only available in Python 3.2+
2183
+ class memoryview_scoped_release {
2184
+ public:
2185
+ explicit memoryview_scoped_release (memoryview view) : m_view(view) {}
2186
+ ~memoryview_scoped_release () { m_view.attr (" release" )(); }
2187
+ private:
2188
+ memoryview m_view;
2189
+ };
2190
+ #endif
2191
+
2177
2192
error_already_set::~error_already_set () {
2178
2193
if (m_type) {
2179
2194
gil_scoped_acquire gil;
Original file line number Diff line number Diff line change @@ -1421,7 +1421,8 @@ class memoryview : public object {
1421
1421
This method is meant for providing a ``memoryview`` for C/C++ buffer not
1422
1422
managed by Python. The caller is responsible for managing the lifetime
1423
1423
of ``ptr`` and ``format``, which MUST outlive the memoryview constructed
1424
- here.
1424
+ here. Consider using ``memoryview_scoped_release`` to manage the lifetime
1425
+ for short-lived memoryview objects.
1425
1426
1426
1427
See also: Python C API documentation for `PyMemoryView_FromBuffer`_.
1427
1428
@@ -1475,6 +1476,8 @@ class memoryview : public object {
1475
1476
This method is meant for providing a ``memoryview`` for C/C++ buffer not
1476
1477
managed by Python. The caller is responsible for managing the lifetime
1477
1478
of ``mem``, which MUST outlive the memoryview constructed here.
1479
+ Consider using ``memoryview_scoped_release`` to manage the lifetime
1480
+ for short-lived memoryview objects.
1478
1481
1479
1482
This method is not available in Python 2.
1480
1483
Original file line number Diff line number Diff line change @@ -410,4 +410,14 @@ TEST_SUBMODULE(pytypes, m) {
410
410
411
411
// test_builtin_functions
412
412
m.def (" get_len" , [](py::handle h) { return py::len (h); });
413
+
414
+ #if PY_VERSION_HEX >= 0x03020000
415
+ m.def (" test_memoryview_scoped_release" , [](py::function f) {
416
+ const char * buf = " \x42 " ;
417
+ auto view = py::memoryview::from_memory (buf, 1 );
418
+ py::memoryview_scoped_release release (view);
419
+ f (view);
420
+ });
421
+ #endif
422
+
413
423
}
Original file line number Diff line number Diff line change @@ -486,3 +486,17 @@ def test_builtin_functions():
486
486
"object of type 'generator' has no len()" ,
487
487
"'generator' has no length" ,
488
488
] # PyPy
489
+
490
+
491
+ @pytest .mark .skipif (sys .version_info < (3 , 2 ), reason = "API not available" )
492
+ def test_memoryview_scoped_release ():
493
+ class C :
494
+ def fn (self , view ):
495
+ self .view = view
496
+ assert bytes (view ) == b"\x42 "
497
+
498
+ c = C ()
499
+ m .test_memoryview_scoped_release (c .fn )
500
+ assert hasattr (c , "view" )
501
+ with pytest .raises (ValueError ):
502
+ bytes (c .view )
You can’t perform that action at this time.
0 commit comments