From b3ca8c09226e7a0eec664f1a1428219420b39505 Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Mon, 18 Nov 2024 21:39:14 +0100 Subject: [PATCH] add second lifetime to `PyRef` and `PyRefMut` --- guide/src/class.md | 68 +-- guide/src/class/numeric.md | 16 +- guide/src/class/protocols.md | 498 +++++++++--------- pyo3-macros-backend/src/method.rs | 2 +- pyo3-macros-backend/src/pyclass.rs | 18 +- pyo3-macros-backend/src/pymethod.rs | 12 +- pytests/src/awaitable.rs | 10 +- src/conversion.rs | 8 +- src/impl_/extract_argument.rs | 54 +- src/impl_/pyclass.rs | 6 +- src/impl_/pymethods.rs | 10 +- src/instance.rs | 65 ++- src/marker.rs | 4 +- src/pycell.rs | 154 +++--- src/pycell/impl_.rs | 46 +- src/tests/hygiene/pymethods.rs | 29 +- src/types/any.rs | 2 +- tests/test_arithmetics.rs | 119 +++-- tests/test_buffer.rs | 2 +- tests/test_buffer_protocol.rs | 2 +- tests/test_class_conversion.rs | 12 +- tests/test_gc.rs | 2 +- tests/test_getter_setter.rs | 4 +- tests/test_methods.rs | 4 +- tests/test_proto_methods.rs | 20 +- tests/test_pyself.rs | 6 +- tests/test_sequence.rs | 4 +- tests/test_various.rs | 2 +- tests/ui/invalid_cancel_handle.stderr | 34 +- tests/ui/invalid_frozen_pyclass_borrow.stderr | 12 +- tests/ui/invalid_pymethod_enum.stderr | 16 +- tests/ui/invalid_pymethods.stderr | 2 +- 32 files changed, 666 insertions(+), 577 deletions(-) diff --git a/guide/src/class.md b/guide/src/class.md index 5d2c8435416..f265961ae26 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -2,7 +2,7 @@ PyO3 exposes a group of attributes powered by Rust's proc macro system for defining Python classes as Rust structs. -The main attribute is `#[pyclass]`, which is placed upon a Rust `struct` or `enum` to generate a Python type for it. They will usually also have *one* `#[pymethods]`-annotated `impl` block for the struct, which is used to define Python methods and constants for the generated Python type. (If the [`multiple-pymethods`] feature is enabled, each `#[pyclass]` is allowed to have multiple `#[pymethods]` blocks.) `#[pymethods]` may also have implementations for Python magic methods such as `__str__`. +The main attribute is `#[pyclass]`, which is placed upon a Rust `struct` or `enum` to generate a Python type for it. They will usually also have _one_ `#[pymethods]`-annotated `impl` block for the struct, which is used to define Python methods and constants for the generated Python type. (If the [`multiple-pymethods`] feature is enabled, each `#[pyclass]` is allowed to have multiple `#[pymethods]` blocks.) `#[pymethods]` may also have implementations for Python magic methods such as `__str__`. This chapter will discuss the functionality and configuration these attributes offer. Below is a list of links to the relevant section of this chapter for each: @@ -22,6 +22,7 @@ This chapter will discuss the functionality and configuration these attributes o ## Defining a new class To define a custom Python class, add the `#[pyclass]` attribute to a Rust struct or enum. + ```rust # #![allow(dead_code)] use pyo3::prelude::*; @@ -122,7 +123,8 @@ create_interface!(FloatClass, String); #### Must be thread-safe Python objects are freely shared between threads by the Python interpreter. This means that: -- Python objects may be created and destroyed by different Python threads; therefore #[pyclass]` objects must be `Send`. + +- Python objects may be created and destroyed by different Python threads; therefore #[pyclass]`objects must be`Send`. - Python objects may be accessed by multiple python threads simultaneously; therefore `#[pyclass]` objects must be `Sync`. For now, don't worry about these requirements; simple classes will already be thread-safe. There is a [detailed discussion on thread-safety](./class/thread-safety.md) later in the guide. @@ -202,13 +204,14 @@ fn my_module(m: &Bound<'_, PyModule>) -> PyResult<()> { Often is useful to turn a `#[pyclass]` type `T` into a Python object and access it from Rust code. The [`Py`] and [`Bound<'py, T>`] smart pointers are the ways to represent a Python object in PyO3's API. More detail can be found about them [in the Python objects](./types.md#pyo3s-smart-pointers) section of the guide. -Most Python objects do not offer exclusive (`&mut`) access (see the [section on Python's memory model](./python-from-rust.md#pythons-memory-model)). However, Rust structs wrapped as Python objects (called `pyclass` types) often *do* need `&mut` access. Due to the GIL, PyO3 *can* guarantee exclusive access to them. +Most Python objects do not offer exclusive (`&mut`) access (see the [section on Python's memory model](./python-from-rust.md#pythons-memory-model)). However, Rust structs wrapped as Python objects (called `pyclass` types) often _do_ need `&mut` access. Due to the GIL, PyO3 _can_ guarantee exclusive access to them. The Rust borrow checker cannot reason about `&mut` references once an object's ownership has been passed to the Python interpreter. This means that borrow checking is done at runtime using with a scheme very similar to `std::cell::RefCell`. This is known as [interior mutability](https://doc.rust-lang.org/book/ch15-05-interior-mutability.html). Users who are familiar with `RefCell` can use `Py` and `Bound<'py, T>` just like `RefCell`. For users who are not very familiar with `RefCell`, here is a reminder of Rust's rules of borrowing: + - At any given time, you can have either (but not both of) one mutable reference or any number of immutable references. - References can never outlast the data they refer to. @@ -309,11 +312,11 @@ Generally, `#[new]` methods have to return `T: Into>` o For constructors that may fail, you should wrap the return type in a PyResult as well. Consult the table below to determine which type your constructor should return: -| | **Cannot fail** | **May fail** | -|-----------------------------|---------------------------|-----------------------------------| -|**No inheritance** | `T` | `PyResult` | -|**Inheritance(T Inherits U)**| `(T, U)` | `PyResult<(T, U)>` | -|**Inheritance(General Case)**| [`PyClassInitializer`] | `PyResult>` | +| | **Cannot fail** | **May fail** | +| ----------------------------- | ------------------------- | --------------------------------- | +| **No inheritance** | `T` | `PyResult` | +| **Inheritance(T Inherits U)** | `(T, U)` | `PyResult<(T, U)>` | +| **Inheritance(General Case)** | [`PyClassInitializer`] | `PyResult>` | ## Inheritance @@ -323,7 +326,6 @@ Currently, only classes defined in Rust and builtins provided by PyO3 can be inh from; inheriting from other classes defined in Python is not yet supported ([#991](https://github.com/PyO3/pyo3/issues/991)). - For convenience, `(T, U)` implements `Into>` where `U` is the base class of `T`. But for a more deeply nested inheritance, you have to return `PyClassInitializer` @@ -370,7 +372,7 @@ impl SubClass { (SubClass { val2: 15 }, BaseClass::new()) } - fn method2(self_: PyRef<'_, Self>) -> PyResult { + fn method2(self_: PyRef<'_, '_, Self>) -> PyResult { let super_ = self_.as_super(); // Get &PyRef super_.method1().map(|x| x * self_.val2) } @@ -388,24 +390,24 @@ impl SubSubClass { PyClassInitializer::from(SubClass::new()).add_subclass(SubSubClass { val3: 20 }) } - fn method3(self_: PyRef<'_, Self>) -> PyResult { + fn method3(self_: PyRef<'_, '_, Self>) -> PyResult { let base = self_.as_super().as_super(); // Get &PyRef<'_, BaseClass> base.method1().map(|x| x * self_.val3) } - fn method4(self_: PyRef<'_, Self>) -> PyResult { + fn method4(self_: PyRef<'_, '_, Self>) -> PyResult { let v = self_.val3; let super_ = self_.into_super(); // Get PyRef<'_, SubClass> SubClass::method2(super_).map(|x| x * v) } - fn get_values(self_: PyRef<'_, Self>) -> (usize, usize, usize) { + fn get_values(self_: PyRef<'_, '_, Self>) -> (usize, usize, usize) { let val1 = self_.as_super().as_super().val1; let val2 = self_.as_super().val2; (val1, val2, self_.val3) } - fn double_values(mut self_: PyRefMut<'_, Self>) { + fn double_values(mut self_: PyRefMut<'_, '_, Self>) { self_.as_super().as_super().val1 *= 2; self_.as_super().val2 *= 2; self_.val3 *= 2; @@ -481,6 +483,7 @@ impl DictWithCounter { ``` If `SubClass` does not provide a base class initialization, the compilation fails. + ```rust,compile_fail # use pyo3::prelude::*; @@ -504,7 +507,7 @@ impl SubClass { ``` The `__new__` constructor of a native base class is called implicitly when -creating a new instance from Python. Be sure to accept arguments in the +creating a new instance from Python. Be sure to accept arguments in the `#[new]` method that you want the base class to get, even if they are not used in that `fn`: @@ -542,6 +545,7 @@ initial items, such as `MyDict(item_sequence)` or `MyDict(a=1, b=2)`. ## Object properties PyO3 supports two ways to add properties to your `#[pyclass]`: + - For simple struct fields with no side effects, a `#[pyo3(get, set)]` attribute can be added directly to the field definition in the `#[pyclass]`. - For properties which require computation you can define `#[getter]` and `#[setter]` functions in the [`#[pymethods]`](#instance-methods) block. @@ -566,6 +570,7 @@ The above would make the `num` field available for reading and writing as a `sel Properties can be readonly or writeonly by using just `#[pyo3(get)]` or `#[pyo3(set)]` respectively. To use these annotations, your field type must implement some conversion traits: + - For `get` the field type must implement both `IntoPy` and `Clone`. - For `set` the field type must implement `FromPyObject`. @@ -733,15 +738,16 @@ impl MyClass { Declares a class method callable from Python. -* The first parameter is the type object of the class on which the method is called. +- The first parameter is the type object of the class on which the method is called. This may be the type object of a derived class. -* The first parameter implicitly has type `&Bound<'_, PyType>`. -* For details on `parameter-list`, see the documentation of `Method arguments` section. -* The return type must be `PyResult` or `T` for some `T` that implements `IntoPy`. +- The first parameter implicitly has type `&Bound<'_, PyType>`. +- For details on `parameter-list`, see the documentation of `Method arguments` section. +- The return type must be `PyResult` or `T` for some `T` that implements `IntoPy`. ### Constructors which accept a class argument To create a constructor which takes a positional class argument, you can combine the `#[classmethod]` and `#[new]` modifiers: + ```rust # #![allow(dead_code)] # use pyo3::prelude::*; @@ -807,7 +813,7 @@ Python::with_gil(|py| { ``` > Note: if the method has a `Result` return type and returns an `Err`, PyO3 will panic during -class creation. +> class creation. If the class attribute is defined with `const` code only, one can also annotate associated constants: @@ -844,7 +850,7 @@ fn increment_field(my_class: &mut MyClass) { // Take a reference wrapper when borrowing should be automatic, // but interaction with the underlying `Bound` is desired. #[pyfunction] -fn print_field(my_class: PyRef<'_, MyClass>) { +fn print_field(my_class: PyRef<'_, '_, MyClass>) { println!("{}", my_class.my_field); } @@ -1193,7 +1199,7 @@ Python::with_gil(|py| { ``` Ordering of enum variants is optionally added using `#[pyo3(ord)]`. -*Note: Implementation of the `PartialOrd` trait is required when passing the `ord` argument. If not implemented, a compile time error is raised.* +_Note: Implementation of the `PartialOrd` trait is required when passing the `ord` argument. If not implemented, a compile time error is raised._ ```rust # use pyo3::prelude::*; @@ -1390,22 +1396,22 @@ impl pyo3::PyClass for MyClass { type Frozen = pyo3::pyclass::boolean_struct::False; } -impl<'a, 'py> pyo3::impl_::extract_argument::PyFunctionArgument<'a, 'py> for &'a MyClass +impl<'a, 'holder, 'py> pyo3::impl_::extract_argument::PyFunctionArgument<'a, 'holder, 'py> for &'holder MyClass { - type Holder = ::std::option::Option>; + type Holder = ::std::option::Option>; #[inline] - fn extract(obj: &'a pyo3::Bound<'py, PyAny>, holder: &'a mut Self::Holder) -> pyo3::PyResult { + fn extract(obj: &'a pyo3::Bound<'py, PyAny>, holder: &'holder mut Self::Holder) -> pyo3::PyResult { pyo3::impl_::extract_argument::extract_pyclass_ref(obj, holder) } } -impl<'a, 'py> pyo3::impl_::extract_argument::PyFunctionArgument<'a, 'py> for &'a mut MyClass +impl<'a, 'holder, 'py> pyo3::impl_::extract_argument::PyFunctionArgument<'a, 'holder, 'py> for &'holder mut MyClass { - type Holder = ::std::option::Option>; + type Holder = ::std::option::Option>; #[inline] - fn extract(obj: &'a pyo3::Bound<'py, PyAny>, holder: &'a mut Self::Holder) -> pyo3::PyResult { + fn extract(obj: &'a pyo3::Bound<'py, PyAny>, holder: &'holder mut Self::Holder) -> pyo3::PyResult { pyo3::impl_::extract_argument::extract_pyclass_ref_mut(obj, holder) } } @@ -1459,22 +1465,16 @@ impl pyo3::impl_::pyclass::PyClassImpl for MyClass { # } ``` - [`PyTypeInfo`]: {{#PYO3_DOCS_URL}}/pyo3/type_object/trait.PyTypeInfo.html - [`Py`]: {{#PYO3_DOCS_URL}}/pyo3/struct.Py.html [`Bound<'_, T>`]: {{#PYO3_DOCS_URL}}/pyo3/struct.Bound.html [`PyClass`]: {{#PYO3_DOCS_URL}}/pyo3/pyclass/trait.PyClass.html [`PyRef`]: {{#PYO3_DOCS_URL}}/pyo3/pycell/struct.PyRef.html [`PyRefMut`]: {{#PYO3_DOCS_URL}}/pyo3/pycell/struct.PyRefMut.html [`PyClassInitializer`]: {{#PYO3_DOCS_URL}}/pyo3/pyclass_init/struct.PyClassInitializer.html - [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html [`RefCell`]: https://doc.rust-lang.org/std/cell/struct.RefCell.html - [classattr]: https://docs.python.org/3/tutorial/classes.html#class-and-instance-variables - [`multiple-pymethods`]: features.md#multiple-pymethods - [lifetime-elision]: https://doc.rust-lang.org/reference/lifetime-elision.html [compiler-error-e0106]: https://doc.rust-lang.org/error_codes/E0106.html diff --git a/guide/src/class/numeric.md b/guide/src/class/numeric.md index a441eba4e13..38071c94487 100644 --- a/guide/src/class/numeric.md +++ b/guide/src/class/numeric.md @@ -3,11 +3,12 @@ At this point we have a `Number` class that we can't actually do any math on! Before proceeding, we should think about how we want to handle overflows. There are three obvious solutions: + - We can have infinite precision just like Python's `int`. However that would be quite boring - we'd - be reinventing the wheel. + be reinventing the wheel. - We can raise exceptions whenever `Number` overflows, but that makes the API painful to use. - We can wrap around the boundary of `i32`. This is the approach we'll take here. To do that we'll just forward to `i32`'s - `wrapping_*` methods. + `wrapping_*` methods. ### Fixing our constructor @@ -42,6 +43,7 @@ fn wrap(obj: &Bound<'_, PyAny>) -> PyResult { Ok(val as i32) } ``` + We also add documentation, via `///` comments, which are visible to Python users. ```rust @@ -68,8 +70,8 @@ impl Number { } ``` - With that out of the way, let's implement some operators: + ```rust use pyo3::exceptions::{PyZeroDivisionError, PyValueError}; @@ -132,7 +134,7 @@ impl Number { # #[pymethods] impl Number { - fn __pos__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __pos__<'a, 'py>(slf: PyRef<'a, 'py, Self>) -> PyRef<'a, 'py, Self> { slf } @@ -178,7 +180,7 @@ impl Number { We do not implement the in-place operations like `__iadd__` because we do not wish to mutate `Number`. Similarly we're not interested in supporting operations with different types, so we do not implement - the reflected operations like `__radd__` either. +the reflected operations like `__radd__` either. Now Python can use our `Number` class: @@ -405,13 +407,15 @@ function that does: unsigned long PyLong_AsUnsignedLongMask(PyObject *obj) ``` -We can call this function from Rust by using [`pyo3::ffi::PyLong_AsUnsignedLongMask`]. This is an *unsafe* +We can call this function from Rust by using [`pyo3::ffi::PyLong_AsUnsignedLongMask`]. This is an _unsafe_ function, which means we have to use an unsafe block to call it and take responsibility for upholding the contracts of this function. Let's review those contracts: + - The GIL must be held. If it's not, calling this function causes a data race. - The pointer must be valid, i.e. it must be properly aligned and point to a valid Python object. Let's create that helper function. The signature has to be `fn(&Bound<'_, PyAny>) -> PyResult`. + - `&Bound<'_, PyAny>` represents a checked borrowed reference, so the pointer derived from it is valid (and not null). - Whenever we have borrowed references to Python objects in scope, it is guaranteed that the GIL is held. This reference is also where we can get a [`Python`] token to use in our call to [`PyErr::take`]. diff --git a/guide/src/class/protocols.md b/guide/src/class/protocols.md index 8a361a1442e..bfd675fd9b3 100644 --- a/guide/src/class/protocols.md +++ b/guide/src/class/protocols.md @@ -3,6 +3,7 @@ Python's object model defines several protocols for different object behavior, such as the sequence, mapping, and number protocols. Python classes support these protocols by implementing "magic" methods, such as `__str__` or `__repr__`. Because of the double-underscores surrounding their name, these are also known as "dunder" methods. PyO3 makes it possible for every magic method to be implemented in `#[pymethods]` just as they would be done in a regular Python class, with a few notable differences: + - `__new__` and `__init__` are replaced by the [`#[new]` attribute](../class.md#constructor). - `__del__` is not yet supported, but may be in the future. - `__buffer__` and `__release_buffer__` are currently not supported and instead PyO3 supports [`__getbuffer__` and `__releasebuffer__`](#buffer-objects) methods (these predate [PEP 688](https://peps.python.org/pep-0688/#python-level-buffer-protocol)), again this may change in the future. @@ -18,138 +19,141 @@ If a function name in `#[pymethods]` is a magic method which is known to need sp The magic methods handled by PyO3 are very similar to the standard Python ones on [this page](https://docs.python.org/3/reference/datamodel.html#special-method-names) - in particular they are the subset which have slots as [defined here](https://docs.python.org/3/c-api/typeobj.html). When PyO3 handles a magic method, a couple of changes apply compared to other `#[pymethods]`: - - The Rust function signature is restricted to match the magic method. - - The `#[pyo3(signature = (...)]` and `#[pyo3(text_signature = "...")]` attributes are not allowed. - -The following sections list all magic methods for which PyO3 implements the necessary special handling. The -given signatures should be interpreted as follows: - - All methods take a receiver as first argument, shown as ``. It can be - `&self`, `&mut self` or a `Bound` reference like `self_: PyRef<'_, Self>` and - `self_: PyRefMut<'_, Self>`, as described [here](../class.md#inheritance). - - An optional `Python<'py>` argument is always allowed as the first argument. - - Return values can be optionally wrapped in `PyResult`. - - `object` means that any type is allowed that can be extracted from a Python - object (if argument) or converted to a Python object (if return value). - - Other types must match what's given, e.g. `pyo3::basic::CompareOp` for - `__richcmp__`'s second argument. - - For the comparison and arithmetic methods, extraction errors are not - propagated as exceptions, but lead to a return of `NotImplemented`. - - For some magic methods, the return values are not restricted by PyO3, but - checked by the Python interpreter. For example, `__str__` needs to return a - string object. This is indicated by `object (Python type)`. - -### Basic object customization - - - `__str__() -> object (str)` - - `__repr__() -> object (str)` - - - `__hash__() -> isize` - - Objects that compare equal must have the same hash value. Any type up to 64 bits may be returned instead of `isize`, PyO3 will convert to an isize automatically (wrapping unsigned types like `u64` and `usize`). -
- Disabling Python's default hash - By default, all `#[pyclass]` types have a default hash implementation from Python. Types which should not be hashable can override this by setting `__hash__` to `None`. This is the same mechanism as for a pure-Python class. This is done like so: - - ```rust - # use pyo3::prelude::*; - # - #[pyclass] - struct NotHashable {} - - #[pymethods] - impl NotHashable { - #[classattr] - const __hash__: Option = None; - } - ``` -
- - - `__lt__(, object) -> object` - - `__le__(, object) -> object` - - `__eq__(, object) -> object` - - `__ne__(, object) -> object` - - `__gt__(, object) -> object` - - `__ge__(, object) -> object` - - The implementations of Python's "rich comparison" operators `<`, `<=`, `==`, `!=`, `>` and `>=` respectively. - - _Note that implementing any of these methods will cause Python not to generate a default `__hash__` implementation, so consider also implementing `__hash__`._ -
- Return type - The return type will normally be `bool` or `PyResult`, however any Python object can be returned. -
- - - `__richcmp__(, object, pyo3::basic::CompareOp) -> object` - - Implements Python comparison operations (`==`, `!=`, `<`, `<=`, `>`, and `>=`) in a single method. - The `CompareOp` argument indicates the comparison operation being performed. You can use - [`CompareOp::matches`] to adapt a Rust `std::cmp::Ordering` result to the requested comparison. - - _This method cannot be implemented in combination with any of `__lt__`, `__le__`, `__eq__`, `__ne__`, `__gt__`, or `__ge__`._ - - _Note that implementing `__richcmp__` will cause Python not to generate a default `__hash__` implementation, so consider implementing `__hash__` when implementing `__richcmp__`._ -
- Return type - The return type will normally be `PyResult`, but any Python object can be returned. - - If you want to leave some operations unimplemented, you can return `py.NotImplemented()` - for some of the operations: - - ```rust - use pyo3::class::basic::CompareOp; - use pyo3::types::PyNotImplemented; - - # use pyo3::prelude::*; - # use pyo3::BoundObject; - # - # #[pyclass] - # struct Number(i32); - # - #[pymethods] - impl Number { - fn __richcmp__<'py>(&self, other: &Self, op: CompareOp, py: Python<'py>) -> PyResult> { - match op { - CompareOp::Eq => Ok((self.0 == other.0).into_pyobject(py)?.into_any()), - CompareOp::Ne => Ok((self.0 != other.0).into_pyobject(py)?.into_any()), - _ => Ok(PyNotImplemented::get(py).into_any()), - } - } - } - ``` - - If the second argument `object` is not of the type specified in the - signature, the generated code will automatically `return NotImplemented`. -
- - - `__getattr__(, object) -> object` - - `__getattribute__(, object) -> object` -
- Differences between `__getattr__` and `__getattribute__` - As in Python, `__getattr__` is only called if the attribute is not found - by normal attribute lookup. `__getattribute__`, on the other hand, is - called for *every* attribute access. If it wants to access existing - attributes on `self`, it needs to be very careful not to introduce - infinite recursion, and use `baseclass.__getattribute__()`. -
- - `__setattr__(, value: object) -> ()` - - `__delattr__(, object) -> ()` +- The Rust function signature is restricted to match the magic method. +- The `#[pyo3(signature = (...)]` and `#[pyo3(text_signature = "...")]` attributes are not allowed. - Overrides attribute access. +The following sections list all magic methods for which PyO3 implements the necessary special handling. The +given signatures should be interpreted as follows: - - `__bool__() -> bool` +- All methods take a receiver as first argument, shown as ``. It can be + `&self`, `&mut self` or a `Bound` reference like `self_: PyRef<'_, Self>` and + `self_: PyRefMut<'_, Self>`, as described [here](../class.md#inheritance). +- An optional `Python<'py>` argument is always allowed as the first argument. +- Return values can be optionally wrapped in `PyResult`. +- `object` means that any type is allowed that can be extracted from a Python + object (if argument) or converted to a Python object (if return value). +- Other types must match what's given, e.g. `pyo3::basic::CompareOp` for + `__richcmp__`'s second argument. +- For the comparison and arithmetic methods, extraction errors are not + propagated as exceptions, but lead to a return of `NotImplemented`. +- For some magic methods, the return values are not restricted by PyO3, but + checked by the Python interpreter. For example, `__str__` needs to return a + string object. This is indicated by `object (Python type)`. - Determines the "truthyness" of an object. +### Basic object customization - - `__call__(, ...) -> object` - here, any argument list can be defined - as for normal `pymethods` +- `__str__() -> object (str)` +- `__repr__() -> object (str)` + +- `__hash__() -> isize` + + Objects that compare equal must have the same hash value. Any type up to 64 bits may be returned instead of `isize`, PyO3 will convert to an isize automatically (wrapping unsigned types like `u64` and `usize`). +
+ Disabling Python's default hash + By default, all `#[pyclass]` types have a default hash implementation from Python. Types which should not be hashable can override this by setting `__hash__` to `None`. This is the same mechanism as for a pure-Python class. This is done like so: + + ```rust + # use pyo3::prelude::*; + # + #[pyclass] + struct NotHashable {} + + #[pymethods] + impl NotHashable { + #[classattr] + const __hash__: Option = None; + } + ``` + +
+ +- `__lt__(, object) -> object` +- `__le__(, object) -> object` +- `__eq__(, object) -> object` +- `__ne__(, object) -> object` +- `__gt__(, object) -> object` +- `__ge__(, object) -> object` + + The implementations of Python's "rich comparison" operators `<`, `<=`, `==`, `!=`, `>` and `>=` respectively. + + _Note that implementing any of these methods will cause Python not to generate a default `__hash__` implementation, so consider also implementing `__hash__`._ +
+ Return type + The return type will normally be `bool` or `PyResult`, however any Python object can be returned. +
+ +- `__richcmp__(, object, pyo3::basic::CompareOp) -> object` + + Implements Python comparison operations (`==`, `!=`, `<`, `<=`, `>`, and `>=`) in a single method. + The `CompareOp` argument indicates the comparison operation being performed. You can use + [`CompareOp::matches`] to adapt a Rust `std::cmp::Ordering` result to the requested comparison. + + _This method cannot be implemented in combination with any of `__lt__`, `__le__`, `__eq__`, `__ne__`, `__gt__`, or `__ge__`._ + + _Note that implementing `__richcmp__` will cause Python not to generate a default `__hash__` implementation, so consider implementing `__hash__` when implementing `__richcmp__`._ +
+ Return type + The return type will normally be `PyResult`, but any Python object can be returned. + + If you want to leave some operations unimplemented, you can return `py.NotImplemented()` + for some of the operations: + + ```rust + use pyo3::class::basic::CompareOp; + use pyo3::types::PyNotImplemented; + + # use pyo3::prelude::*; + # use pyo3::BoundObject; + # + # #[pyclass] + # struct Number(i32); + # + #[pymethods] + impl Number { + fn __richcmp__<'py>(&self, other: &Self, op: CompareOp, py: Python<'py>) -> PyResult> { + match op { + CompareOp::Eq => Ok((self.0 == other.0).into_pyobject(py)?.into_any()), + CompareOp::Ne => Ok((self.0 != other.0).into_pyobject(py)?.into_any()), + _ => Ok(PyNotImplemented::get(py).into_any()), + } + } + } + ``` + + If the second argument `object` is not of the type specified in the + signature, the generated code will automatically `return NotImplemented`. +
+ +- `__getattr__(, object) -> object` +- `__getattribute__(, object) -> object` +
+ Differences between `__getattr__` and `__getattribute__` + As in Python, `__getattr__` is only called if the attribute is not found + by normal attribute lookup. `__getattribute__`, on the other hand, is + called for *every* attribute access. If it wants to access existing + attributes on `self`, it needs to be very careful not to introduce + infinite recursion, and use `baseclass.__getattribute__()`. +
+ +- `__setattr__(, value: object) -> ()` +- `__delattr__(, object) -> ()` + + Overrides attribute access. + +- `__bool__() -> bool` + + Determines the "truthyness" of an object. + +- `__call__(, ...) -> object` - here, any argument list can be defined + as for normal `pymethods` ### Iterable objects Iterators can be defined using these methods: - - `__iter__() -> object` - - `__next__() -> Option or IterNextOutput` ([see details](#returning-a-value-from-iteration)) +- `__iter__() -> object` +- `__next__() -> Option or IterNextOutput` ([see details](#returning-a-value-from-iteration)) Returning `None` from `__next__` indicates that that there are no further items. @@ -167,17 +171,17 @@ struct MyIterator { #[pymethods] impl MyIterator { - fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __iter__<'a, 'py>(slf: PyRef<'a, 'py, Self>) -> PyRef<'a, 'py, Self> { slf } - fn __next__(slf: PyRefMut<'_, Self>) -> Option { + fn __next__(slf: PyRefMut<'_, '_, Self>) -> Option { slf.iter.lock().unwrap().next() } } ``` In many cases you'll have a distinction between the type being iterated over -(i.e. the *iterable*) and the iterator it provides. In this case, the iterable +(i.e. the _iterable_) and the iterator it provides. In this case, the iterable only needs to implement `__iter__()` while the iterator must implement both `__iter__()` and `__next__()`. For example: @@ -191,11 +195,11 @@ struct Iter { #[pymethods] impl Iter { - fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __iter__<'a, 'py>(slf: PyRef<'a, 'py, Self>) -> PyRef<'a, 'py, Self> { slf } - fn __next__(mut slf: PyRefMut<'_, Self>) -> Option { + fn __next__(mut slf: PyRefMut<'_, '_, Self>) -> Option { slf.inner.next() } } @@ -207,7 +211,7 @@ struct Container { #[pymethods] impl Container { - fn __iter__(slf: PyRef<'_, Self>) -> PyResult> { + fn __iter__(slf: PyRef<'_, '_, Self>) -> PyResult> { let iter = Iter { inner: slf.iter.clone().into_iter(), }; @@ -229,15 +233,15 @@ documentation](https://docs.python.org/library/stdtypes.html#iterator-types). #### Returning a value from iteration This guide has so far shown how to use `Option` to implement yielding values -during iteration. In Python a generator can also return a value. This is done by +during iteration. In Python a generator can also return a value. This is done by raising a `StopIteration` exception. To express this in Rust, return `PyResult::Err` with a `PyStopIteration` as the error. ### Awaitable objects - - `__await__() -> object` - - `__aiter__() -> object` - - `__anext__() -> Option` +- `__await__() -> object` +- `__aiter__() -> object` +- `__anext__() -> Option` ### Mapping & Sequence types @@ -248,6 +252,7 @@ The Python C-API which PyO3 is built upon has separate "slots" for sequences and By default PyO3 reproduces the Python behaviour of filling both mapping and sequence slots. This makes sense for the "simple" case which matches Python, and also for sequences, where the mapping slot is used anyway to implement slice indexing. Mapping types usually will not want the sequence slots filled. Having them filled will lead to outcomes which may be unwanted, such as: + - The mapping type will successfully cast to [`PySequence`]. This may lead to consumers of the type handling it incorrectly. - Python provides a default implementation of `__iter__` for sequences, which calls `__getitem__` with consecutive positive integers starting from 0 until an `IndexError` is returned. Unless the mapping only contains consecutive positive integer keys, this `__iter__` implementation will likely not be the intended behavior. @@ -255,91 +260,92 @@ Use the `#[pyclass(mapping)]` annotation to instruct PyO3 to only fill the mappi Use the `#[pyclass(sequence)]` annotation to instruct PyO3 to fill the `sq_length` slot instead of the `mp_length` slot for `__len__`. This will help libraries such as `numpy` recognise the class as a sequence, however will also cause CPython to automatically add the sequence length to any negative indices before passing them to `__getitem__`. (`__getitem__`, `__setitem__` and `__delitem__` mapping slots are still used for sequences, for slice operations.) - - `__len__() -> usize` +- `__len__() -> usize` - Implements the built-in function `len()`. + Implements the built-in function `len()`. - - `__contains__(, object) -> bool` +- `__contains__(, object) -> bool` - Implements membership test operators. - Should return true if `item` is in `self`, false otherwise. - For objects that don’t define `__contains__()`, the membership test simply - traverses the sequence until it finds a match. + Implements membership test operators. + Should return true if `item` is in `self`, false otherwise. + For objects that don’t define `__contains__()`, the membership test simply + traverses the sequence until it finds a match. -
- Disabling Python's default contains +
+ Disabling Python's default contains - By default, all `#[pyclass]` types with an `__iter__` method support a - default implementation of the `in` operator. Types which do not want this - can override this by setting `__contains__` to `None`. This is the same - mechanism as for a pure-Python class. This is done like so: + By default, all `#[pyclass]` types with an `__iter__` method support a + default implementation of the `in` operator. Types which do not want this + can override this by setting `__contains__` to `None`. This is the same + mechanism as for a pure-Python class. This is done like so: - ```rust - # use pyo3::prelude::*; - # - #[pyclass] - struct NoContains {} + ```rust + # use pyo3::prelude::*; + # + #[pyclass] + struct NoContains {} - #[pymethods] - impl NoContains { - #[classattr] - const __contains__: Option = None; - } - ``` -
+ #[pymethods] + impl NoContains { + #[classattr] + const __contains__: Option = None; + } + ``` + +
- - `__getitem__(, object) -> object` +- `__getitem__(, object) -> object` - Implements retrieval of the `self[a]` element. + Implements retrieval of the `self[a]` element. - *Note:* Negative integer indexes are not handled specially by PyO3. - However, for classes with `#[pyclass(sequence)]`, when a negative index is - accessed via `PySequence::get_item`, the underlying C API already adjusts - the index to be positive. + _Note:_ Negative integer indexes are not handled specially by PyO3. + However, for classes with `#[pyclass(sequence)]`, when a negative index is + accessed via `PySequence::get_item`, the underlying C API already adjusts + the index to be positive. - - `__setitem__(, object, object) -> ()` +- `__setitem__(, object, object) -> ()` - Implements assignment to the `self[a]` element. - Should only be implemented if elements can be replaced. + Implements assignment to the `self[a]` element. + Should only be implemented if elements can be replaced. - Same behavior regarding negative indices as for `__getitem__`. + Same behavior regarding negative indices as for `__getitem__`. - - `__delitem__(, object) -> ()` +- `__delitem__(, object) -> ()` - Implements deletion of the `self[a]` element. - Should only be implemented if elements can be deleted. + Implements deletion of the `self[a]` element. + Should only be implemented if elements can be deleted. - Same behavior regarding negative indices as for `__getitem__`. + Same behavior regarding negative indices as for `__getitem__`. - * `fn __concat__(&self, other: impl FromPyObject) -> PyResult` +* `fn __concat__(&self, other: impl FromPyObject) -> PyResult` - Concatenates two sequences. - Used by the `+` operator, after trying the numeric addition via - the `__add__` and `__radd__` methods. + Concatenates two sequences. + Used by the `+` operator, after trying the numeric addition via + the `__add__` and `__radd__` methods. - * `fn __repeat__(&self, count: isize) -> PyResult` +* `fn __repeat__(&self, count: isize) -> PyResult` - Repeats the sequence `count` times. - Used by the `*` operator, after trying the numeric multiplication via - the `__mul__` and `__rmul__` methods. + Repeats the sequence `count` times. + Used by the `*` operator, after trying the numeric multiplication via + the `__mul__` and `__rmul__` methods. - * `fn __inplace_concat__(&self, other: impl FromPyObject) -> PyResult` +* `fn __inplace_concat__(&self, other: impl FromPyObject) -> PyResult` - Concatenates two sequences. - Used by the `+=` operator, after trying the numeric addition via - the `__iadd__` method. + Concatenates two sequences. + Used by the `+=` operator, after trying the numeric addition via + the `__iadd__` method. - * `fn __inplace_repeat__(&self, count: isize) -> PyResult` +* `fn __inplace_repeat__(&self, count: isize) -> PyResult` - Concatenates two sequences. - Used by the `*=` operator, after trying the numeric multiplication via - the `__imul__` method. + Concatenates two sequences. + Used by the `*=` operator, after trying the numeric multiplication via + the `__imul__` method. ### Descriptors - - `__get__(, object, object) -> object` - - `__set__(, object, object) -> ()` - - `__delete__(, object) -> ()` +- `__get__(, object, object) -> object` +- `__set__(, object, object) -> ()` +- `__delete__(, object) -> ()` ### Numeric types @@ -349,84 +355,84 @@ Binary arithmetic operations (`+`, `-`, `*`, `@`, `/`, `//`, `%`, `divmod()`, (If the `object` is not of the type specified in the signature, the generated code will automatically `return NotImplemented`.) - - `__add__(, object) -> object` - - `__radd__(, object) -> object` - - `__sub__(, object) -> object` - - `__rsub__(, object) -> object` - - `__mul__(, object) -> object` - - `__rmul__(, object) -> object` - - `__matmul__(, object) -> object` - - `__rmatmul__(, object) -> object` - - `__floordiv__(, object) -> object` - - `__rfloordiv__(, object) -> object` - - `__truediv__(, object) -> object` - - `__rtruediv__(, object) -> object` - - `__divmod__(, object) -> object` - - `__rdivmod__(, object) -> object` - - `__mod__(, object) -> object` - - `__rmod__(, object) -> object` - - `__lshift__(, object) -> object` - - `__rlshift__(, object) -> object` - - `__rshift__(, object) -> object` - - `__rrshift__(, object) -> object` - - `__and__(, object) -> object` - - `__rand__(, object) -> object` - - `__xor__(, object) -> object` - - `__rxor__(, object) -> object` - - `__or__(, object) -> object` - - `__ror__(, object) -> object` - - `__pow__(, object, object) -> object` - - `__rpow__(, object, object) -> object` +- `__add__(, object) -> object` +- `__radd__(, object) -> object` +- `__sub__(, object) -> object` +- `__rsub__(, object) -> object` +- `__mul__(, object) -> object` +- `__rmul__(, object) -> object` +- `__matmul__(, object) -> object` +- `__rmatmul__(, object) -> object` +- `__floordiv__(, object) -> object` +- `__rfloordiv__(, object) -> object` +- `__truediv__(, object) -> object` +- `__rtruediv__(, object) -> object` +- `__divmod__(, object) -> object` +- `__rdivmod__(, object) -> object` +- `__mod__(, object) -> object` +- `__rmod__(, object) -> object` +- `__lshift__(, object) -> object` +- `__rlshift__(, object) -> object` +- `__rshift__(, object) -> object` +- `__rrshift__(, object) -> object` +- `__and__(, object) -> object` +- `__rand__(, object) -> object` +- `__xor__(, object) -> object` +- `__rxor__(, object) -> object` +- `__or__(, object) -> object` +- `__ror__(, object) -> object` +- `__pow__(, object, object) -> object` +- `__rpow__(, object, object) -> object` In-place assignment operations (`+=`, `-=`, `*=`, `@=`, `/=`, `//=`, `%=`, `**=`, `<<=`, `>>=`, `&=`, `^=`, `|=`): - - `__iadd__(, object) -> ()` - - `__isub__(, object) -> ()` - - `__imul__(, object) -> ()` - - `__imatmul__(, object) -> ()` - - `__itruediv__(, object) -> ()` - - `__ifloordiv__(, object) -> ()` - - `__imod__(, object) -> ()` - - `__ipow__(, object, object) -> ()` - - `__ilshift__(, object) -> ()` - - `__irshift__(, object) -> ()` - - `__iand__(, object) -> ()` - - `__ixor__(, object) -> ()` - - `__ior__(, object) -> ()` +- `__iadd__(, object) -> ()` +- `__isub__(, object) -> ()` +- `__imul__(, object) -> ()` +- `__imatmul__(, object) -> ()` +- `__itruediv__(, object) -> ()` +- `__ifloordiv__(, object) -> ()` +- `__imod__(, object) -> ()` +- `__ipow__(, object, object) -> ()` +- `__ilshift__(, object) -> ()` +- `__irshift__(, object) -> ()` +- `__iand__(, object) -> ()` +- `__ixor__(, object) -> ()` +- `__ior__(, object) -> ()` Unary operations (`-`, `+`, `abs()` and `~`): - - `__pos__() -> object` - - `__neg__() -> object` - - `__abs__() -> object` - - `__invert__() -> object` +- `__pos__() -> object` +- `__neg__() -> object` +- `__abs__() -> object` +- `__invert__() -> object` Coercions: - - `__index__() -> object (int)` - - `__int__() -> object (int)` - - `__float__() -> object (float)` +- `__index__() -> object (int)` +- `__int__() -> object (int)` +- `__float__() -> object (float)` ### Buffer objects - - `__getbuffer__(, *mut ffi::Py_buffer, flags) -> ()` - - `__releasebuffer__(, *mut ffi::Py_buffer) -> ()` - Errors returned from `__releasebuffer__` will be sent to `sys.unraiseablehook`. It is strongly advised to never return an error from `__releasebuffer__`, and if it really is necessary, to make best effort to perform any required freeing operations before returning. `__releasebuffer__` will not be called a second time; anything not freed will be leaked. +- `__getbuffer__(, *mut ffi::Py_buffer, flags) -> ()` +- `__releasebuffer__(, *mut ffi::Py_buffer) -> ()` + Errors returned from `__releasebuffer__` will be sent to `sys.unraiseablehook`. It is strongly advised to never return an error from `__releasebuffer__`, and if it really is necessary, to make best effort to perform any required freeing operations before returning. `__releasebuffer__` will not be called a second time; anything not freed will be leaked. ### Garbage Collector Integration If your type owns references to other Python objects, you will need to integrate -with Python's garbage collector so that the GC is aware of those references. To -do this, implement the two methods `__traverse__` and `__clear__`. These +with Python's garbage collector so that the GC is aware of those references. To +do this, implement the two methods `__traverse__` and `__clear__`. These correspond to the slots `tp_traverse` and `tp_clear` in the Python C API. `__traverse__` must call `visit.call()` for each reference to another Python -object. `__clear__` must clear out any mutable references to other Python +object. `__clear__` must clear out any mutable references to other Python objects (thus breaking reference cycles). Immutable references do not have to be cleared, as every cycle must contain at least one mutable reference. - - `__traverse__(, pyo3::class::gc::PyVisit<'_>) -> Result<(), pyo3::class::gc::PyTraverseError>` - - `__clear__() -> ()` +- `__traverse__(, pyo3::class::gc::PyVisit<'_>) -> Result<(), pyo3::class::gc::PyTraverseError>` +- `__clear__() -> ()` Example: diff --git a/pyo3-macros-backend/src/method.rs b/pyo3-macros-backend/src/method.rs index f99e64562b7..5c887ce3665 100644 --- a/pyo3-macros-backend/src/method.rs +++ b/pyo3-macros-backend/src/method.rs @@ -1078,7 +1078,7 @@ fn parse_method_attributes(attrs: &mut Vec) -> Result` or `slf: PyRefMut<'_, '_, Self>`."; fn ensure_signatures_on_valid_method( fn_type: &FnType, diff --git a/pyo3-macros-backend/src/pyclass.rs b/pyo3-macros-backend/src/pyclass.rs index 87d02c6f878..919cfd6bb67 100644 --- a/pyo3-macros-backend/src/pyclass.rs +++ b/pyo3-macros-backend/src/pyclass.rs @@ -2110,34 +2110,34 @@ impl<'a> PyClassImplsBuilder<'a> { let cls = self.cls; if self.attr.options.frozen.is_some() { quote! { - impl<'a, 'py> #pyo3_path::impl_::extract_argument::PyFunctionArgument<'a, 'py> for &'a #cls + impl<'a, 'holder, 'py> #pyo3_path::impl_::extract_argument::PyFunctionArgument<'a, 'holder, 'py> for &'holder #cls { - type Holder = ::std::option::Option<#pyo3_path::PyRef<'py, #cls>>; + type Holder = ::std::option::Option<#pyo3_path::PyRef<'a, 'py, #cls>>; #[inline] - fn extract(obj: &'a #pyo3_path::Bound<'py, #pyo3_path::PyAny>, holder: &'a mut Self::Holder) -> #pyo3_path::PyResult { + fn extract(obj: &'a #pyo3_path::Bound<'py, #pyo3_path::PyAny>, holder: &'holder mut Self::Holder) -> #pyo3_path::PyResult { #pyo3_path::impl_::extract_argument::extract_pyclass_ref(obj, holder) } } } } else { quote! { - impl<'a, 'py> #pyo3_path::impl_::extract_argument::PyFunctionArgument<'a, 'py> for &'a #cls + impl<'a, 'holder, 'py> #pyo3_path::impl_::extract_argument::PyFunctionArgument<'a, 'holder, 'py> for &'holder #cls { - type Holder = ::std::option::Option<#pyo3_path::PyRef<'py, #cls>>; + type Holder = ::std::option::Option<#pyo3_path::PyRef<'a, 'py, #cls>>; #[inline] - fn extract(obj: &'a #pyo3_path::Bound<'py, #pyo3_path::PyAny>, holder: &'a mut Self::Holder) -> #pyo3_path::PyResult { + fn extract(obj: &'a #pyo3_path::Bound<'py, #pyo3_path::PyAny>, holder: &'holder mut Self::Holder) -> #pyo3_path::PyResult { #pyo3_path::impl_::extract_argument::extract_pyclass_ref(obj, holder) } } - impl<'a, 'py> #pyo3_path::impl_::extract_argument::PyFunctionArgument<'a, 'py> for &'a mut #cls + impl<'a, 'holder, 'py> #pyo3_path::impl_::extract_argument::PyFunctionArgument<'a, 'holder, 'py> for &'holder mut #cls { - type Holder = ::std::option::Option<#pyo3_path::PyRefMut<'py, #cls>>; + type Holder = ::std::option::Option<#pyo3_path::PyRefMut<'a, 'py, #cls>>; #[inline] - fn extract(obj: &'a #pyo3_path::Bound<'py, #pyo3_path::PyAny>, holder: &'a mut Self::Holder) -> #pyo3_path::PyResult { + fn extract(obj: &'a #pyo3_path::Bound<'py, #pyo3_path::PyAny>, holder: &'holder mut Self::Holder) -> #pyo3_path::PyResult { #pyo3_path::impl_::extract_argument::extract_pyclass_ref_mut(obj, holder) } } diff --git a/pyo3-macros-backend/src/pymethod.rs b/pyo3-macros-backend/src/pymethod.rs index 560c3c9dcc1..41288b73b42 100644 --- a/pyo3-macros-backend/src/pymethod.rs +++ b/pyo3-macros-backend/src/pymethod.rs @@ -1140,7 +1140,7 @@ impl Ty { extract_error_mode, holders, arg, - quote! { #ident.as_ptr() }, + quote! { #ident }, ctx ), Ty::IPowModulo => extract_object( @@ -1465,6 +1465,15 @@ impl SlotFragmentDef { let method = syn::Ident::new(fragment, Span::call_site()); let wrapper_ident = format_ident!("__pymethod_{}__", fragment); let arg_types: &Vec<_> = &arguments.iter().map(|arg| arg.ffi_type(ctx)).collect(); + let nn = arguments + .iter() + .enumerate() + .filter(|(_, arg)| matches!(arg, Ty::NonNullObject)) + .map(|(i, _)| { + let i = format_ident!("arg{}", i); + quote! { let #i = #i.as_ptr(); } + }) + .collect::(); let arg_idents: &Vec<_> = &(0..arguments.len()) .map(|i| format_ident!("arg{}", i)) .collect(); @@ -1489,6 +1498,7 @@ impl SlotFragmentDef { #(#arg_idents: #arg_types),* ) -> #pyo3_path::PyResult<#ret_ty> { let _slf = _raw_slf; + #nn #holders #body } diff --git a/pytests/src/awaitable.rs b/pytests/src/awaitable.rs index fb04c33ed05..7e5ea3f6012 100644 --- a/pytests/src/awaitable.rs +++ b/pytests/src/awaitable.rs @@ -23,11 +23,11 @@ impl IterAwaitable { } } - fn __await__(pyself: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __await__<'a, 'py>(pyself: PyRef<'a, 'py, Self>) -> PyRef<'a, 'py, Self> { pyself } - fn __iter__(pyself: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __iter__<'a, 'py>(pyself: PyRef<'a, 'py, Self>) -> PyRef<'a, 'py, Self> { pyself } @@ -59,15 +59,15 @@ impl FutureAwaitable { } } - fn __await__(pyself: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __await__<'a, 'py>(pyself: PyRef<'a, 'py, Self>) -> PyRef<'a, 'py, Self> { pyself } - fn __iter__(pyself: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __iter__<'a, 'py>(pyself: PyRef<'a, 'py, Self>) -> PyRef<'a, 'py, Self> { pyself } - fn __next__(mut pyself: PyRefMut<'_, Self>) -> PyResult> { + fn __next__<'a, 'py>(mut pyself: PyRefMut<'a, 'py, Self>) -> PyResult> { match pyself.result { Some(_) => match pyself.result.take().unwrap() { Ok(v) => Err(PyStopIteration::new_err(v)), diff --git a/src/conversion.rs b/src/conversion.rs index a309a6d6e40..6c5998ede12 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -541,20 +541,20 @@ where } } -impl<'py, T> FromPyObject<'_, 'py> for PyRef<'py, T> +impl<'a, 'py, T> FromPyObject<'a, 'py> for PyRef<'a, 'py, T> where T: PyClass, { - fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'a, 'py, PyAny>) -> PyResult { obj.downcast::()?.try_borrow().map_err(Into::into) } } -impl<'py, T> FromPyObject<'_, 'py> for PyRefMut<'py, T> +impl<'a, 'py, T> FromPyObject<'a, 'py> for PyRefMut<'a, 'py, T> where T: PyClass, { - fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'a, 'py, PyAny>) -> PyResult { obj.downcast::()?.try_borrow_mut().map_err(Into::into) } } diff --git a/src/impl_/extract_argument.rs b/src/impl_/extract_argument.rs index 5be637ac39c..f238a664b9b 100644 --- a/src/impl_/extract_argument.rs +++ b/src/impl_/extract_argument.rs @@ -20,43 +20,45 @@ type PyArg<'py> = Borrowed<'py, 'py, PyAny>; /// will be dropped as soon as the pyfunction call ends. /// /// There exists a trivial blanket implementation for `T: FromPyObject` with `Holder = ()`. -pub trait PyFunctionArgument<'a, 'py>: Sized + 'a { +pub trait PyFunctionArgument<'a, 'holder, 'py>: Sized + 'holder { type Holder: FunctionArgumentHolder; - fn extract(obj: &'a Bound<'py, PyAny>, holder: &'a mut Self::Holder) -> PyResult; + fn extract(obj: &'a Bound<'py, PyAny>, holder: &'holder mut Self::Holder) -> PyResult; } -impl<'a, 'py, T> PyFunctionArgument<'a, 'py> for T +impl<'a, 'holder, 'py, T> PyFunctionArgument<'a, 'holder, 'py> for T where - T: FromPyObject<'a, 'py> + 'a, + T: FromPyObject<'a, 'py> + 'holder, { type Holder = (); #[inline] - fn extract(obj: &'a Bound<'py, PyAny>, _: &'a mut ()) -> PyResult { + fn extract(obj: &'a Bound<'py, PyAny>, _: &'holder mut ()) -> PyResult { obj.extract() } } -impl<'a, 'py, T: 'py> PyFunctionArgument<'a, 'py> for &'a Bound<'py, T> +impl<'a: 'holder, 'holder, 'py, T: 'py> PyFunctionArgument<'a, 'holder, 'py> + for &'holder Bound<'py, T> where T: PyTypeCheck, { type Holder = Option<()>; #[inline] - fn extract(obj: &'a Bound<'py, PyAny>, _: &'a mut Option<()>) -> PyResult { + fn extract(obj: &'a Bound<'py, PyAny>, _: &'holder mut Option<()>) -> PyResult { obj.downcast().map_err(Into::into) } } -impl<'a, 'py, T: 'py> PyFunctionArgument<'a, 'py> for Option<&'a Bound<'py, T>> +impl<'a: 'holder, 'holder, 'py, T: 'py> PyFunctionArgument<'a, 'holder, 'py> + for Option<&'holder Bound<'py, T>> where T: PyTypeCheck, { type Holder = (); #[inline] - fn extract(obj: &'a Bound<'py, PyAny>, _: &'a mut ()) -> PyResult { + fn extract(obj: &'a Bound<'py, PyAny>, _: &'holder mut ()) -> PyResult { if obj.is_none() { Ok(None) } else { @@ -66,13 +68,13 @@ where } #[cfg(all(Py_LIMITED_API, not(Py_3_10)))] -impl<'a> PyFunctionArgument<'a, '_> for &'a str { +impl<'a: 'holder, 'holder> PyFunctionArgument<'a, 'holder, '_> for &'holder str { type Holder = Option>; #[inline] fn extract( obj: &'a Bound<'_, PyAny>, - holder: &'a mut Option>, + holder: &'holder mut Option>, ) -> PyResult { Ok(holder.insert(obj.extract()?)) } @@ -93,30 +95,30 @@ impl FunctionArgumentHolder for Option { } #[inline] -pub fn extract_pyclass_ref<'a, 'py: 'a, T: PyClass>( +pub fn extract_pyclass_ref<'a, 'holder, 'py: 'a, T: PyClass>( obj: &'a Bound<'py, PyAny>, - holder: &'a mut Option>, -) -> PyResult<&'a T> { + holder: &'holder mut Option>, +) -> PyResult<&'holder T> { Ok(&*holder.insert(obj.extract()?)) } #[inline] -pub fn extract_pyclass_ref_mut<'a, 'py: 'a, T: PyClass>( +pub fn extract_pyclass_ref_mut<'a, 'holder, 'py: 'a, T: PyClass>( obj: &'a Bound<'py, PyAny>, - holder: &'a mut Option>, -) -> PyResult<&'a mut T> { + holder: &'holder mut Option>, +) -> PyResult<&'holder mut T> { Ok(&mut *holder.insert(obj.extract()?)) } /// The standard implementation of how PyO3 extracts a `#[pyfunction]` or `#[pymethod]` function argument. #[doc(hidden)] -pub fn extract_argument<'a, 'py, T>( +pub fn extract_argument<'a, 'holder, 'py, T>( obj: &'a Bound<'py, PyAny>, - holder: &'a mut T::Holder, + holder: &'holder mut T::Holder, arg_name: &str, ) -> PyResult where - T: PyFunctionArgument<'a, 'py>, + T: PyFunctionArgument<'a, 'holder, 'py>, { match PyFunctionArgument::extract(obj, holder) { Ok(value) => Ok(value), @@ -127,14 +129,14 @@ where /// Alternative to [`extract_argument`] used for `Option` arguments. This is necessary because Option<&T> /// does not implement `PyFunctionArgument` for `T: PyClass`. #[doc(hidden)] -pub fn extract_optional_argument<'a, 'py, T>( +pub fn extract_optional_argument<'a, 'holder, 'py, T>( obj: Option<&'a Bound<'py, PyAny>>, - holder: &'a mut T::Holder, + holder: &'holder mut T::Holder, arg_name: &str, default: fn() -> Option, ) -> PyResult> where - T: PyFunctionArgument<'a, 'py>, + T: PyFunctionArgument<'a, 'holder, 'py>, { match obj { Some(obj) => { @@ -151,14 +153,14 @@ where /// Alternative to [`extract_argument`] used when the argument has a default value provided by an annotation. #[doc(hidden)] -pub fn extract_argument_with_default<'a, 'py, T>( +pub fn extract_argument_with_default<'a, 'holder, 'py, T>( obj: Option<&'a Bound<'py, PyAny>>, - holder: &'a mut T::Holder, + holder: &'holder mut T::Holder, arg_name: &str, default: fn() -> T, ) -> PyResult where - T: PyFunctionArgument<'a, 'py>, + T: PyFunctionArgument<'a, 'holder, 'py>, { match obj { Some(obj) => extract_argument(obj, holder, arg_name), diff --git a/src/impl_/pyclass.rs b/src/impl_/pyclass.rs index 7bb61442ec5..de73ec1e6e9 100644 --- a/src/impl_/pyclass.rs +++ b/src/impl_/pyclass.rs @@ -1427,10 +1427,10 @@ impl> /// ensures `obj` is not mutably aliased #[inline] -unsafe fn ensure_no_mutable_alias<'py, ClassT: PyClass>( +unsafe fn ensure_no_mutable_alias<'a, 'py: 'a, ClassT: PyClass>( py: Python<'py>, - obj: &*mut ffi::PyObject, -) -> Result, PyBorrowError> { + obj: &'a *mut ffi::PyObject, +) -> Result, PyBorrowError> { BoundRef::ref_from_ptr(py, obj) .downcast_unchecked::() .try_borrow() diff --git a/src/impl_/pymethods.rs b/src/impl_/pymethods.rs index 58d0c93c240..76af9255a1c 100644 --- a/src/impl_/pymethods.rs +++ b/src/impl_/pymethods.rs @@ -651,7 +651,13 @@ impl<'a, 'py> BoundRef<'a, 'py, PyAny> { } } -impl<'a, 'py, T: PyClass> TryFrom> for PyRef<'py, T> { +impl<'a, 'py, T: PyClass> BoundRef<'a, 'py, T> { + pub fn try_borrow(self) -> Result, PyBorrowError> { + PyRef::try_borrow(self.0.as_borrowed()) + } +} + +impl<'a, 'py, T: PyClass> TryFrom> for PyRef<'a, 'py, T> { type Error = PyBorrowError; #[inline] fn try_from(value: BoundRef<'a, 'py, T>) -> Result { @@ -659,7 +665,7 @@ impl<'a, 'py, T: PyClass> TryFrom> for PyRef<'py, T> { } } -impl<'a, 'py, T: PyClass> TryFrom> for PyRefMut<'py, T> { +impl<'a, 'py, T: PyClass> TryFrom> for PyRefMut<'a, 'py, T> { type Error = PyBorrowMutError; #[inline] fn try_from(value: BoundRef<'a, 'py, T>) -> Result { diff --git a/src/instance.rs b/src/instance.rs index 3052a60ee23..05e6e575026 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -259,8 +259,8 @@ where /// [`try_borrow`](#method.try_borrow). #[inline] #[track_caller] - pub fn borrow(&self) -> PyRef<'py, T> { - PyRef::borrow(self) + pub fn borrow<'a>(&'a self) -> PyRef<'a, 'py, T> { + PyRef::borrow(self.as_borrowed()) } /// Mutably borrows the value `T`. @@ -294,11 +294,11 @@ where /// [`try_borrow_mut`](#method.try_borrow_mut). #[inline] #[track_caller] - pub fn borrow_mut(&self) -> PyRefMut<'py, T> + pub fn borrow_mut<'a>(&'a self) -> PyRefMut<'a, 'py, T> where T: PyClass, { - PyRefMut::borrow(self) + PyRefMut::borrow(self.as_borrowed()) } /// Attempts to immutably borrow the value `T`, returning an error if the value is currently mutably borrowed. @@ -309,8 +309,8 @@ where /// /// For frozen classes, the simpler [`get`][Self::get] is available. #[inline] - pub fn try_borrow(&self) -> Result, PyBorrowError> { - PyRef::try_borrow(self) + pub fn try_borrow<'a>(&'a self) -> Result, PyBorrowError> { + self.as_borrowed().try_borrow() } /// Attempts to mutably borrow the value `T`, returning an error if the value is currently borrowed. @@ -319,11 +319,11 @@ where /// /// This is the non-panicking variant of [`borrow_mut`](#method.borrow_mut). #[inline] - pub fn try_borrow_mut(&self) -> Result, PyBorrowMutError> + pub fn try_borrow_mut<'a>(&'a self) -> Result, PyBorrowMutError> where T: PyClass, { - PyRefMut::try_borrow(self) + PyRefMut::try_borrow(self.as_borrowed()) } /// Provide an immutable borrow of the value `T` without acquiring the GIL. @@ -706,6 +706,22 @@ impl<'a, 'py, T> Borrowed<'a, 'py, T> { { FromPyObject::extract(self.to_any()) } + + #[inline] + pub(crate) fn try_borrow(self) -> Result, PyBorrowError> + where + T: PyClass, + { + PyRef::try_borrow(self) + } + + #[inline] + pub(crate) fn try_borrow_mut(self) -> Result, PyBorrowMutError> + where + T: PyClass, + { + PyRefMut::try_borrow(self) + } } impl<'a, 'py> Borrowed<'a, 'py, PyAny> { @@ -1194,8 +1210,8 @@ where /// [`try_borrow`](#method.try_borrow). #[inline] #[track_caller] - pub fn borrow<'py>(&'py self, py: Python<'py>) -> PyRef<'py, T> { - self.bind(py).borrow() + pub fn borrow<'a, 'py>(&'a self, py: Python<'py>) -> PyRef<'a, 'py, T> { + PyRef::borrow(self.bind_borrowed(py)) } /// Mutably borrows the value `T`. @@ -1231,11 +1247,11 @@ where /// [`try_borrow_mut`](#method.try_borrow_mut). #[inline] #[track_caller] - pub fn borrow_mut<'py>(&'py self, py: Python<'py>) -> PyRefMut<'py, T> + pub fn borrow_mut<'a, 'py>(&'a self, py: Python<'py>) -> PyRefMut<'a, 'py, T> where T: PyClass, { - self.bind(py).borrow_mut() + PyRefMut::borrow(self.bind_borrowed(py)) } /// Attempts to immutably borrow the value `T`, returning an error if the value is currently mutably borrowed. @@ -1248,8 +1264,11 @@ where /// /// Equivalent to `self.bind(py).try_borrow()` - see [`Bound::try_borrow`]. #[inline] - pub fn try_borrow<'py>(&'py self, py: Python<'py>) -> Result, PyBorrowError> { - self.bind(py).try_borrow() + pub fn try_borrow<'a, 'py>( + &'a self, + py: Python<'py>, + ) -> Result, PyBorrowError> { + self.bind_borrowed(py).try_borrow() } /// Attempts to mutably borrow the value `T`, returning an error if the value is currently borrowed. @@ -1260,14 +1279,14 @@ where /// /// Equivalent to `self.bind(py).try_borrow_mut()` - see [`Bound::try_borrow_mut`]. #[inline] - pub fn try_borrow_mut<'py>( - &'py self, + pub fn try_borrow_mut<'a, 'py>( + &'a self, py: Python<'py>, - ) -> Result, PyBorrowMutError> + ) -> Result, PyBorrowMutError> where T: PyClass, { - self.bind(py).try_borrow_mut() + self.bind_borrowed(py).try_borrow_mut() } /// Provide an immutable borrow of the value `T` without acquiring the GIL. @@ -1833,20 +1852,20 @@ impl std::convert::From> for Py { } } -impl<'a, T> std::convert::From> for Py +impl<'py, T> std::convert::From> for Py where T: PyClass, { - fn from(pyref: PyRef<'a, T>) -> Self { + fn from(pyref: PyRef<'_, 'py, T>) -> Self { unsafe { Py::from_borrowed_ptr(pyref.py(), pyref.as_ptr()) } } } -impl<'a, T> std::convert::From> for Py +impl<'py, T> std::convert::From> for Py where T: PyClass, { - fn from(pyref: PyRefMut<'a, T>) -> Self { + fn from(pyref: PyRefMut<'_, 'py, T>) -> Self { unsafe { Py::from_borrowed_ptr(pyref.py(), pyref.as_ptr()) } } } @@ -1971,7 +1990,7 @@ impl PyObject { /// class_bound.borrow_mut().i += 1; /// /// // Alternatively you can get a `PyRefMut` directly - /// let class_ref: PyRefMut<'_, Class> = class.extract(py)?; + /// let class_ref: PyRefMut<'_, '_, Class> = class.extract(py)?; /// assert_eq!(class_ref.i, 1); /// Ok(()) /// }) diff --git a/src/marker.rs b/src/marker.rs index 5962b47b60b..3afca52da2e 100644 --- a/src/marker.rs +++ b/src/marker.rs @@ -281,8 +281,8 @@ mod nightly { // This means that PyString, PyList, etc all inherit !Ungil from this. impl !Ungil for crate::PyAny {} - impl !Ungil for crate::PyRef<'_, T> {} - impl !Ungil for crate::PyRefMut<'_, T> {} + impl !Ungil for crate::PyRef<'_, '_, T> {} + impl !Ungil for crate::PyRefMut<'_, '_, T> {} // FFI pointees impl !Ungil for crate::ffi::PyObject {} diff --git a/src/pycell.rs b/src/pycell.rs index c7e5226a292..b1b7c20ce88 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -97,7 +97,7 @@ //! //! // We borrow the guard and then dereference //! // it to get a mutable reference to Number -//! let mut guard: PyRefMut<'_, Number> = n.bind(py).borrow_mut(); +//! let mut guard: PyRefMut<'_, '_, Number> = n.bind(py).borrow_mut(); //! let n_mutable: &mut Number = &mut *guard; //! //! n_mutable.increment(); @@ -195,10 +195,8 @@ use crate::conversion::{AsPyPointer, IntoPyObject}; use crate::exceptions::PyRuntimeError; -use crate::ffi_ptr_ext::FfiPtrExt; use crate::internal_tricks::{ptr_from_mut, ptr_from_ref}; use crate::pyclass::{boolean_struct::False, PyClass}; -use crate::types::any::PyAnyMethods; #[allow(deprecated)] use crate::IntoPy; use crate::{ffi, Borrowed, Bound, PyErr, PyObject, Python}; @@ -238,7 +236,7 @@ use impl_::{PyClassBorrowChecker, PyClassObjectLayout}; /// (Child { name: "Caterpillar" }, Parent { basename: "Butterfly" }) /// } /// -/// fn format(slf: PyRef<'_, Self>) -> String { +/// fn format(slf: PyRef<'_, '_, Self>) -> String { /// // We can get *mut ffi::PyObject from PyRef /// let refcnt = unsafe { pyo3::ffi::Py_REFCNT(slf.as_ptr()) }; /// // We can get &Self::BaseType by as_ref @@ -248,26 +246,24 @@ use impl_::{PyClassBorrowChecker, PyClassObjectLayout}; /// } /// # Python::with_gil(|py| { /// # let sub = Py::new(py, Child::new()).unwrap(); -/// # pyo3::py_run!(py, sub, "assert sub.format() == 'Caterpillar(base: Butterfly, cnt: 4)', sub.format()"); +/// # pyo3::py_run!(py, sub, "assert sub.format() == 'Caterpillar(base: Butterfly, cnt: 3)', sub.format()"); /// # }); /// ``` /// /// See the [module-level documentation](self) for more information. #[repr(transparent)] -pub struct PyRef<'p, T: PyClass> { - // TODO: once the GIL Ref API is removed, consider adding a lifetime parameter to `PyRef` to - // store `Borrowed` here instead, avoiding reference counting overhead. - inner: Bound<'p, T>, +pub struct PyRef<'a, 'py, T: PyClass> { + inner: Borrowed<'a, 'py, T>, } -impl<'p, T: PyClass> PyRef<'p, T> { +impl<'py, T: PyClass> PyRef<'_, 'py, T> { /// Returns a `Python` token that is bound to the lifetime of the `PyRef`. - pub fn py(&self) -> Python<'p> { + pub fn py(&self) -> Python<'py> { self.inner.py() } } -impl AsRef for PyRef<'_, T> +impl AsRef for PyRef<'_, '_, T> where T: PyClass, U: PyClass, @@ -277,7 +273,7 @@ where } } -impl<'py, T: PyClass> PyRef<'py, T> { +impl<'a, 'py, T: PyClass> PyRef<'a, 'py, T> { /// Returns the raw FFI pointer represented by self. /// /// # Safety @@ -299,24 +295,24 @@ impl<'py, T: PyClass> PyRef<'py, T> { /// of the pointer or decrease the reference count (e.g. with [`pyo3::ffi::Py_DecRef`](crate::ffi::Py_DecRef)). #[inline] pub fn into_ptr(self) -> *mut ffi::PyObject { - self.inner.clone().into_ptr() + self.inner.to_owned().into_ptr() } #[track_caller] - pub(crate) fn borrow(obj: &Bound<'py, T>) -> Self { + pub(crate) fn borrow(obj: Borrowed<'a, 'py, T>) -> Self { Self::try_borrow(obj).expect("Already mutably borrowed") } - pub(crate) fn try_borrow(obj: &Bound<'py, T>) -> Result { + pub(crate) fn try_borrow(obj: Borrowed<'a, 'py, T>) -> Result { let cell = obj.get_class_object(); cell.ensure_threadsafe(); cell.borrow_checker() .try_borrow() - .map(|_| Self { inner: obj.clone() }) + .map(|_| Self { inner: obj }) } } -impl<'p, T, U> PyRef<'p, T> +impl<'a, 'py, T, U> PyRef<'a, 'py, T> where T: PyClass, U: PyClass, @@ -355,7 +351,7 @@ where /// .add_subclass(Base2 { name2: "base2" }) /// .add_subclass(Self { name3: "sub" }) /// } - /// fn name(slf: PyRef<'_, Self>) -> String { + /// fn name(slf: PyRef<'_, '_, Self>) -> String { /// let subname = slf.name3; /// let super_ = slf.into_super(); /// format!("{} {} {}", super_.as_ref().name1, super_.name2, subname) @@ -366,15 +362,9 @@ where /// # pyo3::py_run!(py, sub, "assert sub.name() == 'base1 base2 sub'") /// # }); /// ``` - pub fn into_super(self) -> PyRef<'p, U> { - let py = self.py(); + pub fn into_super(self) -> PyRef<'a, 'py, U> { PyRef { - inner: unsafe { - ManuallyDrop::new(self) - .as_ptr() - .assume_owned_unchecked(py) - .downcast_into_unchecked() - }, + inner: unsafe { ManuallyDrop::new(self).inner.to_any().downcast_unchecked() }, } } @@ -412,7 +402,7 @@ where /// fn sub_name_len(&self) -> usize { /// self.sub_name.len() /// } - /// fn format_name_lengths(slf: PyRef<'_, Self>) -> String { + /// fn format_name_lengths(slf: PyRef<'_, '_, Self>) -> String { /// format!("{} {}", slf.as_super().base_name_len(), slf.sub_name_len()) /// } /// } @@ -421,17 +411,17 @@ where /// # pyo3::py_run!(py, sub, "assert sub.format_name_lengths() == '9 8'") /// # }); /// ``` - pub fn as_super(&self) -> &PyRef<'p, U> { - let ptr = ptr_from_ref::>(&self.inner) - // `Bound` has the same layout as `Bound` - .cast::>() - // `Bound` has the same layout as `PyRef` - .cast::>(); + pub fn as_super(&self) -> &PyRef<'a, 'py, U> { + let ptr = ptr_from_ref::>(&self.inner) + // `Borrowed` has the same layout as `Borrowed` + .cast::>() + // `Borrowed` has the same layout as `PyRef` + .cast::>(); unsafe { &*ptr } } } -impl Deref for PyRef<'_, T> { +impl Deref for PyRef<'_, '_, T> { type Target = T; #[inline] @@ -440,7 +430,7 @@ impl Deref for PyRef<'_, T> { } } -impl Drop for PyRef<'_, T> { +impl Drop for PyRef<'_, '_, T> { fn drop(&mut self) { self.inner .get_class_object() @@ -450,46 +440,46 @@ impl Drop for PyRef<'_, T> { } #[allow(deprecated)] -impl IntoPy for PyRef<'_, T> { +impl IntoPy for PyRef<'_, '_, T> { fn into_py(self, py: Python<'_>) -> PyObject { unsafe { PyObject::from_borrowed_ptr(py, self.inner.as_ptr()) } } } #[allow(deprecated)] -impl IntoPy for &'_ PyRef<'_, T> { +impl IntoPy for &'_ PyRef<'_, '_, T> { fn into_py(self, py: Python<'_>) -> PyObject { unsafe { PyObject::from_borrowed_ptr(py, self.inner.as_ptr()) } } } -impl<'py, T: PyClass> IntoPyObject<'py> for PyRef<'py, T> { +impl<'a, 'py, T: PyClass> IntoPyObject<'py> for PyRef<'a, 'py, T> { type Target = T; - type Output = Bound<'py, T>; + type Output = Borrowed<'a, 'py, T>; type Error = Infallible; fn into_pyobject(self, _py: Python<'py>) -> Result { - Ok(self.inner.clone()) + Ok(self.inner) } } -impl<'a, 'py, T: PyClass> IntoPyObject<'py> for &'a PyRef<'py, T> { +impl<'a, 'py, T: PyClass> IntoPyObject<'py> for &PyRef<'a, 'py, T> { type Target = T; type Output = Borrowed<'a, 'py, T>; type Error = Infallible; fn into_pyobject(self, _py: Python<'py>) -> Result { - Ok(self.inner.as_borrowed()) + Ok(self.inner) } } -unsafe impl AsPyPointer for PyRef<'_, T> { +unsafe impl AsPyPointer for PyRef<'_, '_, T> { fn as_ptr(&self) -> *mut ffi::PyObject { self.inner.as_ptr() } } -impl fmt::Debug for PyRef<'_, T> { +impl fmt::Debug for PyRef<'_, '_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&**self, f) } @@ -499,20 +489,18 @@ impl fmt::Debug for PyRef<'_, T> { /// /// See the [module-level documentation](self) for more information. #[repr(transparent)] -pub struct PyRefMut<'p, T: PyClass> { - // TODO: once the GIL Ref API is removed, consider adding a lifetime parameter to `PyRef` to - // store `Borrowed` here instead, avoiding reference counting overhead. - inner: Bound<'p, T>, +pub struct PyRefMut<'a, 'py, T: PyClass> { + inner: Borrowed<'a, 'py, T>, } -impl<'p, T: PyClass> PyRefMut<'p, T> { +impl<'py, T: PyClass> PyRefMut<'_, 'py, T> { /// Returns a `Python` token that is bound to the lifetime of the `PyRefMut`. - pub fn py(&self) -> Python<'p> { + pub fn py(&self) -> Python<'py> { self.inner.py() } } -impl AsRef for PyRefMut<'_, T> +impl AsRef for PyRefMut<'_, '_, T> where T: PyClass, U: PyClass, @@ -522,7 +510,7 @@ where } } -impl AsMut for PyRefMut<'_, T> +impl AsMut for PyRefMut<'_, '_, T> where T: PyClass, U: PyClass, @@ -532,7 +520,7 @@ where } } -impl<'py, T: PyClass> PyRefMut<'py, T> { +impl<'a, 'py, T: PyClass> PyRefMut<'a, 'py, T> { /// Returns the raw FFI pointer represented by self. /// /// # Safety @@ -554,30 +542,30 @@ impl<'py, T: PyClass> PyRefMut<'py, T> { /// of the pointer or decrease the reference count (e.g. with [`pyo3::ffi::Py_DecRef`](crate::ffi::Py_DecRef)). #[inline] pub fn into_ptr(self) -> *mut ffi::PyObject { - self.inner.clone().into_ptr() + self.inner.to_owned().into_ptr() } #[inline] #[track_caller] - pub(crate) fn borrow(obj: &Bound<'py, T>) -> Self { + pub(crate) fn borrow(obj: Borrowed<'a, 'py, T>) -> Self { Self::try_borrow(obj).expect("Already borrowed") } - pub(crate) fn try_borrow(obj: &Bound<'py, T>) -> Result { + pub(crate) fn try_borrow(obj: Borrowed<'a, 'py, T>) -> Result { let cell = obj.get_class_object(); cell.ensure_threadsafe(); cell.borrow_checker() .try_borrow_mut() - .map(|_| Self { inner: obj.clone() }) + .map(|_| Self { inner: obj }) } - pub(crate) fn downgrade(slf: &Self) -> &PyRef<'py, T> { + pub(crate) fn downgrade(slf: &Self) -> &PyRef<'a, 'py, T> { // `PyRefMut` and `PyRef` have the same layout unsafe { &*ptr_from_ref(slf).cast() } } } -impl<'p, T, U> PyRefMut<'p, T> +impl<'a, 'py, T, U> PyRefMut<'a, 'py, T> where T: PyClass, U: PyClass, @@ -585,15 +573,9 @@ where /// Gets a `PyRef`. /// /// See [`PyRef::into_super`] for more. - pub fn into_super(self) -> PyRefMut<'p, U> { - let py = self.py(); + pub fn into_super(self) -> PyRefMut<'a, 'py, U> { PyRefMut { - inner: unsafe { - ManuallyDrop::new(self) - .as_ptr() - .assume_owned_unchecked(py) - .downcast_into_unchecked() - }, + inner: unsafe { ManuallyDrop::new(self).inner.to_any().downcast_unchecked() }, } } @@ -604,18 +586,18 @@ where /// can also be chained to access the super-superclass (and so on). /// /// See [`PyRef::as_super`] for more. - pub fn as_super(&mut self) -> &mut PyRefMut<'p, U> { - let ptr = ptr_from_mut::>(&mut self.inner) - // `Bound` has the same layout as `Bound` - .cast::>() - // `Bound` has the same layout as `PyRefMut`, + pub fn as_super(&mut self) -> &mut PyRefMut<'a, 'py, U> { + let ptr = ptr_from_mut::>(&mut self.inner) + // `Borrowed` has the same layout as `Borrowed` + .cast::>() + // `Borrowed` has the same layout as `PyRefMut`, // and the mutable borrow on `self` prevents aliasing - .cast::>(); + .cast::>(); unsafe { &mut *ptr } } } -impl> Deref for PyRefMut<'_, T> { +impl> Deref for PyRefMut<'_, '_, T> { type Target = T; #[inline] @@ -624,14 +606,14 @@ impl> Deref for PyRefMut<'_, T> { } } -impl> DerefMut for PyRefMut<'_, T> { +impl> DerefMut for PyRefMut<'_, '_, T> { #[inline] fn deref_mut(&mut self) -> &mut T { unsafe { &mut *self.inner.get_class_object().get_ptr() } } } -impl> Drop for PyRefMut<'_, T> { +impl> Drop for PyRefMut<'_, '_, T> { fn drop(&mut self) { self.inner .get_class_object() @@ -641,30 +623,30 @@ impl> Drop for PyRefMut<'_, T> { } #[allow(deprecated)] -impl> IntoPy for PyRefMut<'_, T> { +impl> IntoPy for PyRefMut<'_, '_, T> { fn into_py(self, py: Python<'_>) -> PyObject { unsafe { PyObject::from_borrowed_ptr(py, self.inner.as_ptr()) } } } #[allow(deprecated)] -impl> IntoPy for &'_ PyRefMut<'_, T> { +impl> IntoPy for &'_ PyRefMut<'_, '_, T> { fn into_py(self, py: Python<'_>) -> PyObject { - self.inner.clone().into_py(py) + self.inner.into_py(py) } } -impl<'py, T: PyClass> IntoPyObject<'py> for PyRefMut<'py, T> { +impl<'py, T: PyClass> IntoPyObject<'py> for PyRefMut<'_, 'py, T> { type Target = T; type Output = Bound<'py, T>; type Error = Infallible; fn into_pyobject(self, _py: Python<'py>) -> Result { - Ok(self.inner.clone()) + Ok(self.inner.to_owned()) } } -impl<'a, 'py, T: PyClass> IntoPyObject<'py> for &'a PyRefMut<'py, T> { +impl<'a, 'py, T: PyClass> IntoPyObject<'py> for &'a PyRefMut<'_, 'py, T> { type Target = T; type Output = Borrowed<'a, 'py, T>; type Error = Infallible; @@ -674,7 +656,7 @@ impl<'a, 'py, T: PyClass> IntoPyObject<'py> for &'a PyRefMut<'py } } -impl + fmt::Debug> fmt::Debug for PyRefMut<'_, T> { +impl + fmt::Debug> fmt::Debug for PyRefMut<'_, '_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(self.deref(), f) } @@ -795,13 +777,13 @@ mod tests { crate::Py::new(py, init).expect("allocation error") } - fn get_values(self_: PyRef<'_, Self>) -> (usize, usize, usize) { + fn get_values(self_: PyRef<'_, '_, Self>) -> (usize, usize, usize) { let val1 = self_.as_super().as_super().val1; let val2 = self_.as_super().val2; (val1, val2, self_.val3) } - fn double_values(mut self_: PyRefMut<'_, Self>) { + fn double_values(mut self_: PyRefMut<'_, '_, Self>) { self_.as_super().as_super().val1 *= 2; self_.as_super().val2 *= 2; self_.val3 *= 2; diff --git a/src/pycell/impl_.rs b/src/pycell/impl_.rs index 1b0724d8481..6367ecdda5d 100644 --- a/src/pycell/impl_.rs +++ b/src/pycell/impl_.rs @@ -461,37 +461,39 @@ mod tests { // Cannot take any other mutable or immutable borrows whilst the object is borrowed mutably assert!(mmm_bound - .extract::>() + .extract::>() .is_err()); assert!(mmm_bound - .extract::>() + .extract::>() .is_err()); - assert!(mmm_bound.extract::>().is_err()); + assert!(mmm_bound.extract::>().is_err()); assert!(mmm_bound - .extract::>() + .extract::>() .is_err()); assert!(mmm_bound - .extract::>() + .extract::>() + .is_err()); + assert!(mmm_bound + .extract::>() .is_err()); - assert!(mmm_bound.extract::>().is_err()); // With the borrow dropped, all other borrow attempts will succeed drop(mmm_refmut); assert!(mmm_bound - .extract::>() + .extract::>() .is_ok()); assert!(mmm_bound - .extract::>() + .extract::>() .is_ok()); - assert!(mmm_bound.extract::>().is_ok()); + assert!(mmm_bound.extract::>().is_ok()); assert!(mmm_bound - .extract::>() + .extract::>() .is_ok()); assert!(mmm_bound - .extract::>() + .extract::>() .is_ok()); - assert!(mmm_bound.extract::>().is_ok()); + assert!(mmm_bound.extract::>().is_ok()); }) } @@ -512,32 +514,34 @@ mod tests { // Further immutable borrows are ok assert!(mmm_bound - .extract::>() + .extract::>() .is_ok()); assert!(mmm_bound - .extract::>() + .extract::>() .is_ok()); - assert!(mmm_bound.extract::>().is_ok()); + assert!(mmm_bound.extract::>().is_ok()); // Further mutable borrows are not ok assert!(mmm_bound - .extract::>() + .extract::>() + .is_err()); + assert!(mmm_bound + .extract::>() .is_err()); assert!(mmm_bound - .extract::>() + .extract::>() .is_err()); - assert!(mmm_bound.extract::>().is_err()); // With the borrow dropped, all mutable borrow attempts will succeed drop(mmm_refmut); assert!(mmm_bound - .extract::>() + .extract::>() .is_ok()); assert!(mmm_bound - .extract::>() + .extract::>() .is_ok()); - assert!(mmm_bound.extract::>().is_ok()); + assert!(mmm_bound.extract::>().is_ok()); }) } diff --git a/src/tests/hygiene/pymethods.rs b/src/tests/hygiene/pymethods.rs index a5856e6413e..9220acfffa4 100644 --- a/src/tests/hygiene/pymethods.rs +++ b/src/tests/hygiene/pymethods.rs @@ -123,7 +123,10 @@ impl Dummy { fn __delitem__(&self, key: u32) {} - fn __iter__(_: crate::pycell::PyRef<'_, Self>, py: crate::Python<'_>) -> crate::Py { + fn __iter__( + _: crate::pycell::PyRef<'_, '_, Self>, + py: crate::Python<'_>, + ) -> crate::Py { crate::Py::new(py, DummyIter {}).unwrap() } @@ -132,7 +135,7 @@ impl Dummy { } fn __reversed__( - slf: crate::pycell::PyRef<'_, Self>, + slf: crate::pycell::PyRef<'_, '_, Self>, py: crate::Python<'_>, ) -> crate::Py { crate::Py::new(py, DummyIter {}).unwrap() @@ -274,19 +277,27 @@ impl Dummy { fn __ior__(&mut self, other: &Self) {} - fn __neg__(slf: crate::pycell::PyRef<'_, Self>) -> crate::pycell::PyRef<'_, Self> { + fn __neg__<'a, 'py>( + slf: crate::pycell::PyRef<'a, 'py, Self>, + ) -> crate::pycell::PyRef<'a, 'py, Self> { slf } - fn __pos__(slf: crate::pycell::PyRef<'_, Self>) -> crate::pycell::PyRef<'_, Self> { + fn __pos__<'a, 'py>( + slf: crate::pycell::PyRef<'a, 'py, Self>, + ) -> crate::pycell::PyRef<'a, 'py, Self> { slf } - fn __abs__(slf: crate::pycell::PyRef<'_, Self>) -> crate::pycell::PyRef<'_, Self> { + fn __abs__<'a, 'py>( + slf: crate::pycell::PyRef<'a, 'py, Self>, + ) -> crate::pycell::PyRef<'a, 'py, Self> { slf } - fn __invert__(slf: crate::pycell::PyRef<'_, Self>) -> crate::pycell::PyRef<'_, Self> { + fn __invert__<'a, 'py>( + slf: crate::pycell::PyRef<'a, 'py, Self>, + ) -> crate::pycell::PyRef<'a, 'py, Self> { slf } @@ -344,7 +355,9 @@ impl Dummy { // Awaitable Objects ////////////////////// - fn __await__(slf: crate::pycell::PyRef<'_, Self>) -> crate::pycell::PyRef<'_, Self> { + fn __await__<'a, 'py>( + slf: crate::pycell::PyRef<'a, 'py, Self>, + ) -> crate::pycell::PyRef<'a, 'py, Self> { slf } @@ -354,7 +367,7 @@ impl Dummy { ////////////////////// fn __aiter__( - slf: crate::pycell::PyRef<'_, Self>, + slf: crate::pycell::PyRef<'_, '_, Self>, py: crate::Python<'_>, ) -> crate::Py { crate::Py::new(py, DummyIter {}).unwrap() diff --git a/src/types/any.rs b/src/types/any.rs index f5052716561..cef699024ae 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -744,7 +744,7 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed { /// class_bound.borrow_mut().i += 1; /// /// // Alternatively you can get a `PyRefMut` directly - /// let class_ref: PyRefMut<'_, Class> = class.extract()?; + /// let class_ref: PyRefMut<'_, '_, Class> = class.extract()?; /// assert_eq!(class_ref.i, 1); /// Ok(()) /// }) diff --git a/tests/test_arithmetics.rs b/tests/test_arithmetics.rs index 21e32d4c13c..8e49a5af5b5 100644 --- a/tests/test_arithmetics.rs +++ b/tests/test_arithmetics.rs @@ -379,43 +379,43 @@ impl LhsAndRhs { // "BA" // } - fn __add__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { + fn __add__(lhs: PyRef<'_, '_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{:?} + {:?}", lhs, rhs) } - fn __sub__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { + fn __sub__(lhs: PyRef<'_, '_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{:?} - {:?}", lhs, rhs) } - fn __mul__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { + fn __mul__(lhs: PyRef<'_, '_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{:?} * {:?}", lhs, rhs) } - fn __lshift__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { + fn __lshift__(lhs: PyRef<'_, '_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{:?} << {:?}", lhs, rhs) } - fn __rshift__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { + fn __rshift__(lhs: PyRef<'_, '_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{:?} >> {:?}", lhs, rhs) } - fn __and__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { + fn __and__(lhs: PyRef<'_, '_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{:?} & {:?}", lhs, rhs) } - fn __xor__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { + fn __xor__(lhs: PyRef<'_, '_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{:?} ^ {:?}", lhs, rhs) } - fn __or__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { + fn __or__(lhs: PyRef<'_, '_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{:?} | {:?}", lhs, rhs) } - fn __pow__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>, _mod: Option) -> String { + fn __pow__(lhs: PyRef<'_, '_, Self>, rhs: &Bound<'_, PyAny>, _mod: Option) -> String { format!("{:?} ** {:?}", lhs, rhs) } - fn __matmul__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { + fn __matmul__(lhs: PyRef<'_, '_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{:?} @ {:?}", lhs, rhs) } @@ -608,67 +608,110 @@ mod return_not_implemented { "RC_Self" } - fn __richcmp__(&self, other: PyRef<'_, Self>, _op: CompareOp) -> PyObject { + fn __richcmp__(&self, other: PyRef<'_, '_, Self>, _op: CompareOp) -> PyObject { other.py().None() } - fn __add__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __add__<'a, 'py>( + slf: PyRef<'a, 'py, Self>, + _other: PyRef<'a, 'py, Self>, + ) -> PyRef<'a, 'py, Self> { slf } - fn __sub__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __sub__<'a, 'py>( + slf: PyRef<'a, 'py, Self>, + _other: PyRef<'a, 'py, Self>, + ) -> PyRef<'a, 'py, Self> { slf } - fn __mul__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __mul__<'a, 'py>( + slf: PyRef<'a, 'py, Self>, + _other: PyRef<'a, 'py, Self>, + ) -> PyRef<'a, 'py, Self> { slf } - fn __matmul__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __matmul__<'a, 'py>( + slf: PyRef<'a, 'py, Self>, + _other: PyRef<'a, 'py, Self>, + ) -> PyRef<'a, 'py, Self> { slf } - fn __truediv__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __truediv__<'a, 'py>( + slf: PyRef<'a, 'py, Self>, + _other: PyRef<'a, 'py, Self>, + ) -> PyRef<'a, 'py, Self> { slf } - fn __floordiv__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __floordiv__<'a, 'py>( + slf: PyRef<'a, 'py, Self>, + _other: PyRef<'a, 'py, Self>, + ) -> PyRef<'a, 'py, Self> { slf } - fn __mod__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __mod__<'a, 'py>( + slf: PyRef<'a, 'py, Self>, + _other: PyRef<'a, 'py, Self>, + ) -> PyRef<'a, 'py, Self> { slf } - fn __pow__(slf: PyRef<'_, Self>, _other: u8, _modulo: Option) -> PyRef<'_, Self> { + fn __pow__<'a, 'py>( + slf: PyRef<'a, 'py, Self>, + _other: u8, + _modulo: Option, + ) -> PyRef<'a, 'py, Self> { slf } - fn __lshift__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __lshift__<'a, 'py>( + slf: PyRef<'a, 'py, Self>, + _other: PyRef<'a, 'py, Self>, + ) -> PyRef<'a, 'py, Self> { slf } - fn __rshift__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __rshift__<'a, 'py>( + slf: PyRef<'a, 'py, Self>, + _other: PyRef<'a, 'py, Self>, + ) -> PyRef<'a, 'py, Self> { slf } - fn __divmod__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __divmod__<'a, 'py>( + slf: PyRef<'a, 'py, Self>, + _other: PyRef<'a, 'py, Self>, + ) -> PyRef<'a, 'py, Self> { slf } - fn __and__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __and__<'a, 'py>( + slf: PyRef<'a, 'py, Self>, + _other: PyRef<'a, 'py, Self>, + ) -> PyRef<'a, 'py, Self> { slf } - fn __or__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __or__<'a, 'py>( + slf: PyRef<'a, 'py, Self>, + _other: PyRef<'a, 'py, Self>, + ) -> PyRef<'a, 'py, Self> { slf } - fn __xor__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __xor__<'a, 'py>( + slf: PyRef<'a, 'py, Self>, + _other: PyRef<'a, 'py, Self>, + ) -> PyRef<'a, 'py, Self> { slf } // Inplace assignments - fn __iadd__(&mut self, _other: PyRef<'_, Self>) {} - fn __isub__(&mut self, _other: PyRef<'_, Self>) {} - fn __imul__(&mut self, _other: PyRef<'_, Self>) {} - fn __imatmul__(&mut self, _other: PyRef<'_, Self>) {} - fn __itruediv__(&mut self, _other: PyRef<'_, Self>) {} - fn __ifloordiv__(&mut self, _other: PyRef<'_, Self>) {} - fn __imod__(&mut self, _other: PyRef<'_, Self>) {} - fn __ilshift__(&mut self, _other: PyRef<'_, Self>) {} - fn __irshift__(&mut self, _other: PyRef<'_, Self>) {} - fn __iand__(&mut self, _other: PyRef<'_, Self>) {} - fn __ior__(&mut self, _other: PyRef<'_, Self>) {} - fn __ixor__(&mut self, _other: PyRef<'_, Self>) {} - fn __ipow__(&mut self, _other: PyRef<'_, Self>, _modulo: Option) {} + fn __iadd__(&mut self, _other: PyRef<'_, '_, Self>) {} + fn __isub__(&mut self, _other: PyRef<'_, '_, Self>) {} + fn __imul__(&mut self, _other: PyRef<'_, '_, Self>) {} + fn __imatmul__(&mut self, _other: PyRef<'_, '_, Self>) {} + fn __itruediv__(&mut self, _other: PyRef<'_, '_, Self>) {} + fn __ifloordiv__(&mut self, _other: PyRef<'_, '_, Self>) {} + fn __imod__(&mut self, _other: PyRef<'_, '_, Self>) {} + fn __ilshift__(&mut self, _other: PyRef<'_, '_, Self>) {} + fn __irshift__(&mut self, _other: PyRef<'_, '_, Self>) {} + fn __iand__(&mut self, _other: PyRef<'_, '_, Self>) {} + fn __ior__(&mut self, _other: PyRef<'_, '_, Self>) {} + fn __ixor__(&mut self, _other: PyRef<'_, '_, Self>) {} + fn __ipow__(&mut self, _other: PyRef<'_, '_, Self>, _modulo: Option) {} } fn _test_binary_dunder(dunder: &str) { diff --git a/tests/test_buffer.rs b/tests/test_buffer.rs index 60db80b81c8..105eca487f6 100644 --- a/tests/test_buffer.rs +++ b/tests/test_buffer.rs @@ -28,7 +28,7 @@ struct TestBufferErrors { #[pymethods] impl TestBufferErrors { unsafe fn __getbuffer__( - slf: PyRefMut<'_, Self>, + slf: PyRefMut<'_, '_, Self>, view: *mut ffi::Py_buffer, flags: c_int, ) -> PyResult<()> { diff --git a/tests/test_buffer_protocol.rs b/tests/test_buffer_protocol.rs index 1f15e34b384..7c777bcbaa2 100644 --- a/tests/test_buffer_protocol.rs +++ b/tests/test_buffer_protocol.rs @@ -28,7 +28,7 @@ impl TestBufferClass { view: *mut ffi::Py_buffer, flags: c_int, ) -> PyResult<()> { - fill_view_from_readonly_data(view, flags, &slf.borrow().vec, slf.into_any()) + fill_view_from_readonly_data(view, flags, &slf.clone().borrow().vec, slf.into_any()) } unsafe fn __releasebuffer__(&self, view: *mut ffi::Py_buffer) { diff --git a/tests/test_class_conversion.rs b/tests/test_class_conversion.rs index a1e2188e83a..29e8ebf8950 100644 --- a/tests/test_class_conversion.rs +++ b/tests/test_class_conversion.rs @@ -22,11 +22,11 @@ fn test_cloneable_pyclass() { let c2: Cloneable = py_c.extract(py).unwrap(); assert_eq!(c, c2); { - let rc: PyRef<'_, Cloneable> = py_c.extract(py).unwrap(); + let rc: PyRef<'_, '_, Cloneable> = py_c.extract(py).unwrap(); assert_eq!(&c, &*rc); // Drops PyRef before taking PyRefMut } - let mrc: PyRefMut<'_, Cloneable> = py_c.extract(py).unwrap(); + let mrc: PyRefMut<'_, '_, Cloneable> = py_c.extract(py).unwrap(); assert_eq!(&c, &*mrc); }); } @@ -126,16 +126,16 @@ fn test_pyref_as_base() { let cell = Bound::new(py, (SubClass {}, BaseClass { value: 120 })).unwrap(); // First try PyRefMut - let sub: PyRefMut<'_, SubClass> = cell.borrow_mut(); - let mut base: PyRefMut<'_, BaseClass> = sub.into_super(); + let sub: PyRefMut<'_, '_, SubClass> = cell.borrow_mut(); + let mut base: PyRefMut<'_, '_, BaseClass> = sub.into_super(); assert_eq!(120, base.value); base.value = 999; assert_eq!(999, base.value); drop(base); // Repeat for PyRef - let sub: PyRef<'_, SubClass> = cell.borrow(); - let base: PyRef<'_, BaseClass> = sub.into_super(); + let sub: PyRef<'_, '_, SubClass> = cell.borrow(); + let base: PyRef<'_, '_, BaseClass> = sub.into_super(); assert_eq!(999, base.value); }); } diff --git a/tests/test_gc.rs b/tests/test_gc.rs index 4b293449b36..4af4fdf11f4 100644 --- a/tests/test_gc.rs +++ b/tests/test_gc.rs @@ -438,7 +438,7 @@ fn traverse_cannot_be_hijacked() { fn __traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>; } - impl Traversable for PyRef<'_, HijackedTraverse> { + impl Traversable for PyRef<'_, '_, HijackedTraverse> { fn __traverse__(&self, _visit: PyVisit<'_>) -> Result<(), PyTraverseError> { self.hijacked.store(true, Ordering::Release); Ok(()) diff --git a/tests/test_getter_setter.rs b/tests/test_getter_setter.rs index cdc8136bede..04dc10ee15f 100644 --- a/tests/test_getter_setter.rs +++ b/tests/test_getter_setter.rs @@ -138,12 +138,12 @@ struct RefGetterSetter { #[pymethods] impl RefGetterSetter { #[getter] - fn get_num(slf: PyRef<'_, Self>) -> i32 { + fn get_num(slf: PyRef<'_, '_, Self>) -> i32 { slf.num } #[setter] - fn set_num(mut slf: PyRefMut<'_, Self>, value: i32) { + fn set_num(mut slf: PyRefMut<'_, '_, Self>, value: i32) { slf.num = value; } } diff --git a/tests/test_methods.rs b/tests/test_methods.rs index 743fa6e6b4f..76c0f4be006 100644 --- a/tests/test_methods.rs +++ b/tests/test_methods.rs @@ -775,7 +775,7 @@ impl MethodWithPyClassArg { value: self.value + other.value, } } - fn add_pyref(&self, other: PyRef<'_, MethodWithPyClassArg>) -> MethodWithPyClassArg { + fn add_pyref(&self, other: PyRef<'_, '_, MethodWithPyClassArg>) -> MethodWithPyClassArg { MethodWithPyClassArg { value: self.value + other.value, } @@ -783,7 +783,7 @@ impl MethodWithPyClassArg { fn inplace_add(&self, other: &mut MethodWithPyClassArg) { other.value += self.value; } - fn inplace_add_pyref(&self, mut other: PyRefMut<'_, MethodWithPyClassArg>) { + fn inplace_add_pyref(&self, mut other: PyRefMut<'_, '_, MethodWithPyClassArg>) { other.value += self.value; } #[pyo3(signature=(other = None))] diff --git a/tests/test_proto_methods.rs b/tests/test_proto_methods.rs index 23f7f6cf213..e4f1736064f 100644 --- a/tests/test_proto_methods.rs +++ b/tests/test_proto_methods.rs @@ -367,11 +367,11 @@ struct Iterator { #[pymethods] impl Iterator { - fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __iter__<'a, 'py>(slf: PyRef<'a, 'py, Self>) -> PyRef<'a, 'py, Self> { slf } - fn __next__(slf: PyRefMut<'_, Self>) -> Option { + fn __next__(slf: PyRefMut<'_, '_, Self>) -> Option { slf.iter.lock().unwrap().next() } } @@ -648,11 +648,11 @@ impl OnceFuture { } } - fn __await__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __await__<'a, 'py>(slf: PyRef<'a, 'py, Self>) -> PyRef<'a, 'py, Self> { slf } - fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __iter__<'a, 'py>(slf: PyRef<'a, 'py, Self>) -> PyRef<'a, 'py, Self> { slf } fn __next__<'py>(&mut self, py: Python<'py>) -> Option<&Bound<'py, PyAny>> { @@ -708,7 +708,7 @@ impl AsyncIterator { } } - fn __aiter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __aiter__<'a, 'py>(slf: PyRef<'a, 'py, Self>) -> PyRef<'a, 'py, Self> { slf } @@ -767,11 +767,11 @@ impl DescrCounter { DescrCounter { count: 0 } } /// Each access will increase the count - fn __get__<'a>( - mut slf: PyRefMut<'a, Self>, - _instance: &Bound<'_, PyAny>, - _owner: Option<&Bound<'_, PyType>>, - ) -> PyRefMut<'a, Self> { + fn __get__<'a, 'py>( + mut slf: PyRefMut<'a, 'py, Self>, + _instance: &Bound<'py, PyAny>, + _owner: Option<&Bound<'py, PyType>>, + ) -> PyRefMut<'a, 'py, Self> { slf.count += 1; slf } diff --git a/tests/test_pyself.rs b/tests/test_pyself.rs index c4f3e6d6fa6..5e310e1133f 100644 --- a/tests/test_pyself.rs +++ b/tests/test_pyself.rs @@ -35,7 +35,7 @@ impl Reader { } } fn get_iter_and_reset( - mut slf: PyRefMut<'_, Self>, + mut slf: PyRefMut<'_, '_, Self>, keys: Py, py: Python<'_>, ) -> PyResult { @@ -60,11 +60,11 @@ struct Iter { #[pymethods] impl Iter { #[allow(clippy::self_named_constructors)] - fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __iter__<'a, 'py>(slf: PyRef<'a, 'py, Self>) -> PyRef<'a, 'py, Self> { slf } - fn __next__(mut slf: PyRefMut<'_, Self>) -> PyResult> { + fn __next__(mut slf: PyRefMut<'_, '_, Self>) -> PyResult> { let bytes = slf.keys.bind(slf.py()).as_bytes(); match bytes.get(slf.idx) { Some(&b) => { diff --git a/tests/test_sequence.rs b/tests/test_sequence.rs index e8f61e80e42..ed3e37e5084 100644 --- a/tests/test_sequence.rs +++ b/tests/test_sequence.rs @@ -74,7 +74,7 @@ impl ByteSequence { Self { elements } } - fn __inplace_concat__(mut slf: PyRefMut<'_, Self>, other: &Self) -> Py { + fn __inplace_concat__(mut slf: PyRefMut<'_, '_, Self>, other: &Self) -> Py { slf.elements.extend_from_slice(&other.elements); slf.into() } @@ -91,7 +91,7 @@ impl ByteSequence { } } - fn __inplace_repeat__(mut slf: PyRefMut<'_, Self>, count: isize) -> PyResult> { + fn __inplace_repeat__(mut slf: PyRefMut<'_, '_, Self>, count: isize) -> PyResult> { if count >= 0 { let mut elements = Vec::with_capacity(slf.elements.len() * count as usize); for _ in 0..count { diff --git a/tests/test_various.rs b/tests/test_various.rs index a8aa6b7a71f..1b3684bd4fa 100644 --- a/tests/test_various.rs +++ b/tests/test_various.rs @@ -19,7 +19,7 @@ impl MutRefArg { fn get(&self) -> i32 { self.n } - fn set_other(&self, mut other: PyRefMut<'_, MutRefArg>) { + fn set_other(&self, mut other: PyRefMut<'_, '_, MutRefArg>) { other.n = 100; } } diff --git a/tests/ui/invalid_cancel_handle.stderr b/tests/ui/invalid_cancel_handle.stderr index acde907d519..d6f2bec4a7e 100644 --- a/tests/ui/invalid_cancel_handle.stderr +++ b/tests/ui/invalid_cancel_handle.stderr @@ -38,42 +38,42 @@ note: function defined here | ^^^^^^^^^^^^^^^^^^^^^^^^ -------------- = note: this error originates in the attribute macro `pyfunction` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: the trait bound `CancelHandle: PyFunctionArgument<'_, '_>` is not satisfied +error[E0277]: the trait bound `CancelHandle: PyFunctionArgument<'_, '_, '_>` is not satisfied --> tests/ui/invalid_cancel_handle.rs:20:50 | 20 | async fn missing_cancel_handle_attribute(_param: pyo3::coroutine::CancelHandle) {} - | ^^^^ the trait `PyClass` is not implemented for `CancelHandle`, which is required by `CancelHandle: PyFunctionArgument<'_, '_>` + | ^^^^ the trait `PyClass` is not implemented for `CancelHandle`, which is required by `CancelHandle: PyFunctionArgument<'_, '_, '_>` | = help: the trait `PyClass` is implemented for `pyo3::coroutine::Coroutine` = note: required for `CancelHandle` to implement `FromPyObject<'_, '_>` - = note: required for `CancelHandle` to implement `PyFunctionArgument<'_, '_>` + = note: required for `CancelHandle` to implement `PyFunctionArgument<'_, '_, '_>` note: required by a bound in `extract_argument` --> src/impl_/extract_argument.rs | - | pub fn extract_argument<'a, 'py, T>( + | pub fn extract_argument<'a, 'holder, 'py, T>( | ---------------- required by a bound in this function ... - | T: PyFunctionArgument<'a, 'py>, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extract_argument` + | T: PyFunctionArgument<'a, 'holder, 'py>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extract_argument` -error[E0277]: the trait bound `CancelHandle: PyFunctionArgument<'_, '_>` is not satisfied +error[E0277]: the trait bound `CancelHandle: PyFunctionArgument<'_, '_, '_>` is not satisfied --> tests/ui/invalid_cancel_handle.rs:20:50 | 20 | async fn missing_cancel_handle_attribute(_param: pyo3::coroutine::CancelHandle) {} - | ^^^^ the trait `Clone` is not implemented for `CancelHandle`, which is required by `CancelHandle: PyFunctionArgument<'_, '_>` + | ^^^^ the trait `Clone` is not implemented for `CancelHandle`, which is required by `CancelHandle: PyFunctionArgument<'_, '_, '_>` | - = help: the following other types implement trait `PyFunctionArgument<'a, 'py>`: - &'a mut pyo3::coroutine::Coroutine - &'a pyo3::Bound<'py, T> - &'a pyo3::coroutine::Coroutine - Option<&'a pyo3::Bound<'py, T>> + = help: the following other types implement trait `PyFunctionArgument<'a, 'holder, 'py>`: + &'holder mut pyo3::coroutine::Coroutine + &'holder pyo3::Bound<'py, T> + &'holder pyo3::coroutine::Coroutine + Option<&'holder pyo3::Bound<'py, T>> = note: required for `CancelHandle` to implement `FromPyObject<'_, '_>` - = note: required for `CancelHandle` to implement `PyFunctionArgument<'_, '_>` + = note: required for `CancelHandle` to implement `PyFunctionArgument<'_, '_, '_>` note: required by a bound in `extract_argument` --> src/impl_/extract_argument.rs | - | pub fn extract_argument<'a, 'py, T>( + | pub fn extract_argument<'a, 'holder, 'py, T>( | ---------------- required by a bound in this function ... - | T: PyFunctionArgument<'a, 'py>, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extract_argument` + | T: PyFunctionArgument<'a, 'holder, 'py>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extract_argument` diff --git a/tests/ui/invalid_frozen_pyclass_borrow.stderr b/tests/ui/invalid_frozen_pyclass_borrow.stderr index 52a0623f282..b4230e7c245 100644 --- a/tests/ui/invalid_frozen_pyclass_borrow.stderr +++ b/tests/ui/invalid_frozen_pyclass_borrow.stderr @@ -13,8 +13,8 @@ error[E0271]: type mismatch resolving `::Frozen == False` note: required by a bound in `extract_pyclass_ref_mut` --> src/impl_/extract_argument.rs | - | pub fn extract_pyclass_ref_mut<'a, 'py: 'a, T: PyClass>( - | ^^^^^^^^^^^^^^ required by this bound in `extract_pyclass_ref_mut` + | pub fn extract_pyclass_ref_mut<'a, 'holder, 'py: 'a, T: PyClass>( + | ^^^^^^^^^^^^^^ required by this bound in `extract_pyclass_ref_mut` error[E0271]: type mismatch resolving `::Frozen == False` --> tests/ui/invalid_frozen_pyclass_borrow.rs:9:1 @@ -25,8 +25,8 @@ error[E0271]: type mismatch resolving `::Frozen == False` note: required by a bound in `PyRefMut` --> src/pycell.rs | - | pub struct PyRefMut<'p, T: PyClass> { - | ^^^^^^^^^^^^^^ required by this bound in `PyRefMut` + | pub struct PyRefMut<'a, 'py, T: PyClass> { + | ^^^^^^^^^^^^^^ required by this bound in `PyRefMut` = note: this error originates in the attribute macro `pymethods` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0271]: type mismatch resolving `::Frozen == False` @@ -38,7 +38,7 @@ error[E0271]: type mismatch resolving `::Frozen == False` note: required by a bound in `pyo3::Bound::<'py, T>::borrow_mut` --> src/instance.rs | - | pub fn borrow_mut(&self) -> PyRefMut<'py, T> + | pub fn borrow_mut<'a>(&'a self) -> PyRefMut<'a, 'py, T> | ---------- required by a bound in this associated function | where | T: PyClass, @@ -53,7 +53,7 @@ error[E0271]: type mismatch resolving `::Frozen == Fa note: required by a bound in `pyo3::Bound::<'py, T>::borrow_mut` --> src/instance.rs | - | pub fn borrow_mut(&self) -> PyRefMut<'py, T> + | pub fn borrow_mut<'a>(&'a self) -> PyRefMut<'a, 'py, T> | ---------- required by a bound in this associated function | where | T: PyClass, diff --git a/tests/ui/invalid_pymethod_enum.stderr b/tests/ui/invalid_pymethod_enum.stderr index bc377d2a055..72307c756f3 100644 --- a/tests/ui/invalid_pymethod_enum.stderr +++ b/tests/ui/invalid_pymethod_enum.stderr @@ -7,8 +7,8 @@ error[E0271]: type mismatch resolving `::Frozen == False note: required by a bound in `extract_pyclass_ref_mut` --> src/impl_/extract_argument.rs | - | pub fn extract_pyclass_ref_mut<'a, 'py: 'a, T: PyClass>( - | ^^^^^^^^^^^^^^ required by this bound in `extract_pyclass_ref_mut` + | pub fn extract_pyclass_ref_mut<'a, 'holder, 'py: 'a, T: PyClass>( + | ^^^^^^^^^^^^^^ required by this bound in `extract_pyclass_ref_mut` error[E0271]: type mismatch resolving `::Frozen == False` --> tests/ui/invalid_pymethod_enum.rs:9:1 @@ -19,8 +19,8 @@ error[E0271]: type mismatch resolving `::Frozen == False note: required by a bound in `PyRefMut` --> src/pycell.rs | - | pub struct PyRefMut<'p, T: PyClass> { - | ^^^^^^^^^^^^^^ required by this bound in `PyRefMut` + | pub struct PyRefMut<'a, 'py, T: PyClass> { + | ^^^^^^^^^^^^^^ required by this bound in `PyRefMut` = note: this error originates in the attribute macro `pymethods` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0271]: type mismatch resolving `::Frozen == False` @@ -32,8 +32,8 @@ error[E0271]: type mismatch resolving `::Frozen == False` note: required by a bound in `extract_pyclass_ref_mut` --> src/impl_/extract_argument.rs | - | pub fn extract_pyclass_ref_mut<'a, 'py: 'a, T: PyClass>( - | ^^^^^^^^^^^^^^ required by this bound in `extract_pyclass_ref_mut` + | pub fn extract_pyclass_ref_mut<'a, 'holder, 'py: 'a, T: PyClass>( + | ^^^^^^^^^^^^^^ required by this bound in `extract_pyclass_ref_mut` error[E0271]: type mismatch resolving `::Frozen == False` --> tests/ui/invalid_pymethod_enum.rs:25:1 @@ -44,6 +44,6 @@ error[E0271]: type mismatch resolving `::Frozen == False` note: required by a bound in `PyRefMut` --> src/pycell.rs | - | pub struct PyRefMut<'p, T: PyClass> { - | ^^^^^^^^^^^^^^ required by this bound in `PyRefMut` + | pub struct PyRefMut<'a, 'py, T: PyClass> { + | ^^^^^^^^^^^^^^ required by this bound in `PyRefMut` = note: this error originates in the attribute macro `pymethods` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/invalid_pymethods.stderr b/tests/ui/invalid_pymethods.stderr index 845b79ed59a..30021f13326 100644 --- a/tests/ui/invalid_pymethods.stderr +++ b/tests/ui/invalid_pymethods.stderr @@ -166,7 +166,7 @@ error: `pass_module` cannot be used on Python methods | ^^^^^^^^^^^ error: Python objects are shared, so 'self' cannot be moved out of the Python interpreter. - Try `&self`, `&mut self, `slf: PyRef<'_, Self>` or `slf: PyRefMut<'_, Self>`. + Try `&self`, `&mut self, `slf: PyRef<'_, '_, Self>` or `slf: PyRefMut<'_, '_, Self>`. --> tests/ui/invalid_pymethods.rs:188:29 | 188 | fn method_self_by_value(self) {}