Implement by default immutable pyclasses

This commit is contained in:
mejrs 2021-11-10 15:28:19 +01:00
parent 00c84eb0ba
commit 7f31000be2
37 changed files with 366 additions and 209 deletions

View File

@ -78,7 +78,7 @@ For users who are not very familiar with `RefCell`, here is a reminder of Rust's
```rust
# use pyo3::prelude::*;
#[pyclass]
#[pyclass(mutable)]
struct MyClass {
#[pyo3(get)]
num: i32,
@ -299,7 +299,7 @@ use pyo3::types::PyDict;
use pyo3::AsPyPointer;
use std::collections::HashMap;
#[pyclass(extends=PyDict)]
#[pyclass(extends=PyDict, mutable)]
#[derive(Default)]
struct DictWithCounter {
counter: HashMap<String, usize>,
@ -362,7 +362,7 @@ For simple cases where a member variable is just read and written with no side e
```rust
# use pyo3::prelude::*;
#[pyclass]
#[pyclass(mutable)]
struct MyClass {
#[pyo3(get, set)]
num: i32
@ -410,7 +410,7 @@ can be used since Rust 2018).
```rust
# use pyo3::prelude::*;
# #[pyclass]
# #[pyclass(mutable)]
# struct MyClass {
# num: i32,
# }
@ -436,7 +436,7 @@ If this parameter is specified, it is used as the property name, i.e.
```rust
# use pyo3::prelude::*;
# #[pyclass]
# #[pyclass(mutable)]
# struct MyClass {
# num: i32,
# }
@ -473,7 +473,7 @@ between those accessible to Python (and Rust) and those accessible only to Rust.
```rust
# use pyo3::prelude::*;
# #[pyclass]
# #[pyclass(mutable)]
# struct MyClass {
# num: i32,
# }
@ -641,7 +641,7 @@ Example:
# use pyo3::prelude::*;
use pyo3::types::{PyDict, PyTuple};
#
# #[pyclass]
# #[pyclass(mutable)]
# struct MyClass {
# num: i32,
# }
@ -731,7 +731,7 @@ unsafe impl pyo3::PyTypeInfo for MyClass {
}
}
impl pyo3::pyclass::PyClass for MyClass {
unsafe impl pyo3::pyclass::PyClass for MyClass {
type Dict = pyo3::pyclass_slots::PyClassDummySlot;
type WeakRef = pyo3::pyclass_slots::PyClassDummySlot;
type BaseNativeType = PyAny;

View File

@ -67,7 +67,7 @@ as argument and calls that object when called.
# use pyo3::prelude::*;
# use pyo3::types::{PyDict, PyTuple};
#
#[pyclass(name = "counter")]
#[pyclass(name = "counter", mutable)]
struct PyCounter {
count: u64,
wraps: Py<PyAny>,
@ -453,7 +453,7 @@ use pyo3::prelude::*;
use pyo3::PyTraverseError;
use pyo3::gc::{PyGCProtocol, PyVisit};
#[pyclass]
#[pyclass(mutable)]
struct ClassWithGCSupport {
obj: Option<PyObject>,
}
@ -505,7 +505,7 @@ Example:
use pyo3::prelude::*;
use pyo3::PyIterProtocol;
#[pyclass]
#[pyclass(mutable)]
struct MyIterator {
iter: Box<dyn Iterator<Item = PyObject> + Send>,
}
@ -530,7 +530,7 @@ implementations in `PyIterProtocol` will ensure that the objects behave correctl
# use pyo3::prelude::*;
# use pyo3::PyIterProtocol;
#[pyclass]
#[pyclass(mutable)]
struct Iter {
inner: std::vec::IntoIter<usize>,
}

View File

@ -436,7 +436,7 @@ Here is an example.
```rust
# use pyo3::prelude::*;
#[pyclass]
#[pyclass(mutable)]
struct Names {
names: Vec<String>
}
@ -514,7 +514,7 @@ After:
```rust
# use pyo3::prelude::*;
# use pyo3::types::IntoPyDict;
# #[pyclass] #[derive(Clone)] struct MyClass {}
# #[pyclass(mutable)] #[derive(Clone)] struct MyClass {}
# #[pymethods] impl MyClass { #[new]fn new() -> Self { MyClass {} }}
# Python::with_gil(|py| {
# let typeobj = py.get_type::<MyClass>();

View File

@ -127,7 +127,7 @@ Let's add the PyO3 annotations and add a constructor:
# use pyo3::prelude::*;
# use pyo3::types::PyAny;
#[pyclass]
#[pyclass(mutable)]
struct UserModel {
model: Py<PyAny>,
}
@ -173,7 +173,7 @@ This wrapper will also perform the type conversions between Python and Rust.
# fn get_results(&self) -> Vec<f64>;
# }
#
# #[pyclass]
# #[pyclass(mutable)]
# struct UserModel {
# model: Py<PyAny>,
# }
@ -342,7 +342,7 @@ We used in our `get_results` method the following call that performs the type co
# fn get_results(&self) -> Vec<f64>;
# }
#
# #[pyclass]
# #[pyclass(mutable)]
# struct UserModel {
# model: Py<PyAny>,
# }
@ -395,7 +395,7 @@ Let's break it down in order to perform better error handling:
# fn get_results(&self) -> Vec<f64>;
# }
#
# #[pyclass]
# #[pyclass(mutable)]
# struct UserModel {
# model: Py<PyAny>,
# }
@ -481,7 +481,7 @@ pub fn solve_wrapper(model: &mut UserModel) {
solve(model);
}
#[pyclass]
#[pyclass(mutable)]
pub struct UserModel {
model: Py<PyAny>,
}

View File

@ -87,7 +87,7 @@ For a `&PyAny` object reference `any` where the underlying object is a `#[pyclas
```rust
# use pyo3::prelude::*;
# use pyo3::{Py, Python, PyAny, PyResult};
# #[pyclass] #[derive(Clone)] struct MyClass { }
# #[pyclass(mutable)] #[derive(Clone)] struct MyClass { }
# Python::with_gil(|py| -> PyResult<()> {
let obj: &PyAny = Py::new(py, MyClass { })?.into_ref(py);
@ -191,7 +191,7 @@ For a `#[pyclass] struct MyClass`, the conversions for `Py<MyClass>` are below:
```rust
# use pyo3::prelude::*;
# Python::with_gil(|py| {
# #[pyclass] struct MyClass { }
# #[pyclass(mutable)] struct MyClass { }
# Python::with_gil(|py| -> PyResult<()> {
let my_class: Py<MyClass> = Py::new(py, MyClass { })?;
@ -236,7 +236,7 @@ so it also exposes all of the methods on `PyAny`.
```rust
# use pyo3::prelude::*;
# #[pyclass] struct MyClass { }
# #[pyclass(mutable)] struct MyClass { }
# Python::with_gil(|py| -> PyResult<()> {
let cell: &PyCell<MyClass> = PyCell::new(py, MyClass { })?;

View File

@ -26,6 +26,7 @@ pub struct PyClassArgs {
pub is_basetype: bool,
pub has_extends: bool,
pub has_unsendable: bool,
pub is_mutable: bool,
pub module: Option<syn::LitStr>,
}
@ -54,6 +55,7 @@ impl Default for PyClassArgs {
is_basetype: false,
has_extends: false,
has_unsendable: false,
is_mutable: false,
}
}
}
@ -158,6 +160,9 @@ impl PyClassArgs {
"unsendable" => {
self.has_unsendable = true;
}
"mutable" => {
self.is_mutable = true;
}
_ => bail_spanned!(
exp.path.span() => "expected one of gc/weakref/subclass/dict/unsendable"
),
@ -515,6 +520,52 @@ fn impl_class(
let is_basetype = attr.is_basetype;
let is_subclass = attr.has_extends;
// If the pyclass has extends/unsendable, we must opt back into PyCell checking
// so that the inner Rust object is not inappropriately shared between threads.
let impl_pyclass = if attr.has_unsendable || attr.has_extends || attr.is_mutable {
quote! {
unsafe impl ::pyo3::pyclass::MutablePyClass for #cls {}
unsafe impl ::pyo3::PyClass for #cls {
type Dict = #dict;
type WeakRef = #weakref;
type BaseNativeType = #base_nativetype;
#[inline]
fn try_borrow_as_pyref(slf: &::pyo3::PyCell<Self>) -> ::std::result::Result<::pyo3::pycell::PyRef<'_, Self>, ::pyo3::pycell::PyBorrowError> {
unsafe { ::pyo3::PyCell::immutable_pyclass_try_borrow(slf) }
}
#[inline]
fn borrow_as_pyref(slf: &::pyo3::PyCell<Self>) -> ::pyo3::pycell::PyRef<'_, Self> {
unsafe { ::pyo3::PyCell::immutable_pyclass_borrow(slf) }
}
#[inline]
unsafe fn try_borrow_unguarded(slf: &::pyo3::PyCell<Self>) -> ::std::result::Result<&Self, ::pyo3::pycell::PyBorrowError> {
::pyo3::PyCell::immutable_pyclass_try_borrow_unguarded(slf)
}
#[inline]
unsafe fn drop_pyref(pyref: &mut ::pyo3::pycell::PyRef<Self>) {
::pyo3::pycell::PyRef::decrement_flag(pyref)
}
}
impl<'a> ::pyo3::derive_utils::ExtractExt<'a> for &'a mut #cls {
type Target = ::pyo3::PyRefMut<'a, #cls>;
}
}
} else {
quote! {
unsafe impl ::pyo3::PyClass for #cls {
type Dict = #dict;
type WeakRef = #weakref;
type BaseNativeType = #base_nativetype;
}
}
};
Ok(quote! {
unsafe impl ::pyo3::type_object::PyTypeInfo for #cls {
type AsRefTarget = ::pyo3::PyCell<Self>;
@ -532,21 +583,14 @@ fn impl_class(
}
}
impl ::pyo3::PyClass for #cls {
type Dict = #dict;
type WeakRef = #weakref;
type BaseNativeType = #base_nativetype;
}
#impl_pyclass
impl<'a> ::pyo3::derive_utils::ExtractExt<'a> for &'a #cls
{
type Target = ::pyo3::PyRef<'a, #cls>;
}
impl<'a> ::pyo3::derive_utils::ExtractExt<'a> for &'a mut #cls
{
type Target = ::pyo3::PyRefMut<'a, #cls>;
}
#into_pyobject

View File

@ -9,6 +9,7 @@
//! [typeobj docs](https://docs.python.org/3/c-api/typeobj.html)
use crate::callback::{HashCallbackOutput, IntoPyCallbackOutput};
use crate::pyclass::MutablePyClass;
use crate::{exceptions, ffi, FromPyObject, PyAny, PyCell, PyClass, PyObject};
use std::os::raw::c_int;
@ -128,12 +129,12 @@ pub trait PyObjectGetAttrProtocol<'p>: PyObjectProtocol<'p> {
type Name: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<PyObject>;
}
pub trait PyObjectSetAttrProtocol<'p>: PyObjectProtocol<'p> {
pub trait PyObjectSetAttrProtocol<'p>: PyObjectProtocol<'p> + MutablePyClass {
type Name: FromPyObject<'p>;
type Value: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
pub trait PyObjectDelAttrProtocol<'p>: PyObjectProtocol<'p> {
pub trait PyObjectDelAttrProtocol<'p>: PyObjectProtocol<'p> + MutablePyClass {
type Name: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}

View File

@ -5,7 +5,8 @@
//! For more information check [buffer protocol](https://docs.python.org/3/c-api/buffer.html)
//! c-api
use crate::callback::IntoPyCallbackOutput;
use crate::{ffi, PyCell, PyClass, PyRefMut};
use crate::pyclass::MutablePyClass;
use crate::{ffi, PyCell, PyRefMut};
use std::os::raw::c_int;
/// Buffer protocol interface
@ -13,7 +14,7 @@ use std::os::raw::c_int;
/// For more information check [buffer protocol](https://docs.python.org/3/c-api/buffer.html)
/// c-api.
#[allow(unused_variables)]
pub trait PyBufferProtocol<'p>: PyClass {
pub trait PyBufferProtocol<'p>: MutablePyClass {
// No default implementations so that implementors of this trait provide both methods.
fn bf_getbuffer(slf: PyRefMut<Self>, view: *mut ffi::Py_buffer, flags: c_int) -> Self::Result
@ -51,7 +52,7 @@ where
#[doc(hidden)]
pub unsafe extern "C" fn releasebuffer<T>(slf: *mut ffi::PyObject, arg1: *mut ffi::Py_buffer)
where
T: for<'p> PyBufferReleaseBufferProtocol<'p>,
T: for<'p> PyBufferReleaseBufferProtocol<'p> + MutablePyClass,
{
crate::callback_body!(py, {
let slf = py.from_borrowed_ptr::<crate::PyCell<T>>(slf);

View File

@ -2,6 +2,7 @@
//! Python GC support
use crate::pyclass::MutablePyClass;
use crate::{ffi, AsPyPointer, PyCell, PyClass, Python};
use std::os::raw::{c_int, c_void};
@ -53,7 +54,7 @@ where
#[doc(hidden)]
pub unsafe extern "C" fn clear<T>(slf: *mut ffi::PyObject) -> c_int
where
T: for<'p> PyGCClearProtocol<'p>,
T: for<'p> PyGCClearProtocol<'p> + MutablePyClass,
{
let pool = crate::GILPool::new();
let slf = pool.python().from_borrowed_ptr::<PyCell<T>>(slf);

View File

@ -21,7 +21,7 @@ use crate::{ffi, IntoPy, IntoPyPointer, PyClass, PyObject, Python};
/// use pyo3::prelude::*;
/// use pyo3::PyIterProtocol;
///
/// #[pyclass]
/// #[pyclass(mutable)]
/// struct Iter {
/// count: usize,
/// }

View File

@ -4,8 +4,8 @@
//! Trait and support implementation for implementing mapping support
use crate::callback::IntoPyCallbackOutput;
use crate::pyclass::MutablePyClass;
use crate::{FromPyObject, PyClass, PyObject};
/// Mapping interface
#[allow(unused_variables)]
pub trait PyMappingProtocol<'p>: PyClass {
@ -61,13 +61,13 @@ pub trait PyMappingGetItemProtocol<'p>: PyMappingProtocol<'p> {
type Result: IntoPyCallbackOutput<PyObject>;
}
pub trait PyMappingSetItemProtocol<'p>: PyMappingProtocol<'p> {
pub trait PyMappingSetItemProtocol<'p>: PyMappingProtocol<'p> + MutablePyClass {
type Key: FromPyObject<'p>;
type Value: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
pub trait PyMappingDelItemProtocol<'p>: PyMappingProtocol<'p> {
pub trait PyMappingDelItemProtocol<'p>: PyMappingProtocol<'p> + MutablePyClass {
type Key: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}

View File

@ -4,6 +4,7 @@
//! Trait and support implementation for implementing number protocol
use crate::callback::IntoPyCallbackOutput;
use crate::err::PyErr;
use crate::pyclass::MutablePyClass;
use crate::{ffi, FromPyObject, PyClass, PyObject};
/// Number interface
@ -481,74 +482,74 @@ pub trait PyNumberROrProtocol<'p>: PyNumberProtocol<'p> {
type Result: IntoPyCallbackOutput<PyObject>;
}
pub trait PyNumberIAddProtocol<'p>: PyNumberProtocol<'p> {
pub trait PyNumberIAddProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass {
type Other: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
pub trait PyNumberISubProtocol<'p>: PyNumberProtocol<'p> {
pub trait PyNumberISubProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass {
type Other: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
pub trait PyNumberIMulProtocol<'p>: PyNumberProtocol<'p> {
pub trait PyNumberIMulProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass {
type Other: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
pub trait PyNumberIMatmulProtocol<'p>: PyNumberProtocol<'p> {
pub trait PyNumberIMatmulProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass {
type Other: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
pub trait PyNumberITruedivProtocol<'p>: PyNumberProtocol<'p> {
pub trait PyNumberITruedivProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass {
type Other: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
pub trait PyNumberIFloordivProtocol<'p>: PyNumberProtocol<'p> {
pub trait PyNumberIFloordivProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass {
type Other: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
pub trait PyNumberIModProtocol<'p>: PyNumberProtocol<'p> {
pub trait PyNumberIModProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass {
type Other: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
pub trait PyNumberIDivmodProtocol<'p>: PyNumberProtocol<'p> {
pub trait PyNumberIDivmodProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass {
type Other: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
pub trait PyNumberIPowProtocol<'p>: PyNumberProtocol<'p> {
pub trait PyNumberIPowProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass {
type Other: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
#[allow(clippy::upper_case_acronyms)]
pub trait PyNumberILShiftProtocol<'p>: PyNumberProtocol<'p> {
pub trait PyNumberILShiftProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass {
type Other: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
#[allow(clippy::upper_case_acronyms)]
pub trait PyNumberIRShiftProtocol<'p>: PyNumberProtocol<'p> {
pub trait PyNumberIRShiftProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass {
type Other: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
pub trait PyNumberIAndProtocol<'p>: PyNumberProtocol<'p> {
pub trait PyNumberIAndProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass {
type Other: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
pub trait PyNumberIXorProtocol<'p>: PyNumberProtocol<'p> {
pub trait PyNumberIXorProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass {
type Other: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
pub trait PyNumberIOrProtocol<'p>: PyNumberProtocol<'p> {
pub trait PyNumberIOrProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass {
type Other: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
@ -750,7 +751,7 @@ pub unsafe extern "C" fn ipow<T>(
_modulo: *mut ffi::PyObject,
) -> *mut ffi::PyObject
where
T: for<'p> PyNumberIPowProtocol<'p>,
T: for<'p> PyNumberIPowProtocol<'p> + MutablePyClass,
{
// NOTE: Somehow __ipow__ causes SIGSEGV in Python < 3.8 when we extract,
// so we ignore it. It's the same as what CPython does.

View File

@ -6,6 +6,7 @@
use crate::callback::IntoPyCallbackOutput;
use crate::conversion::{FromPyObject, IntoPy};
use crate::err::PyErr;
use crate::pyclass::MutablePyClass;
use crate::{exceptions, ffi, PyAny, PyCell, PyClass, PyObject};
use std::os::raw::c_int;
@ -88,13 +89,13 @@ pub trait PySequenceGetItemProtocol<'p>: PySequenceProtocol<'p> {
type Result: IntoPyCallbackOutput<PyObject>;
}
pub trait PySequenceSetItemProtocol<'p>: PySequenceProtocol<'p> {
pub trait PySequenceSetItemProtocol<'p>: PySequenceProtocol<'p> + MutablePyClass {
type Index: FromPyObject<'p> + From<isize>;
type Value: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
pub trait PySequenceDelItemProtocol<'p>: PySequenceProtocol<'p> {
pub trait PySequenceDelItemProtocol<'p>: PySequenceProtocol<'p> + MutablePyClass {
type Index: FromPyObject<'p> + From<isize>;
type Result: IntoPyCallbackOutput<()>;
}
@ -115,14 +116,14 @@ pub trait PySequenceRepeatProtocol<'p>: PySequenceProtocol<'p> {
}
pub trait PySequenceInplaceConcatProtocol<'p>:
PySequenceProtocol<'p> + IntoPy<PyObject> + 'p
PySequenceProtocol<'p> + IntoPy<PyObject> + MutablePyClass + 'p
{
type Other: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<Self>;
}
pub trait PySequenceInplaceRepeatProtocol<'p>:
PySequenceProtocol<'p> + IntoPy<PyObject> + 'p
PySequenceProtocol<'p> + IntoPy<PyObject> + MutablePyClass + 'p
{
type Index: FromPyObject<'p> + From<isize>;
type Result: IntoPyCallbackOutput<Self>;

View File

@ -2,6 +2,7 @@
//! Defines conversions between Rust and Python types.
use crate::err::{self, PyDowncastError, PyResult};
use crate::pyclass::MutablePyClass;
use crate::type_object::PyTypeInfo;
use crate::types::PyTuple;
use crate::{
@ -145,7 +146,7 @@ where
/// ```rust
/// use pyo3::prelude::*;
///
/// #[pyclass]
/// #[pyclass(mutable)]
/// struct Number {
/// #[pyo3(get, set)]
/// value: i32,
@ -329,7 +330,7 @@ where
impl<'a, T> FromPyObject<'a> for PyRefMut<'a, T>
where
T: PyClass,
T: MutablePyClass,
{
fn extract(obj: &'a PyAny) -> PyResult<Self> {
let cell: &PyCell<T> = PyTryFrom::try_from(obj)?;

View File

@ -3,6 +3,7 @@ use crate::conversion::{PyTryFrom, ToBorrowedObject};
use crate::err::{self, PyDowncastError, PyErr, PyResult};
use crate::gil;
use crate::pycell::{PyBorrowError, PyBorrowMutError, PyCell};
use crate::pyclass::MutablePyClass;
use crate::types::{PyDict, PyTuple};
use crate::{
ffi, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, PyAny, PyClass, PyClassInitializer,
@ -391,6 +392,23 @@ where
self.as_ref(py).borrow()
}
/// Attempts to immutably borrow the value `T`, returning an error if the value is currently mutably borrowed.
///
/// The borrow lasts while the returned [`PyRef`] exists.
///
/// This is the non-panicking variant of [`borrow`](#method.borrow).
///
/// Equivalent to `self.as_ref(py).borrow_mut()` -
/// see [`PyCell::try_borrow`](crate::pycell::PyCell::try_borrow).
pub fn try_borrow<'py>(&'py self, py: Python<'py>) -> Result<PyRef<'py, T>, PyBorrowError> {
self.as_ref(py).try_borrow()
}
}
impl<T> Py<T>
where
T: MutablePyClass,
{
/// Mutably borrows the value `T`.
///
/// This borrow lasts while the returned [`PyRefMut`] exists.
@ -403,7 +421,7 @@ where
/// ```
/// # use pyo3::prelude::*;
/// #
/// #[pyclass]
/// #[pyclass(mutable)]
/// struct Foo {
/// inner: u8,
/// }
@ -427,18 +445,6 @@ where
self.as_ref(py).borrow_mut()
}
/// Attempts to immutably borrow the value `T`, returning an error if the value is currently mutably borrowed.
///
/// The borrow lasts while the returned [`PyRef`] exists.
///
/// This is the non-panicking variant of [`borrow`](#method.borrow).
///
/// Equivalent to `self.as_ref(py).borrow_mut()` -
/// see [`PyCell::try_borrow`](crate::pycell::PyCell::try_borrow).
pub fn try_borrow<'py>(&'py self, py: Python<'py>) -> Result<PyRef<'py, T>, PyBorrowError> {
self.as_ref(py).try_borrow()
}
/// Attempts to mutably borrow the value `T`, returning an error if the value is currently borrowed.
///
/// The borrow lasts while the returned [`PyRefMut`] exists.
@ -802,7 +808,7 @@ where
impl<'a, T> std::convert::From<PyRefMut<'a, T>> for Py<T>
where
T: PyClass,
T: MutablePyClass,
{
fn from(pyref: PyRefMut<'a, T>) -> Self {
unsafe { Py::from_borrowed_ptr(pyref.py(), pyref.as_ptr()) }

View File

@ -25,7 +25,7 @@
//! ```rust
//! use pyo3::prelude::*;
//!
//! #[pyclass]
//! #[pyclass(mutable)]
//! struct Number {
//! inner: u32,
//! }
@ -60,7 +60,7 @@
//! ```rust
//! # use pyo3::prelude::*;
//! #
//! # #[pyclass]
//! # #[pyclass(mutable)]
//! # struct Number {
//! # inner: u32,
//! # }
@ -99,7 +99,7 @@
//! Suppose the following function that swaps the values of two `Number`s:
//! ```
//! # use pyo3::prelude::*;
//! # #[pyclass]
//! # #[pyclass(mutable)]
//! # pub struct Number {
//! # inner: u32,
//! # }
@ -131,7 +131,7 @@
//! It is better to write that function like this:
//! ```rust
//! # use pyo3::prelude::*;
//! # #[pyclass]
//! # #[pyclass(mutable)]
//! # pub struct Number {
//! # inner: u32,
//! # }
@ -175,7 +175,7 @@
//! [Interior Mutability]: https://doc.rust-lang.org/book/ch15-05-interior-mutability.html "RefCell<T> and the Interior Mutability Pattern - The Rust Programming Language"
use crate::exceptions::PyRuntimeError;
use crate::pyclass::PyClass;
use crate::pyclass::{MutablePyClass, PyClass};
use crate::pyclass_init::PyClassInitializer;
use crate::pyclass_slots::{PyClassDict, PyClassWeakRef};
use crate::type_object::{PyLayout, PySizedLayout};
@ -214,7 +214,7 @@ unsafe impl<T, U> PyLayout<T> for PyCellBase<U> where U: PySizedLayout<T> {}
/// ```rust
/// use pyo3::prelude::*;
///
/// #[pyclass]
/// #[pyclass(mutable)]
/// struct Number {
/// inner: u32,
/// }
@ -352,18 +352,15 @@ impl<T: PyClass> PyCell<T> {
///
/// Panics if the value is currently mutably borrowed. For a non-panicking variant, use
/// [`try_borrow`](#method.try_borrow).
#[inline]
pub fn borrow(&self) -> PyRef<'_, T> {
self.try_borrow().expect("Already mutably borrowed")
PyClass::borrow_as_pyref(self)
}
/// Mutably borrows the value `T`. This borrow lasts as long as the returned `PyRefMut` exists.
///
/// # Panics
///
/// Panics if the value is currently borrowed. For a non-panicking variant, use
/// [`try_borrow_mut`](#method.try_borrow_mut).
pub fn borrow_mut(&self) -> PyRefMut<'_, T> {
self.try_borrow_mut().expect("Already borrowed")
#[inline]
#[doc(hidden)]
pub unsafe fn immutable_pyclass_borrow(&self) -> PyRef<'_, T> {
self.try_borrow().expect("Already mutably borrowed")
}
/// Immutably borrows the value `T`, returning an error if the value is currently
@ -375,7 +372,7 @@ impl<T: PyClass> PyCell<T> {
///
/// ```
/// # use pyo3::prelude::*;
/// #[pyclass]
/// #[pyclass(mutable)]
/// struct Class {}
///
/// Python::with_gil(|py| {
@ -391,7 +388,14 @@ impl<T: PyClass> PyCell<T> {
/// }
/// });
/// ```
#[inline]
pub fn try_borrow(&self) -> Result<PyRef<'_, T>, PyBorrowError> {
PyClass::try_borrow_as_pyref(self)
}
#[inline]
#[doc(hidden)]
pub unsafe fn immutable_pyclass_try_borrow(&self) -> Result<PyRef<'_, T>, PyBorrowError> {
let flag = self.get_borrow_flag();
if flag == BorrowFlag::HAS_MUTABLE_BORROW {
Err(PyBorrowError { _private: () })
@ -401,6 +405,73 @@ impl<T: PyClass> PyCell<T> {
}
}
/// Immutably borrows the value `T`, returning an error if the value is
/// currently mutably borrowed.
///
/// # Safety
///
/// This method is unsafe because it does not return a `PyRef`,
/// thus leaving the borrow flag untouched. Mutably borrowing the `PyCell`
/// while the reference returned by this method is alive is undefined behaviour.
///
/// # Examples
///
/// ```
/// # use pyo3::prelude::*;
/// #[pyclass(mutable)]
/// struct Class {}
/// Python::with_gil(|py| {
/// let c = PyCell::new(py, Class {}).unwrap();
///
/// {
/// let m = c.borrow_mut();
/// assert!(unsafe { c.try_borrow_unguarded() }.is_err());
/// }
///
/// {
/// let m = c.borrow();
/// assert!(unsafe { c.try_borrow_unguarded() }.is_ok());
/// }
/// });
/// ```
#[inline]
pub unsafe fn try_borrow_unguarded(&self) -> Result<&T, PyBorrowError> {
PyClass::try_borrow_unguarded(self)
}
#[inline]
#[doc(hidden)]
pub unsafe fn immutable_pyclass_try_borrow_unguarded(&self) -> Result<&T, PyBorrowError> {
if self.get_borrow_flag() == BorrowFlag::HAS_MUTABLE_BORROW {
Err(PyBorrowError { _private: () })
} else {
Ok(&*self.contents.value.get())
}
}
pub(crate) unsafe fn immutable_pyclass_try_borrow_unchecked_unguarded(&self) -> &T {
&*self.contents.value.get()
}
pub(crate) unsafe fn borrow_unchecked_unincremented(&self) -> PyRef<'_, T> {
PyRef { inner: self }
}
fn get_ptr(&self) -> *mut T {
self.contents.value.get()
}
}
impl<T: MutablePyClass> PyCell<T> {
/// Mutably borrows the value `T`. This borrow lasts as long as the returned `PyRefMut` exists.
///
/// # Panics
///
/// Panics if the value is currently borrowed. For a non-panicking variant, use
/// [`try_borrow_mut`](#method.try_borrow_mut).
pub fn borrow_mut(&self) -> PyRefMut<'_, T> {
self.try_borrow_mut().expect("Already borrowed")
}
/// Mutably borrows the value `T`, returning an error if the value is currently borrowed.
/// This borrow lasts as long as the returned `PyRefMut` exists.
///
@ -410,7 +481,7 @@ impl<T: PyClass> PyCell<T> {
///
/// ```
/// # use pyo3::prelude::*;
/// #[pyclass]
/// #[pyclass(mutable)]
/// struct Class {}
/// Python::with_gil(|py| {
/// let c = PyCell::new(py, Class {}).unwrap();
@ -430,44 +501,6 @@ impl<T: PyClass> PyCell<T> {
Ok(PyRefMut { inner: self })
}
}
/// Immutably borrows the value `T`, returning an error if the value is
/// currently mutably borrowed.
///
/// # Safety
///
/// This method is unsafe because it does not return a `PyRef`,
/// thus leaving the borrow flag untouched. Mutably borrowing the `PyCell`
/// while the reference returned by this method is alive is undefined behaviour.
///
/// # Examples
///
/// ```
/// # use pyo3::prelude::*;
/// #[pyclass]
/// struct Class {}
/// Python::with_gil(|py| {
/// let c = PyCell::new(py, Class {}).unwrap();
///
/// {
/// let m = c.borrow_mut();
/// assert!(unsafe { c.try_borrow_unguarded() }.is_err());
/// }
///
/// {
/// let m = c.borrow();
/// assert!(unsafe { c.try_borrow_unguarded() }.is_ok());
/// }
/// });
/// ```
pub unsafe fn try_borrow_unguarded(&self) -> Result<&T, PyBorrowError> {
if self.get_borrow_flag() == BorrowFlag::HAS_MUTABLE_BORROW {
Err(PyBorrowError { _private: () })
} else {
Ok(&*self.contents.value.get())
}
}
/// Replaces the wrapped value with a new one, returning the old value.
///
/// # Panics
@ -498,10 +531,6 @@ impl<T: PyClass> PyCell<T> {
pub fn swap(&self, other: &Self) {
std::mem::swap(&mut *self.borrow_mut(), &mut *other.borrow_mut())
}
fn get_ptr(&self) -> *mut T {
self.contents.value.get()
}
}
unsafe impl<T: PyClass> PyLayout<T> for PyCell<T> {}
@ -605,6 +634,12 @@ impl<'p, T: PyClass> PyRef<'p, T> {
pub fn py(&self) -> Python {
unsafe { Python::assume_gil_acquired() }
}
#[doc(hidden)]
pub unsafe fn decrement_flag(&mut self) {
let flag = self.inner.get_borrow_flag();
self.inner.set_borrow_flag(flag.decrement())
}
}
impl<'p, T, U> AsRef<U> for PyRef<'p, T>
@ -687,8 +722,7 @@ impl<'p, T: PyClass> Deref for PyRef<'p, T> {
impl<'p, T: PyClass> Drop for PyRef<'p, T> {
fn drop(&mut self) {
let flag = self.inner.get_borrow_flag();
self.inner.set_borrow_flag(flag.decrement())
unsafe { PyClass::drop_pyref(self) }
}
}
@ -720,11 +754,11 @@ impl<T: PyClass + fmt::Debug> fmt::Debug for PyRef<'_, T> {
/// A wrapper type for a mutably borrowed value from a[`PyCell`]`<T>`.
///
/// See the [module-level documentation](self) for more information.
pub struct PyRefMut<'p, T: PyClass> {
pub struct PyRefMut<'p, T: MutablePyClass> {
inner: &'p PyCell<T>,
}
impl<'p, T: PyClass> PyRefMut<'p, T> {
impl<'p, T: MutablePyClass> PyRefMut<'p, T> {
/// Returns a `Python` token that is bound to the lifetime of the `PyRefMut`.
pub fn py(&self) -> Python {
unsafe { Python::assume_gil_acquired() }
@ -733,8 +767,8 @@ impl<'p, T: PyClass> PyRefMut<'p, T> {
impl<'p, T, U> AsRef<U> for PyRefMut<'p, T>
where
T: PyClass<BaseType = U>,
U: PyClass,
T: PyClass<BaseType = U> + MutablePyClass,
U: MutablePyClass,
{
fn as_ref(&self) -> &T::BaseType {
unsafe { &*self.inner.ob_base.get_ptr() }
@ -743,8 +777,8 @@ where
impl<'p, T, U> AsMut<U> for PyRefMut<'p, T>
where
T: PyClass<BaseType = U>,
U: PyClass,
T: PyClass<BaseType = U> + MutablePyClass,
U: MutablePyClass,
{
fn as_mut(&mut self) -> &mut T::BaseType {
unsafe { &mut *self.inner.ob_base.get_ptr() }
@ -753,8 +787,8 @@ where
impl<'p, T, U> PyRefMut<'p, T>
where
T: PyClass<BaseType = U>,
U: PyClass,
T: PyClass<BaseType = U> + MutablePyClass,
U: MutablePyClass,
{
/// Gets a `PyRef<T::BaseType>`.
///
@ -768,7 +802,7 @@ where
}
}
impl<'p, T: PyClass> Deref for PyRefMut<'p, T> {
impl<'p, T: MutablePyClass> Deref for PyRefMut<'p, T> {
type Target = T;
#[inline]
@ -777,39 +811,39 @@ impl<'p, T: PyClass> Deref for PyRefMut<'p, T> {
}
}
impl<'p, T: PyClass> DerefMut for PyRefMut<'p, T> {
impl<'p, T: MutablePyClass> DerefMut for PyRefMut<'p, T> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.inner.get_ptr() }
}
}
impl<'p, T: PyClass> Drop for PyRefMut<'p, T> {
impl<'p, T: MutablePyClass> Drop for PyRefMut<'p, T> {
fn drop(&mut self) {
self.inner.set_borrow_flag(BorrowFlag::UNUSED)
}
}
impl<T: PyClass> IntoPy<PyObject> for PyRefMut<'_, T> {
impl<T: MutablePyClass> IntoPy<PyObject> for PyRefMut<'_, T> {
fn into_py(self, py: Python) -> PyObject {
unsafe { PyObject::from_borrowed_ptr(py, self.inner.as_ptr()) }
}
}
impl<'a, T: PyClass> AsPyPointer for PyRefMut<'a, T> {
impl<'a, T: MutablePyClass> AsPyPointer for PyRefMut<'a, T> {
fn as_ptr(&self) -> *mut ffi::PyObject {
self.inner.as_ptr()
}
}
impl<'a, T: PyClass> std::convert::TryFrom<&'a PyCell<T>> for crate::PyRefMut<'a, T> {
impl<'a, T: MutablePyClass> std::convert::TryFrom<&'a PyCell<T>> for crate::PyRefMut<'a, T> {
type Error = PyBorrowMutError;
fn try_from(cell: &'a crate::PyCell<T>) -> Result<Self, Self::Error> {
cell.try_borrow_mut()
}
}
impl<T: PyClass + fmt::Debug> fmt::Debug for PyRefMut<'_, T> {
impl<T: MutablePyClass + fmt::Debug> fmt::Debug for PyRefMut<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&*(self.deref()), f)
}

View File

@ -1,4 +1,5 @@
//! `PyClass` and related traits.
use crate::pycell::{PyBorrowError, PyRef};
use crate::{
class::impl_::{fallback_new, tp_dealloc, PyClassImpl},
ffi,
@ -17,7 +18,14 @@ use std::{
///
/// The `#[pyclass]` attribute automatically implements this trait for your Rust struct,
/// so you normally don't have to use this trait directly.
pub trait PyClass:
///
/// # Safety
///
/// If `T` implements [`MutablePyClass`], then implementations must override the default methods.
///
/// If `T` does not implement [`MutablePyClass`], then implementations should not override the
/// default methods.
pub unsafe trait PyClass:
PyTypeInfo<AsRefTarget = PyCell<Self>> + PyClassImpl<Layout = PyCell<Self>>
{
/// Specify this class has `#[pyclass(dict)]` or not.
@ -27,8 +35,65 @@ pub trait PyClass:
/// The closest native ancestor. This is `PyAny` by default, and when you declare
/// `#[pyclass(extends=PyDict)]`, it's `PyDict`.
type BaseNativeType: PyTypeInfo + PyNativeType;
/// Default implementation that borrows as a PyRef without checking or incrementing the
/// borrowflag.
///
/// # Safety
///
/// Implementations that implement [`MutablePyClass`] **must** override this method
/// by wrapping `PyCell::immutable_pyclass_try_borrow()`
#[inline]
fn try_borrow_as_pyref(slf: &PyCell<Self>) -> Result<PyRef<'_, Self>, PyBorrowError> {
Ok(Self::borrow_as_pyref(slf))
}
/// Default implementation that borrows as a PyRef without checking or incrementing the
/// borrowflag.
///
/// # Safety
///
/// Implementations that implement [`MutablePyClass`] **must** override this method
/// by wrapping `PyCell::immutable_pyclass_borrow()`
#[inline]
fn borrow_as_pyref(slf: &PyCell<Self>) -> PyRef<'_, Self> {
unsafe { PyCell::borrow_unchecked_unincremented(slf) }
}
/// Default implementation that borrows as a PyRef without checking or incrementing the
/// borrowflag.
///
/// # Safety
///
/// Please see the safety requirements on [`PyCell::try_borrow_unguarded`].
///
/// Implementations that implement [`MutablePyClass`] **must** override this method
/// by wrapping `PyCell::immutable_pyclass_try_borrow_unguarded()`.
#[inline]
unsafe fn try_borrow_unguarded(slf: &PyCell<Self>) -> Result<&Self, PyBorrowError> {
Ok(PyCell::_try_borrow_unchecked_unguarded(slf))
}
/// Default implementation that does nothing.
///
/// # Safety
///
/// This function is only called inside [`PyRef`]s [`Drop`] implementation.
///
/// Implementations that also implement [`MutablePyClass`] **must** make this method call
/// [`PyRef::decrement_flag()`] so that [`PyRef`]s [`Drop`] implementation correctly decrements
/// the borrowflag.
#[inline]
unsafe fn drop_pyref(_: &mut PyRef<Self>) {}
}
/// Declares that a pyclass can be mutably borrowed.
///
/// # Safety
///
/// Implementations must correctly implement [`PyClass`].
pub unsafe trait MutablePyClass: PyClass {}
/// For collecting slot items.
#[derive(Default)]
struct TypeSlots(Vec<ffi::PyType_Slot>);

View File

@ -15,7 +15,8 @@ pub struct Foo2;
unsendable,
subclass,
extends = ::pyo3::types::PyAny,
module = "Spam"
module = "Spam",
mutable
)]
pub struct Bar {
#[pyo3(get, set)]

View File

@ -1,10 +1,10 @@
#![no_implicit_prelude]
#![allow(unused_variables)]
#[::pyo3::pyclass]
#[::pyo3::pyclass(mutable)]
pub struct Dummy;
#[::pyo3::pyclass]
#[::pyo3::pyclass(mutable)]
pub struct DummyIter;
#[::pyo3::pymethods]

View File

@ -16,7 +16,8 @@ pub struct Foo2;
gc,
subclass,
extends = ::pyo3::types::PyAny,
module = "Spam"
module = "Spam",
mutable
)]
pub struct Bar {
#[pyo3(get, set)]

View File

@ -50,7 +50,7 @@ fn unary_arithmetic() {
py_run!(py, c, "assert repr(round(c, 1)) == 'UA(3)'");
}
#[pyclass]
#[pyclass(mutable)]
struct InPlaceOperations {
value: u32,
}
@ -500,7 +500,7 @@ fn rich_comparisons_python_3_type_error() {
mod return_not_implemented {
use super::*;
#[pyclass]
#[pyclass(mutable)]
struct RichComparisonToSelf {}
#[pymethods]

View File

@ -67,7 +67,7 @@ impl PyObjectProtocol for BinaryArithmetic {
}
}
#[pyclass]
#[pyclass(mutable)]
struct InPlaceOperations {
value: u32,
}
@ -527,7 +527,7 @@ fn rich_comparisons_python_3_type_error() {
mod return_not_implemented {
use super::*;
#[pyclass]
#[pyclass(mutable)]
struct RichComparisonToSelf {}
#[pyproto]

View File

@ -21,7 +21,7 @@ enum TestGetBufferError {
IncorrectAlignment,
}
#[pyclass]
#[pyclass(mutable)]
struct TestBufferErrors {
buf: Vec<u32>,
error: Option<TestGetBufferError>,

View File

@ -15,7 +15,7 @@ use std::sync::Arc;
mod common;
#[pyclass]
#[pyclass(mutable)]
struct TestBufferClass {
vec: Vec<u8>,
drop_called: Arc<AtomicBool>,

View File

@ -36,7 +36,7 @@ fn unit_class() {
///Line2
/// Line3
// this is not doc string
#[pyclass]
#[pyclass(mutable)]
struct ClassWithDocs {
/// Property field
#[pyo3(get, set)]
@ -122,7 +122,7 @@ fn custom_names() {
py_assert!(py, typeobj, "not hasattr(typeobj, 'foo')");
}
#[pyclass]
#[pyclass(mutable)]
struct RawIdents {
#[pyo3(get, set)]
r#type: i64,
@ -171,7 +171,7 @@ fn empty_class_in_module() {
assert_eq!(module, "builtins");
}
#[pyclass]
#[pyclass(mutable)]
struct ClassWithObjectField {
// It used to be that PyObject was not supported with (get, set)
// - this test is just ensuring it compiles.
@ -316,7 +316,7 @@ fn test_pymethods_from_py_with() {
})
}
#[pyclass]
#[pyclass(mutable)]
struct TupleClass(#[pyo3(get, set, name = "value")] i32);
#[test]

View File

@ -4,7 +4,7 @@ use pyo3::ToPyObject;
#[macro_use]
mod common;
#[pyclass]
#[pyclass(mutable)]
#[derive(Clone, Debug, PartialEq)]
struct Cloneable {
x: i32,
@ -30,7 +30,7 @@ fn test_cloneable_pyclass() {
assert_eq!(&c, &*mrc);
}
#[pyclass(subclass)]
#[pyclass(subclass, mutable)]
#[derive(Default)]
struct BaseClass {
value: i32,
@ -43,7 +43,7 @@ impl BaseClass {
}
}
#[pyclass(extends=BaseClass)]
#[pyclass(extends=BaseClass, mutable)]
struct SubClass {}
#[pymethods]
@ -53,7 +53,7 @@ impl SubClass {
}
}
#[pyclass]
#[pyclass(mutable)]
struct PolymorphicContainer {
#[pyo3(get, set)]
inner: Py<BaseClass>,

View File

@ -81,7 +81,7 @@ fn data_is_dropped() {
}
#[allow(dead_code)]
#[pyclass]
#[pyclass(mutable)]
struct GcIntegration {
self_ref: PyObject,
dropped: TestDropCall,
@ -127,7 +127,7 @@ fn gc_integration() {
assert!(drop_called.load(Ordering::Relaxed));
}
#[pyclass(gc)]
#[pyclass(gc, mutable)]
struct GcIntegration2 {}
#[pyproto]
@ -181,7 +181,7 @@ fn inherited_weakref() {
);
}
#[pyclass(subclass)]
#[pyclass(subclass, mutable)]
struct BaseClassWithDrop {
data: Option<Arc<AtomicBool>>,
}
@ -202,7 +202,7 @@ impl Drop for BaseClassWithDrop {
}
}
#[pyclass(extends = BaseClassWithDrop)]
#[pyclass(extends = BaseClassWithDrop, mutable)]
struct SubClassWithDrop {
data: Option<Arc<AtomicBool>>,
}
@ -249,7 +249,7 @@ fn inheritance_with_new_methods_with_drop() {
assert!(drop_called2.load(Ordering::Relaxed));
}
#[pyclass(gc)]
#[pyclass(gc, mutable)]
struct TraversableClass {
traversed: AtomicBool,
}

View File

@ -4,7 +4,7 @@ use pyo3::types::{IntoPyDict, PyList};
mod common;
#[pyclass]
#[pyclass(mutable)]
struct ClassWithProperties {
num: i32,
}
@ -65,7 +65,7 @@ fn class_with_properties() {
py_assert!(py, *d, "C.DATA.__doc__ == 'a getter for data'");
}
#[pyclass]
#[pyclass(mutable)]
struct GetterSetter {
#[pyo3(get, set)]
num: i32,
@ -103,7 +103,7 @@ fn getter_setter_autogen() {
);
}
#[pyclass]
#[pyclass(mutable)]
struct RefGetterSetter {
num: i32,
}
@ -133,7 +133,7 @@ fn ref_getter_setter() {
py_run!(py, inst, "inst.num = 20; assert inst.num == 20");
}
#[pyclass]
#[pyclass(mutable)]
struct TupleClassGetterSetter(i32);
#[pymethods]

View File

@ -5,7 +5,7 @@ use pyo3::types::IntoPyDict;
mod common;
#[pyclass(subclass)]
#[pyclass(subclass, mutable)]
struct BaseClass {
#[pyo3(get)]
val1: usize,
@ -45,7 +45,7 @@ impl BaseClass {
}
}
#[pyclass(extends=BaseClass)]
#[pyclass(extends=BaseClass, mutable)]
struct SubClass {
#[pyo3(get)]
val2: usize,

View File

@ -45,7 +45,7 @@ fn_macro!("(a, b=None, *, c=42)", a, b = "None", c = 42);
macro_rules! property_rename_via_macro {
($prop_name:ident) => {
#[pyclass]
#[pyclass(mutable)]
struct ClassWithProperty {
member: u64,
}

View File

@ -11,7 +11,7 @@ use pyo3::PyMappingProtocol;
mod common;
#[pyclass]
#[pyclass(mutable)]
struct Mapping {
index: HashMap<String, usize>,
}

View File

@ -675,7 +675,7 @@ fn method_with_lifetime() {
);
}
#[pyclass]
#[pyclass(mutable)]
struct MethodWithPyClassArg {
#[pyo3(get)]
value: i64,
@ -824,7 +824,7 @@ fn test_from_sequence() {
py_assert!(py, typeobj, "typeobj(range(0, 4)).numbers == [0, 1, 2, 3]")
}
#[pyclass]
#[pyclass(mutable)]
struct r#RawIdents {
#[pyo3(get, set)]
r#type: PyObject,

View File

@ -6,7 +6,7 @@ use std::{isize, iter};
mod common;
#[pyclass]
#[pyclass(mutable)]
struct ExampleClass {
#[pyo3(get, set)]
value: i32,
@ -194,7 +194,7 @@ fn len() {
py_expect_exception!(py, inst, "len(inst)", PyOverflowError);
}
#[pyclass]
#[pyclass(mutable)]
struct Iterator {
iter: Box<dyn iter::Iterator<Item = i32> + Send>,
}
@ -278,7 +278,7 @@ mod deprecated {
}
}
#[pyclass]
#[pyclass(mutable)]
#[derive(Debug)]
struct SetItem {
key: i32,
@ -308,7 +308,7 @@ fn setitem() {
py_expect_exception!(py, c, "del c[1]", PyNotImplementedError);
}
#[pyclass]
#[pyclass(mutable)]
struct DelItem {
key: i32,
}
@ -334,7 +334,7 @@ fn delitem() {
py_expect_exception!(py, c, "c[1] = 2", PyNotImplementedError);
}
#[pyclass]
#[pyclass(mutable)]
struct SetDelItem {
val: Option<i32>,
}
@ -418,7 +418,7 @@ fn test_getitem() {
py_assert!(py, ob, "ob[100:200:1] == 'slice'");
}
#[pyclass]
#[pyclass(mutable)]
struct ClassWithGetAttr {
#[pyo3(get, set)]
data: u32,
@ -441,7 +441,7 @@ fn getattr_doesnt_override_member() {
}
/// Wraps a Python future and yield it once.
#[pyclass]
#[pyclass(mutable)]
struct OnceFuture {
future: PyObject,
polled: bool,
@ -505,7 +505,7 @@ loop.close()
}
/// Increment the count when `__get__` is called.
#[pyclass]
#[pyclass(mutable)]
struct DescrCounter {
#[pyo3(get)]
count: usize,

View File

@ -47,7 +47,7 @@ fn len() {
py_expect_exception!(py, inst, "len(inst)", PyOverflowError);
}
#[pyclass]
#[pyclass(mutable)]
struct Iterator {
iter: Box<dyn iter::Iterator<Item = i32> + Send>,
}
@ -149,7 +149,7 @@ fn comparisons() {
py_assert!(py, zero, "not zero");
}
#[pyclass]
#[pyclass(mutable)]
#[derive(Debug)]
struct Sequence {
fields: Vec<String>,
@ -210,7 +210,7 @@ fn sequence() {
py_expect_exception!(py, c, "c['abc']", PyTypeError);
}
#[pyclass]
#[pyclass(mutable)]
#[derive(Debug)]
struct SetItem {
key: i32,
@ -240,7 +240,7 @@ fn setitem() {
py_expect_exception!(py, c, "del c[1]", PyNotImplementedError);
}
#[pyclass]
#[pyclass(mutable)]
struct DelItem {
key: i32,
}
@ -266,7 +266,7 @@ fn delitem() {
py_expect_exception!(py, c, "c[1] = 2", PyNotImplementedError);
}
#[pyclass]
#[pyclass(mutable)]
struct SetDelItem {
val: Option<i32>,
}
@ -338,7 +338,7 @@ fn contains() {
py_expect_exception!(py, c, "assert 'wrong type' not in c", PyTypeError);
}
#[pyclass]
#[pyclass(mutable)]
struct ContextManager {
exit_called: bool,
}
@ -507,7 +507,7 @@ fn weakref_dunder_dict_support() {
);
}
#[pyclass]
#[pyclass(mutable)]
struct ClassWithGetAttr {
#[pyo3(get, set)]
data: u32,
@ -530,7 +530,7 @@ fn getattr_doesnt_override_member() {
}
/// Wraps a Python future and yield it once.
#[pyclass]
#[pyclass(mutable)]
struct OnceFuture {
future: PyObject,
polled: bool,
@ -600,7 +600,7 @@ loop.close()
}
/// Increment the count when `__get__` is called.
#[pyclass]
#[pyclass(mutable)]
struct DescrCounter {
#[pyo3(get)]
count: usize,

View File

@ -8,7 +8,7 @@ mod common;
/// Assumes it's a file reader or so.
/// Inspired by https://github.com/jothan/cordoba, thanks.
#[pyclass]
#[pyclass(mutable)]
#[derive(Clone, Debug)]
struct Reader {
inner: HashMap<u8, String>,
@ -44,7 +44,7 @@ impl Reader {
}
}
#[pyclass]
#[pyclass(mutable)]
#[derive(Debug)]
struct Iter {
reader: Py<Reader>,

View File

@ -7,7 +7,7 @@ use pyo3::py_run;
mod common;
#[pyclass]
#[pyclass(mutable)]
struct ByteSequence {
elements: Vec<u8>,
}
@ -232,7 +232,7 @@ fn test_inplace_repeat() {
// Check that #[pyo3(get, set)] works correctly for Vec<PyObject>
#[pyclass]
#[pyclass(mutable)]
struct GenericList {
#[pyo3(get, set)]
items: Vec<PyObject>,
@ -265,7 +265,7 @@ fn test_generic_list_set() {
);
}
#[pyclass]
#[pyclass(mutable)]
struct OptionList {
#[pyo3(get, set)]
items: Vec<Option<i64>>,

View File

@ -6,7 +6,7 @@ use std::fmt;
mod common;
#[pyclass]
#[pyclass(mutable)]
struct MutRefArg {
n: i32,
}