Skip to content

Commit 22a1846

Browse files
default to gil_used = false (#5564)
* default to `gil_used = false` * further tidy ups * fix formatting * newsfragment * Apply suggestions from code review Co-authored-by: Nathan Goldbaum <nathan.goldbaum@gmail.com> --------- Co-authored-by: Nathan Goldbaum <nathan.goldbaum@gmail.com>
1 parent 613e542 commit 22a1846

File tree

23 files changed

+70
-70
lines changed

23 files changed

+70
-70
lines changed

guide/src/free-threading.md

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,29 +19,36 @@ This document provides advice for porting Rust code using PyO3 to run under free
1919

2020
## Supporting free-threaded Python with PyO3
2121

22-
Many simple uses of PyO3, like exposing bindings for a "pure" Rust function with no side-effects or defining an immutable Python class, will likely work "out of the box" on the free-threaded build.
23-
All that will be necessary is to annotate Python modules declared by rust code in your project to declare that they support free-threaded Python, for example by declaring the module with `#[pymodule(gil_used = false)]`.
22+
Since PyO3 0.28, PyO3 defaults to assuming Python modules created with it are thread-safe.
23+
This will be the case except for Rust code which has used `unsafe` to assume thread-safety incorrectly.
24+
An example of this is `unsafe` code which was written with the historical assumption that Python was single-threaded due to the GIL, and so the `Python<'py>` token used by PyO3 could be used to guarantee thread-safety.
25+
A module can opt-out of supporting free-threaded Python until it has audited its `unsafe` code for correctness by declaring the module with `#[pymodule(gil_used = true)]` (see below).
2426

25-
More complicated `#[pyclass]` types may need to deal with thread-safety directly; there is [a dedicated section of the guide](./class/thread-safety.md) to discuss this.
27+
Complicated `#[pyclass]` types may need to deal with thread-safety directly; there is [a dedicated section of the guide](./class/thread-safety.md) to discuss this.
2628

2729
At a low-level, annotating a module sets the `Py_MOD_GIL` slot on modules defined by an extension to `Py_MOD_GIL_NOT_USED`, which allows the interpreter to see at runtime that the author of the extension thinks the extension is thread-safe.
28-
You should only do this if you know that your extension is thread-safe.
29-
Because of Rust's guarantees, this is already true for many extensions, however see below for more discussion about how to evaluate the thread safety of existing Rust extensions and how to think about the PyO3 API using a Python runtime with no GIL.
3030

31-
If you do not explicitly mark that modules are thread-safe, the Python interpreter will re-enable the GIL at runtime while importing your module and print a `RuntimeWarning` with a message containing the name of the module causing it to re-enable the GIL.
31+
By opting-out of supporting free-threaded Python, the Python interpreter will re-enable the GIL at runtime while importing your module and print a `RuntimeWarning` with a message containing the name of the module causing it to re-enable the GIL.
3232
You can force the GIL to remain disabled by setting the `PYTHON_GIL=0` as an environment variable or passing `-Xgil=0` when starting Python (`0` means the GIL is turned off).
3333

3434
If you are sure that all data structures exposed in a `PyModule` are thread-safe, then pass `gil_used = false` as a parameter to the `pymodule` procedural macro declaring the module or call `PyModule::gil_used` on a `PyModule` instance.
3535
For example:
3636

37-
```rust,no_run
38-
use pyo3::prelude::*;
37+
### Example opting-in
38+
39+
(Note: for PyO3 versions 0.23 through 0.27, the default was `gil_used = true` and so the opposite was needed; modules needed to opt-in to free-threaded Python support with `gil_used = false`.)
3940

40-
/// This module supports free-threaded Python
41-
#[pymodule(gil_used = false)]
42-
fn my_extension(m: &Bound<'_, PyModule>) -> PyResult<()> {
43-
// add members to the module that you know are thread-safe
44-
Ok(())
41+
```rust,no_run
42+
/// This module relies on the GIL for thread safety
43+
#[pyo3::pymodule(gil_used = true)]
44+
mod my_extension {
45+
use pyo3::prelude::*;
46+
47+
// this type is not thread-safe
48+
#[pyclass]
49+
struct MyNotThreadSafeType {
50+
// insert not thread-safe code
51+
}
4552
}
4653
```
4754

@@ -53,15 +60,12 @@ use pyo3::prelude::*;
5360
# #[allow(dead_code)]
5461
fn register_child_module(parent_module: &Bound<'_, PyModule>) -> PyResult<()> {
5562
let child_module = PyModule::new(parent_module.py(), "child_module")?;
56-
child_module.gil_used(false)?;
63+
child_module.gil_used(true)?;
5764
parent_module.add_submodule(&child_module)
5865
}
5966
6067
```
6168

62-
For now you must explicitly opt in to free-threading support by annotating modules defined in your extension.
63-
In a future version of `PyO3`, we plan to make `gil_used = false` the default.
64-
6569
See the [`string-sum`](https://github.com/PyO3/pyo3/tree/main/pyo3-ffi/examples/string-sum) example for how to declare free-threaded support using raw FFI calls for modules using single-phase initialization and the [`sequential`](https://github.com/PyO3/pyo3/tree/main/pyo3-ffi/examples/sequential) example for modules using multi-phase initialization.
6670

6771
If you would like to use conditional compilation to trigger different code paths under the free-threaded build, you can use the `Py_GIL_DISABLED` attribute once you have configured your crate to generate the necessary build configuration data.

guide/src/migration.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ For a detailed list of all changes, see the [CHANGELOG](changelog.md).
55

66
## from 0.27.* to 0.28
77

8+
### Default to supporting free-threaded Python
9+
10+
When PyO3 0.23 added support for free-threaded Python, this was as an opt-in feature for modules by annotating with `#[pymodule(gil_used = false)]`.
11+
12+
As the support has matured and PyO3's own API has evolved to remove reliance on the GIL, the time is right to switch the default.
13+
Modules now automatically allow use on free-threaded Python, unless they directly state they require the GIL with `#[pymodule(gil_used = true)]`.
14+
815
### Deprecation of automatic `FromPyObject` for `#[pyclass]` types which implement `Clone`
916

1017
`#[pyclass]` types which implement `Clone` used to also implement `FromPyObject` automatically.

newsfragments/5564.packaging.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Support for free-threaded Python is now opt-out rather than opt-in.

pyo3-macros-backend/src/module.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -536,9 +536,6 @@ fn module_initialization(
536536

537537
impl_::ModuleDef::new(__PYO3_NAME, #doc, &SLOTS)
538538
};
539-
#[doc(hidden)]
540-
// so wrapped submodules can see what gil_used is
541-
pub static __PYO3_GIL_USED: bool = #gil_used;
542539
};
543540
if !is_submodule {
544541
result.extend(quote! {
@@ -547,10 +544,7 @@ fn module_initialization(
547544
#[doc(hidden)]
548545
#[export_name = #pyinit_symbol]
549546
pub unsafe extern "C" fn __pyo3_init() -> *mut #pyo3_path::ffi::PyObject {
550-
_PYO3_DEF.init_multi_phase(
551-
unsafe { #pyo3_path::Python::assume_attached() },
552-
#gil_used
553-
)
547+
_PYO3_DEF.init_multi_phase()
554548
}
555549
});
556550
}

pyo3-macros/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ use syn::{parse_macro_input, Item};
2727
/// | `#[pyo3(submodule)]` | Skips adding a `PyInit_` FFI symbol to the compiled binary. |
2828
/// | `#[pyo3(module = "...")]` | Defines the Python `dotted.path` to the parent module for use in introspection. |
2929
/// | `#[pyo3(crate = "pyo3")]` | Defines the path to PyO3 to use code generated by the macro. |
30+
/// | `#[pyo3(gil_used = true)]` | Declares the GIL is needed to run this module safely under free-threaded Python. |
3031
///
3132
/// For more on creating Python modules see the [module section of the guide][1].
3233
///

pytests/src/awaitable.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ impl FutureAwaitable {
7878
}
7979
}
8080

81-
#[pymodule(gil_used = false)]
81+
#[pymodule]
8282
pub fn awaitable(m: &Bound<'_, PyModule>) -> PyResult<()> {
8383
m.add_class::<IterAwaitable>()?;
8484
m.add_class::<FutureAwaitable>()?;

pytests/src/buf_and_str.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ fn return_memoryview(py: Python<'_>) -> PyResult<Bound<'_, PyMemoryView>> {
4747
PyMemoryView::from(&bytes)
4848
}
4949

50-
#[pymodule(gil_used = false)]
50+
#[pymodule]
5151
pub fn buf_and_str(m: &Bound<'_, PyModule>) -> PyResult<()> {
5252
m.add_class::<BytesExtractor>()?;
5353
m.add_function(wrap_pyfunction!(return_memoryview, m)?)?;

pytests/src/comparisons.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ impl OrderedDefaultNe {
147147
}
148148
}
149149

150-
#[pymodule(gil_used = false)]
150+
#[pymodule]
151151
pub mod comparisons {
152152
#[pymodule_export]
153153
use super::{

pytests/src/datetime.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ impl TzClass {
203203
}
204204
}
205205

206-
#[pymodule(gil_used = false)]
206+
#[pymodule]
207207
pub fn datetime(m: &Bound<'_, PyModule>) -> PyResult<()> {
208208
m.add_function(wrap_pyfunction!(make_date, m)?)?;
209209
m.add_function(wrap_pyfunction!(get_date_tuple, m)?)?;

pytests/src/enums.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use pyo3::{pyclass, pyfunction, pymodule};
22

3-
#[pymodule(gil_used = false)]
3+
#[pymodule]
44
pub mod enums {
55
#[pymodule_export]
66
use super::{

0 commit comments

Comments
 (0)