diff --git a/examples/word-count/src/lib.rs b/examples/word-count/src/lib.rs index 5bc73df97a4..baa9cf18e57 100644 --- a/examples/word-count/src/lib.rs +++ b/examples/word-count/src/lib.rs @@ -17,8 +17,8 @@ fn search_sequential(contents: &str, needle: &str) -> usize { } #[pyfunction] -fn search_sequential_allow_threads(py: Python<'_>, contents: &str, needle: &str) -> usize { - py.allow_threads(|| search_sequential(contents, needle)) +fn search_sequential_detached(py: Python<'_>, contents: &str, needle: &str) -> usize { + py.detach(|| search_sequential(contents, needle)) } /// Count the occurrences of needle in line, case insensitive @@ -36,7 +36,7 @@ fn count_line(line: &str, needle: &str) -> usize { fn word_count(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(search, m)?)?; m.add_function(wrap_pyfunction!(search_sequential, m)?)?; - m.add_function(wrap_pyfunction!(search_sequential_allow_threads, m)?)?; + m.add_function(wrap_pyfunction!(search_sequential_detached, m)?)?; Ok(()) } diff --git a/examples/word-count/tests/test_word_count.py b/examples/word-count/tests/test_word_count.py index 5991e4ae1de..5b665d8e29c 100644 --- a/examples/word-count/tests/test_word_count.py +++ b/examples/word-count/tests/test_word_count.py @@ -50,12 +50,8 @@ def test_word_count_python_sequential(benchmark, contents): def run_rust_sequential_twice( executor: ThreadPoolExecutor, contents: str, needle: str ) -> int: - future_1 = executor.submit( - word_count.search_sequential_allow_threads, contents, needle - ) - future_2 = executor.submit( - word_count.search_sequential_allow_threads, contents, needle - ) + future_1 = executor.submit(word_count.search_sequential_detached, contents, needle) + future_2 = executor.submit(word_count.search_sequential_detached, contents, needle) result_1 = future_1.result() result_2 = future_2.result() return result_1 + result_2 diff --git a/examples/word-count/word_count/__init__.py b/examples/word-count/word_count/__init__.py index 8ce7a175471..4a7f1b5ee5e 100644 --- a/examples/word-count/word_count/__init__.py +++ b/examples/word-count/word_count/__init__.py @@ -1,10 +1,10 @@ -from .word_count import search, search_sequential, search_sequential_allow_threads +from .word_count import search, search_sequential, search_sequential_detached __all__ = [ "search_py", "search", "search_sequential", - "search_sequential_allow_threads", + "search_sequential_detached", ] diff --git a/guide/src/async-await.md b/guide/src/async-await.md index 59ea99b6ef8..bf2dd84f5b3 100644 --- a/guide/src/async-await.md +++ b/guide/src/async-await.md @@ -67,7 +67,7 @@ where fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let waker = cx.waker(); Python::attach(|py| { - py.allow_threads(|| pin!(&mut self.0).poll(&mut Context::from_waker(waker))) + py.detach(|| pin!(&mut self.0).poll(&mut Context::from_waker(waker))) }) } } diff --git a/guide/src/conversions/tables.md b/guide/src/conversions/tables.md index 23d6942b177..cec255178ed 100644 --- a/guide/src/conversions/tables.md +++ b/guide/src/conversions/tables.md @@ -67,7 +67,7 @@ Using Rust library types as function arguments will incur a conversion cost comp However, once that conversion cost has been paid, the Rust standard library types offer a number of benefits: - You can write functionality in native-speed Rust code (free of Python's runtime costs). - You get better interoperability with the rest of the Rust ecosystem. -- You can use `Python::allow_threads` to release the Python GIL and let other Python threads make progress while your Rust code is executing. +- You can use `Python::detach` to release the Python GIL and let other Python threads make progress while your Rust code is executing. - You also benefit from stricter type checking. For example you can specify `Vec`, 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. 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! diff --git a/guide/src/features.md b/guide/src/features.md index 8339816310c..13c2d2b8d8f 100644 --- a/guide/src/features.md +++ b/guide/src/features.md @@ -105,7 +105,7 @@ See [the `#[pyclass]` implementation details](class.md#implementation-details) f ### `nightly` -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. +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. ### `resolve-config` diff --git a/guide/src/free-threading.md b/guide/src/free-threading.md index 96866d32f04..3af858029f3 100644 --- a/guide/src/free-threading.md +++ b/guide/src/free-threading.md @@ -180,7 +180,7 @@ This is a non-exhaustive list and there may be other situations in future Python versions that can trigger global synchronization events. This means that you should detach from the interpreter runtime using -[`Python::allow_threads`] in exactly the same situations as you should detach +[`Python::detach`] in exactly the same situations as you should detach from the runtime in the GIL-enabled build: when doing long-running tasks that do not require the CPython runtime or when doing any task that needs to re-attach to the runtime (see the [guide @@ -197,7 +197,7 @@ Data attached to `pyclass` instances is protected from concurrent access by a `RefCell`-like pattern of runtime borrow checking. Like a `RefCell`, PyO3 will raise exceptions (or in some cases panic) to enforce exclusive access for mutable borrows. It was always possible to generate panics like this in PyO3 in -code that releases the GIL with [`Python::allow_threads`] or calling a python +code that releases the GIL with [`Python::detach`] or calling a python method accepting `&self` from a `&mut self` (see [the docs on interior mutability](./class.md#bound-and-interior-mutability),) but now in free-threaded Python there are more opportunities to trigger these panics from Python because @@ -402,7 +402,7 @@ interpreter. [`OnceLockExt::get_or_init_py_attached`]: {{#PYO3_DOCS_URL}}/pyo3/sync/trait.OnceLockExt.html#tymethod.get_or_init_py_attached [`OnceLock`]: https://doc.rust-lang.org/stable/std/sync/struct.OnceLock.html [`OnceLock::get_or_init`]: https://doc.rust-lang.org/stable/std/sync/struct.OnceLock.html#method.get_or_init -[`Python::allow_threads`]: {{#PYO3_DOCS_URL}}/pyo3/marker/struct.Python.html#method.allow_threads +[`Python::detach`]: {{#PYO3_DOCS_URL}}/pyo3/marker/struct.Python.html#method.detach [`Python::attach`]: {{#PYO3_DOCS_URL}}/pyo3/marker/struct.Python.html#method.attach [`Python<'py>`]: {{#PYO3_DOCS_URL}}/pyo3/marker/struct.Python.html [`threading`]: https://docs.python.org/3/library/threading.html diff --git a/guide/src/migration.md b/guide/src/migration.md index 91c00ce5017..6d7ba316185 100644 --- a/guide/src/migration.md +++ b/guide/src/migration.md @@ -3,9 +3,19 @@ This guide can help you upgrade code through breaking changes from one PyO3 version to the next. 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` +
+Click to expand +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. +
+ ## from 0.24.* to 0.25 ### `AsPyPointer` removal -
+
Click to expand 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`). diff --git a/guide/src/parallelism.md b/guide/src/parallelism.md index c215211dce4..c9c01d4f3b8 100644 --- a/guide/src/parallelism.md +++ b/guide/src/parallelism.md @@ -49,7 +49,7 @@ fn search_sequential(contents: &str, needle: &str) -> usize { } ``` -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: +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: ```rust,no_run # #![allow(dead_code)] # use pyo3::prelude::*; @@ -68,23 +68,23 @@ To enable parallel execution of this function, the [`Python::allow_threads`] met # contents.lines().map(|line| count_line(line, needle)).sum() # } #[pyfunction] -fn search_sequential_allow_threads(py: Python<'_>, contents: &str, needle: &str) -> usize { - py.allow_threads(|| search_sequential(contents, needle)) +fn search_sequential_detached(py: Python<'_>, contents: &str, needle: &str) -> usize { + py.detach(|| search_sequential(contents, needle)) } ``` 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: ```Python from concurrent.futures import ThreadPoolExecutor -from word_count import search_sequential_allow_threads +from word_count import search_sequential_detached executor = ThreadPoolExecutor(max_workers=2) future_1 = executor.submit( - word_count.search_sequential_allow_threads, contents, needle + word_count.search_sequential_detached, contents, needle ) future_2 = executor.submit( - word_count.search_sequential_allow_threads, contents, needle + word_count.search_sequential_detached, contents, needle ) result_1 = future_1.result() result_2 = future_2.result() @@ -149,7 +149,7 @@ struct UserID { let allowed_ids: Vec = Python::attach(|outer_py| { let instances: Vec> = (0..10).map(|x| Py::new(outer_py, UserID { id: x }).unwrap()).collect(); - outer_py.allow_threads(|| { + outer_py.detach(|| { instances.par_iter().map(|instance| { Python::attach(|inner_py| { instance.borrow(inner_py).id > 5 @@ -165,13 +165,13 @@ an `inner_py` token. Sharing GIL lifetime tokens between threads is not allowed and threads must individually acquire the GIL to access data wrapped by a python object. -It's also important to see that this example uses [`Python::allow_threads`] to +It's also important to see that this example uses [`Python::detach`] to wrap the code that spawns OS threads via `rayon`. If this example didn't use -`allow_threads`, a rayon worker thread would block on acquiring the GIL while a +`detach`, a rayon worker thread would block on acquiring the GIL while a thread that owns the GIL spins forever waiting for the result of the rayon -thread. Calling `allow_threads` allows the GIL to be released in the thread +thread. Calling `detach` allows the GIL to be released in the thread collecting the results from the worker threads. You should always call -`allow_threads` in situations that spawn worker threads, but especially so in +`detach` in situations that spawn worker threads, but especially so in cases where worker threads need to acquire the GIL, to prevent deadlocks. -[`Python::allow_threads`]: {{#PYO3_DOCS_URL}}/pyo3/marker/struct.Python.html#method.allow_threads +[`Python::detach`]: {{#PYO3_DOCS_URL}}/pyo3/marker/struct.Python.html#method.detach diff --git a/newsfragments/5221.changed.md b/newsfragments/5221.changed.md new file mode 100644 index 00000000000..afc86a3dd12 --- /dev/null +++ b/newsfragments/5221.changed.md @@ -0,0 +1 @@ +rename `Python::allow_threads` to `Python::detach` \ No newline at end of file diff --git a/pytests/src/pyclasses.rs b/pytests/src/pyclasses.rs index 4e681b2c941..d9ec2547478 100644 --- a/pytests/src/pyclasses.rs +++ b/pytests/src/pyclasses.rs @@ -62,7 +62,7 @@ impl PyClassThreadIter { let current_count = self.count; self.count += 1; if current_count == 0 { - py.allow_threads(|| thread::sleep(time::Duration::from_millis(100))); + py.detach(|| thread::sleep(time::Duration::from_millis(100))); } self.count } diff --git a/src/err/err_state.rs b/src/err/err_state.rs index f9a64c96d25..1a8a54186cd 100644 --- a/src/err/err_state.rs +++ b/src/err/err_state.rs @@ -45,7 +45,7 @@ impl PyErrState { pub(crate) fn normalized(normalized: PyErrStateNormalized) -> Self { let state = Self::from_inner(PyErrStateInner::Normalized(normalized)); // This state is already normalized, by completing the Once immediately we avoid - // reaching the `py.allow_threads` in `make_normalized` which is less efficient + // reaching the `py.detach` in `make_normalized` which is less efficient // and introduces a GIL switch which could deadlock. // See https://github.com/PyO3/pyo3/issues/4764 state.normalized.call_once(|| {}); @@ -98,7 +98,7 @@ impl PyErrState { } // avoid deadlock of `.call_once` with the GIL - py.allow_threads(|| { + py.detach(|| { self.normalized.call_once(|| { self.normalizing_thread .lock() @@ -406,7 +406,7 @@ mod tests { fn arguments(self, py: Python<'_>) -> PyObject { // releasing the GIL potentially allows for other threads to deadlock // with the normalization going on here - py.allow_threads(|| { + py.detach(|| { std::thread::sleep(std::time::Duration::from_millis(10)); }); py.None() diff --git a/src/gil.rs b/src/gil.rs index e4cea3ff2a0..ade83e6c412 100644 --- a/src/gil.rs +++ b/src/gil.rs @@ -551,13 +551,13 @@ mod tests { } #[test] - fn test_allow_threads() { + fn test_detach() { assert!(!gil_is_acquired()); Python::attach(|py| { assert!(gil_is_acquired()); - py.allow_threads(move || { + py.detach(move || { assert!(!gil_is_acquired()); Python::attach(|_| assert!(gil_is_acquired())); @@ -574,13 +574,13 @@ mod tests { #[cfg(feature = "py-clone")] #[test] #[should_panic] - fn test_allow_threads_updates_refcounts() { + fn test_detach_updates_refcounts() { Python::attach(|py| { // Make a simple object with 1 reference let obj = get_object(py); assert!(obj.get_refcnt(py) == 1); // Clone the object without the GIL which should panic - py.allow_threads(|| obj.clone()); + py.detach(|| obj.clone()); }); } diff --git a/src/instance.rs b/src/instance.rs index 2fe67677dc9..bc9c949516f 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -58,7 +58,7 @@ mod bound_object_sealed { /// /// To access the object in situations where the GIL is not held, convert it to [`Py`] /// using [`.unbind()`][Bound::unbind]. This includes situations where the GIL is temporarily -/// released, such as [`Python::allow_threads`](crate::Python::allow_threads)'s closure. +/// released, such as [`Python::detach`](crate::Python::detach)'s closure. /// /// See #[doc = concat!("[the guide](https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/types.html#boundpy-t)")] diff --git a/src/marker.rs b/src/marker.rs index bcf5d2fcb48..860405ab0ac 100644 --- a/src/marker.rs +++ b/src/marker.rs @@ -14,7 +14,7 @@ //! awaiting a future //! - Once that is done, reacquire the GIL //! -//! That API is provided by [`Python::allow_threads`] and enforced via the [`Ungil`] bound on the +//! That API is provided by [`Python::detach`] and enforced via the [`Ungil`] bound on the //! closure and the return type. This is done by relying on the [`Send`] auto trait. `Ungil` is //! defined as the following: //! @@ -34,7 +34,7 @@ //! ## Drawbacks //! //! There is no reason to prevent `!Send` types like [`Rc`] from crossing the closure. After all, -//! [`Python::allow_threads`] just lets other Python threads run - it does not itself launch a new +//! [`Python::detach`] just lets other Python threads run - it does not itself launch a new //! thread. //! //! ```rust, compile_fail @@ -47,7 +47,7 @@ //! Python::attach(|py| { //! let rc = Rc::new(5); //! -//! py.allow_threads(|| { +//! py.detach(|| { //! // This would actually be fine... //! println!("{:?}", *rc); //! }); @@ -77,7 +77,7 @@ //! //! let wrapped = SendWrapper::new(string); //! -//! py.allow_threads(|| { +//! py.detach(|| { //! # #[cfg(not(feature = "nightly"))] //! # { //! // 💥 Unsound! 💥 @@ -154,13 +154,13 @@ use std::os::raw::c_int; /// Python::attach(|py| { /// let rc = Rc::new(42); /// -/// py.allow_threads(|| { +/// py.detach(|| { /// println!("{:?}", rc); /// }); /// }); /// ``` /// -/// This also implies that the interplay between `attach` and `allow_threads` is unsound, for example +/// This also implies that the interplay between `attach` and `detach` is unsound, for example /// one can circumvent this protection using the [`send_wrapper`](https://docs.rs/send_wrapper/) crate: /// /// ```no_run @@ -173,7 +173,7 @@ use std::os::raw::c_int; /// /// let wrapped = SendWrapper::new(string); /// -/// py.allow_threads(|| { +/// py.detach(|| { /// let sneaky: &Bound<'_, PyString> = &*wrapped; /// /// println!("{:?}", sneaky); @@ -217,7 +217,7 @@ mod nightly { /// Python::attach(|py| { /// let string = PyString::new(py, "foo"); /// - /// py.allow_threads(|| { + /// py.detach(|| { /// println!("{:?}", string); /// }); /// }); @@ -228,7 +228,7 @@ mod nightly { /// ```compile_fail /// # use pyo3::prelude::*; /// Python::attach(|py| { - /// py.allow_threads(|| { + /// py.detach(|| { /// drop(py); /// }); /// }); @@ -247,7 +247,7 @@ mod nightly { /// /// let wrapped = SendWrapper::new(string); /// - /// py.allow_threads(|| { + /// py.detach(|| { /// let sneaky: &PyString = *wrapped; /// /// println!("{:?}", sneaky); @@ -255,7 +255,7 @@ mod nightly { /// }); /// ``` /// - /// This also enables using non-[`Send`] types in `allow_threads`, + /// This also enables using non-[`Send`] types in `detach`, /// at least if they are not also bound to the GIL: /// /// ```rust @@ -265,7 +265,7 @@ mod nightly { /// Python::attach(|py| { /// let rc = Rc::new(42); /// - /// py.allow_threads(|| { + /// py.detach(|| { /// println!("{:?}", rc); /// }); /// }); @@ -347,7 +347,7 @@ pub use nightly::Ungil; /// * Thread 1's Python interpreter call blocks trying to reacquire the GIL held by thread 2 /// /// To avoid deadlocking, you should release the GIL before trying to lock a mutex or `await`ing in -/// asynchronous code, e.g. with [`Python::allow_threads`]. +/// asynchronous code, e.g. with [`Python::detach`]. /// /// # Releasing and freeing memory /// @@ -457,6 +457,17 @@ impl Python<'_> { } impl<'py> Python<'py> { + /// See [Python::detach] + #[inline] + #[deprecated(note = "use `Python::detach` instead", since = "0.26.0")] + pub fn allow_threads(self, f: F) -> T + where + F: Ungil + FnOnce() -> T, + T: Ungil, + { + self.detach(f) + } + /// Temporarily releases the GIL, thus allowing other Python threads to run. The GIL will be /// reacquired when `F`'s scope ends. /// @@ -479,7 +490,7 @@ impl<'py> Python<'py> { /// #[pyfunction] /// fn sum_numbers(py: Python<'_>, numbers: Vec) -> PyResult { /// // We release the GIL here so any other Python threads get a chance to run. - /// py.allow_threads(move || { + /// py.detach(move || { /// // An example of an "expensive" Rust calculation /// let sum = numbers.iter().sum(); /// @@ -498,7 +509,7 @@ impl<'py> Python<'py> { /// ``` /// /// Please see the [Parallelism] chapter of the guide for a thorough discussion of using - /// [`Python::allow_threads`] in this manner. + /// [`Python::detach`] in this manner. /// /// # Example: Passing borrowed Python references into the closure is not allowed /// @@ -508,7 +519,7 @@ impl<'py> Python<'py> { /// /// fn parallel_print(py: Python<'_>) { /// let s = PyString::new(py, "This object cannot be accessed without holding the GIL >_<"); - /// py.allow_threads(move || { + /// py.detach(move || { /// println!("{:?}", s); // This causes a compile error. /// }); /// } @@ -518,7 +529,7 @@ impl<'py> Python<'py> { /// [`PyString`]: crate::types::PyString /// [auto-traits]: https://doc.rust-lang.org/nightly/unstable-book/language-features/auto-traits.html /// [Parallelism]: https://pyo3.rs/main/parallelism.html - pub fn allow_threads(self, f: F) -> T + pub fn detach(self, f: F) -> T where F: Ungil + FnOnce() -> T, T: Ungil, @@ -869,21 +880,21 @@ mod tests { #[test] #[cfg(not(target_arch = "wasm32"))] // We are building wasm Python with pthreads disabled - fn test_allow_threads_releases_and_acquires_gil() { + fn test_detach_releases_and_acquires_gil() { Python::attach(|py| { let b = std::sync::Arc::new(std::sync::Barrier::new(2)); let b2 = b.clone(); std::thread::spawn(move || Python::attach(|_| b2.wait())); - py.allow_threads(|| { - // If allow_threads does not release the GIL, this will deadlock because + py.detach(|| { + // If `detach` does not release the GIL, this will deadlock because // the thread spawned above will never be able to acquire the GIL. b.wait(); }); unsafe { - // If the GIL is not reacquired at the end of allow_threads, this call + // If the GIL is not reacquired at the end of `detach`, this call // will crash the Python interpreter. let tstate = ffi::PyEval_SaveThread(); ffi::PyEval_RestoreThread(tstate); @@ -892,11 +903,11 @@ mod tests { } #[test] - fn test_allow_threads_panics_safely() { + fn test_detach_panics_safely() { Python::attach(|py| { let result = std::panic::catch_unwind(|| unsafe { let py = Python::assume_gil_acquired(); - py.allow_threads(|| { + py.detach(|| { panic!("There was a panic!"); }); }); @@ -904,7 +915,7 @@ mod tests { // Check panic was caught assert!(result.is_err()); - // If allow_threads is implemented correctly, this thread still owns the GIL here + // If `detach` is implemented correctly, this thread still owns the GIL here // so the following Python calls should not cause crashes. let list = PyList::new(py, [1, 2, 3, 4]).unwrap(); assert_eq!(list.extract::>().unwrap(), vec![1, 2, 3, 4]); @@ -913,13 +924,13 @@ mod tests { #[cfg(not(pyo3_disable_reference_pool))] #[test] - fn test_allow_threads_pass_stuff_in() { + fn test_detach_pass_stuff_in() { let list = Python::attach(|py| PyList::new(py, vec!["foo", "bar"]).unwrap().unbind()); let mut v = vec![1, 2, 3]; let a = std::sync::Arc::new(String::from("foo")); Python::attach(|py| { - py.allow_threads(|| { + py.detach(|| { drop((list, &mut v, a)); }); }); diff --git a/src/pycell/impl_.rs b/src/pycell/impl_.rs index 49f4bf32f72..c3d697d56d0 100644 --- a/src/pycell/impl_.rs +++ b/src/pycell/impl_.rs @@ -555,7 +555,7 @@ mod tests { Python::attach(|py| { let inst = Py::new(py, MyClass { x: 0 }).unwrap(); - let total_modifications = py.allow_threads(|| { + let total_modifications = py.detach(|| { std::thread::scope(|s| { // Spawn a bunch of threads all racing to write to // the same instance of `MyClass`. diff --git a/src/types/bytearray.rs b/src/types/bytearray.rs index 12634b0ac2d..5aa7acceb86 100644 --- a/src/types/bytearray.rs +++ b/src/types/bytearray.rs @@ -192,7 +192,7 @@ pub trait PyByteArrayMethods<'py>: crate::sealed::Sealed { /// /// // This explicitly yields control back to the Python interpreter... /// // ...but it's not always this obvious. Many things do this implicitly. - /// py.allow_threads(|| { + /// py.detach(|| { /// // Python code could be mutating through its handle to `bytes`, /// // which makes reading it a data race, which is undefined behavior. /// println!("{:?}", slice[0]); diff --git a/tests/test_class_basics.rs b/tests/test_class_basics.rs index 8c3efe07aa5..d085db60351 100644 --- a/tests/test_class_basics.rs +++ b/tests/test_class_basics.rs @@ -650,7 +650,7 @@ fn drop_unsendable_elsewhere() { ) .unwrap(); - py.allow_threads(|| { + py.detach(|| { spawn(move || { Python::attach(move |_py| { drop(unsendable); diff --git a/tests/test_pyfunction.rs b/tests/test_pyfunction.rs index 02e184ead06..39790e34652 100644 --- a/tests/test_pyfunction.rs +++ b/tests/test_pyfunction.rs @@ -569,7 +569,7 @@ fn return_value_borrows_from_arguments<'py>( key: &'py Key, value: &'py Value, ) -> HashMap<&'py str, i32> { - py.allow_threads(move || { + py.detach(move || { let mut map = HashMap::new(); map.insert(key.0.as_str(), value.0); map diff --git a/tests/ui/not_send.rs b/tests/ui/not_send.rs index 291bd7470f8..926343538aa 100644 --- a/tests/ui/not_send.rs +++ b/tests/ui/not_send.rs @@ -1,11 +1,11 @@ use pyo3::prelude::*; -fn test_not_send_allow_threads(py: Python<'_>) { - py.allow_threads(|| { drop(py); }); +fn test_not_send_detach(py: Python<'_>) { + py.detach(|| { drop(py); }); } fn main() { Python::attach(|py| { - test_not_send_allow_threads(py); + test_not_send_detach(py); }) } diff --git a/tests/ui/not_send.stderr b/tests/ui/not_send.stderr index 5d05b3de059..7987c34c25a 100644 --- a/tests/ui/not_send.stderr +++ b/tests/ui/not_send.stderr @@ -1,8 +1,8 @@ error[E0277]: `*mut pyo3::Python<'static>` cannot be shared between threads safely - --> tests/ui/not_send.rs:4:22 + --> tests/ui/not_send.rs:4:15 | -4 | py.allow_threads(|| { drop(py); }); - | ------------- ^^^^^^^^^^^^^^^^ `*mut pyo3::Python<'static>` cannot be shared between threads safely +4 | py.detach(|| { drop(py); }); + | ------ ^^^^^^^^^^^^^^^^ `*mut pyo3::Python<'static>` cannot be shared between threads safely | | | required by a bound introduced by this call | @@ -30,16 +30,16 @@ note: required because it appears within the type `pyo3::Python<'_>` | ^^^^^^ = note: required for `&pyo3::Python<'_>` to implement `Send` note: required because it's used within this closure - --> tests/ui/not_send.rs:4:22 + --> tests/ui/not_send.rs:4:15 | -4 | py.allow_threads(|| { drop(py); }); - | ^^ - = note: required for `{closure@$DIR/tests/ui/not_send.rs:4:22: 4:24}` to implement `Ungil` -note: required by a bound in `pyo3::Python::<'py>::allow_threads` +4 | py.detach(|| { drop(py); }); + | ^^ + = note: required for `{closure@$DIR/tests/ui/not_send.rs:4:15: 4:17}` to implement `Ungil` +note: required by a bound in `pyo3::Python::<'py>::detach` --> src/marker.rs | - | pub fn allow_threads(self, f: F) -> T - | ------------- required by a bound in this associated function + | pub fn detach(self, f: F) -> T + | ------ required by a bound in this associated function | where | F: Ungil + FnOnce() -> T, - | ^^^^^ required by this bound in `Python::<'py>::allow_threads` + | ^^^^^ required by this bound in `Python::<'py>::detach` diff --git a/tests/ui/not_send2.rs b/tests/ui/not_send2.rs index 9b76bbe591a..78c73d5e9c8 100644 --- a/tests/ui/not_send2.rs +++ b/tests/ui/not_send2.rs @@ -5,7 +5,7 @@ fn main() { Python::attach(|py| { let string = PyString::new(py, "foo"); - py.allow_threads(|| { + py.detach(|| { println!("{:?}", string); }); }); diff --git a/tests/ui/not_send2.stderr b/tests/ui/not_send2.stderr index eec2b644e9a..f01bb9b5208 100644 --- a/tests/ui/not_send2.stderr +++ b/tests/ui/not_send2.stderr @@ -1,8 +1,8 @@ error[E0277]: `*mut pyo3::Python<'static>` cannot be shared between threads safely - --> tests/ui/not_send2.rs:8:26 + --> tests/ui/not_send2.rs:8:19 | -8 | py.allow_threads(|| { - | ____________-------------_^ +8 | py.detach(|| { + | ____________------_^ | | | | | required by a bound introduced by this call 9 | | println!("{:?}", string); @@ -38,16 +38,16 @@ note: required because it appears within the type `pyo3::Bound<'_, PyString>` | ^^^^^ = note: required for `&pyo3::Bound<'_, PyString>` to implement `Send` note: required because it's used within this closure - --> tests/ui/not_send2.rs:8:26 + --> tests/ui/not_send2.rs:8:19 | -8 | py.allow_threads(|| { - | ^^ - = note: required for `{closure@$DIR/tests/ui/not_send2.rs:8:26: 8:28}` to implement `Ungil` -note: required by a bound in `pyo3::Python::<'py>::allow_threads` +8 | py.detach(|| { + | ^^ + = note: required for `{closure@$DIR/tests/ui/not_send2.rs:8:19: 8:21}` to implement `Ungil` +note: required by a bound in `pyo3::Python::<'py>::detach` --> src/marker.rs | - | pub fn allow_threads(self, f: F) -> T - | ------------- required by a bound in this associated function + | pub fn detach(self, f: F) -> T + | ------ required by a bound in this associated function | where | F: Ungil + FnOnce() -> T, - | ^^^^^ required by this bound in `Python::<'py>::allow_threads` + | ^^^^^ required by this bound in `Python::<'py>::detach`