From 33617bfffc6187aa7f57e56bd21800fa52b387eb Mon Sep 17 00:00:00 2001 From: David <1939362+davidhewitt@users.noreply.github.com> Date: Sun, 31 May 2020 16:15:33 +0100 Subject: [PATCH] Require Send for #[pyclass] --- CHANGELOG.md | 1 + guide/src/class.md | 5 ++--- src/pyclass.rs | 4 +++- tests/test_gc.rs | 11 +++++------ tests/ui/static_ref.rs | 14 +++----------- tests/ui/static_ref.stderr | 6 +++--- 6 files changed, 17 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53020074e40..52b31d41721 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Simplify internals of `#[pyo3(get)]` attribute. (Remove the hidden API `GetPropertyValue`.) [#934](https://github.com/PyO3/pyo3/pull/934) - Call `Py_Finalize` at exit to flush buffers, etc. [#943](https://github.com/PyO3/pyo3/pull/943) - Add type parameter to PyBuffer. #[951](https://github.com/PyO3/pyo3/pull/951) +- Require `Send` bound for `#[pyclass]`. [#966](https://github.com/PyO3/pyo3/pull/966) ### Removed - Remove `ManagedPyRef` (unused, and needs specialization) [#930](https://github.com/PyO3/pyo3/pull/930) diff --git a/guide/src/class.md b/guide/src/class.md index a8990556e65..95c9f1877c8 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -14,10 +14,9 @@ struct MyClass { } ``` -The above example generates implementations for [`PyTypeInfo`], [`PyTypeObject`], -and [`PyClass`] for `MyClass`. +Because Python objects are freely shared between threads by the Python interpreter, all structs annotated with `#[pyclass]` must implement `Send`. -If you curious what `#[pyclass]` generates, see [How methods are implemented](#how-methods-are-implemented) section. +The above example generates implementations for [`PyTypeInfo`], [`PyTypeObject`], and [`PyClass`] for `MyClass`. To see these generated implementations, refer to the section [How methods are implemented](#how-methods-are-implemented) at the end of this chapter. ## Adding the class to a module diff --git a/src/pyclass.rs b/src/pyclass.rs index 5e0b05e3ae8..9d7bc8def10 100644 --- a/src/pyclass.rs +++ b/src/pyclass.rs @@ -72,7 +72,9 @@ pub unsafe fn tp_free_fallback(obj: *mut ffi::PyObject) { /// /// The `#[pyclass]` attribute automatically implements this trait for your Rust struct, /// so you don't have to use this trait directly. -pub trait PyClass: PyTypeInfo> + Sized + PyClassAlloc + PyMethods { +pub trait PyClass: + PyTypeInfo> + Sized + PyClassAlloc + PyMethods + Send +{ /// Specify this class has `#[pyclass(dict)]` or not. type Dict: PyClassDict; /// Specify this class has `#[pyclass(weakref)]` or not. diff --git a/tests/test_gc.rs b/tests/test_gc.rs index 0b01de83211..ba63b2a57b4 100644 --- a/tests/test_gc.rs +++ b/tests/test_gc.rs @@ -4,7 +4,6 @@ use pyo3::class::PyVisit; use pyo3::prelude::*; use pyo3::type_object::PyTypeObject; use pyo3::{py_run, AsPyPointer, PyCell, PyTryInto}; -use std::cell::RefCell; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; @@ -84,19 +83,19 @@ fn data_is_dropped() { #[allow(dead_code)] #[pyclass] struct GCIntegration { - self_ref: RefCell, + self_ref: PyObject, dropped: TestDropCall, } #[pyproto] impl PyGCProtocol for GCIntegration { fn __traverse__(&self, visit: PyVisit) -> Result<(), PyTraverseError> { - visit.call(&*self.self_ref.borrow()) + visit.call(&self.self_ref) } fn __clear__(&mut self) { let gil = GILGuard::acquire(); - *self.self_ref.borrow_mut() = gil.python().None(); + self.self_ref = gil.python().None(); } } @@ -110,7 +109,7 @@ fn gc_integration() { let inst = PyCell::new( py, GCIntegration { - self_ref: RefCell::new(py.None()), + self_ref: py.None(), dropped: TestDropCall { drop_called: Arc::clone(&drop_called), }, @@ -119,7 +118,7 @@ fn gc_integration() { .unwrap(); let mut borrow = inst.borrow_mut(); - *borrow.self_ref.borrow_mut() = inst.to_object(py); + borrow.self_ref = inst.to_object(py); } let gil = Python::acquire_gil(); diff --git a/tests/ui/static_ref.rs b/tests/ui/static_ref.rs index 5202920ac03..a426536d499 100644 --- a/tests/ui/static_ref.rs +++ b/tests/ui/static_ref.rs @@ -1,17 +1,9 @@ use pyo3::prelude::*; use pyo3::types::PyList; -#[pyclass] -struct MyClass { - list: &'static PyList, -} - -#[pymethods] -impl MyClass { - #[new] - fn new(list: &'static PyList) -> Self { - Self { list } - } +#[pyfunction] +fn static_ref(list: &'static PyList) -> usize { + list.len() } fn main() {} diff --git a/tests/ui/static_ref.stderr b/tests/ui/static_ref.stderr index a708fdeab3b..1e093a35a29 100644 --- a/tests/ui/static_ref.stderr +++ b/tests/ui/static_ref.stderr @@ -1,8 +1,8 @@ error[E0597]: `pool` does not live long enough - --> $DIR/static_ref.rs:9:1 + --> $DIR/static_ref.rs:4:1 | -9 | #[pymethods] - | ^^^^^^^^^^^^ +4 | #[pyfunction] + | ^^^^^^^^^^^^^ | | | borrowed value does not live long enough | `pool` dropped here while still borrowed