pyo3/
interpreter_lifecycle.rs

1#[cfg(not(any(PyPy, GraalPy)))]
2use crate::{ffi, internal::state::AttachGuard, Python};
3
4static START: std::sync::Once = std::sync::Once::new();
5
6#[cfg(not(any(PyPy, GraalPy)))]
7pub(crate) fn initialize() {
8    // Protect against race conditions when Python is not yet initialized and multiple threads
9    // concurrently call 'initialize()'. Note that we do not protect against
10    // concurrent initialization of the Python runtime by other users of the Python C API.
11    START.call_once_force(|_| unsafe {
12        // Use call_once_force because if initialization panics, it's okay to try again.
13        if ffi::Py_IsInitialized() == 0 {
14            ffi::Py_InitializeEx(0);
15
16            // Release the GIL.
17            ffi::PyEval_SaveThread();
18        }
19    });
20}
21
22/// See [Python::initialize]
23#[cfg(not(any(PyPy, GraalPy)))]
24#[inline]
25#[deprecated(note = "use `Python::initialize` instead", since = "0.26.0")]
26pub fn prepare_freethreaded_python() {
27    initialize();
28}
29
30/// Executes the provided closure with an embedded Python interpreter.
31///
32/// This function initializes the Python interpreter, executes the provided closure, and then
33/// finalizes the Python interpreter.
34///
35/// After execution all Python resources are cleaned up, and no further Python APIs can be called.
36/// Because many Python modules implemented in C do not support multiple Python interpreters in a
37/// single process, it is not safe to call this function more than once. (Many such modules will not
38/// initialize correctly on the second run.)
39///
40/// # Panics
41/// - If the Python interpreter is already initialized before calling this function.
42///
43/// # Safety
44/// - This function should only ever be called once per process (usually as part of the `main`
45///   function). It is also not thread-safe.
46/// - No Python APIs can be used after this function has finished executing.
47/// - The return value of the closure must not contain any Python value, _including_ `PyResult`.
48///
49/// # Examples
50///
51/// ```rust
52/// unsafe {
53///     pyo3::with_embedded_python_interpreter(|py| {
54///         if let Err(e) = py.run(pyo3::ffi::c_str!("print('Hello World')"), None, None) {
55///             // We must make sure to not return a `PyErr`!
56///             e.print(py);
57///         }
58///     });
59/// }
60/// ```
61#[cfg(not(any(PyPy, GraalPy)))]
62pub unsafe fn with_embedded_python_interpreter<F, R>(f: F) -> R
63where
64    F: for<'p> FnOnce(Python<'p>) -> R,
65{
66    assert_eq!(
67        unsafe { ffi::Py_IsInitialized() },
68        0,
69        "called `with_embedded_python_interpreter` but a Python interpreter is already running."
70    );
71
72    unsafe { ffi::Py_InitializeEx(0) };
73
74    let result = {
75        let guard = unsafe { AttachGuard::assume() };
76        let py = guard.python();
77        // Import the threading module - this ensures that it will associate this thread as the "main"
78        // thread, which is important to avoid an `AssertionError` at finalization.
79        py.import("threading").unwrap();
80
81        // Execute the closure.
82        f(py)
83    };
84
85    // Finalize the Python interpreter.
86    unsafe { ffi::Py_Finalize() };
87
88    result
89}
90
91pub(crate) fn ensure_initialized() {
92    // Maybe auto-initialize the interpreter:
93    //  - If auto-initialize feature set and supported, try to initialize the interpreter.
94    //  - If the auto-initialize feature is set but unsupported, emit hard errors only when the
95    //    extension-module feature is not activated - extension modules don't care about
96    //    auto-initialize so this avoids breaking existing builds.
97    //  - Otherwise, just check the interpreter is initialized.
98    #[cfg(all(feature = "auto-initialize", not(any(PyPy, GraalPy))))]
99    {
100        initialize();
101    }
102    #[cfg(not(all(feature = "auto-initialize", not(any(PyPy, GraalPy)))))]
103    {
104        // This is a "hack" to make running `cargo test` for PyO3 convenient (i.e. no need
105        // to specify `--features auto-initialize` manually). Tests within the crate itself
106        // all depend on the auto-initialize feature for conciseness but Cargo does not
107        // provide a mechanism to specify required features for tests.
108        #[cfg(not(any(PyPy, GraalPy)))]
109        if option_env!("CARGO_PRIMARY_PACKAGE").is_some() {
110            initialize();
111        }
112
113        START.call_once_force(|_| unsafe {
114            // Use call_once_force because if there is a panic because the interpreter is
115            // not initialized, it's fine for the user to initialize the interpreter and
116            // retry.
117            assert_ne!(
118                crate::ffi::Py_IsInitialized(),
119                0,
120                "The Python interpreter is not initialized and the `auto-initialize` \
121                        feature is not enabled.\n\n\
122                        Consider calling `Python::initialize()` before attempting \
123                        to use Python APIs."
124            );
125        });
126    }
127}