Skip to content

Commit ebfc854

Browse files
Icxoludavidhewitt
andauthored
rename Python::allow_threads to Python::detach (#5221)
* rename `Python::allow_threads` to `Python::detach` * Update guide/src/migration.md Co-authored-by: David Hewitt <mail@davidhewitt.dev> --------- Co-authored-by: David Hewitt <mail@davidhewitt.dev>
1 parent 834ad10 commit ebfc854

File tree

23 files changed

+113
-95
lines changed

23 files changed

+113
-95
lines changed

examples/word-count/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ fn search_sequential(contents: &str, needle: &str) -> usize {
1717
}
1818

1919
#[pyfunction]
20-
fn search_sequential_allow_threads(py: Python<'_>, contents: &str, needle: &str) -> usize {
21-
py.allow_threads(|| search_sequential(contents, needle))
20+
fn search_sequential_detached(py: Python<'_>, contents: &str, needle: &str) -> usize {
21+
py.detach(|| search_sequential(contents, needle))
2222
}
2323

2424
/// Count the occurrences of needle in line, case insensitive
@@ -36,7 +36,7 @@ fn count_line(line: &str, needle: &str) -> usize {
3636
fn word_count(m: &Bound<'_, PyModule>) -> PyResult<()> {
3737
m.add_function(wrap_pyfunction!(search, m)?)?;
3838
m.add_function(wrap_pyfunction!(search_sequential, m)?)?;
39-
m.add_function(wrap_pyfunction!(search_sequential_allow_threads, m)?)?;
39+
m.add_function(wrap_pyfunction!(search_sequential_detached, m)?)?;
4040

4141
Ok(())
4242
}

examples/word-count/tests/test_word_count.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,8 @@ def test_word_count_python_sequential(benchmark, contents):
5050
def run_rust_sequential_twice(
5151
executor: ThreadPoolExecutor, contents: str, needle: str
5252
) -> int:
53-
future_1 = executor.submit(
54-
word_count.search_sequential_allow_threads, contents, needle
55-
)
56-
future_2 = executor.submit(
57-
word_count.search_sequential_allow_threads, contents, needle
58-
)
53+
future_1 = executor.submit(word_count.search_sequential_detached, contents, needle)
54+
future_2 = executor.submit(word_count.search_sequential_detached, contents, needle)
5955
result_1 = future_1.result()
6056
result_2 = future_2.result()
6157
return result_1 + result_2

examples/word-count/word_count/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
from .word_count import search, search_sequential, search_sequential_allow_threads
1+
from .word_count import search, search_sequential, search_sequential_detached
22

33
__all__ = [
44
"search_py",
55
"search",
66
"search_sequential",
7-
"search_sequential_allow_threads",
7+
"search_sequential_detached",
88
]
99

1010

guide/src/async-await.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ where
6767
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
6868
let waker = cx.waker();
6969
Python::attach(|py| {
70-
py.allow_threads(|| pin!(&mut self.0).poll(&mut Context::from_waker(waker)))
70+
py.detach(|| pin!(&mut self.0).poll(&mut Context::from_waker(waker)))
7171
})
7272
}
7373
}

guide/src/conversions/tables.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ Using Rust library types as function arguments will incur a conversion cost comp
6767
However, once that conversion cost has been paid, the Rust standard library types offer a number of benefits:
6868
- You can write functionality in native-speed Rust code (free of Python's runtime costs).
6969
- You get better interoperability with the rest of the Rust ecosystem.
70-
- You can use `Python::allow_threads` to release the Python GIL and let other Python threads make progress while your Rust code is executing.
70+
- You can use `Python::detach` to release the Python GIL and let other Python threads make progress while your Rust code is executing.
7171
- You also benefit from stricter type checking. For example you can specify `Vec<i32>`, which will only accept a Python `list` containing integers. The Python-native equivalent, `&PyList`, would accept a Python `list` containing Python objects of any type.
7272

7373
For most PyO3 usage the conversion cost is worth paying to get these benefits. As always, if you're not sure it's worth it in your case, benchmark it!

guide/src/features.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ See [the `#[pyclass]` implementation details](class.md#implementation-details) f
105105

106106
### `nightly`
107107

108-
The `nightly` feature needs the nightly Rust compiler. This allows PyO3 to use the `auto_traits` and `negative_impls` features to fix the `Python::allow_threads` function.
108+
The `nightly` feature needs the nightly Rust compiler. This allows PyO3 to use the `auto_traits` and `negative_impls` features to fix the `Python::detach` function.
109109

110110
### `resolve-config`
111111

guide/src/free-threading.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ This is a non-exhaustive list and there may be other situations in future Python
180180
versions that can trigger global synchronization events.
181181

182182
This means that you should detach from the interpreter runtime using
183-
[`Python::allow_threads`] in exactly the same situations as you should detach
183+
[`Python::detach`] in exactly the same situations as you should detach
184184
from the runtime in the GIL-enabled build: when doing long-running tasks that do
185185
not require the CPython runtime or when doing any task that needs to re-attach
186186
to the runtime (see the [guide
@@ -197,7 +197,7 @@ Data attached to `pyclass` instances is protected from concurrent access by a
197197
`RefCell`-like pattern of runtime borrow checking. Like a `RefCell`, PyO3 will
198198
raise exceptions (or in some cases panic) to enforce exclusive access for
199199
mutable borrows. It was always possible to generate panics like this in PyO3 in
200-
code that releases the GIL with [`Python::allow_threads`] or calling a python
200+
code that releases the GIL with [`Python::detach`] or calling a python
201201
method accepting `&self` from a `&mut self` (see [the docs on interior
202202
mutability](./class.md#bound-and-interior-mutability),) but now in free-threaded
203203
Python there are more opportunities to trigger these panics from Python because
@@ -402,7 +402,7 @@ interpreter.
402402
[`OnceLockExt::get_or_init_py_attached`]: {{#PYO3_DOCS_URL}}/pyo3/sync/trait.OnceLockExt.html#tymethod.get_or_init_py_attached
403403
[`OnceLock`]: https://doc.rust-lang.org/stable/std/sync/struct.OnceLock.html
404404
[`OnceLock::get_or_init`]: https://doc.rust-lang.org/stable/std/sync/struct.OnceLock.html#method.get_or_init
405-
[`Python::allow_threads`]: {{#PYO3_DOCS_URL}}/pyo3/marker/struct.Python.html#method.allow_threads
405+
[`Python::detach`]: {{#PYO3_DOCS_URL}}/pyo3/marker/struct.Python.html#method.detach
406406
[`Python::attach`]: {{#PYO3_DOCS_URL}}/pyo3/marker/struct.Python.html#method.attach
407407
[`Python<'py>`]: {{#PYO3_DOCS_URL}}/pyo3/marker/struct.Python.html
408408
[`threading`]: https://docs.python.org/3/library/threading.html

guide/src/migration.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,19 @@
33
This guide can help you upgrade code through breaking changes from one PyO3 version to the next.
44
For a detailed list of all changes, see the [CHANGELOG](changelog.md).
55

6+
## from 0.25.* to 0.26
7+
### Rename of `Python::with_gil` and `Python::allow_threads`
8+
<details open>
9+
<summary><small>Click to expand</small></summary>
10+
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.
11+
For this reason we chose to rename these to more modern terminology introduced in free-threading:
12+
- `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.
13+
- `Python::allow_threads` is now called `Python::detach`, it detaches a previously attached thread-state.
14+
</details>
15+
616
## from 0.24.* to 0.25
717
### `AsPyPointer` removal
8-
<details open>
18+
<details>
919
<summary><small>Click to expand</small></summary>
1020
The `AsPyPointer` trait is mostly a leftover from the now removed gil-refs API. The last remaining uses were the GC API, namely `PyVisit::call`, and identity comparison (`PyAnyMethods::is` and `Py::is`).
1121

guide/src/parallelism.md

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ fn search_sequential(contents: &str, needle: &str) -> usize {
4949
}
5050
```
5151

52-
To enable parallel execution of this function, the [`Python::allow_threads`] method can be used to temporarily release the GIL, thus allowing other Python threads to run. We then have a function exposed to the Python runtime which calls `search_sequential` inside a closure passed to [`Python::allow_threads`] to enable true parallelism:
52+
To enable parallel execution of this function, the [`Python::detach`] method can be used to temporarily release the GIL, thus allowing other Python threads to run. We then have a function exposed to the Python runtime which calls `search_sequential` inside a closure passed to [`Python::detach`] to enable true parallelism:
5353
```rust,no_run
5454
# #![allow(dead_code)]
5555
# use pyo3::prelude::*;
@@ -68,23 +68,23 @@ To enable parallel execution of this function, the [`Python::allow_threads`] met
6868
# contents.lines().map(|line| count_line(line, needle)).sum()
6969
# }
7070
#[pyfunction]
71-
fn search_sequential_allow_threads(py: Python<'_>, contents: &str, needle: &str) -> usize {
72-
py.allow_threads(|| search_sequential(contents, needle))
71+
fn search_sequential_detached(py: Python<'_>, contents: &str, needle: &str) -> usize {
72+
py.detach(|| search_sequential(contents, needle))
7373
}
7474
```
7575

7676
Now Python threads can use more than one CPU core, resolving the limitation which usually makes multi-threading in Python only good for IO-bound tasks:
7777
```Python
7878
from concurrent.futures import ThreadPoolExecutor
79-
from word_count import search_sequential_allow_threads
79+
from word_count import search_sequential_detached
8080

8181
executor = ThreadPoolExecutor(max_workers=2)
8282

8383
future_1 = executor.submit(
84-
word_count.search_sequential_allow_threads, contents, needle
84+
word_count.search_sequential_detached, contents, needle
8585
)
8686
future_2 = executor.submit(
87-
word_count.search_sequential_allow_threads, contents, needle
87+
word_count.search_sequential_detached, contents, needle
8888
)
8989
result_1 = future_1.result()
9090
result_2 = future_2.result()
@@ -149,7 +149,7 @@ struct UserID {
149149
150150
let allowed_ids: Vec<bool> = Python::attach(|outer_py| {
151151
let instances: Vec<Py<UserID>> = (0..10).map(|x| Py::new(outer_py, UserID { id: x }).unwrap()).collect();
152-
outer_py.allow_threads(|| {
152+
outer_py.detach(|| {
153153
instances.par_iter().map(|instance| {
154154
Python::attach(|inner_py| {
155155
instance.borrow(inner_py).id > 5
@@ -165,13 +165,13 @@ an `inner_py` token. Sharing GIL lifetime tokens between threads is not allowed
165165
and threads must individually acquire the GIL to access data wrapped by a python
166166
object.
167167

168-
It's also important to see that this example uses [`Python::allow_threads`] to
168+
It's also important to see that this example uses [`Python::detach`] to
169169
wrap the code that spawns OS threads via `rayon`. If this example didn't use
170-
`allow_threads`, a rayon worker thread would block on acquiring the GIL while a
170+
`detach`, a rayon worker thread would block on acquiring the GIL while a
171171
thread that owns the GIL spins forever waiting for the result of the rayon
172-
thread. Calling `allow_threads` allows the GIL to be released in the thread
172+
thread. Calling `detach` allows the GIL to be released in the thread
173173
collecting the results from the worker threads. You should always call
174-
`allow_threads` in situations that spawn worker threads, but especially so in
174+
`detach` in situations that spawn worker threads, but especially so in
175175
cases where worker threads need to acquire the GIL, to prevent deadlocks.
176176

177-
[`Python::allow_threads`]: {{#PYO3_DOCS_URL}}/pyo3/marker/struct.Python.html#method.allow_threads
177+
[`Python::detach`]: {{#PYO3_DOCS_URL}}/pyo3/marker/struct.Python.html#method.detach

newsfragments/5221.changed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rename `Python::allow_threads` to `Python::detach`

0 commit comments

Comments
 (0)