diff --git a/guide/src/python-from-rust/function-calls.md b/guide/src/python-from-rust/function-calls.md index 6fde4cda33e..e3f346bb777 100644 --- a/guide/src/python-from-rust/function-calls.md +++ b/guide/src/python-from-rust/function-calls.md @@ -12,7 +12,7 @@ Both of these APIs take `args` and `kwargs` arguments (for positional and keywor * [`call1`]({{#PYO3_DOCS_URL}}/pyo3/types/trait.PyAnyMethods.html#tymethod.call1) and [`call_method1`]({{#PYO3_DOCS_URL}}/pyo3/types/trait.PyAnyMethods.html#tymethod.call_method1) to call only with positional `args`. * [`call0`]({{#PYO3_DOCS_URL}}/pyo3/types/trait.PyAnyMethods.html#tymethod.call0) and [`call_method0`]({{#PYO3_DOCS_URL}}/pyo3/types/trait.PyAnyMethods.html#tymethod.call_method0) to call with no arguments. -For convenience the [`Py`](../types.md#pyt) smart pointer also exposes these same six API methods, but needs a `Python` token as an additional first argument to prove the GIL is held. +For convenience the [`Py`](../types.md#pyt) smart pointer also exposes these same six API methods, but needs a `Python` token as an additional first argument to prove the thread is attached to the Python interpreter. The example below calls a Python function behind a `Py` reference: diff --git a/pyo3-ffi/README.md b/pyo3-ffi/README.md index 0f6bc8d9533..be7428b712e 100644 --- a/pyo3-ffi/README.md +++ b/pyo3-ffi/README.md @@ -77,7 +77,7 @@ static mut MODULE_DEF: PyModuleDef = PyModuleDef { m_doc: c_str!("A Python module written in Rust.").as_ptr(), m_size: 0, m_methods: unsafe { METHODS as *const [PyMethodDef] as *mut PyMethodDef }, - m_slots: std::ptr::null_mut(), + m_slots: unsafe { SLOTS as *const [PyModuleDef_Slot] as *mut PyModuleDef_Slot }, m_traverse: None, m_clear: None, m_free: None, @@ -96,22 +96,31 @@ static mut METHODS: &[PyMethodDef] = &[ PyMethodDef::zeroed(), ]; +static mut SLOTS: &[PyModuleDef_Slot] = &[ + // NB: only include this slot if the module does not store any global state in `static` variables + // or other data which could cross between subinterpreters + #[cfg(Py_3_12)] + PyModuleDef_Slot { + slot: Py_mod_multiple_interpreters, + value: Py_MOD_PER_INTERPRETER_GIL_SUPPORTED, + }, + // NB: only include this slot if the module does not depend on the GIL for thread safety + #[cfg(Py_GIL_DISABLED)] + PyModuleDef_Slot { + slot: Py_mod_gil, + value: Py_MOD_GIL_NOT_USED, + }, + PyModuleDef_Slot { + slot: 0, + value: ptr::null_mut(), + }, +]; + // The module initialization function, which must be named `PyInit_`. #[allow(non_snake_case)] #[no_mangle] pub unsafe extern "C" fn PyInit_string_sum() -> *mut PyObject { - let module = PyModule_Create(ptr::addr_of_mut!(MODULE_DEF)); - if module.is_null() { - return module; - } - #[cfg(Py_GIL_DISABLED)] - { - if PyUnstable_Module_SetGIL(module, Py_MOD_GIL_NOT_USED) < 0 { - Py_DECREF(module); - return std::ptr::null_mut(); - } - } - module + PyModuleDef_Init(ptr::addr_of_mut!(MODULE_DEF)) } /// A helper to parse function arguments diff --git a/pyo3-ffi/examples/sequential/README.md b/pyo3-ffi/examples/sequential/README.md index e3c0078241d..6eb718d9d98 100644 --- a/pyo3-ffi/examples/sequential/README.md +++ b/pyo3-ffi/examples/sequential/README.md @@ -1,6 +1,6 @@ # sequential -A project built using only `pyo3_ffi`, without any of PyO3's safe api. It can be executed by subinterpreters that have their own GIL. +A project built using only `pyo3_ffi`, without any of PyO3's safe api. It supports both subinterpreters and free-threaded Python. ## Building and Testing diff --git a/pyo3-ffi/examples/sequential/src/module.rs b/pyo3-ffi/examples/sequential/src/module.rs index 3f95f8e8783..8895b32a734 100644 --- a/pyo3-ffi/examples/sequential/src/module.rs +++ b/pyo3-ffi/examples/sequential/src/module.rs @@ -19,6 +19,7 @@ static mut SEQUENTIAL_SLOTS: &[PyModuleDef_Slot] = &[ slot: Py_mod_exec, value: sequential_exec as *mut c_void, }, + #[cfg(Py_3_12)] PyModuleDef_Slot { slot: Py_mod_multiple_interpreters, value: Py_MOD_PER_INTERPRETER_GIL_SUPPORTED, diff --git a/pyo3-ffi/examples/string-sum/src/lib.rs b/pyo3-ffi/examples/string-sum/src/lib.rs index 94d2445af1d..2c086a0889b 100644 --- a/pyo3-ffi/examples/string-sum/src/lib.rs +++ b/pyo3-ffi/examples/string-sum/src/lib.rs @@ -9,7 +9,7 @@ static mut MODULE_DEF: PyModuleDef = PyModuleDef { m_doc: c_str!("A Python module written in Rust.").as_ptr(), m_size: 0, m_methods: unsafe { METHODS as *const [PyMethodDef] as *mut PyMethodDef }, - m_slots: std::ptr::null_mut(), + m_slots: unsafe { SLOTS as *const [PyModuleDef_Slot] as *mut PyModuleDef_Slot }, m_traverse: None, m_clear: None, m_free: None, @@ -28,22 +28,28 @@ static mut METHODS: &[PyMethodDef] = &[ PyMethodDef::zeroed(), ]; +static mut SLOTS: &[PyModuleDef_Slot] = &[ + #[cfg(Py_3_12)] + PyModuleDef_Slot { + slot: Py_mod_multiple_interpreters, + value: Py_MOD_PER_INTERPRETER_GIL_SUPPORTED, + }, + #[cfg(Py_GIL_DISABLED)] + PyModuleDef_Slot { + slot: Py_mod_gil, + value: Py_MOD_GIL_NOT_USED, + }, + PyModuleDef_Slot { + slot: 0, + value: ptr::null_mut(), + }, +]; + // The module initialization function, which must be named `PyInit_`. #[allow(non_snake_case)] #[no_mangle] pub unsafe extern "C" fn PyInit_string_sum() -> *mut PyObject { - let module = PyModule_Create(ptr::addr_of_mut!(MODULE_DEF)); - if module.is_null() { - return module; - } - #[cfg(Py_GIL_DISABLED)] - { - if PyUnstable_Module_SetGIL(module, Py_MOD_GIL_NOT_USED) < 0 { - Py_DECREF(module); - return std::ptr::null_mut(); - } - } - module + PyModuleDef_Init(ptr::addr_of_mut!(MODULE_DEF)) } /// A helper to parse function arguments diff --git a/pyo3-ffi/src/lib.rs b/pyo3-ffi/src/lib.rs index 986ceabef32..70451acce47 100644 --- a/pyo3-ffi/src/lib.rs +++ b/pyo3-ffi/src/lib.rs @@ -17,7 +17,7 @@ //! generally the following apply: //! - Pointer arguments have to point to a valid Python object of the correct type, //! although null pointers are sometimes valid input. -//! - The vast majority can only be used safely while the GIL is held. +//! - The vast majority can only be used safely while the thread is attached to the Python interpreter. //! - Some functions have additional safety requirements, consult the //! [Python/C API Reference Manual][capi] //! for more information. diff --git a/pyo3-macros-backend/src/pymethod.rs b/pyo3-macros-backend/src/pymethod.rs index d08220490da..628dd19bf25 100644 --- a/pyo3-macros-backend/src/pymethod.rs +++ b/pyo3-macros-backend/src/pymethod.rs @@ -468,8 +468,8 @@ fn impl_traverse_slot( if let (Some(py_arg), _) = split_off_python_arg(&spec.signature.arguments) { return Err(syn::Error::new_spanned(py_arg.ty, "__traverse__ may not take `Python`. \ Usually, an implementation of `__traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>` \ - should do nothing but calls to `visit.call`. Most importantly, safe access to the GIL is prohibited \ - inside implementations of `__traverse__`, i.e. `Python::attach` will panic.")); + should do nothing but calls to `visit.call`. Most importantly, safe access to the Python interpreter is \ + prohibited inside implementations of `__traverse__`, i.e. `Python::attach` will panic.")); } // check that the receiver does not try to smuggle an (implicit) `Python` token into here @@ -482,8 +482,8 @@ fn impl_traverse_slot( bail_spanned! { span => "__traverse__ may not take a receiver other than `&self`. Usually, an implementation of \ `__traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>` \ - should do nothing but calls to `visit.call`. Most importantly, safe access to the GIL is prohibited \ - inside implementations of `__traverse__`, i.e. `Python::attach` will panic." + should do nothing but calls to `visit.call`. Most importantly, safe access to the Python interpreter is \ + prohibited inside implementations of `__traverse__`, i.e. `Python::attach` will panic." } } diff --git a/pytests/src/misc.rs b/pytests/src/misc.rs index a356d9bea3a..811923f971a 100644 --- a/pytests/src/misc.rs +++ b/pytests/src/misc.rs @@ -5,7 +5,7 @@ use pyo3::{ #[pyfunction] fn issue_219() { - // issue 219: acquiring GIL inside #[pyfunction] deadlocks. + // issue 219: attaching inside #[pyfunction] deadlocks. Python::attach(|_| {}); } @@ -15,13 +15,14 @@ struct LockHolder { sender: std::sync::mpsc::Sender<()>, } -// This will hammer the GIL once the LockHolder is dropped. +// This will repeatedly attach and detach from the Python interpreter +// once the LockHolder is dropped. #[pyfunction] -fn hammer_gil_in_thread() -> LockHolder { +fn hammer_attaching_in_thread() -> LockHolder { let (sender, receiver) = std::sync::mpsc::channel(); std::thread::spawn(move || { receiver.recv().ok(); - // now the interpreter has shut down, so hammer the GIL. In buggy + // now the interpreter has shut down, so hammer the attach API. In buggy // versions of PyO3 this will cause a crash. loop { Python::try_attach(|_py| ()); @@ -56,7 +57,7 @@ fn get_item_and_run_callback(dict: Bound<'_, PyDict>, callback: Bound<'_, PyAny> #[pymodule(gil_used = false)] pub fn misc(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(issue_219, m)?)?; - m.add_function(wrap_pyfunction!(hammer_gil_in_thread, m)?)?; + m.add_function(wrap_pyfunction!(hammer_attaching_in_thread, m)?)?; m.add_function(wrap_pyfunction!(get_type_fully_qualified_name, m)?)?; m.add_function(wrap_pyfunction!(accepts_bool, m)?)?; m.add_function(wrap_pyfunction!(get_item_and_run_callback, m)?)?; diff --git a/pytests/tests/test_hammer_gil_in_thread.py b/pytests/tests/test_hammer_attaching_in_thread.py similarity index 86% rename from pytests/tests/test_hammer_gil_in_thread.py rename to pytests/tests/test_hammer_attaching_in_thread.py index 9eed640f65c..15ec8953bc6 100644 --- a/pytests/tests/test_hammer_gil_in_thread.py +++ b/pytests/tests/test_hammer_attaching_in_thread.py @@ -24,5 +24,5 @@ def make_loop(): sysconfig.get_config_var("Py_DEBUG"), reason="causes a crash on debug builds, see discussion in https://github.com/PyO3/pyo3/pull/4874", ) -def test_hammer_gil(): - loopy.append(misc.hammer_gil_in_thread()) +def test_hammer_attaching_in_thread(): + loopy.append(misc.hammer_attaching_in_thread()) diff --git a/src/buffer.rs b/src/buffer.rs index 6b0e424a9cc..78a92f712b7 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -46,7 +46,6 @@ pub struct PyBuffer( struct RawBuffer(ffi::Py_buffer, PhantomPinned); // PyBuffer is thread-safe: the shape of the buffer is immutable while a Py_buffer exists. -// Accessing the buffer contents is protected using the GIL. unsafe impl Send for PyBuffer {} unsafe impl Sync for PyBuffer {} @@ -641,7 +640,7 @@ impl PyBuffer { /// This will automatically be called on drop. pub fn release(self, _py: Python<'_>) { // First move self into a ManuallyDrop, so that PyBuffer::drop will - // never be called. (It would acquire the GIL and call PyBuffer_Release + // never be called. (It would attach to the interpreter and call PyBuffer_Release // again.) let mut mdself = mem::ManuallyDrop::new(self); unsafe { diff --git a/src/instance.rs b/src/instance.rs index 37a05ccebc6..7dbbcdc269e 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -20,7 +20,8 @@ use std::ops::Deref; use std::ptr; use std::ptr::NonNull; -/// Owned or borrowed gil-bound Python smart pointer +/// Owned or borrowed Python smart pointer with a lifetime `'py` signalling +/// attachment to the Python interpreter. /// /// This is implemented for [`Bound`] and [`Borrowed`]. pub trait BoundObject<'py, T>: bound_object_sealed::Sealed { @@ -52,15 +53,16 @@ mod bound_object_sealed { unsafe impl Sealed for super::Borrowed<'_, '_, T> {} } -/// A GIL-attached equivalent to [`Py`]. +/// A Python thread-attached equivalent to [`Py`]. /// /// This type can be thought of as equivalent to the tuple `(Py, Python<'py>)`. By having the `'py` /// lifetime of the [`Python<'py>`] token, this ties the lifetime of the [`Bound<'py, T>`] smart pointer -/// to the lifetime of the GIL and allows PyO3 to call Python APIs at maximum efficiency. +/// to the lifetime the thread is attached to the Python interpreter and allows PyO3 to call Python APIs +/// at maximum efficiency. /// -/// 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::detach`](crate::Python::detach)'s closure. +/// To access the object in situations where the thread is not attached, convert it to [`Py`] +/// using [`.unbind()`][Bound::unbind]. This includes, for example, usage in +/// [`Python::detach`](crate::Python::detach)'s closure. /// /// See #[doc = concat!("[the guide](https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/types.html#boundpy-t)")] @@ -585,7 +587,7 @@ where PyRefMut::try_borrow(self) } - /// Provide an immutable borrow of the value `T` without acquiring the GIL. + /// Provide an immutable borrow of the value `T`. /// /// This is available if the class is [`frozen`][macro@crate::pyclass] and [`Sync`]. /// @@ -802,7 +804,7 @@ impl Drop for Bound<'_, T> { } impl<'py, T> Bound<'py, T> { - /// Returns the [`Python``] token associated with this object. + /// Returns the [`Python`] token associated with this object. #[inline] pub fn py(&self) -> Python<'py> { self.0 @@ -854,8 +856,8 @@ impl<'py, T> Bound<'py, T> { unsafe { Borrowed::from_non_null(self.py(), (self.1).0).cast_unchecked() } } - /// Removes the connection for this `Bound` from the GIL, allowing - /// it to cross thread boundaries. + /// Removes the connection for this `Bound` from the [`Python<'py>`] token, + /// allowing it to cross thread boundaries. #[inline] pub fn unbind(self) -> Py { let non_null = (ManuallyDrop::new(self).1).0; @@ -864,8 +866,8 @@ impl<'py, T> Bound<'py, T> { unsafe { Py::from_non_null(non_null) } } - /// Removes the connection for this `Bound` from the GIL, allowing - /// it to cross thread boundaries, without transferring ownership. + /// Removes the connection for this `Bound` from the [`Python<'py>`] token, + /// allowing it to cross thread boundaries, without transferring ownership. #[inline] pub fn as_unbound(&self) -> &Py { &self.1 @@ -1215,11 +1217,10 @@ impl<'a, 'py, T> BoundObject<'py, T> for Borrowed<'a, 'py, T> { } } -/// A GIL-independent reference to an object allocated on the Python heap. +/// A reference to an object allocated on the Python heap. /// -/// This type does not auto-dereference to the inner object because you must prove you hold the GIL to access it. -/// Instead, call one of its methods to access the inner object: -/// - [`Py::bind`] or [`Py::into_bound`], to borrow a GIL-bound reference to the contained object. +/// To access the contained data use the following methods: +/// - [`Py::bind`] or [`Py::into_bound`], to bind the reference to the lifetime of the [`Python<'py>`] token. /// - [`Py::borrow`], [`Py::try_borrow`], [`Py::borrow_mut`], or [`Py::try_borrow_mut`], /// /// to get a (mutable) reference to a contained pyclass, using a scheme similar to std's [`RefCell`]. @@ -1234,7 +1235,8 @@ impl<'a, 'py, T> BoundObject<'py, T> for Borrowed<'a, 'py, T> { /// # Example: Storing Python objects in `#[pyclass]` structs /// /// Usually `Bound<'py, T>` is recommended for interacting with Python objects as its lifetime `'py` -/// is an association to the GIL and that enables many operations to be done as efficiently as possible. +/// proves the thread is attached to the Python interpreter and that enables many operations to be +/// done as efficiently as possible. /// /// However, `#[pyclass]` structs cannot carry a lifetime, so `Py` is the only way to store /// a Python object in a `#[pyclass]` struct. @@ -1255,8 +1257,8 @@ impl<'a, 'py, T> BoundObject<'py, T> for Borrowed<'a, 'py, T> { /// let foo = Python::attach(|py| { /// // `py` will only last for this scope. /// -/// // `Bound<'py, PyDict>` inherits the GIL lifetime from `py` and -/// // so won't be able to outlive this closure. +/// // `Bound<'py, PyDict>` inherits the Python token lifetime from `py` +/// // and so won't be able to outlive this closure. /// let dict: Bound<'_, PyDict> = PyDict::new(py); /// /// // because `Foo` contains `dict` its lifetime @@ -1270,7 +1272,7 @@ impl<'a, 'py, T> BoundObject<'py, T> for Borrowed<'a, 'py, T> { /// } /// ``` /// -/// [`Py`]`` can be used to get around this by converting `dict` into a GIL-independent reference: +/// [`Py`]`` can be used to get around this by removing the lifetime from `dict` and with it the proof of attachment. /// /// ```rust /// use pyo3::prelude::*; @@ -1349,8 +1351,9 @@ impl<'a, 'py, T> BoundObject<'py, T> for Borrowed<'a, 'py, T> { /// As with [`Rc`]``, cloning it increases its reference count rather than duplicating /// the underlying object. /// -/// This can be done using either [`Py::clone_ref`] or [`Py`]``'s [`Clone`] trait implementation. -/// [`Py::clone_ref`] will be faster if you happen to be already holding the GIL. +/// This can be done using either [`Py::clone_ref`] or [`Py`]'s [`Clone`] trait implementation. +/// [`Py::clone_ref`] is recommended; the [`Clone`] implementation will panic if the thread +/// is not attached to the Python interpreter (and is gated behind the `py-clone` feature flag). /// /// ```rust /// use pyo3::prelude::*; @@ -1394,21 +1397,21 @@ impl<'a, 'py, T> BoundObject<'py, T> for Borrowed<'a, 'py, T> { /// of the pointed-to variable, allowing Python's garbage collector to free /// the associated memory, but this may not happen immediately. This is /// because a [`Py`]`` can be dropped at any time, but the Python reference -/// count can only be modified when the GIL is held. +/// count can only be modified when the thread is attached to the Python interpreter. /// -/// If a [`Py`]`` is dropped while its thread happens to be holding the -/// GIL then the Python reference count will be decreased immediately. -/// Otherwise, the reference count will be decreased the next time the GIL is -/// reacquired. +/// If a [`Py`]`` is dropped while its thread is attached to the Python interpreter +/// then the Python reference count will be decreased immediately. +/// Otherwise, the reference count will be decreased the next time the thread is +/// attached to the interpreter. /// -/// If you happen to be already holding the GIL, [`Py::drop_ref`] will decrease +/// If you have a [`Python<'py>`] token, [`Py::drop_ref`] will decrease /// the Python reference count immediately and will execute slightly faster than /// relying on implicit [`Drop`]s. /// /// # A note on `Send` and `Sync` /// -/// Accessing this object is thread-safe, since any access to its API requires a [`Python<'py>`](crate::Python) token. -/// As you can only get this by acquiring the GIL, `Py<...>` implements [`Send`] and [`Sync`]. +/// [`Py`] implements [`Send`] and [`Sync`], as Python allows objects to be freely +/// shared between threads. /// /// [`Rc`]: std::rc::Rc /// [`RefCell`]: std::cell::RefCell @@ -1416,7 +1419,6 @@ impl<'a, 'py, T> BoundObject<'py, T> for Borrowed<'a, 'py, T> { #[repr(transparent)] pub struct Py(NonNull, PhantomData); -// The inner value is only accessed through ways that require proving the gil is held #[cfg(feature = "nightly")] unsafe impl crate::marker::Ungil for Py {} // SAFETY: Python objects can be sent between threads @@ -1615,9 +1617,10 @@ where self.bind(py).try_borrow_mut() } - /// Provide an immutable borrow of the value `T` without acquiring the GIL. + /// Provide an immutable borrow of the value `T`. /// - /// This is available if the class is [`frozen`][macro@crate::pyclass] and [`Sync`]. + /// This is available if the class is [`frozen`][macro@crate::pyclass] and [`Sync`], and + /// does not require attaching to the Python interpreter. /// /// # Examples /// @@ -1703,7 +1706,7 @@ impl Py { /// /// This creates another pointer to the same object, increasing its reference count. /// - /// You should prefer using this method over [`Clone`] if you happen to be holding the GIL already. + /// You should prefer using this method over [`Clone`]. /// /// # Examples /// @@ -1734,11 +1737,11 @@ impl Py { /// Drops `self` and immediately decreases its reference count. /// - /// This method is a micro-optimisation over [`Drop`] if you happen to be holding the GIL - /// already. + /// This method is a micro-optimisation over [`Drop`] if you happen to have a [`Python<'py>`] + /// token to prove attachment to the Python interpreter. /// /// Note that if you are using [`Bound`], you do not need to use [`Self::drop_ref`] since - /// [`Bound`] guarantees that the GIL is held. + /// [`Bound`] guarantees that the thread is attached to the interpreter. /// /// # Examples /// @@ -2119,7 +2122,7 @@ where } } -/// If the GIL is held this increments `self`'s reference count. +/// If the thread is attached to the Python interpreter this increments `self`'s reference count. /// Otherwise, it will panic. /// /// Only available if the `py-clone` feature is enabled. @@ -2154,10 +2157,10 @@ impl Clone for Py { } /// Dropping a `Py` instance decrements the reference count -/// on the object by one if the GIL is held. +/// on the object by one if the thread is attached to the Python interpreter. /// /// Otherwise and by default, this registers the underlying pointer to have its reference count -/// decremented the next time PyO3 acquires the GIL. +/// decremented the next time PyO3 attaches to the Python interpreter. /// /// However, if the `pyo3_disable_reference_pool` conditional compilation flag /// is enabled, it will abort the process. diff --git a/tests/ui/traverse.stderr b/tests/ui/traverse.stderr index 5cac370c3a9..343a67f5641 100644 --- a/tests/ui/traverse.stderr +++ b/tests/ui/traverse.stderr @@ -1,28 +1,28 @@ -error: __traverse__ may not take a receiver other than `&self`. Usually, an implementation of `__traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>` should do nothing but calls to `visit.call`. Most importantly, safe access to the GIL is prohibited inside implementations of `__traverse__`, i.e. `Python::attach` will panic. +error: __traverse__ may not take a receiver other than `&self`. Usually, an implementation of `__traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>` should do nothing but calls to `visit.call`. Most importantly, safe access to the Python interpreter is prohibited inside implementations of `__traverse__`, i.e. `Python::attach` will panic. --> tests/ui/traverse.rs:10:27 | 10 | fn __traverse__(_slf: PyRef, _visit: PyVisit) -> Result<(), PyTraverseError> { | ^^^^^ -error: __traverse__ may not take a receiver other than `&self`. Usually, an implementation of `__traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>` should do nothing but calls to `visit.call`. Most importantly, safe access to the GIL is prohibited inside implementations of `__traverse__`, i.e. `Python::attach` will panic. +error: __traverse__ may not take a receiver other than `&self`. Usually, an implementation of `__traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>` should do nothing but calls to `visit.call`. Most importantly, safe access to the Python interpreter is prohibited inside implementations of `__traverse__`, i.e. `Python::attach` will panic. --> tests/ui/traverse.rs:20:27 | 20 | fn __traverse__(_slf: PyRefMut, _visit: PyVisit) -> Result<(), PyTraverseError> { | ^^^^^^^^ -error: __traverse__ may not take a receiver other than `&self`. Usually, an implementation of `__traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>` should do nothing but calls to `visit.call`. Most importantly, safe access to the GIL is prohibited inside implementations of `__traverse__`, i.e. `Python::attach` will panic. +error: __traverse__ may not take a receiver other than `&self`. Usually, an implementation of `__traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>` should do nothing but calls to `visit.call`. Most importantly, safe access to the Python interpreter is prohibited inside implementations of `__traverse__`, i.e. `Python::attach` will panic. --> tests/ui/traverse.rs:30:27 | 30 | fn __traverse__(_slf: Bound<'_, Self>, _visit: PyVisit) -> Result<(), PyTraverseError> { | ^^^^^ -error: __traverse__ may not take a receiver other than `&self`. Usually, an implementation of `__traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>` should do nothing but calls to `visit.call`. Most importantly, safe access to the GIL is prohibited inside implementations of `__traverse__`, i.e. `Python::attach` will panic. +error: __traverse__ may not take a receiver other than `&self`. Usually, an implementation of `__traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>` should do nothing but calls to `visit.call`. Most importantly, safe access to the Python interpreter is prohibited inside implementations of `__traverse__`, i.e. `Python::attach` will panic. --> tests/ui/traverse.rs:40:21 | 40 | fn __traverse__(&mut self, _visit: PyVisit) -> Result<(), PyTraverseError> { | ^ -error: __traverse__ may not take `Python`. Usually, an implementation of `__traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>` should do nothing but calls to `visit.call`. Most importantly, safe access to the GIL is prohibited inside implementations of `__traverse__`, i.e. `Python::attach` will panic. +error: __traverse__ may not take `Python`. Usually, an implementation of `__traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>` should do nothing but calls to `visit.call`. Most importantly, safe access to the Python interpreter is prohibited inside implementations of `__traverse__`, i.e. `Python::attach` will panic. --> tests/ui/traverse.rs:60:33 | 60 | fn __traverse__(&self, _py: Python<'_>, _visit: PyVisit<'_>) -> Result<(), PyTraverseError> {