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