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 @@ -419,6 +419,23 @@ managed by Python. The user is responsible for managing the lifetime of the
419
419
buffer. Using a ``memoryview `` created in this way after deleting the buffer in
420
420
C++ side results in undefined behavior.
421
421
422
+ To prevent undefined behavior, you can call the ``release `` function on a
423
+ ``memoryview ``. After ``release `` is called, any further operation on the view
424
+ will raise a ``ValueError ``. For short lived buffers, consider using
425
+ ``memoryview_scoped_release `` to release the memoryview:
426
+
427
+ .. code-block :: cpp
428
+
429
+ {
430
+ auto view = py::memoryview::from_memory(buffer, size);
431
+ py::memoryview_scoped_release release(view);
432
+
433
+ some_function(view);
434
+ }
435
+
436
+ // operations on the memoryview after this scope exits will raise a
437
+ // ValueError exception
438
+
422
439
We can also use ``memoryview::from_memory `` for a simple 1D contiguous buffer:
423
440
424
441
.. code-block :: cpp
Original file line number Diff line number Diff line change @@ -2181,6 +2181,21 @@ void print(Args &&...args) {
2181
2181
detail::print (c.args (), c.kwargs ());
2182
2182
}
2183
2183
2184
+ #if PY_VERSION_HEX >= 0x03020000
2185
+ // / Release the underlying buffer exposed by the memoryview object when this
2186
+ // / object goes out of scope. Any further operation on the view raises a
2187
+ // / ValueError.
2188
+ // /
2189
+ // / Only available in Python 3.2+
2190
+ class memoryview_scoped_release {
2191
+ public:
2192
+ explicit memoryview_scoped_release (memoryview view) : m_view(std::move(view)) {}
2193
+ ~memoryview_scoped_release () { m_view.attr (" release" )(); }
2194
+ private:
2195
+ memoryview m_view;
2196
+ };
2197
+ #endif
2198
+
2184
2199
error_already_set::~error_already_set () {
2185
2200
if (m_type) {
2186
2201
gil_scoped_acquire gil;
Original file line number Diff line number Diff line change @@ -1514,7 +1514,8 @@ class memoryview : public object {
1514
1514
This method is meant for providing a ``memoryview`` for C/C++ buffer not
1515
1515
managed by Python. The caller is responsible for managing the lifetime
1516
1516
of ``ptr`` and ``format``, which MUST outlive the memoryview constructed
1517
- here.
1517
+ here. Consider using ``memoryview_scoped_release`` to manage the lifetime
1518
+ for short-lived memoryview objects.
1518
1519
1519
1520
See also: Python C API documentation for `PyMemoryView_FromBuffer`_.
1520
1521
@@ -1568,6 +1569,8 @@ class memoryview : public object {
1568
1569
This method is meant for providing a ``memoryview`` for C/C++ buffer not
1569
1570
managed by Python. The caller is responsible for managing the lifetime
1570
1571
of ``mem``, which MUST outlive the memoryview constructed here.
1572
+ Consider using ``memoryview_scoped_release`` to manage the lifetime
1573
+ for short-lived memoryview objects.
1571
1574
1572
1575
This method is not available in Python 2.
1573
1576
Original file line number Diff line number Diff line change @@ -434,4 +434,14 @@ TEST_SUBMODULE(pytypes, m) {
434
434
m.def (" weakref_from_object" , [](const py::object &o) { return py::weakref (o); });
435
435
m.def (" weakref_from_object_and_function" ,
436
436
[](py::object o, py::function f) { return py::weakref (std::move (o), std::move (f)); });
437
+
438
+ #if PY_VERSION_HEX >= 0x03020000
439
+ m.def (" test_memoryview_scoped_release" , [](const py::function f) {
440
+ const char * buf = " \x42 " ;
441
+ auto view = py::memoryview::from_memory (buf, 1 );
442
+ py::memoryview_scoped_release release (view);
443
+ f (view);
444
+ });
445
+ #endif
446
+
437
447
}
Original file line number Diff line number Diff line change @@ -589,3 +589,17 @@ def callback(wr):
589
589
del obj
590
590
pytest .gc_collect ()
591
591
assert callback .called
592
+
593
+
594
+ @pytest .mark .skipif (sys .version_info < (3 , 2 ), reason = "API not available" )
595
+ def test_memoryview_scoped_release ():
596
+ class C :
597
+ def fn (self , view ):
598
+ self .view = view
599
+ assert bytes (view ) == b"\x42 "
600
+
601
+ c = C ()
602
+ m .test_memoryview_scoped_release (c .fn )
603
+ assert hasattr (c , "view" )
604
+ with pytest .raises (ValueError ):
605
+ bytes (c .view )
You can’t perform that action at this time.
0 commit comments