File: test_pybuffer_drop_without_interpreter.rs

package info (click to toggle)
rust-pyo3 0.27.2-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 4,648 kB
  • sloc: javascript: 59; makefile: 58; python: 39; sh: 1
file content (34 lines) | stat: -rw-r--r-- 1,366 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#![cfg(any(not(Py_LIMITED_API), Py_3_11))] // buffer availability
#![cfg(not(any(PyPy, GraalPy)))] // cannot control interpreter lifecycle in PyPy or GraalPy

//! Dropping `Py<T>` after the interpreter has been finalized should be sound.
//!
//! See e.g. https://github.com/PyO3/pyo3/issues/4632 for an extension of this problem
//! where the interpreter was finalized before `PyBuffer<T>` was dropped.
//!
//! This test runs in its own process to control the interpreter lifecycle.

use pyo3::{buffer::PyBuffer, types::PyBytes};

#[test]
fn test_pybuffer_drop_without_interpreter() {
    // SAFETY: this is knowingly unsafe as we're preserving the `Py<T>` object
    // after the Python interpreter has been finalized.
    //
    // However we should still be able to drop it without causing undefined behavior,
    // so that process shutdown is sound.
    let obj: PyBuffer<u8> = unsafe {
        pyo3::with_embedded_python_interpreter(|py| {
            PyBuffer::get(&PyBytes::new(py, b"abcdef")).unwrap()
        })
    };

    // there should be no interpreter outside of the `with_embedded_python_interpreter` block
    assert_eq!(unsafe { pyo3_ffi::Py_IsInitialized() }, 0);

    // dropping object should be sound
    drop(obj);

    // dropping object should not re-initialize the interpreter
    assert_eq!(unsafe { pyo3_ffi::Py_IsInitialized() }, 0);
}