remove `Python::acquire_gil`

This commit is contained in:
David Hewitt 2023-02-22 21:46:42 +00:00 committed by Adam Reichold
parent 3c634dd9a9
commit dd24c9ea71
15 changed files with 158 additions and 271 deletions

View File

@ -91,8 +91,7 @@ abi3-py311 = ["abi3", "pyo3-build-config/abi3-py311", "pyo3-ffi/abi3-py311"]
# Automatically generates `python3.dll` import libraries for Windows targets.
generate-import-lib = ["pyo3-ffi/generate-import-lib"]
# Changes `Python::with_gil` and `Python::acquire_gil` to automatically initialize the
# Python interpreter if needed.
# Changes `Python::with_gil` to automatically initialize the Python interpreter if needed.
auto-initialize = []
# Optimizes PyObject to Vec conversion and so on.

View File

@ -45,7 +45,7 @@ section for further detail.
### `auto-initialize`
This feature changes [`Python::with_gil`]({{#PYO3_DOCS_URL}}/pyo3/struct.Python.html#method.with_gil) and [`Python::acquire_gil`]({{#PYO3_DOCS_URL}}/pyo3/struct.Python.html#method.acquire_gil) 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::with_gil`]({{#PYO3_DOCS_URL}}/pyo3/struct.Python.html#method.with_gil) to automatically initialize a Python interpreter (by calling [`prepare_freethreaded_python`]({{#PYO3_DOCS_URL}}/pyo3/fn.prepare_freethreaded_python.html)) if needed.
If you do not enable this feature, you should call `pyo3::prepare_freethreaded_python()` before attempting to call any other Python APIs.

View File

@ -35,10 +35,9 @@ Python::with_gil(|py| -> PyResult<()> {
# }
```
Internally, calling `Python::with_gil()` or `Python::acquire_gil()` creates a
`GILPool` which owns the memory pointed to by the reference. In the example
above, the lifetime of the reference `hello` is bound to the `GILPool`. When
the `with_gil()` closure ends or the `GILGuard` from `acquire_gil()` is dropped,
Internally, calling `Python::with_gil()` creates a `GILPool` which owns the
memory pointed to by the reference. In the example above, the lifetime of the
reference `hello` is bound to the `GILPool`. When the `with_gil()` closure ends
the `GILPool` is also dropped and the Python reference counts of the variables
it owns are decreased, releasing them to the Python garbage collector. Most
of the time we don't have to think about this, but consider the following:

View File

@ -44,7 +44,7 @@ However, if the `anyhow::Error` or `eyre::Report` has a source, then the origina
While the API provided by [`Python::acquire_gil`](https://docs.rs/pyo3/0.18.3/pyo3/marker/struct.Python.html#method.acquire_gil) seems convenient, it is somewhat brittle as the design of the GIL token [`Python`](https://docs.rs/pyo3/0.18.3/pyo3/marker/struct.Python.html) relies on proper nesting and panics if not used correctly, e.g.
```rust,should_panic
```rust,ignore
# #![allow(dead_code, deprecated)]
# use pyo3::prelude::*;

View File

@ -2,14 +2,6 @@ use pyo3::prelude::*;
#[pyfunction]
fn issue_219() {
// issue 219: acquiring GIL inside #[pyfunction] deadlocks.
#[allow(deprecated)]
let gil = Python::acquire_gil();
let _py = gil.python();
}
#[pyfunction]
fn issue_219_2() {
// issue 219: acquiring GIL inside #[pyfunction] deadlocks.
Python::with_gil(|_| {});
}
@ -17,6 +9,5 @@ fn issue_219_2() {
#[pymodule]
pub fn misc(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(issue_219, m)?)?;
m.add_function(wrap_pyfunction!(issue_219_2, m)?)?;
Ok(())
}

View File

@ -8,7 +8,6 @@ import pytest
def test_issue_219():
# Should not deadlock
pyo3_pytests.misc.issue_219()
pyo3_pytests.misc.issue_219_2()
@pytest.mark.skipif(

View File

@ -6,11 +6,7 @@ use crate::impl_::not_send::{NotSend, NOT_SEND};
use crate::{ffi, Python};
use parking_lot::{const_mutex, Mutex, Once};
use std::cell::{Cell, RefCell};
use std::{
mem::{self, ManuallyDrop},
ptr::NonNull,
sync::atomic,
};
use std::{mem, ptr::NonNull, sync::atomic};
static START: Once = Once::new();
@ -139,25 +135,9 @@ where
}
/// RAII type that represents the Global Interpreter Lock acquisition.
///
/// Users are strongly encouraged to use [`Python::with_gil`](struct.Python.html#method.with_gil)
/// instead of directly constructing this type.
/// See [`Python::acquire_gil`](struct.Python.html#method.acquire_gil) for more.
///
/// # Examples
/// ```
/// use pyo3::Python;
///
/// {
/// #[allow(deprecated)]
/// let gil_guard = Python::acquire_gil();
/// let py = gil_guard.python();
/// } // GIL is released when gil_guard is dropped
/// ```
#[must_use]
pub struct GILGuard {
struct GILGuard {
gstate: ffi::PyGILState_STATE,
pool: ManuallyDrop<Option<GILPool>>,
pool: Option<GILPool>,
_not_send: NotSend,
}
@ -168,11 +148,11 @@ impl GILGuard {
unsafe { Python::assume_gil_acquired() }
}
/// PyO3 internal API for acquiring the GIL. The public API is Python::acquire_gil.
/// PyO3 internal API for acquiring the GIL. The public API is Python::with_gil.
///
/// If PyO3 does not yet have a `GILPool` for tracking owned PyObject references, then this new
/// `GILGuard` will also contain a `GILPool`.
pub(crate) fn acquire() -> GILGuard {
fn acquire() -> GILGuard {
// Maybe auto-initialize the GIL:
// - If auto-initialize feature set and supported, try to initialize the interpreter.
// - If the auto-initialize feature is set but unsupported, emit hard errors only when the
@ -231,7 +211,7 @@ impl GILGuard {
GILGuard {
gstate,
pool: ManuallyDrop::new(pool),
pool,
_not_send: NOT_SEND,
}
}
@ -240,28 +220,12 @@ impl GILGuard {
/// The Drop implementation for `GILGuard` will release the GIL.
impl Drop for GILGuard {
fn drop(&mut self) {
// First up, try to detect if the order of destruction is correct.
#[allow(clippy::manual_assert)]
let _ = GIL_COUNT.try_with(|c| {
if self.gstate == ffi::PyGILState_STATE::PyGILState_UNLOCKED && c.get() != 1 {
// XXX: this panic commits to leaking all objects in the pool as well as
// potentially meaning the GIL never releases. Perhaps should be an abort?
// Unfortunately abort UX is much worse than panic.
panic!("The first GILGuard acquired must be the last one dropped.");
}
});
// If this GILGuard doesn't own a pool, then need to decrease the count after dropping
// all objects from the pool.
let should_decrement = self.pool.is_none();
// Drop the objects in the pool before attempting to release the thread state
unsafe {
ManuallyDrop::drop(&mut self.pool);
}
if should_decrement {
decrement_gil_count();
if let Some(pool) = self.pool.take() {
drop(pool)
} else {
// This GILGuard didn't own a pool, need to decrease the count manually.
decrement_gil_count()
}
unsafe {
@ -554,62 +518,60 @@ mod tests {
#[test]
fn test_owned() {
#[allow(deprecated)]
let gil = Python::acquire_gil();
let py = gil.python();
let obj = get_object(py);
let obj_ptr = obj.as_ptr();
// Ensure that obj does not get freed
let _ref = obj.clone_ref(py);
Python::with_gil(|py| {
let obj = get_object(py);
let obj_ptr = obj.as_ptr();
// Ensure that obj does not get freed
let _ref = obj.clone_ref(py);
unsafe {
{
let pool = py.new_pool();
gil::register_owned(pool.python(), NonNull::new_unchecked(obj.into_ptr()));
unsafe {
{
let pool = py.new_pool();
gil::register_owned(pool.python(), NonNull::new_unchecked(obj.into_ptr()));
assert_eq!(owned_object_count(), 1);
assert_eq!(ffi::Py_REFCNT(obj_ptr), 2);
assert_eq!(owned_object_count(), 1);
assert_eq!(ffi::Py_REFCNT(obj_ptr), 2);
}
{
let _pool = py.new_pool();
assert_eq!(owned_object_count(), 0);
assert_eq!(ffi::Py_REFCNT(obj_ptr), 1);
}
}
{
let _pool = py.new_pool();
assert_eq!(owned_object_count(), 0);
assert_eq!(ffi::Py_REFCNT(obj_ptr), 1);
}
}
})
}
#[test]
fn test_owned_nested() {
#[allow(deprecated)]
let gil = Python::acquire_gil();
let py = gil.python();
let obj = get_object(py);
// Ensure that obj does not get freed
let _ref = obj.clone_ref(py);
let obj_ptr = obj.as_ptr();
Python::with_gil(|py| {
let obj = get_object(py);
// Ensure that obj does not get freed
let _ref = obj.clone_ref(py);
let obj_ptr = obj.as_ptr();
unsafe {
{
let _pool = py.new_pool();
assert_eq!(owned_object_count(), 0);
gil::register_owned(py, NonNull::new_unchecked(obj.into_ptr()));
assert_eq!(owned_object_count(), 1);
assert_eq!(ffi::Py_REFCNT(obj_ptr), 2);
unsafe {
{
let _pool = py.new_pool();
let obj = get_object(py);
assert_eq!(owned_object_count(), 0);
gil::register_owned(py, NonNull::new_unchecked(obj.into_ptr()));
assert_eq!(owned_object_count(), 2);
assert_eq!(owned_object_count(), 1);
assert_eq!(ffi::Py_REFCNT(obj_ptr), 2);
{
let _pool = py.new_pool();
let obj = get_object(py);
gil::register_owned(py, NonNull::new_unchecked(obj.into_ptr()));
assert_eq!(owned_object_count(), 2);
}
assert_eq!(owned_object_count(), 1);
}
{
assert_eq!(owned_object_count(), 0);
assert_eq!(ffi::Py_REFCNT(obj_ptr), 1);
}
assert_eq!(owned_object_count(), 1);
}
{
assert_eq!(owned_object_count(), 0);
assert_eq!(ffi::Py_REFCNT(obj_ptr), 1);
}
}
});
}
#[test]
@ -666,59 +628,53 @@ mod tests {
#[test]
fn test_gil_counts() {
// Check GILGuard and GILPool both increase counts correctly
// Check with_gil and GILPool both increase counts correctly
let get_gil_count = || GIL_COUNT.with(|c| c.get());
assert_eq!(get_gil_count(), 0);
#[allow(deprecated)]
let gil = Python::acquire_gil();
assert_eq!(get_gil_count(), 1);
Python::with_gil(|_| {
assert_eq!(get_gil_count(), 1);
assert_eq!(get_gil_count(), 1);
let pool = unsafe { GILPool::new() };
assert_eq!(get_gil_count(), 2);
let pool = unsafe { GILPool::new() };
assert_eq!(get_gil_count(), 2);
let pool2 = unsafe { GILPool::new() };
assert_eq!(get_gil_count(), 3);
let pool2 = unsafe { GILPool::new() };
assert_eq!(get_gil_count(), 3);
drop(pool);
assert_eq!(get_gil_count(), 2);
drop(pool);
assert_eq!(get_gil_count(), 2);
#[allow(deprecated)]
let gil2 = Python::acquire_gil();
assert_eq!(get_gil_count(), 3);
Python::with_gil(|_| {
// nested with_gil doesn't update gil count
assert_eq!(get_gil_count(), 2);
});
assert_eq!(get_gil_count(), 2);
drop(gil2);
assert_eq!(get_gil_count(), 2);
drop(pool2);
assert_eq!(get_gil_count(), 1);
drop(gil);
drop(pool2);
assert_eq!(get_gil_count(), 1);
});
assert_eq!(get_gil_count(), 0);
}
#[test]
fn test_allow_threads() {
// allow_threads should temporarily release GIL in PyO3's internal tracking too.
#[allow(deprecated)]
let gil = Python::acquire_gil();
let py = gil.python();
assert!(!gil_is_acquired());
assert!(gil_is_acquired());
py.allow_threads(move || {
assert!(!gil_is_acquired());
#[allow(deprecated)]
let gil = Python::acquire_gil();
Python::with_gil(|py| {
assert!(gil_is_acquired());
drop(gil);
assert!(!gil_is_acquired());
py.allow_threads(move || {
assert!(!gil_is_acquired());
Python::with_gil(|_| assert!(gil_is_acquired()));
assert!(!gil_is_acquired());
});
assert!(gil_is_acquired());
});
assert!(gil_is_acquired());
assert!(!gil_is_acquired());
}
#[test]
@ -740,32 +696,25 @@ mod tests {
#[test]
fn dropping_gil_does_not_invalidate_references() {
// Acquiring GIL for the second time should be safe - see #864
#[allow(deprecated)]
let gil = Python::acquire_gil();
let py = gil.python();
Python::with_gil(|py| {
let obj = Python::with_gil(|_| py.eval("object()", None, None).unwrap());
#[allow(deprecated)]
let gil2 = Python::acquire_gil();
let obj = py.eval("object()", None, None).unwrap();
drop(gil2);
// After gil2 drops, obj should still have a reference count of one
assert_eq!(obj.get_refcnt(), 1);
// After gil2 drops, obj should still have a reference count of one
assert_eq!(obj.get_refcnt(), 1);
})
}
#[test]
fn test_clone_with_gil() {
#[allow(deprecated)]
let gil = Python::acquire_gil();
let py = gil.python();
Python::with_gil(|py| {
let obj = get_object(py);
let count = obj.get_refcnt(py);
let obj = get_object(py);
let count = obj.get_refcnt(py);
// Cloning with the GIL should increase reference count immediately
#[allow(clippy::redundant_clone)]
let c = obj.clone();
assert_eq!(count + 1, c.get_refcnt(py));
// Cloning with the GIL should increase reference count immediately
#[allow(clippy::redundant_clone)]
let c = obj.clone();
assert_eq!(count + 1, c.get_refcnt(py));
})
}
#[cfg(not(target_arch = "wasm32"))]
@ -912,11 +861,9 @@ mod tests {
// update_counts can run arbitrary Python code during Py_DECREF.
// if the locking is implemented incorrectly, it will deadlock.
#[allow(deprecated)]
let gil = Python::acquire_gil();
let obj = get_object(gil.python());
Python::with_gil(|py| {
let obj = get_object(py);
unsafe {
unsafe extern "C" fn capsule_drop(capsule: *mut ffi::PyObject) {
// This line will implicitly call update_counts
// -> and so cause deadlock if update_counts is not handling recursion correctly.
@ -930,12 +877,14 @@ mod tests {
}
let ptr = obj.into_ptr();
let capsule = ffi::PyCapsule_New(ptr as _, std::ptr::null(), Some(capsule_drop));
let capsule =
unsafe { ffi::PyCapsule_New(ptr as _, std::ptr::null(), Some(capsule_drop)) };
POOL.register_decref(NonNull::new(capsule).unwrap());
// Updating the counts will call decref on the capsule, which calls capsule_drop
POOL.update_counts(gil.python())
}
POOL.update_counts(py);
})
}
}

View File

@ -79,8 +79,8 @@
//!
//! - `abi3`: Restricts PyO3's API to a subset of the full Python API which is guaranteed by
//! [PEP 384] to be forward-compatible with future Python versions.
//! - `auto-initialize`: Changes [`Python::with_gil`] and [`Python::acquire_gil`] to automatically
//! initialize the Python interpreter if needed.
//! - `auto-initialize`: Changes [`Python::with_gil`] to automatically initialize the Python
//! interpreter if needed.
//! - `extension-module`: This will tell the linker to keep the Python symbols unresolved, so that
//! your module can also be used with statically linked Python interpreters. Use this feature when
//! building an extension module.
@ -307,9 +307,9 @@ pub use crate::conversion::{
ToPyObject,
};
pub use crate::err::{PyDowncastError, PyErr, PyErrArguments, PyResult};
pub use crate::gil::GILPool;
#[cfg(not(PyPy))]
pub use crate::gil::{prepare_freethreaded_python, with_embedded_python_interpreter};
pub use crate::gil::{GILGuard, GILPool};
pub use crate::instance::{Py, PyNativeType, PyObject};
pub use crate::marker::Python;
pub use crate::pycell::{PyCell, PyRef, PyRefMut};

View File

@ -120,7 +120,7 @@
//! [`Rc`]: std::rc::Rc
//! [`Py`]: crate::Py
use crate::err::{self, PyDowncastError, PyErr, PyResult};
use crate::gil::{self, GILGuard, GILPool, SuspendGIL};
use crate::gil::{self, EnsureGIL, GILPool, SuspendGIL};
use crate::impl_::not_send::NotSend;
use crate::types::{PyAny, PyDict, PyModule, PyString, PyType};
use crate::version::PythonVersionInfo;
@ -273,12 +273,16 @@ mod negative_impls {
/// [`Py::clone_ref`]: crate::Py::clone_ref
/// [Memory Management]: https://pyo3.rs/main/memory.html#gil-bound-memory
#[derive(Copy, Clone)]
pub struct Python<'py>(PhantomData<(&'py GILGuard, NotSend)>);
pub struct Python<'py>(PhantomData<(&'py EnsureGIL, NotSend)>);
impl Python<'_> {
/// Acquires the global interpreter lock, allowing access to the Python interpreter. The
/// provided closure `F` will be executed with the acquired `Python` marker token.
///
/// If implementing [`#[pymethods]`](crate::pymethods) or [`#[pyfunction]`](crate::pyfunction),
/// declare `py: Python` as an argument. PyO3 will pass in the token to grant access to the GIL
/// context in which the function is running, avoiding the need to call `with_gil`.
///
/// If the [`auto-initialize`] feature is enabled and the Python runtime is not already
/// initialized, this function will initialize it. See
#[cfg_attr(
@ -353,52 +357,6 @@ impl Python<'_> {
}
impl<'py> Python<'py> {
/// Acquires the global interpreter lock, allowing access to the Python interpreter.
///
/// If the [`auto-initialize`] feature is enabled and the Python runtime is not already
/// initialized, this function will initialize it. See
#[cfg_attr(
not(PyPy),
doc = "[`prepare_freethreaded_python`](crate::prepare_freethreaded_python)"
)]
#[cfg_attr(PyPy, doc = "`prepare_freethreaded_python`")]
/// for details.
///
/// Most users should not need to use this API directly, and should prefer one of two options:
/// 1. If implementing [`#[pymethods]`](crate::pymethods) or [`#[pyfunction]`](crate::pyfunction), declare `py: Python` as an argument.
/// PyO3 will pass in the token to grant access to the GIL context in which the function is running.
/// 2. Use [`Python::with_gil`] to run a closure with the GIL, acquiring only if needed.
///
/// # Panics
///
/// - If the [`auto-initialize`] feature is not enabled and the Python interpreter is not
/// initialized.
/// - If multiple [`GILGuard`]s are not dropped in in the reverse order of acquisition, PyO3
/// may panic. It is recommended to use [`Python::with_gil`] instead to avoid this.
///
/// # Notes
///
/// The return type from this function, [`GILGuard`], is implemented as a RAII guard
/// around [`PyGILState_Ensure`]. This means that multiple `acquire_gil()` calls are
/// allowed, and will not deadlock. However, [`GILGuard`]s must be dropped in the reverse order
/// to acquisition. If PyO3 detects this order is not maintained, it will panic when the out-of-order drop occurs.
///
/// # Deprecation
///
/// This API has been deprecated for several reasons:
/// - GIL drop order tracking has turned out to be [error prone](https://github.com/PyO3/pyo3/issues/1683).
/// With a scoped API like `Python::with_gil`, these are always dropped in the correct order.
/// - It promotes passing and keeping the GILGuard around, which is almost always not what you actually want.
///
/// [`PyGILState_Ensure`]: crate::ffi::PyGILState_Ensure
/// [`auto-initialize`]: https://pyo3.rs/main/features.html#auto-initialize
#[inline]
// Once removed, we can remove GILGuard's drop tracking.
#[deprecated(since = "0.17.0", note = "prefer Python::with_gil")]
pub fn acquire_gil() -> GILGuard {
GILGuard::acquire()
}
/// Temporarily releases the GIL, thus allowing other Python threads to run. The GIL will be
/// reacquired when `F`'s scope ends.
///
@ -820,8 +778,8 @@ impl<'py> Python<'py> {
/// all have their Python reference counts decremented, potentially allowing Python to drop
/// the corresponding Python objects.
///
/// Typical usage of PyO3 will not need this API, as [`Python::with_gil`] and
/// [`Python::acquire_gil`] automatically create a `GILPool` where appropriate.
/// Typical usage of PyO3 will not need this API, as [`Python::with_gil`] automatically creates
/// a `GILPool` where appropriate.
///
/// Advanced uses of PyO3 which perform long-running tasks which never free the GIL may need
/// to use this API to clear memory, as PyO3 usually does not clear memory until the GIL is

View File

@ -14,7 +14,6 @@ pub use crate::conversion::{
FromPyObject, IntoPy, IntoPyPointer, PyTryFrom, PyTryInto, ToPyObject,
};
pub use crate::err::{PyErr, PyResult};
pub use crate::gil::GILGuard;
pub use crate::instance::{Py, PyObject};
pub use crate::marker::Python;
pub use crate::pycell::{PyCell, PyRef, PyRefMut};

View File

@ -319,22 +319,20 @@ fn test_option_list_get() {
#[test]
fn sequence_is_not_mapping() {
#[allow(deprecated)]
let gil = Python::acquire_gil();
let py = gil.python();
Python::with_gil(|py| {
let list = PyCell::new(
py,
OptionList {
items: vec![Some(1), None],
},
)
.unwrap();
let list = PyCell::new(
py,
OptionList {
items: vec![Some(1), None],
},
)
.unwrap();
PySequence::register::<OptionList>(py).unwrap();
PySequence::register::<OptionList>(py).unwrap();
assert!(list.as_ref().downcast::<PyMapping>().is_err());
assert!(list.as_ref().downcast::<PySequence>().is_ok());
assert!(list.as_ref().downcast::<PyMapping>().is_err());
assert!(list.as_ref().downcast::<PySequence>().is_ok());
})
}
#[test]

View File

@ -9,8 +9,8 @@ error[E0277]: `*mut pyo3::Python<'static>` cannot be shared between threads safe
= help: within `pyo3::Python<'_>`, the trait `Sync` is not implemented for `*mut pyo3::Python<'static>`
= note: required because it appears within the type `PhantomData<*mut Python<'static>>`
= note: required because it appears within the type `NotSend`
= note: required because it appears within the type `(&GILGuard, NotSend)`
= note: required because it appears within the type `PhantomData<(&GILGuard, NotSend)>`
= note: required because it appears within the type `(&EnsureGIL, NotSend)`
= note: required because it appears within the type `PhantomData<(&EnsureGIL, NotSend)>`
= note: required because it appears within the type `Python<'_>`
= note: required for `&pyo3::Python<'_>` to implement `Send`
note: required because it's used within this closure

View File

@ -3,23 +3,24 @@ use std::rc::Rc;
#[pyclass]
struct NotThreadSafe {
data: Rc<i32>
data: Rc<i32>,
}
fn main() {
let gil = Python::acquire_gil();
let py = gil.python();
let obj = PyCell::new(py, NotThreadSafe { data: Rc::new(5) }).unwrap().to_object(py);
drop(gil);
let obj = Python::with_gil(|py| {
PyCell::new(py, NotThreadSafe { data: Rc::new(5) })
.unwrap()
.to_object(py)
});
std::thread::spawn(move || {
let gil = Python::acquire_gil();
let py = gil.python();
Python::with_gil(|py| {
// Uh oh, moved Rc to a new thread!
let c: &PyCell<NotThreadSafe> = obj.as_ref(py).downcast().unwrap();
// Uh oh, moved Rc to a new thread!
let c: &PyCell<NotThreadSafe> = obj.as_ref(py).downcast().unwrap();
assert_eq!(*c.borrow().data, 5);
}).join().unwrap();
assert_eq!(*c.borrow().data, 5);
})
})
.join()
.unwrap();
}

View File

@ -1,11 +1,10 @@
use pyo3::{types::PyDict, Py, Python};
fn main() {
#[allow(deprecated)]
let gil = Python::acquire_gil();
let dict: Py<PyDict> = PyDict::new(gil.python()).into();
let dict: &PyDict = dict.as_ref(gil.python());
drop(gil);
let dict: Py<PyDict> = Python::with_gil(|py| PyDict::new(py).into());
// Should not be able to get access to Py contents outside of with_gil.
let dict: &PyDict = Python::with_gil(|py| dict.as_ref(py));
let _py: Python = dict.py(); // Obtain a Python<'p> without GIL.
}

View File

@ -1,13 +1,8 @@
error[E0505]: cannot move out of `gil` because it is borrowed
--> tests/ui/wrong_aspyref_lifetimes.rs:8:10
|
5 | let gil = Python::acquire_gil();
| --- binding `gil` declared here
6 | let dict: Py<PyDict> = PyDict::new(gil.python()).into();
7 | let dict: &PyDict = dict.as_ref(gil.python());
| ------------ borrow of `gil` occurs here
8 | drop(gil);
| ^^^ move out of `gil` occurs here
9 |
10 | let _py: Python = dict.py(); // Obtain a Python<'p> without GIL.
| --------- borrow later used here
error: lifetime may not live long enough
--> tests/ui/wrong_aspyref_lifetimes.rs:7:47
|
7 | let dict: &PyDict = Python::with_gil(|py| dict.as_ref(py));
| --- ^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
| | |
| | return type of closure is &'2 PyDict
| has type `pyo3::Python<'1>`