Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/plugin/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
//"export" our API module to the python runtime
pyo3::append_to_inittab!(pylib_module);
//spawn runtime
pyo3::prepare_freethreaded_python();
Python::initialize();
//import path for python
let path = Path::new("./python_plugin/");
//do useful work
Expand Down
2 changes: 1 addition & 1 deletion guide/src/building-and-distribution.md
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ If you encounter these or other complications when linking the interpreter stati

### Import your module when embedding the Python interpreter

When you run your Rust binary with an embedded interpreter, any `#[pymodule]` created modules won't be accessible to import unless added to a table called `PyImport_Inittab` before the embedded interpreter is initialized. This will cause Python statements in your embedded interpreter such as `import your_new_module` to fail. You can call the macro [`append_to_inittab`]({{#PYO3_DOCS_URL}}/pyo3/macro.append_to_inittab.html) with your module before initializing the Python interpreter to add the module function into that table. (The Python interpreter will be initialized by calling `prepare_freethreaded_python`, `with_embedded_python_interpreter`, or `Python::attach` with the [`auto-initialize`](features.md#auto-initialize) feature enabled.)
When you run your Rust binary with an embedded interpreter, any `#[pymodule]` created modules won't be accessible to import unless added to a table called `PyImport_Inittab` before the embedded interpreter is initialized. This will cause Python statements in your embedded interpreter such as `import your_new_module` to fail. You can call the macro [`append_to_inittab`]({{#PYO3_DOCS_URL}}/pyo3/macro.append_to_inittab.html) with your module before initializing the Python interpreter to add the module function into that table. (The Python interpreter will be initialized by calling `Python::initialize`, `with_embedded_python_interpreter`, or `Python::attach` with the [`auto-initialize`](features.md#auto-initialize) feature enabled.)

## Cross Compiling

Expand Down
4 changes: 2 additions & 2 deletions guide/src/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ section for further detail.

### `auto-initialize`

This feature changes [`Python::attach`]({{#PYO3_DOCS_URL}}/pyo3/marker/struct.Python.html#method.attach) to automatically initialize a Python interpreter (by calling [`prepare_freethreaded_python`]({{#PYO3_DOCS_URL}}/pyo3/fn.prepare_freethreaded_python.html)) if needed.
This feature changes [`Python::attach`]({{#PYO3_DOCS_URL}}/pyo3/marker/struct.Python.html#method.attach) to automatically initialize a Python interpreter (by calling [`Python::initialize`]({{#PYO3_DOCS_URL}}/pyo3/marker/struct.Python.html#method.initialize)) if needed.

If you do not enable this feature, you should call `pyo3::prepare_freethreaded_python()` before attempting to call any other Python APIs.
If you do not enable this feature, you should call `Python::initialize()` before attempting to call any other Python APIs.

## Advanced Features

Expand Down
4 changes: 3 additions & 1 deletion guide/src/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ This guide can help you upgrade code through breaking changes from one PyO3 vers
For a detailed list of all changes, see the [CHANGELOG](changelog.md).

## from 0.25.* to 0.26
### Rename of `Python::with_gil` and `Python::allow_threads`
### Rename of `Python::with_gil`, `Python::allow_threads`, and `pyo3::prepare_freethreaded_python`
<details open>
<summary><small>Click to expand</small></summary>
The names for these APIs were created when the global interpreter lock (GIL) was mandatory. With the introduction of free-threading in Python 3.13 this is no longer the case, and the naming does not has no universal meaning anymore.
For this reason we chose to rename these to more modern terminology introduced in free-threading:

- `Python::with_gil` is now called `Python::attach`, it attaches a Python thread-state to the current thread. In GIL enabled builds there can only be 1 thread attached to the interpreter, in free-threading there can be more.
- `Python::allow_threads` is now called `Python::detach`, it detaches a previously attached thread-state.
- `pyo3::prepare_freethreaded_python` is now called `Python::initialize`.
</details>

## from 0.24.* to 0.25
Expand Down
1 change: 1 addition & 0 deletions newsfragments/5247.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rename `pyo3::prepare_freethreaded_python` to `Python::initialize`
2 changes: 1 addition & 1 deletion src/conversions/chrono.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
//! use pyo3::{Python, PyResult, IntoPyObject, types::PyAnyMethods};
//!
//! fn main() -> PyResult<()> {
//! pyo3::prepare_freethreaded_python();
//! Python::initialize();
//! Python::attach(|py| {
//! // Build some chrono values
//! let chrono_datetime = Utc.with_ymd_and_hms(2022, 1, 1, 12, 0, 0).unwrap();
Expand Down
2 changes: 1 addition & 1 deletion src/conversions/chrono_tz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
//! use pyo3::{Python, PyResult, IntoPyObject, types::PyAnyMethods};
//!
//! fn main() -> PyResult<()> {
//! pyo3::prepare_freethreaded_python();
//! Python::initialize();
//! Python::attach(|py| {
//! // Convert to Python
//! let py_tzinfo = Tz::Europe__Paris.into_pyobject(py)?;
Expand Down
2 changes: 1 addition & 1 deletion src/conversions/either.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
//! use pyo3::{Python, PyResult, IntoPyObject, types::PyAnyMethods};
//!
//! fn main() -> PyResult<()> {
//! pyo3::prepare_freethreaded_python();
//! Python::initialize();
//! Python::attach(|py| {
//! // Create a string and an int in Python.
//! let py_str = "crab".into_pyobject(py)?;
Expand Down
2 changes: 1 addition & 1 deletion src/conversions/jiff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
//! # fn main() -> () {}
//! # #[cfg(not(windows))]
//! fn main() -> PyResult<()> {
//! pyo3::prepare_freethreaded_python();
//! Python::initialize();
//! Python::attach(|py| {
//! // Build some jiff values
//! let jiff_zoned = Zoned::now();
Expand Down
2 changes: 1 addition & 1 deletion src/conversions/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
//! use pyo3::{Python, PyResult, IntoPyObject, types::PyAnyMethods};
//!
//! fn main() -> PyResult<()> {
//! pyo3::prepare_freethreaded_python();
//! Python::initialize();
//! Python::attach(|py| {
//! // Create a fixed date and time (2022-01-01 12:00:00 UTC)
//! let date = Date::from_calendar_date(2022, Month::January, 1).unwrap();
Expand Down
42 changes: 14 additions & 28 deletions src/interpreter_lifecycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,10 @@ use crate::{ffi, internal::state::AttachGuard, Python};

static START: std::sync::Once = std::sync::Once::new();

/// Prepares the use of Python in a free-threaded context.
///
/// If the Python interpreter is not already initialized, this function will initialize it with
/// signal handling disabled (Python will not raise the `KeyboardInterrupt` exception). Python
/// signal handling depends on the notion of a 'main thread', which must be the thread that
/// initializes the Python interpreter.
///
/// If the Python interpreter is already initialized, this function has no effect.
///
/// This function is unavailable under PyPy because PyPy cannot be embedded in Rust (or any other
/// software). Support for this is tracked on the
/// [PyPy issue tracker](https://github.com/pypy/pypy/issues/3836).
///
/// # Examples
/// ```rust
/// use pyo3::prelude::*;
///
/// # fn main() -> PyResult<()> {
/// pyo3::prepare_freethreaded_python();
/// Python::attach(|py| py.run(pyo3::ffi::c_str!("print('Hello World')"), None, None))
/// # }
/// ```
#[cfg(not(any(PyPy, GraalPy)))]
pub fn prepare_freethreaded_python() {
pub(crate) fn initialize() {
// Protect against race conditions when Python is not yet initialized and multiple threads
// concurrently call 'prepare_freethreaded_python()'. Note that we do not protect against
// concurrently call 'initialize()'. Note that we do not protect against
// concurrent initialization of the Python runtime by other users of the Python C API.
START.call_once_force(|_| unsafe {
// Use call_once_force because if initialization panics, it's okay to try again.
Expand All @@ -41,6 +19,14 @@ pub fn prepare_freethreaded_python() {
});
}

/// See [Python::initialize]
#[cfg(not(any(PyPy, GraalPy)))]
#[inline]
#[deprecated(note = "use `Python::initialize` instead", since = "0.26.0")]
pub fn prepare_freethreaded_python() {
initialize();
}

/// Executes the provided closure with an embedded Python interpreter.
///
/// This function initializes the Python interpreter, executes the provided closure, and then
Expand Down Expand Up @@ -111,17 +97,17 @@ pub(crate) fn ensure_initialized() {
// - Otherwise, just check the interpreter is initialized.
#[cfg(all(feature = "auto-initialize", not(any(PyPy, GraalPy))))]
{
prepare_freethreaded_python();
initialize();
}
#[cfg(not(all(feature = "auto-initialize", not(any(PyPy, GraalPy)))))]
{
// This is a "hack" to make running `cargo test` for PyO3 convenient (i.e. no need
// to specify `--features auto-initialize` manually. Tests within the crate itself
// to specify `--features auto-initialize` manually). Tests within the crate itself
// all depend on the auto-initialize feature for conciseness but Cargo does not
// provide a mechanism to specify required features for tests.
#[cfg(not(any(PyPy, GraalPy)))]
if option_env!("CARGO_PRIMARY_PACKAGE").is_some() {
prepare_freethreaded_python();
initialize();
}

START.call_once_force(|_| unsafe {
Expand All @@ -133,7 +119,7 @@ pub(crate) fn ensure_initialized() {
0,
"The Python interpreter is not initialized and the `auto-initialize` \
feature is not enabled.\n\n\
Consider calling `pyo3::prepare_freethreaded_python()` before attempting \
Consider calling `Python::initialize()` before attempting \
to use Python APIs."
);
});
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ pub use crate::conversion::{FromPyObject, IntoPyObject, IntoPyObjectExt};
pub use crate::err::{DowncastError, DowncastIntoError, PyErr, PyErrArguments, PyResult, ToPyErr};
pub use crate::instance::{Borrowed, Bound, BoundObject, Py, PyObject};
#[cfg(not(any(PyPy, GraalPy)))]
#[allow(deprecated)]
pub use crate::interpreter_lifecycle::{
prepare_freethreaded_python, with_embedded_python_interpreter,
};
Expand Down
2 changes: 1 addition & 1 deletion src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ macro_rules! wrap_pymodule {
/// Add the module to the initialization table in order to make embedded Python code to use it.
/// Module name is the argument.
///
/// Use it before [`prepare_freethreaded_python`](crate::prepare_freethreaded_python) and
/// Use it before [`Python::initialize`](crate::marker::Python::initialize) and
/// leave feature `auto-initialize` off
#[cfg(not(any(PyPy, GraalPy)))]
#[macro_export]
Expand Down
33 changes: 30 additions & 3 deletions src/marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,9 +379,9 @@ impl Python<'_> {
/// initialized, this function will initialize it. See
#[cfg_attr(
not(any(PyPy, GraalPy)),
doc = "[`prepare_freethreaded_python`](crate::prepare_freethreaded_python)"
doc = "[`Python::initialize`](crate::marker::Python::initialize)"
)]
#[cfg_attr(PyPy, doc = "`prepare_freethreaded_python`")]
#[cfg_attr(PyPy, doc = "`Python::initialize")]
/// for details.
///
/// If the current thread does not yet have a Python "thread state" associated with it,
Expand Down Expand Up @@ -419,6 +419,33 @@ impl Python<'_> {
f(guard.python())
}

/// Prepares the use of Python.
///
/// If the Python interpreter is not already initialized, this function will initialize it with
/// signal handling disabled (Python will not raise the `KeyboardInterrupt` exception). Python
/// signal handling depends on the notion of a 'main thread', which must be the thread that
/// initializes the Python interpreter.
///
/// If the Python interpreter is already initialized, this function has no effect.
///
/// This function is unavailable under PyPy because PyPy cannot be embedded in Rust (or any other
/// software). Support for this is tracked on the
/// [PyPy issue tracker](https://github.com/pypy/pypy/issues/3836).
///
/// # Examples
/// ```rust
/// use pyo3::prelude::*;
///
/// # fn main() -> PyResult<()> {
/// Python::initialize();
/// Python::attach(|py| py.run(pyo3::ffi::c_str!("print('Hello World')"), None, None))
/// # }
/// ```
#[cfg(not(any(PyPy, GraalPy)))]
pub fn initialize() {
crate::interpreter_lifecycle::initialize();
}

/// Like [`Python::attach`] except Python interpreter state checking is skipped.
///
/// Normally when the GIL is acquired, we check that the Python interpreter is an
Expand Down Expand Up @@ -889,7 +916,7 @@ mod tests {
// Before starting the interpreter the state of calling `PyGILState_Check`
// seems to be undefined, so let's ensure that Python is up.
#[cfg(not(any(PyPy, GraalPy)))]
crate::prepare_freethreaded_python();
Python::initialize();

let state = unsafe { crate::ffi::PyGILState_Check() };
assert_eq!(state, GIL_NOT_HELD);
Expand Down
Loading