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/guide/src/migration.md b/guide/src/migration.md index 8a8f3694f6d..c76d13f88bb 100644 --- a/guide/src/migration.md +++ b/guide/src/migration.md @@ -3,6 +3,123 @@ This guide can help you upgrade code through breaking changes from one PyO3 version to the next. For a detailed list of all changes, see the [CHANGELOG](changelog.md). +## from 0.23.* to 0.24 + +### `FromPyObject` gains additional lifetime +
+Click to expand + +With the removal of the `gil-ref` API it is now possible to fully split the Python GIL lifetime +`'py` and the input lifetime `'a`. This allows borrowing from the input data without extending the +GIL lifetime. + +`FromPyObject` now takes an additional lifetime `'a` describing the input lifetime. The argument +type of the `extract` method changed from `&Bound<'py, PyAny>` to `Borrowed<'a, 'py, PyAny>`. This was +done because `&'a Bound<'py, PyAny>` would have an implicit restriction `'py: 'a` due to the reference type. `extract_bound` with its +old signature is deprecated, but still available during migration. + +This new form was partly implemented already in 0.22 using the internal `FromPyObjectBound` trait and +is now extended to all types. + +Most implementations can just add an elided lifetime to migrate. + +Before: +```rust,ignore +impl<'py> FromPyObject<'py> for IpAddr { + fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult { + ... + } +} +``` + +After +```rust,ignore +impl<'py> FromPyObject<'_, 'py> for IpAddr { + fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { + ... + // since `Borrowed` derefs to `&Bound`, the body often + // needs no changes, or adding an occasional `&` + } +} +``` + +Occasionally, more steps are necessary. For generic types, the bounds need to be adjusted. The +correct bound depends on how the type is used. + +For simple wrapper types usually it's possible to just forward the bound. + +Before: +```rust,ignore +struct MyWrapper(T); + +impl<'py, T> FromPyObject<'py> for MyWrapper +where + T: FromPyObject<'py> +{ + fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult { + ob.extract().map(MyWrapper) + } +} +``` + +After: +```rust +# use pyo3::prelude::*; +# pub struct MyWrapper(T); +impl<'a, 'py, T> FromPyObject<'a, 'py> for MyWrapper +where + T: FromPyObject<'a, 'py> +{ + fn extract(obj: Borrowed<'a, 'py, PyAny>) -> PyResult { + obj.extract().map(MyWrapper) + } +} +``` + +Container types that need to create temporary Python references during extraction, for example +extracing from a `PyList`, require a stronger bound. For these the `FromPyObjectOwned` trait was +introduced. It is automatically implemented for any type that implements `FromPyObject` and does not +borrow from the input. It is intended to be used as a trait bound in these situations. + +Before: +```rust,ignore +struct MyVec(Vec); +impl<'py, T> FromPyObject<'py> for Vec +where + T: FromPyObject<'py>, +{ + fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult { + let mut v = MyVec(Vec::new()); + for item in obj.try_iter()? { + v.0.push(item?.extract::()?); + } + Ok(v) + } +} +``` + +After: +```rust +# use pyo3::prelude::*; +# pub struct MyVec(Vec); +impl<'py, T> FromPyObject<'_, 'py> for MyVec +where + T: FromPyObjectOwned<'py> // 👈 can only extract owned values, because each `item` below + // is a temporary short lived owned reference +{ + fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { + let mut v = MyVec(Vec::new()); + for item in obj.try_iter()? { + v.0.push(item?.extract::()?); + } + Ok(v) + } +} +``` + +This is very similar to `serde`s `Deserialize` and `DeserializeOwned` traits. +
+ ## from 0.22.* to 0.23
Click to expand diff --git a/newsfragments/4390.added.md b/newsfragments/4390.added.md new file mode 100644 index 00000000000..c234b0b6547 --- /dev/null +++ b/newsfragments/4390.added.md @@ -0,0 +1,2 @@ +added `FromPyObjectOwned` as more convenient trait bound +added `Borrowed::extract`, same as `PyAnyMethods::extract`, but does not restrict the lifetime by deref \ No newline at end of file diff --git a/newsfragments/4390.changed.md b/newsfragments/4390.changed.md new file mode 100644 index 00000000000..50c9ffb7fec --- /dev/null +++ b/newsfragments/4390.changed.md @@ -0,0 +1,3 @@ +added second lifetime to `FromPyObject` +reintroduced `extract` method +deprecated `extract_bound` method \ No newline at end of file diff --git a/newsfragments/4390.removed.md b/newsfragments/4390.removed.md new file mode 100644 index 00000000000..a40ee2db268 --- /dev/null +++ b/newsfragments/4390.removed.md @@ -0,0 +1 @@ +removed `FromPyObjectBound` \ No newline at end of file diff --git a/pyo3-macros-backend/src/frompyobject.rs b/pyo3-macros-backend/src/frompyobject.rs index 14c8755e9be..250571dcea0 100644 --- a/pyo3-macros-backend/src/frompyobject.rs +++ b/pyo3-macros-backend/src/frompyobject.rs @@ -591,7 +591,7 @@ pub fn build_derive_from_pyobject(tokens: &DeriveInput) -> Result { let gen_ident = ¶m.ident; where_clause .predicates - .push(parse_quote!(#gen_ident: #pyo3_path::FromPyObject<'py>)) + .push(parse_quote!(#gen_ident: #pyo3_path::conversion::FromPyObjectOwned<#lt_param>)) } let derives = match &tokens.data { @@ -619,8 +619,9 @@ pub fn build_derive_from_pyobject(tokens: &DeriveInput) -> Result { let ident = &tokens.ident; Ok(quote!( #[automatically_derived] - impl #impl_generics #pyo3_path::FromPyObject<#lt_param> for #ident #ty_generics #where_clause { - fn extract_bound(obj: &#pyo3_path::Bound<#lt_param, #pyo3_path::PyAny>) -> #pyo3_path::PyResult { + impl #impl_generics #pyo3_path::FromPyObject<'_, #lt_param> for #ident #ty_generics #where_clause { + fn extract(obj: #pyo3_path::Borrowed<'_, #lt_param, #pyo3_path::PyAny>) -> #pyo3_path::PyResult { + let obj: &#pyo3_path::Bound<'_, _> = &*obj; #derives } } 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/buffer.rs b/src/buffer.rs index 2d94681a5c7..fb5f6c4730f 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -18,8 +18,8 @@ // DEALINGS IN THE SOFTWARE. //! `PyBuffer` implementation -use crate::Bound; use crate::{err, exceptions::PyBufferError, ffi, FromPyObject, PyAny, PyResult, Python}; +use crate::{Borrowed, Bound}; use std::marker::PhantomData; use std::os::raw; use std::pin::Pin; @@ -182,9 +182,9 @@ pub unsafe trait Element: Copy { fn is_compatible_format(format: &CStr) -> bool; } -impl FromPyObject<'_> for PyBuffer { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult> { - Self::get(obj) +impl FromPyObject<'_, '_> for PyBuffer { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult> { + Self::get(&obj) } } diff --git a/src/conversion.rs b/src/conversion.rs index 82ad4d84977..6c5998ede12 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -3,7 +3,6 @@ use crate::err::PyResult; #[cfg(feature = "experimental-inspect")] use crate::inspect::types::TypeInfo; use crate::pyclass::boolean_struct::False; -use crate::types::any::PyAnyMethods; use crate::types::PyTuple; use crate::{ ffi, Borrowed, Bound, BoundObject, Py, PyAny, PyClass, PyErr, PyObject, PyRef, PyRefMut, Python, @@ -403,7 +402,8 @@ impl<'py, T> IntoPyObjectExt<'py> for T where T: IntoPyObject<'py> {} /// Extract a type from a Python object. /// /// -/// Normal usage is through the `extract` methods on [`Bound`] and [`Py`], which forward to this trait. +/// Normal usage is through the `extract` methods on [`Bound`], [`Borrowed`] and [`Py`], which +/// forward to this trait. /// /// # Examples /// @@ -427,91 +427,38 @@ impl<'py, T> IntoPyObjectExt<'py> for T where T: IntoPyObject<'py> {} /// # } /// ``` /// -// /// FIXME: until `FromPyObject` can pick up a second lifetime, the below commentary is no longer -// /// true. Update and restore this documentation at that time. -// /// -// /// Note: depending on the implementation, the lifetime of the extracted result may -// /// depend on the lifetime of the `obj` or the `prepared` variable. -// /// -// /// For example, when extracting `&str` from a Python byte string, the resulting string slice will -// /// point to the existing string data (lifetime: `'py`). -// /// On the other hand, when extracting `&str` from a Python Unicode string, the preparation step -// /// will convert the string to UTF-8, and the resulting string slice will have lifetime `'prepared`. -// /// Since which case applies depends on the runtime type of the Python object, -// /// both the `obj` and `prepared` variables must outlive the resulting string slice. -/// -/// During the migration of PyO3 from the "GIL Refs" API to the `Bound` smart pointer, this trait -/// has two methods `extract` and `extract_bound` which are defaulted to call each other. To avoid -/// infinite recursion, implementors must implement at least one of these methods. The recommendation -/// is to implement `extract_bound` and leave `extract` as the default implementation. -pub trait FromPyObject<'py>: Sized { - /// Extracts `Self` from the bound smart pointer `obj`. - /// - /// Implementors are encouraged to implement this method and leave `extract` defaulted, as - /// this will be most compatible with PyO3's future API. - fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult; - - /// Extracts the type hint information for this type when it appears as an argument. - /// - /// For example, `Vec` would return `Sequence[int]`. - /// The default implementation returns `Any`, which is correct for any type. - /// - /// For most types, the return value for this method will be identical to that of - /// [`IntoPyObject::type_output`]. It may be different for some types, such as `Dict`, - /// to allow duck-typing: functions return `Dict` but take `Mapping` as argument. - #[cfg(feature = "experimental-inspect")] - fn type_input() -> TypeInfo { - TypeInfo::Any - } -} - -mod from_py_object_bound_sealed { - /// Private seal for the `FromPyObjectBound` trait. - /// - /// This prevents downstream types from implementing the trait before - /// PyO3 is ready to declare the trait as public API. - pub trait Sealed {} - - // This generic implementation is why the seal is separate from - // `crate::sealed::Sealed`. - impl<'py, T> Sealed for T where T: super::FromPyObject<'py> {} - impl Sealed for &'_ str {} - impl Sealed for std::borrow::Cow<'_, str> {} - impl Sealed for &'_ [u8] {} - impl Sealed for std::borrow::Cow<'_, [u8]> {} -} - -/// Expected form of [`FromPyObject`] to be used in a future PyO3 release. -/// -/// The difference between this and `FromPyObject` is that this trait takes an -/// additional lifetime `'a`, which is the lifetime of the input `Bound`. -/// -/// This allows implementations for `&'a str` and `&'a [u8]`, which could not -/// be expressed by the existing `FromPyObject` trait once the GIL Refs API was -/// removed. +/// Note: Depending on the Python version and implementation, some [`FromPyObject`] implementations +/// may produce a result that borrows into the Python type. This is described by the input lifetime +/// `'a` of `obj`. /// -/// # Usage +/// Types that must not borrow from the input can use [`FromPyObjectOwned`] as a restriction. This +/// is most often the case for collection types. See its documentation for more details. /// -/// Users are prevented from implementing this trait, instead they should implement -/// the normal `FromPyObject` trait. This trait has a blanket implementation -/// for `T: FromPyObject`. +/// # Details +/// [`Cow<'a, str>`] is an example of an output type that may or may not borrow from the input +/// lifetime `'a`. Which variant will be produced depends on the runtime type of the Python object. +/// For a Python byte string, the existing string data can be borrowed for `'a` into a +/// [`Cow::Borrowed`]. For a Python Unicode string, the data may have to be reencoded to UTF-8, and +/// copied into a [`Cow::Owned`]. It does _not_ depend on the Python lifetime `'py`. /// -/// The only case where this trait may have a use case to be implemented is when the -/// lifetime of the extracted value is tied to the lifetime `'a` of the input `Bound` -/// instead of the GIL lifetime `py`, as is the case for the `&'a str` implementation. +/// The output type may also depend on the Python lifetime `'py`. This allows the output type to +/// keep interacting with the Python interpreter. See also [`Bound<'py, T>`]. /// -/// Please contact the PyO3 maintainers if you believe you have a use case for implementing -/// this trait before PyO3 is ready to change the main `FromPyObject` trait to take an -/// additional lifetime. -/// -/// Similarly, users should typically not call these trait methods and should instead -/// use this via the `extract` method on `Bound` and `Py`. -pub trait FromPyObjectBound<'a, 'py>: Sized + from_py_object_bound_sealed::Sealed { +/// [`Cow<'a, str>`]: std::borrow::Cow +/// [`Cow::Borrowed`]: std::borrow::Cow::Borrowed +/// [`Cow::Owned`]: std::borrow::Cow::Owned +pub trait FromPyObject<'a, 'py>: Sized { /// Extracts `Self` from the bound smart pointer `obj`. /// /// Users are advised against calling this method directly: instead, use this via /// [`Bound<'_, PyAny>::extract`] or [`Py::extract`]. - fn from_py_object_bound(ob: Borrowed<'a, 'py, PyAny>) -> PyResult; + fn extract(obj: Borrowed<'a, 'py, PyAny>) -> PyResult; + + /// Deprecated name for [`FromPyObject::extract`] + #[deprecated(since = "0.23.0", note = "replaced by `FromPyObject::extract`")] + fn extract_bound(ob: &'a Bound<'py, PyAny>) -> PyResult { + Self::extract(ob.as_borrowed()) + } /// Extracts the type hint information for this type when it appears as an argument. /// @@ -527,19 +474,52 @@ pub trait FromPyObjectBound<'a, 'py>: Sized + from_py_object_bound_sealed::Seale } } -impl<'py, T> FromPyObjectBound<'_, 'py> for T -where - T: FromPyObject<'py>, -{ - fn from_py_object_bound(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { - Self::extract_bound(&ob) - } - - #[cfg(feature = "experimental-inspect")] - fn type_input() -> TypeInfo { - ::type_input() - } -} +/// A data structure that can be extracted without borrowing any data from the input. +/// +/// This is primarily useful for trait bounds. For example a [`FromPyObject`] implementation of a +/// wrapper type may be able to borrow data from the input, but a [`FromPyObject`] implementation of +/// a collection type may only extract owned data. +/// +/// For example [`PyList`] will not hand out references tied to its own lifetime, but "owned" +/// references independent of it. (Similar to [`Vec>`] where you clone the [`Arc`] out). +/// This makes it impossible to collect borrowed types in a collection, since they would not borrow +/// from the original [`PyList`], but the much shorter lived element reference. See the example +/// below. +/// +/// ``` +/// # use pyo3::prelude::*; +/// pub struct MyWrapper(T); +/// +/// impl<'a, 'py, T> FromPyObject<'a, 'py> for MyWrapper +/// where +/// T: FromPyObject<'a, 'py> +/// { +/// fn extract(obj: Borrowed<'a, 'py, PyAny>) -> PyResult { +/// obj.extract().map(MyWrapper) +/// } +/// } +/// +/// pub struct MyVec(Vec); +/// +/// impl<'py, T> FromPyObject<'_, 'py> for MyVec +/// where +/// T: FromPyObjectOwned<'py> // 👈 can only extract owned values, because each `item` below +/// // is a temporary short lived owned reference +/// { +/// fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { +/// let mut v = MyVec(Vec::new()); +/// for item in obj.try_iter()? { +/// v.0.push(item?.extract::()?); +/// } +/// Ok(v) +/// } +/// } +/// ``` +/// +/// [`PyList`]: crate::types::PyList +/// [`Arc`]: std::sync::Arc +pub trait FromPyObjectOwned<'py>: for<'a> FromPyObject<'a, 'py> {} +impl<'py, T> FromPyObjectOwned<'py> for T where T: for<'a> FromPyObject<'a, 'py> {} /// Identity conversion: allows using existing `PyObject` instances where /// `T: ToPyObject` is expected. @@ -551,30 +531,30 @@ impl ToPyObject for &'_ T { } } -impl FromPyObject<'_> for T +impl FromPyObject<'_, '_> for T where T: PyClass + Clone, { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { let bound = obj.downcast::()?; Ok(bound.try_borrow()?.clone()) } } -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_bound(obj: &Bound<'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_bound(obj: &Bound<'py, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'a, 'py, PyAny>) -> PyResult { obj.downcast::()?.try_borrow_mut().map_err(Into::into) } } diff --git a/src/conversions/chrono.rs b/src/conversions/chrono.rs index 90f9c69761d..7c3606750fd 100644 --- a/src/conversions/chrono.rs +++ b/src/conversions/chrono.rs @@ -41,9 +41,11 @@ //! } //! ``` -use crate::conversion::IntoPyObject; +use crate::conversion::{FromPyObjectOwned, IntoPyObject}; use crate::exceptions::{PyTypeError, PyUserWarning, PyValueError}; #[cfg(Py_LIMITED_API)] +use crate::intern; +#[cfg(Py_LIMITED_API)] use crate::sync::GILOnceCell; use crate::types::any::PyAnyMethods; #[cfg(not(Py_LIMITED_API))] @@ -54,9 +56,7 @@ use crate::types::{ timezone_utc, PyDate, PyDateAccess, PyDateTime, PyDelta, PyDeltaAccess, PyTime, PyTimeAccess, PyTzInfo, PyTzInfoAccess, }; -use crate::{ffi, Bound, FromPyObject, PyAny, PyErr, PyObject, PyResult, Python}; -#[cfg(Py_LIMITED_API)] -use crate::{intern, DowncastError}; +use crate::{ffi, Borrowed, Bound, FromPyObject, PyAny, PyErr, PyObject, PyResult, Python}; #[allow(deprecated)] use crate::{IntoPy, ToPyObject}; use chrono::offset::{FixedOffset, Utc}; @@ -139,8 +139,8 @@ impl<'py> IntoPyObject<'py> for &Duration { } } -impl FromPyObject<'_> for Duration { - fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult { +impl FromPyObject<'_, '_> for Duration { + fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { // Python size are much lower than rust size so we do not need bound checks. // 0 <= microseconds < 1000000 // 0 <= seconds < 3600*24 @@ -223,8 +223,8 @@ impl<'py> IntoPyObject<'py> for &NaiveDate { } } -impl FromPyObject<'_> for NaiveDate { - fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult { +impl FromPyObject<'_, '_> for NaiveDate { + fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { #[cfg(not(Py_LIMITED_API))] { let date = ob.downcast::()?; @@ -300,8 +300,8 @@ impl<'py> IntoPyObject<'py> for &NaiveTime { } } -impl FromPyObject<'_> for NaiveTime { - fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult { +impl FromPyObject<'_, '_> for NaiveTime { + fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { #[cfg(not(Py_LIMITED_API))] { let time = ob.downcast::()?; @@ -381,8 +381,8 @@ impl<'py> IntoPyObject<'py> for &NaiveDateTime { } } -impl FromPyObject<'_> for NaiveDateTime { - fn extract_bound(dt: &Bound<'_, PyAny>) -> PyResult { +impl FromPyObject<'_, '_> for NaiveDateTime { + fn extract(dt: Borrowed<'_, '_, PyAny>) -> PyResult { #[cfg(not(Py_LIMITED_API))] let dt = dt.downcast::()?; #[cfg(Py_LIMITED_API)] @@ -473,8 +473,11 @@ impl<'py, Tz: TimeZone> IntoPyObject<'py> for &DateTime { } } -impl FromPyObject<'py>> FromPyObject<'_> for DateTime { - fn extract_bound(dt: &Bound<'_, PyAny>) -> PyResult> { +impl<'py, Tz> FromPyObject<'_, 'py> for DateTime +where + Tz: TimeZone + FromPyObjectOwned<'py>, +{ + fn extract(dt: Borrowed<'_, 'py, PyAny>) -> PyResult> { #[cfg(not(Py_LIMITED_API))] let dt = dt.downcast::()?; #[cfg(Py_LIMITED_API)] @@ -556,12 +559,12 @@ impl<'py> IntoPyObject<'py> for &FixedOffset { } } -impl FromPyObject<'_> for FixedOffset { +impl FromPyObject<'_, '_> for FixedOffset { /// Convert python tzinfo to rust [`FixedOffset`]. /// /// Note that the conversion will result in precision lost in microseconds as chrono offset /// does not supports microseconds. - fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult { + fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { #[cfg(not(Py_LIMITED_API))] let ob = ob.downcast::()?; #[cfg(Py_LIMITED_API)] @@ -637,8 +640,8 @@ impl<'py> IntoPyObject<'py> for &Utc { } } -impl FromPyObject<'_> for Utc { - fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult { +impl FromPyObject<'_, '_> for Utc { + fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { let py_utc = timezone_utc(ob.py()); if ob.eq(py_utc)? { Ok(Utc) @@ -730,7 +733,9 @@ fn warn_truncated_leap_second(obj: &Bound<'_, PyAny>) { } #[cfg(not(Py_LIMITED_API))] -fn py_date_to_naive_date(py_date: &impl PyDateAccess) -> PyResult { +fn py_date_to_naive_date( + py_date: impl std::ops::Deref, +) -> PyResult { NaiveDate::from_ymd_opt( py_date.get_year(), py_date.get_month().into(), @@ -740,7 +745,7 @@ fn py_date_to_naive_date(py_date: &impl PyDateAccess) -> PyResult { } #[cfg(Py_LIMITED_API)] -fn py_date_to_naive_date(py_date: &Bound<'_, PyAny>) -> PyResult { +fn py_date_to_naive_date(py_date: Borrowed<'_, '_, PyAny>) -> PyResult { NaiveDate::from_ymd_opt( py_date.getattr(intern!(py_date.py(), "year"))?.extract()?, py_date.getattr(intern!(py_date.py(), "month"))?.extract()?, @@ -750,7 +755,9 @@ fn py_date_to_naive_date(py_date: &Bound<'_, PyAny>) -> PyResult { } #[cfg(not(Py_LIMITED_API))] -fn py_time_to_naive_time(py_time: &impl PyTimeAccess) -> PyResult { +fn py_time_to_naive_time( + py_time: impl std::ops::Deref, +) -> PyResult { NaiveTime::from_hms_micro_opt( py_time.get_hour().into(), py_time.get_minute().into(), @@ -761,7 +768,7 @@ fn py_time_to_naive_time(py_time: &impl PyTimeAccess) -> PyResult { } #[cfg(Py_LIMITED_API)] -fn py_time_to_naive_time(py_time: &Bound<'_, PyAny>) -> PyResult { +fn py_time_to_naive_time(py_time: Borrowed<'_, '_, PyAny>) -> PyResult { NaiveTime::from_hms_micro_opt( py_time.getattr(intern!(py_time.py(), "hour"))?.extract()?, py_time @@ -778,9 +785,15 @@ fn py_time_to_naive_time(py_time: &Bound<'_, PyAny>) -> PyResult { } #[cfg(Py_LIMITED_API)] -fn check_type(value: &Bound<'_, PyAny>, t: &PyObject, type_name: &'static str) -> PyResult<()> { +fn check_type( + value: Borrowed<'_, '_, PyAny>, + t: &PyObject, + type_name: &'static str, +) -> PyResult<()> { + use crate::DowncastError; + if !value.is_instance(t.bind(value.py()))? { - return Err(DowncastError::new(value, type_name).into()); + return Err(DowncastError::new_from_borrowed(value, type_name).into()); } Ok(()) } diff --git a/src/conversions/chrono_tz.rs b/src/conversions/chrono_tz.rs index bb1a74c1519..776042aae33 100644 --- a/src/conversions/chrono_tz.rs +++ b/src/conversions/chrono_tz.rs @@ -39,7 +39,7 @@ use crate::exceptions::PyValueError; use crate::pybacked::PyBackedStr; use crate::sync::GILOnceCell; use crate::types::{any::PyAnyMethods, PyType}; -use crate::{intern, Bound, FromPyObject, Py, PyAny, PyErr, PyObject, PyResult, Python}; +use crate::{intern, Borrowed, Bound, FromPyObject, Py, PyAny, PyErr, PyObject, PyResult, Python}; #[allow(deprecated)] use crate::{IntoPy, ToPyObject}; use chrono_tz::Tz; @@ -85,8 +85,8 @@ impl<'py> IntoPyObject<'py> for &Tz { } } -impl FromPyObject<'_> for Tz { - fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult { +impl FromPyObject<'_, '_> for Tz { + fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { Tz::from_str( &ob.getattr(intern!(ob.py(), "key"))? .extract::()?, @@ -98,6 +98,7 @@ impl FromPyObject<'_> for Tz { #[cfg(all(test, not(windows)))] // Troubles loading timezones on Windows mod tests { use super::*; + use crate::Bound; #[test] fn test_frompyobject() { diff --git a/src/conversions/either.rs b/src/conversions/either.rs index a514b1fde8d..f1999fa3f6a 100644 --- a/src/conversions/either.rs +++ b/src/conversions/either.rs @@ -47,8 +47,8 @@ #[cfg(feature = "experimental-inspect")] use crate::inspect::types::TypeInfo; use crate::{ - exceptions::PyTypeError, types::any::PyAnyMethods, Bound, FromPyObject, IntoPyObject, - IntoPyObjectExt, PyAny, PyErr, PyObject, PyResult, Python, + exceptions::PyTypeError, Borrowed, Bound, FromPyObject, IntoPyObject, IntoPyObjectExt, PyAny, + PyErr, PyObject, PyResult, Python, }; #[allow(deprecated)] use crate::{IntoPy, ToPyObject}; @@ -123,13 +123,13 @@ where } #[cfg_attr(docsrs, doc(cfg(feature = "either")))] -impl<'py, L, R> FromPyObject<'py> for Either +impl<'a, 'py, L, R> FromPyObject<'a, 'py> for Either where - L: FromPyObject<'py>, - R: FromPyObject<'py>, + L: FromPyObject<'a, 'py>, + R: FromPyObject<'a, 'py>, { #[inline] - fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'a, 'py, PyAny>) -> PyResult { if let Ok(l) = obj.extract::() { Ok(Either::Left(l)) } else if let Ok(r) = obj.extract::() { diff --git a/src/conversions/hashbrown.rs b/src/conversions/hashbrown.rs index 0efe7f5161f..b9eea160999 100644 --- a/src/conversions/hashbrown.rs +++ b/src/conversions/hashbrown.rs @@ -17,7 +17,7 @@ //! Note that you must use compatible versions of hashbrown and PyO3. //! The required hashbrown version may vary based on the version of PyO3. use crate::{ - conversion::IntoPyObject, + conversion::{FromPyObjectOwned, IntoPyObject}, types::{ any::PyAnyMethods, dict::PyDictMethods, @@ -25,7 +25,7 @@ use crate::{ set::{new_from_iter, try_new_from_iter, PySetMethods}, PyDict, PyFrozenSet, PySet, }, - Bound, FromPyObject, PyAny, PyErr, PyObject, PyResult, Python, + Borrowed, Bound, FromPyObject, PyAny, PyErr, PyObject, PyResult, Python, }; #[allow(deprecated)] use crate::{IntoPy, ToPyObject}; @@ -101,16 +101,16 @@ where } } -impl<'py, K, V, S> FromPyObject<'py> for hashbrown::HashMap +impl<'py, K, V, S> FromPyObject<'_, 'py> for hashbrown::HashMap where - K: FromPyObject<'py> + cmp::Eq + hash::Hash, - V: FromPyObject<'py>, + K: FromPyObjectOwned<'py> + cmp::Eq + hash::Hash, + V: FromPyObjectOwned<'py>, S: hash::BuildHasher + Default, { - fn extract_bound(ob: &Bound<'py, PyAny>) -> Result { + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> Result { let dict = ob.downcast::()?; let mut ret = hashbrown::HashMap::with_capacity_and_hasher(dict.len(), S::default()); - for (k, v) in dict { + for (k, v) in dict.iter() { ret.insert(k.extract()?, v.extract()?); } Ok(ret) @@ -170,12 +170,12 @@ where } } -impl<'py, K, S> FromPyObject<'py> for hashbrown::HashSet +impl<'py, K, S> FromPyObject<'_, 'py> for hashbrown::HashSet where - K: FromPyObject<'py> + cmp::Eq + hash::Hash, + K: FromPyObjectOwned<'py> + cmp::Eq + hash::Hash, S: hash::BuildHasher + Default, { - fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { match ob.downcast::() { Ok(set) => set.iter().map(|any| any.extract()).collect(), Err(err) => { diff --git a/src/conversions/indexmap.rs b/src/conversions/indexmap.rs index e3787e68091..1600829bd03 100644 --- a/src/conversions/indexmap.rs +++ b/src/conversions/indexmap.rs @@ -87,9 +87,9 @@ //! # if another hash table was used, the order could be random //! ``` -use crate::conversion::IntoPyObject; +use crate::conversion::{FromPyObjectOwned, IntoPyObject}; use crate::types::*; -use crate::{Bound, FromPyObject, PyErr, PyObject, Python}; +use crate::{Borrowed, Bound, FromPyObject, PyErr, PyObject, Python}; #[allow(deprecated)] use crate::{IntoPy, ToPyObject}; use std::{cmp, hash}; @@ -164,16 +164,16 @@ where } } -impl<'py, K, V, S> FromPyObject<'py> for indexmap::IndexMap +impl<'py, K, V, S> FromPyObject<'_, 'py> for indexmap::IndexMap where - K: FromPyObject<'py> + cmp::Eq + hash::Hash, - V: FromPyObject<'py>, + K: FromPyObjectOwned<'py> + cmp::Eq + hash::Hash, + V: FromPyObjectOwned<'py>, S: hash::BuildHasher + Default, { - fn extract_bound(ob: &Bound<'py, PyAny>) -> Result { + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> Result { let dict = ob.downcast::()?; let mut ret = indexmap::IndexMap::with_capacity_and_hasher(dict.len(), S::default()); - for (k, v) in dict { + for (k, v) in dict.iter() { ret.insert(k.extract()?, v.extract()?); } Ok(ret) diff --git a/src/conversions/num_bigint.rs b/src/conversions/num_bigint.rs index d91973b5572..0dce1f3bec1 100644 --- a/src/conversions/num_bigint.rs +++ b/src/conversions/num_bigint.rs @@ -52,9 +52,8 @@ use crate::types::{bytes::PyBytesMethods, PyBytes}; use crate::{ conversion::IntoPyObject, ffi, - instance::Bound, types::{any::PyAnyMethods, PyInt}, - FromPyObject, Py, PyAny, PyErr, PyObject, PyResult, Python, + Borrowed, Bound, FromPyObject, Py, PyAny, PyErr, PyObject, PyResult, Python, }; #[allow(deprecated)] use crate::{IntoPy, ToPyObject}; @@ -145,8 +144,8 @@ bigint_conversion!(BigUint, false, BigUint::to_bytes_le); bigint_conversion!(BigInt, true, BigInt::to_signed_bytes_le); #[cfg_attr(docsrs, doc(cfg(feature = "num-bigint")))] -impl<'py> FromPyObject<'py> for BigInt { - fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { +impl<'py> FromPyObject<'_, 'py> for BigInt { + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { let py = ob.py(); // fast path - checking for subclass of `int` just checks a bit in the type object let num_owned: Py; @@ -154,11 +153,11 @@ impl<'py> FromPyObject<'py> for BigInt { long } else { num_owned = unsafe { Py::from_owned_ptr_or_err(py, ffi::PyNumber_Index(ob.as_ptr()))? }; - num_owned.bind(py) + num_owned.bind_borrowed(py) }; #[cfg(not(Py_LIMITED_API))] { - let mut buffer = int_to_u32_vec::(num)?; + let mut buffer = int_to_u32_vec::(&num)?; let sign = if buffer.last().copied().map_or(false, |last| last >> 31 != 0) { // BigInt::new takes an unsigned array, so need to convert from two's complement // flip all bits, 'subtract' 1 (by adding one to the unsigned array) @@ -182,19 +181,19 @@ impl<'py> FromPyObject<'py> for BigInt { } #[cfg(Py_LIMITED_API)] { - let n_bits = int_n_bits(num)?; + let n_bits = int_n_bits(&num)?; if n_bits == 0 { return Ok(BigInt::from(0isize)); } - let bytes = int_to_py_bytes(num, (n_bits + 8) / 8, true)?; + let bytes = int_to_py_bytes(&num, (n_bits + 8) / 8, true)?; Ok(BigInt::from_signed_bytes_le(bytes.as_bytes())) } } } #[cfg_attr(docsrs, doc(cfg(feature = "num-bigint")))] -impl<'py> FromPyObject<'py> for BigUint { - fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { +impl<'py> FromPyObject<'_, 'py> for BigUint { + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { let py = ob.py(); // fast path - checking for subclass of `int` just checks a bit in the type object let num_owned: Py; @@ -202,20 +201,20 @@ impl<'py> FromPyObject<'py> for BigUint { long } else { num_owned = unsafe { Py::from_owned_ptr_or_err(py, ffi::PyNumber_Index(ob.as_ptr()))? }; - num_owned.bind(py) + num_owned.bind_borrowed(py) }; #[cfg(not(Py_LIMITED_API))] { - let buffer = int_to_u32_vec::(num)?; + let buffer = int_to_u32_vec::(&num)?; Ok(BigUint::new(buffer)) } #[cfg(Py_LIMITED_API)] { - let n_bits = int_n_bits(num)?; + let n_bits = int_n_bits(&num)?; if n_bits == 0 { return Ok(BigUint::from(0usize)); } - let bytes = int_to_py_bytes(num, (n_bits + 7) / 8, false)?; + let bytes = int_to_py_bytes(&num, (n_bits + 7) / 8, false)?; Ok(BigUint::from_bytes_le(bytes.as_bytes())) } } @@ -300,6 +299,7 @@ fn int_to_py_bytes<'py>( is_signed: bool, ) -> PyResult> { use crate::intern; + use crate::types::{PyAnyMethods, PyDictMethods}; let py = long.py(); let kwargs = if is_signed { let kwargs = crate::types::PyDict::new(py); @@ -333,6 +333,7 @@ fn int_n_bits(long: &Bound<'_, PyInt>) -> PyResult { #[cfg(Py_LIMITED_API)] { // slow path + use crate::types::PyAnyMethods; long.call_method0(crate::intern!(py, "bit_length")) .and_then(|any| any.extract()) } @@ -342,7 +343,7 @@ fn int_n_bits(long: &Bound<'_, PyInt>) -> PyResult { mod tests { use super::*; use crate::tests::common::generate_unique_module_name; - use crate::types::{PyDict, PyModule}; + use crate::types::{PyAnyMethods, PyDict, PyModule}; use indoc::indoc; use pyo3_ffi::c_str; diff --git a/src/conversions/num_complex.rs b/src/conversions/num_complex.rs index db981a4179b..54c575329b4 100644 --- a/src/conversions/num_complex.rs +++ b/src/conversions/num_complex.rs @@ -99,7 +99,7 @@ use crate::{ ffi, ffi_ptr_ext::FfiPtrExt, types::{any::PyAnyMethods, PyComplex}, - Bound, FromPyObject, PyAny, PyErr, PyObject, PyResult, Python, + Borrowed, Bound, FromPyObject, PyAny, PyErr, PyObject, PyResult, Python, }; use num_complex::Complex; use std::os::raw::c_double; @@ -171,8 +171,8 @@ macro_rules! complex_conversion { } #[cfg_attr(docsrs, doc(cfg(feature = "num-complex")))] - impl FromPyObject<'_> for Complex<$float> { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult> { + impl FromPyObject<'_, '_> for Complex<$float> { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult> { #[cfg(not(any(Py_LIMITED_API, PyPy)))] unsafe { let val = ffi::PyComplex_AsCComplex(obj.as_ptr()); @@ -193,7 +193,7 @@ macro_rules! complex_conversion { obj.lookup_special(crate::intern!(obj.py(), "__complex__"))? { complex = method.call0()?; - &complex + complex.as_borrowed() } else { // `obj` might still implement `__float__` or `__index__`, which will be // handled by `PyComplex_{Real,Imag}AsDouble`, including propagating any diff --git a/src/conversions/num_rational.rs b/src/conversions/num_rational.rs index 6c48f67fcf2..f3ba184a84a 100644 --- a/src/conversions/num_rational.rs +++ b/src/conversions/num_rational.rs @@ -48,7 +48,7 @@ use crate::ffi; use crate::sync::GILOnceCell; use crate::types::any::PyAnyMethods; use crate::types::PyType; -use crate::{Bound, FromPyObject, Py, PyAny, PyErr, PyObject, PyResult, Python}; +use crate::{Borrowed, Bound, FromPyObject, Py, PyAny, PyErr, PyObject, PyResult, Python}; #[allow(deprecated)] use crate::{IntoPy, ToPyObject}; @@ -64,8 +64,8 @@ fn get_fraction_cls(py: Python<'_>) -> PyResult<&Bound<'_, PyType>> { macro_rules! rational_conversion { ($int: ty) => { - impl<'py> FromPyObject<'py> for Ratio<$int> { - fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult { + impl<'py> FromPyObject<'_, 'py> for Ratio<$int> { + fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { let py = obj.py(); let py_numerator_obj = obj.getattr(crate::intern!(py, "numerator"))?; let py_denominator_obj = obj.getattr(crate::intern!(py, "denominator"))?; diff --git a/src/conversions/rust_decimal.rs b/src/conversions/rust_decimal.rs index 7c6eda21a66..f22b8f8afc8 100644 --- a/src/conversions/rust_decimal.rs +++ b/src/conversions/rust_decimal.rs @@ -55,14 +55,14 @@ use crate::sync::GILOnceCell; use crate::types::any::PyAnyMethods; use crate::types::string::PyStringMethods; use crate::types::PyType; -use crate::{Bound, FromPyObject, Py, PyAny, PyErr, PyObject, PyResult, Python}; +use crate::{Borrowed, Bound, FromPyObject, Py, PyAny, PyErr, PyObject, PyResult, Python}; #[allow(deprecated)] use crate::{IntoPy, ToPyObject}; use rust_decimal::Decimal; use std::str::FromStr; -impl FromPyObject<'_> for Decimal { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { +impl FromPyObject<'_, '_> for Decimal { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { // use the string representation to not be lossy if let Ok(val) = obj.extract() { Ok(Decimal::new(val, 0)) diff --git a/src/conversions/smallvec.rs b/src/conversions/smallvec.rs index 99244d81417..a88beef0dda 100644 --- a/src/conversions/smallvec.rs +++ b/src/conversions/smallvec.rs @@ -15,15 +15,17 @@ //! //! Note that you must use compatible versions of smallvec and PyO3. //! The required smallvec version may vary based on the version of PyO3. -use crate::conversion::IntoPyObject; +use crate::conversion::{FromPyObjectOwned, IntoPyObject}; use crate::exceptions::PyTypeError; #[cfg(feature = "experimental-inspect")] use crate::inspect::types::TypeInfo; use crate::types::any::PyAnyMethods; use crate::types::list::new_from_iter; use crate::types::{PySequence, PyString}; -use crate::PyErr; -use crate::{err::DowncastError, ffi, Bound, FromPyObject, PyAny, PyObject, PyResult, Python}; +use crate::{ + err::DowncastError, ffi, Borrowed, Bound, FromPyObject, PyAny, PyErr, PyObject, PyResult, + Python, +}; #[allow(deprecated)] use crate::{IntoPy, ToPyObject}; use smallvec::{Array, SmallVec}; @@ -97,12 +99,12 @@ where } } -impl<'py, A> FromPyObject<'py> for SmallVec +impl<'py, A> FromPyObject<'_, 'py> for SmallVec where A: Array, - A::Item: FromPyObject<'py>, + A::Item: FromPyObjectOwned<'py>, { - fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { if obj.is_instance_of::() { return Err(PyTypeError::new_err("Can't extract `str` to `SmallVec`")); } @@ -115,10 +117,10 @@ where } } -fn extract_sequence<'py, A>(obj: &Bound<'py, PyAny>) -> PyResult> +fn extract_sequence<'py, A>(obj: Borrowed<'_, 'py, PyAny>) -> PyResult> where A: Array, - A::Item: FromPyObject<'py>, + A::Item: FromPyObjectOwned<'py>, { // Types that pass `PySequence_Check` usually implement enough of the sequence protocol // to support this function and if not, we will only fail extraction safely. @@ -126,7 +128,7 @@ where if ffi::PySequence_Check(obj.as_ptr()) != 0 { obj.downcast_unchecked::() } else { - return Err(DowncastError::new(obj, "Sequence").into()); + return Err(DowncastError::new_from_borrowed(obj, "Sequence").into()); } }; diff --git a/src/conversions/std/array.rs b/src/conversions/std/array.rs index 1c33a610273..624b5846f0b 100644 --- a/src/conversions/std/array.rs +++ b/src/conversions/std/array.rs @@ -1,9 +1,8 @@ -use crate::conversion::IntoPyObject; -use crate::instance::Bound; +use crate::conversion::{FromPyObjectOwned, IntoPyObject}; use crate::types::any::PyAnyMethods; use crate::types::PySequence; use crate::{err::DowncastError, ffi, FromPyObject, Py, PyAny, PyObject, PyResult, Python}; -use crate::{exceptions, PyErr}; +use crate::{exceptions, Borrowed, Bound, PyErr}; #[allow(deprecated)] use crate::{IntoPy, ToPyObject}; @@ -79,18 +78,18 @@ where } } -impl<'py, T, const N: usize> FromPyObject<'py> for [T; N] +impl<'py, T, const N: usize> FromPyObject<'_, 'py> for [T; N] where - T: FromPyObject<'py>, + T: FromPyObjectOwned<'py>, { - fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { create_array_from_obj(obj) } } -fn create_array_from_obj<'py, T, const N: usize>(obj: &Bound<'py, PyAny>) -> PyResult<[T; N]> +fn create_array_from_obj<'py, T, const N: usize>(obj: Borrowed<'_, 'py, PyAny>) -> PyResult<[T; N]> where - T: FromPyObject<'py>, + T: FromPyObjectOwned<'py>, { // Types that pass `PySequence_Check` usually implement enough of the sequence protocol // to support this function and if not, we will only fail extraction safely. @@ -98,7 +97,7 @@ where if ffi::PySequence_Check(obj.as_ptr()) != 0 { obj.downcast_unchecked::() } else { - return Err(DowncastError::new(obj, "Sequence").into()); + return Err(DowncastError::new_from_borrowed(obj, "Sequence").into()); } }; let seq_len = seq.len()?; diff --git a/src/conversions/std/cell.rs b/src/conversions/std/cell.rs index 70da688b70c..2c91d237d63 100644 --- a/src/conversions/std/cell.rs +++ b/src/conversions/std/cell.rs @@ -1,9 +1,6 @@ use std::cell::Cell; -use crate::{ - conversion::IntoPyObject, types::any::PyAnyMethods, Bound, FromPyObject, PyAny, PyObject, - PyResult, Python, -}; +use crate::{conversion::IntoPyObject, Borrowed, FromPyObject, PyAny, PyObject, PyResult, Python}; #[allow(deprecated)] impl crate::ToPyObject for Cell { @@ -41,8 +38,8 @@ impl<'py, T: Copy + IntoPyObject<'py>> IntoPyObject<'py> for &Cell { } } -impl<'py, T: FromPyObject<'py>> FromPyObject<'py> for Cell { - fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { +impl<'a, 'py, T: FromPyObject<'a, 'py>> FromPyObject<'a, 'py> for Cell { + fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult { ob.extract().map(Cell::new) } } diff --git a/src/conversions/std/ipaddr.rs b/src/conversions/std/ipaddr.rs index 7ba8da87749..eabb1d75bc2 100755 --- a/src/conversions/std/ipaddr.rs +++ b/src/conversions/std/ipaddr.rs @@ -2,17 +2,16 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use crate::conversion::IntoPyObject; use crate::exceptions::PyValueError; -use crate::instance::Bound; use crate::sync::GILOnceCell; use crate::types::any::PyAnyMethods; use crate::types::string::PyStringMethods; use crate::types::PyType; -use crate::{intern, FromPyObject, Py, PyAny, PyErr, PyObject, PyResult, Python}; +use crate::{intern, Borrowed, Bound, FromPyObject, Py, PyAny, PyErr, PyObject, PyResult, Python}; #[allow(deprecated)] use crate::{IntoPy, ToPyObject}; -impl FromPyObject<'_> for IpAddr { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { +impl FromPyObject<'_, '_> for IpAddr { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { match obj.getattr(intern!(obj.py(), "packed")) { Ok(packed) => { if let Ok(packed) = packed.extract::<[u8; 4]>() { diff --git a/src/conversions/std/map.rs b/src/conversions/std/map.rs index 8a6ba57012e..4f15cf06e71 100644 --- a/src/conversions/std/map.rs +++ b/src/conversions/std/map.rs @@ -3,10 +3,10 @@ use std::{cmp, collections, hash}; #[cfg(feature = "experimental-inspect")] use crate::inspect::types::TypeInfo; use crate::{ - conversion::IntoPyObject, + conversion::{FromPyObjectOwned, IntoPyObject}, instance::Bound, types::{any::PyAnyMethods, dict::PyDictMethods, PyDict}, - FromPyObject, PyAny, PyErr, PyObject, Python, + Borrowed, FromPyObject, PyAny, PyErr, PyObject, Python, }; #[allow(deprecated)] use crate::{IntoPy, ToPyObject}; @@ -171,16 +171,16 @@ where } } -impl<'py, K, V, S> FromPyObject<'py> for collections::HashMap +impl<'py, K, V, S> FromPyObject<'_, 'py> for collections::HashMap where - K: FromPyObject<'py> + cmp::Eq + hash::Hash, - V: FromPyObject<'py>, + K: FromPyObjectOwned<'py> + cmp::Eq + hash::Hash, + V: FromPyObjectOwned<'py>, S: hash::BuildHasher + Default, { - fn extract_bound(ob: &Bound<'py, PyAny>) -> Result { + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> Result { let dict = ob.downcast::()?; let mut ret = collections::HashMap::with_capacity_and_hasher(dict.len(), S::default()); - for (k, v) in dict { + for (k, v) in dict.iter() { ret.insert(k.extract()?, v.extract()?); } Ok(ret) @@ -192,15 +192,15 @@ where } } -impl<'py, K, V> FromPyObject<'py> for collections::BTreeMap +impl<'py, K, V> FromPyObject<'_, 'py> for collections::BTreeMap where - K: FromPyObject<'py> + cmp::Ord, - V: FromPyObject<'py>, + K: FromPyObjectOwned<'py> + cmp::Ord, + V: FromPyObjectOwned<'py>, { - fn extract_bound(ob: &Bound<'py, PyAny>) -> Result { + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> Result { let dict = ob.downcast::()?; let mut ret = collections::BTreeMap::new(); - for (k, v) in dict { + for (k, v) in dict.iter() { ret.insert(k.extract()?, v.extract()?); } Ok(ret) diff --git a/src/conversions/std/num.rs b/src/conversions/std/num.rs index 0450c591ae8..952eab3c449 100644 --- a/src/conversions/std/num.rs +++ b/src/conversions/std/num.rs @@ -5,7 +5,9 @@ use crate::ffi_ptr_ext::FfiPtrExt; use crate::inspect::types::TypeInfo; use crate::types::any::PyAnyMethods; use crate::types::{PyBytes, PyInt}; -use crate::{exceptions, ffi, Bound, FromPyObject, PyAny, PyErr, PyObject, PyResult, Python}; +use crate::{ + exceptions, ffi, Borrowed, Bound, FromPyObject, PyAny, PyErr, PyObject, PyResult, Python, +}; #[allow(deprecated)] use crate::{IntoPy, ToPyObject}; use std::convert::Infallible; @@ -62,8 +64,8 @@ macro_rules! int_fits_larger_int { } } - impl FromPyObject<'_> for $rust_type { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { + impl FromPyObject<'_, '_> for $rust_type { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { let val: $larger_type = obj.extract()?; <$rust_type>::try_from(val) .map_err(|e| exceptions::PyOverflowError::new_err(e.to_string())) @@ -145,14 +147,9 @@ macro_rules! int_convert_u64_or_i64 { fn into_pyobject(self, py: Python<'py>) -> Result { (*self).into_pyobject(py) } - - #[cfg(feature = "experimental-inspect")] - fn type_output() -> TypeInfo { - TypeInfo::builtin("int") - } } - impl FromPyObject<'_> for $rust_type { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<$rust_type> { + impl FromPyObject<'_, '_> for $rust_type { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult<$rust_type> { extract_int!(obj, !0, $pylong_as_ll_or_ull, $force_index_call) } @@ -216,8 +213,8 @@ macro_rules! int_fits_c_long { } } - impl<'py> FromPyObject<'py> for $rust_type { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { + impl<'py> FromPyObject<'_, 'py> for $rust_type { + fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { let val: c_long = extract_int!(obj, -1, ffi::PyLong_AsLong)?; <$rust_type>::try_from(val) .map_err(|e| exceptions::PyOverflowError::new_err(e.to_string())) @@ -304,8 +301,8 @@ impl<'py> IntoPyObject<'py> for &'_ u8 { } } -impl FromPyObject<'_> for u8 { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { +impl<'py> FromPyObject<'_, 'py> for u8 { + fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { let val: c_long = extract_int!(obj, -1, ffi::PyLong_AsLong)?; u8::try_from(val).map_err(|e| exceptions::PyOverflowError::new_err(e.to_string())) } @@ -442,8 +439,8 @@ mod fast_128bit_int_conversion { } } - impl FromPyObject<'_> for $rust_type { - fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<$rust_type> { + impl FromPyObject<'_, '_> for $rust_type { + fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult<$rust_type> { let num = unsafe { ffi::PyNumber_Index(ob.as_ptr()).assume_owned_or_err(ob.py())? }; let mut buffer = [0u8; std::mem::size_of::<$rust_type>()]; @@ -567,8 +564,8 @@ mod slow_128bit_int_conversion { } } - impl FromPyObject<'_> for $rust_type { - fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<$rust_type> { + impl FromPyObject<'_, '_> for $rust_type { + fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult<$rust_type> { let py = ob.py(); unsafe { let lower = err_if_invalid_value( @@ -662,8 +659,8 @@ macro_rules! nonzero_int_impl { } } - impl FromPyObject<'_> for $nonzero_type { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { + impl FromPyObject<'_, '_> for $nonzero_type { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { let val: $primitive_type = obj.extract()?; <$nonzero_type>::try_from(val) .map_err(|_| exceptions::PyValueError::new_err("invalid zero value")) @@ -693,6 +690,7 @@ nonzero_int_impl!(NonZeroUsize, usize); #[cfg(test)] mod test_128bit_integers { use super::*; + use crate::types::PyAnyMethods; #[cfg(not(target_arch = "wasm32"))] use crate::types::PyDict; diff --git a/src/conversions/std/option.rs b/src/conversions/std/option.rs index aac8c7ab210..80226afc4d6 100644 --- a/src/conversions/std/option.rs +++ b/src/conversions/std/option.rs @@ -1,7 +1,8 @@ use crate::{ - conversion::IntoPyObject, ffi, types::any::PyAnyMethods, AsPyPointer, Bound, BoundObject, + conversion::IntoPyObject, ffi, types::any::PyAnyMethods, AsPyPointer, BoundObject, FromPyObject, PyAny, PyObject, PyResult, Python, }; +use crate::{Borrowed, Bound}; /// `Option::Some` is converted like `T`. /// `Option::None` is converted to Python `None`. @@ -60,11 +61,11 @@ where } } -impl<'py, T> FromPyObject<'py> for Option +impl<'a, 'py, T> FromPyObject<'a, 'py> for Option where - T: FromPyObject<'py>, + T: FromPyObject<'a, 'py>, { - fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'a, 'py, PyAny>) -> PyResult { if obj.is_none() { Ok(None) } else { diff --git a/src/conversions/std/osstr.rs b/src/conversions/std/osstr.rs index 70b5bd152ac..875608bed4a 100644 --- a/src/conversions/std/osstr.rs +++ b/src/conversions/std/osstr.rs @@ -3,7 +3,7 @@ use crate::ffi_ptr_ext::FfiPtrExt; use crate::instance::Bound; use crate::types::any::PyAnyMethods; use crate::types::PyString; -use crate::{ffi, FromPyObject, PyAny, PyObject, PyResult, Python}; +use crate::{ffi, Borrowed, FromPyObject, PyAny, PyObject, PyResult, Python}; #[allow(deprecated)] use crate::{IntoPy, ToPyObject}; use std::borrow::Cow; @@ -81,8 +81,8 @@ impl<'py> IntoPyObject<'py> for &&OsStr { // There's no FromPyObject implementation for &OsStr because albeit possible on Unix, this would // be impossible to implement on Windows. Hence it's omitted entirely -impl FromPyObject<'_> for OsString { - fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult { +impl FromPyObject<'_, '_> for OsString { + fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { let pystring = ob.downcast::()?; #[cfg(not(windows))] @@ -108,8 +108,6 @@ impl FromPyObject<'_> for OsString { #[cfg(windows)] { - use crate::types::string::PyStringMethods; - // Take the quick and easy shortcut if UTF-8 if let Ok(utf8_string) = pystring.to_cow() { return Ok(utf8_string.into_owned().into()); @@ -228,7 +226,7 @@ impl<'py> IntoPyObject<'py> for &OsString { #[cfg(test)] mod tests { - use crate::types::{PyAnyMethods, PyString, PyStringMethods}; + use crate::types::{PyString, PyStringMethods}; use crate::{BoundObject, IntoPyObject, Python}; use std::fmt::Debug; use std::{ @@ -240,6 +238,7 @@ mod tests { #[cfg(not(windows))] fn test_non_utf8_conversion() { Python::with_gil(|py| { + use crate::types::PyAnyMethods; #[cfg(not(target_os = "wasi"))] use std::os::unix::ffi::OsStrExt; #[cfg(target_os = "wasi")] diff --git a/src/conversions/std/path.rs b/src/conversions/std/path.rs index dc528ee3595..badb922fc9a 100644 --- a/src/conversions/std/path.rs +++ b/src/conversions/std/path.rs @@ -1,9 +1,8 @@ use crate::conversion::IntoPyObject; use crate::ffi_ptr_ext::FfiPtrExt; -use crate::instance::Bound; use crate::types::any::PyAnyMethods; use crate::types::PyString; -use crate::{ffi, FromPyObject, PyAny, PyObject, PyResult, Python}; +use crate::{ffi, Borrowed, Bound, FromPyObject, PyAny, PyObject, PyResult, Python}; #[allow(deprecated)] use crate::{IntoPy, ToPyObject}; use std::borrow::Cow; @@ -21,8 +20,8 @@ impl ToPyObject for Path { // See osstr.rs for why there's no FromPyObject impl for &Path -impl FromPyObject<'_> for PathBuf { - fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult { +impl FromPyObject<'_, '_> for PathBuf { + fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { // We use os.fspath to get the underlying path as bytes or str let path = unsafe { ffi::PyOS_FSPath(ob.as_ptr()).assume_owned_or_err(ob.py())? }; Ok(path.extract::()?.into()) @@ -145,7 +144,7 @@ impl<'py> IntoPyObject<'py> for &PathBuf { #[cfg(test)] mod tests { - use crate::types::{PyAnyMethods, PyString, PyStringMethods}; + use crate::types::{PyString, PyStringMethods}; use crate::{BoundObject, IntoPyObject, Python}; use std::borrow::Cow; use std::fmt::Debug; @@ -155,6 +154,7 @@ mod tests { #[cfg(not(windows))] fn test_non_utf8_conversion() { Python::with_gil(|py| { + use crate::types::PyAnyMethods; use std::ffi::OsStr; #[cfg(not(target_os = "wasi"))] use std::os::unix::ffi::OsStrExt; diff --git a/src/conversions/std/set.rs b/src/conversions/std/set.rs index 3f1e6733f1a..fdbd667a2c5 100644 --- a/src/conversions/std/set.rs +++ b/src/conversions/std/set.rs @@ -3,15 +3,14 @@ use std::{cmp, collections, hash}; #[cfg(feature = "experimental-inspect")] use crate::inspect::types::TypeInfo; use crate::{ - conversion::IntoPyObject, - instance::Bound, + conversion::{FromPyObjectOwned, IntoPyObject}, types::{ any::PyAnyMethods, frozenset::PyFrozenSetMethods, set::{new_from_iter, try_new_from_iter, PySetMethods}, PyFrozenSet, PySet, }, - FromPyObject, PyAny, PyErr, PyObject, PyResult, Python, + Borrowed, Bound, FromPyObject, PyAny, PyErr, PyObject, PyResult, Python, }; #[allow(deprecated)] use crate::{IntoPy, ToPyObject}; @@ -93,12 +92,12 @@ where } } -impl<'py, K, S> FromPyObject<'py> for collections::HashSet +impl<'py, K, S> FromPyObject<'_, 'py> for collections::HashSet where - K: FromPyObject<'py> + cmp::Eq + hash::Hash, + K: FromPyObjectOwned<'py> + cmp::Eq + hash::Hash, S: hash::BuildHasher + Default, { - fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { match ob.downcast::() { Ok(set) => set.iter().map(|any| any.extract()).collect(), Err(err) => { @@ -166,11 +165,11 @@ where } } -impl<'py, K> FromPyObject<'py> for collections::BTreeSet +impl<'py, K> FromPyObject<'_, 'py> for collections::BTreeSet where - K: FromPyObject<'py> + cmp::Ord, + K: FromPyObjectOwned<'py> + cmp::Ord, { - fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { match ob.downcast::() { Ok(set) => set.iter().map(|any| any.extract()).collect(), Err(err) => { diff --git a/src/conversions/std/slice.rs b/src/conversions/std/slice.rs index 798e5b54c45..680a6840901 100644 --- a/src/conversions/std/slice.rs +++ b/src/conversions/std/slice.rs @@ -44,8 +44,8 @@ where } } -impl<'a> crate::conversion::FromPyObjectBound<'a, '_> for &'a [u8] { - fn from_py_object_bound(obj: crate::Borrowed<'a, '_, PyAny>) -> PyResult { +impl<'a> crate::conversion::FromPyObject<'a, '_> for &'a [u8] { + fn extract(obj: crate::Borrowed<'a, '_, PyAny>) -> PyResult { Ok(obj.downcast::()?.as_bytes()) } @@ -60,8 +60,8 @@ impl<'a> crate::conversion::FromPyObjectBound<'a, '_> for &'a [u8] { /// If the source object is a `bytes` object, the `Cow` will be borrowed and /// pointing into the source object, and no copying or heap allocations will happen. /// If it is a `bytearray`, its contents will be copied to an owned `Cow`. -impl<'a> crate::conversion::FromPyObjectBound<'a, '_> for Cow<'a, [u8]> { - fn from_py_object_bound(ob: crate::Borrowed<'a, '_, PyAny>) -> PyResult { +impl<'a> crate::conversion::FromPyObject<'a, '_> for Cow<'a, [u8]> { + fn extract(ob: crate::Borrowed<'a, '_, PyAny>) -> PyResult { if let Ok(bytes) = ob.downcast::() { return Ok(Cow::Borrowed(bytes.as_bytes())); } diff --git a/src/conversions/std/string.rs b/src/conversions/std/string.rs index 074b5c2ba73..ec94e682aaf 100644 --- a/src/conversions/std/string.rs +++ b/src/conversions/std/string.rs @@ -3,10 +3,8 @@ use std::{borrow::Cow, convert::Infallible}; #[cfg(feature = "experimental-inspect")] use crate::inspect::types::TypeInfo; use crate::{ - conversion::IntoPyObject, - instance::Bound, - types::{any::PyAnyMethods, string::PyStringMethods, PyString}, - FromPyObject, Py, PyAny, PyObject, PyResult, Python, + conversion::IntoPyObject, instance::Bound, types::PyString, Borrowed, FromPyObject, Py, PyAny, + PyObject, PyResult, Python, }; #[allow(deprecated)] use crate::{IntoPy, ToPyObject}; @@ -225,8 +223,8 @@ impl<'py> IntoPyObject<'py> for &String { } #[cfg(any(Py_3_10, not(Py_LIMITED_API)))] -impl<'a> crate::conversion::FromPyObjectBound<'a, '_> for &'a str { - fn from_py_object_bound(ob: crate::Borrowed<'a, '_, PyAny>) -> PyResult { +impl<'a> crate::conversion::FromPyObject<'a, '_> for &'a str { + fn extract(ob: crate::Borrowed<'a, '_, PyAny>) -> PyResult { ob.downcast::()?.to_str() } @@ -236,8 +234,8 @@ impl<'a> crate::conversion::FromPyObjectBound<'a, '_> for &'a str { } } -impl<'a> crate::conversion::FromPyObjectBound<'a, '_> for Cow<'a, str> { - fn from_py_object_bound(ob: crate::Borrowed<'a, '_, PyAny>) -> PyResult { +impl<'a> crate::conversion::FromPyObject<'a, '_> for Cow<'a, str> { + fn extract(ob: crate::Borrowed<'a, '_, PyAny>) -> PyResult { ob.downcast::()?.to_cow() } @@ -249,8 +247,8 @@ impl<'a> crate::conversion::FromPyObjectBound<'a, '_> for Cow<'a, str> { /// Allows extracting strings from Python objects. /// Accepts Python `str` and `unicode` objects. -impl FromPyObject<'_> for String { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { +impl FromPyObject<'_, '_> for String { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { obj.downcast::()?.to_cow().map(Cow::into_owned) } @@ -260,8 +258,8 @@ impl FromPyObject<'_> for String { } } -impl FromPyObject<'_> for char { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { +impl FromPyObject<'_, '_> for char { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { let s = obj.downcast::()?.to_cow()?; let mut iter = s.chars(); if let (Some(ch), None) = (iter.next(), iter.next()) { diff --git a/src/conversions/std/time.rs b/src/conversions/std/time.rs index 741c28ee905..98cae6ab005 100755 --- a/src/conversions/std/time.rs +++ b/src/conversions/std/time.rs @@ -8,15 +8,15 @@ use crate::types::PyType; use crate::types::{timezone_utc, PyDateTime, PyDelta, PyDeltaAccess}; #[cfg(Py_LIMITED_API)] use crate::Py; -use crate::{intern, Bound, FromPyObject, PyAny, PyErr, PyObject, PyResult, Python}; +use crate::{intern, Borrowed, Bound, FromPyObject, PyAny, PyErr, PyObject, PyResult, Python}; #[allow(deprecated)] use crate::{IntoPy, ToPyObject}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; const SECONDS_PER_DAY: u64 = 24 * 60 * 60; -impl FromPyObject<'_> for Duration { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { +impl FromPyObject<'_, '_> for Duration { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { #[cfg(not(Py_LIMITED_API))] let (days, seconds, microseconds) = { let delta = obj.downcast::()?; @@ -121,8 +121,8 @@ impl<'py> IntoPyObject<'py> for &Duration { // // TODO: it might be nice to investigate using timestamps anyway, at least when the datetime is a safe range. -impl FromPyObject<'_> for SystemTime { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { +impl FromPyObject<'_, '_> for SystemTime { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { let duration_since_unix_epoch: Duration = obj .call_method1(intern!(obj.py(), "__sub__"), (unix_epoch_py(obj.py())?,))? .extract()?; diff --git a/src/impl_/extract_argument.rs b/src/impl_/extract_argument.rs index cbf79a14707..f238a664b9b 100644 --- a/src/impl_/extract_argument.rs +++ b/src/impl_/extract_argument.rs @@ -1,10 +1,10 @@ use crate::{ - conversion::FromPyObjectBound, exceptions::PyTypeError, ffi, pyclass::boolean_struct::False, types::{any::PyAnyMethods, dict::PyDictMethods, tuple::PyTupleMethods, PyDict, PyTuple}, - Borrowed, Bound, PyAny, PyClass, PyErr, PyRef, PyRefMut, PyResult, PyTypeCheck, Python, + Borrowed, Bound, FromPyObject, PyAny, PyClass, PyErr, PyRef, PyRefMut, PyResult, PyTypeCheck, + Python, }; /// Helper type used to keep implementation more concise. @@ -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: FromPyObjectBound<'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_/frompyobject.rs b/src/impl_/frompyobject.rs index 9f3f77340bc..264b38fc7c0 100644 --- a/src/impl_/frompyobject.rs +++ b/src/impl_/frompyobject.rs @@ -42,13 +42,13 @@ fn extract_traceback(py: Python<'_>, mut error: PyErr) -> String { error_msg } -pub fn extract_struct_field<'py, T>( - obj: &Bound<'py, PyAny>, +pub fn extract_struct_field<'a, 'py, T>( + obj: &'a Bound<'py, PyAny>, struct_name: &str, field_name: &str, ) -> PyResult where - T: FromPyObject<'py>, + T: FromPyObject<'a, 'py>, { match obj.extract() { Ok(value) => Ok(value), @@ -93,13 +93,13 @@ fn failed_to_extract_struct_field( new_err } -pub fn extract_tuple_struct_field<'py, T>( - obj: &Bound<'py, PyAny>, +pub fn extract_tuple_struct_field<'a, 'py, T>( + obj: &'a Bound<'py, PyAny>, struct_name: &str, index: usize, ) -> PyResult where - T: FromPyObject<'py>, + T: FromPyObject<'a, 'py>, { match obj.extract() { Ok(value) => Ok(value), 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/inspect/types.rs b/src/inspect/types.rs index 50674ce96f9..3c1bb1a20f0 100644 --- a/src/inspect/types.rs +++ b/src/inspect/types.rs @@ -467,7 +467,7 @@ mod conversion { assert_display(&<&[u8]>::type_output(), "Union[bytes, List[int]]"); assert_display(&<&[String]>::type_output(), "Union[bytes, List[str]]"); assert_display( - &<&[u8] as crate::conversion::FromPyObjectBound>::type_input(), + &<&[u8] as crate::conversion::FromPyObject>::type_input(), "bytes", ); } diff --git a/src/instance.rs b/src/instance.rs index 840416116f3..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. @@ -696,6 +696,32 @@ impl<'a, 'py, T> Borrowed<'a, 'py, T> { pub(crate) fn to_any(self) -> Borrowed<'a, 'py, PyAny> { Borrowed(self.0, PhantomData, self.2) } + + /// Extracts some type from the Python object. + /// + /// This is a wrapper function around [`FromPyObject::extract()`](crate::FromPyObject::extract). + pub fn extract(self) -> PyResult + where + O: FromPyObject<'a, 'py>, + { + 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> { @@ -1184,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`. @@ -1221,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. @@ -1238,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. @@ -1250,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. @@ -1418,7 +1447,7 @@ impl Py { /// This is a wrapper function around `FromPyObject::extract()`. pub fn extract<'a, 'py, D>(&'a self, py: Python<'py>) -> PyResult where - D: crate::conversion::FromPyObjectBound<'a, 'py>, + D: crate::conversion::FromPyObject<'a, 'py>, // TODO it might be possible to relax this bound in future, to allow // e.g. `.extract::<&str>(py)` where `py` is short-lived. 'py: 'a, @@ -1823,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()) } } } @@ -1873,23 +1902,23 @@ impl Drop for Py { } } -impl FromPyObject<'_> for Py +impl FromPyObject<'_, '_> for Py where T: PyTypeCheck, { /// Extracts `Self` from the source `PyObject`. - fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult { + fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { ob.extract::>().map(Bound::unbind) } } -impl<'py, T> FromPyObject<'py> for Bound<'py, T> +impl<'py, T> FromPyObject<'_, 'py> for Bound<'py, T> where T: PyTypeCheck, { /// Extracts `Self` from the source `PyObject`. - fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { - ob.downcast().cloned().map_err(Into::into) + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { + ob.downcast().map(Borrowed::to_owned).map_err(Into::into) } } @@ -1961,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/prelude.rs b/src/prelude.rs index d4f649f552a..c0aebf6796e 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -8,7 +8,7 @@ //! use pyo3::prelude::*; //! ``` -pub use crate::conversion::{FromPyObject, IntoPyObject}; +pub use crate::conversion::{FromPyObject, FromPyObjectOwned, IntoPyObject}; #[allow(deprecated)] pub use crate::conversion::{IntoPy, ToPyObject}; pub use crate::err::{PyErr, PyResult}; diff --git a/src/pybacked.rs b/src/pybacked.rs index 173d8e0e9e4..3b528c91886 100644 --- a/src/pybacked.rs +++ b/src/pybacked.rs @@ -4,10 +4,10 @@ use std::{convert::Infallible, ops::Deref, ptr::NonNull, sync::Arc}; use crate::{ types::{ - any::PyAnyMethods, bytearray::PyByteArrayMethods, bytes::PyBytesMethods, - string::PyStringMethods, PyByteArray, PyBytes, PyString, + bytearray::PyByteArrayMethods, bytes::PyBytesMethods, string::PyStringMethods, PyByteArray, + PyBytes, PyString, }, - Bound, DowncastError, FromPyObject, IntoPyObject, Py, PyAny, PyErr, PyResult, Python, + Borrowed, Bound, DowncastError, FromPyObject, IntoPyObject, Py, PyAny, PyErr, PyResult, Python, }; #[allow(deprecated)] use crate::{IntoPy, ToPyObject}; @@ -80,8 +80,8 @@ impl TryFrom> for PyBackedStr { } } -impl FromPyObject<'_> for PyBackedStr { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { +impl FromPyObject<'_, '_> for PyBackedStr { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { let py_string = obj.downcast::()?.to_owned(); Self::try_from(py_string) } @@ -227,14 +227,14 @@ impl From> for PyBackedBytes { } } -impl FromPyObject<'_> for PyBackedBytes { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { +impl FromPyObject<'_, '_> for PyBackedBytes { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { if let Ok(bytes) = obj.downcast::() { Ok(Self::from(bytes.to_owned())) } else if let Ok(bytearray) = obj.downcast::() { Ok(Self::from(bytearray.to_owned())) } else { - Err(DowncastError::new(obj, "`bytes` or `bytearray`").into()) + Err(DowncastError::new_from_borrowed(obj, "`bytes` or `bytearray`").into()) } } } @@ -361,6 +361,7 @@ use impl_traits; #[cfg(test)] mod test { use super::*; + use crate::types::PyAnyMethods; use crate::{IntoPyObject, Python}; use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; 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 d060c187631..cef699024ae 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -1,5 +1,5 @@ use crate::class::basic::CompareOp; -use crate::conversion::{AsPyPointer, FromPyObjectBound, IntoPyObject}; +use crate::conversion::{AsPyPointer, FromPyObject, IntoPyObject}; use crate::err::{DowncastError, DowncastIntoError, PyErr, PyResult}; use crate::exceptions::{PyAttributeError, PyTypeError}; use crate::ffi_ptr_ext::FfiPtrExt; @@ -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(()) /// }) @@ -835,11 +835,10 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed { /// Extracts some type from the Python object. /// - /// This is a wrapper function around - /// [`FromPyObject::extract_bound()`](crate::FromPyObject::extract_bound). + /// This is a wrapper function around [`FromPyObject::extract()`](crate::FromPyObject::extract). fn extract<'a, T>(&'a self) -> PyResult where - T: FromPyObjectBound<'a, 'py>; + T: FromPyObject<'a, 'py>; /// Returns the reference count for the Python object. fn get_refcnt(&self) -> isize; @@ -1438,9 +1437,9 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> { fn extract<'a, T>(&'a self) -> PyResult where - T: FromPyObjectBound<'a, 'py>, + T: FromPyObject<'a, 'py>, { - FromPyObjectBound::from_py_object_bound(self.as_borrowed()) + FromPyObject::extract(self.as_borrowed()) } fn get_refcnt(&self) -> isize { diff --git a/src/types/boolobject.rs b/src/types/boolobject.rs index 53043fa798c..846105805ea 100644 --- a/src/types/boolobject.rs +++ b/src/types/boolobject.rs @@ -197,8 +197,8 @@ impl<'py> IntoPyObject<'py> for &bool { /// Converts a Python `bool` to a Rust `bool`. /// /// Fails with `TypeError` if the input is not a Python `bool`. -impl FromPyObject<'_> for bool { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { +impl FromPyObject<'_, '_> for bool { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { let err = match obj.downcast::() { Ok(obj) => return Ok(obj.is_true()), Err(err) => err, @@ -213,7 +213,7 @@ impl FromPyObject<'_> for bool { }; if is_numpy_bool { - let missing_conversion = |obj: &Bound<'_, PyAny>| { + let missing_conversion = |obj: Borrowed<'_, '_, PyAny>| { PyTypeError::new_err(format!( "object of type '{}' does not define a '__bool__' conversion", obj.get_type() @@ -259,9 +259,7 @@ impl FromPyObject<'_> for bool { #[cfg(test)] mod tests { - use crate::types::any::PyAnyMethods; - use crate::types::boolobject::PyBoolMethods; - use crate::types::PyBool; + use crate::types::{PyAnyMethods, PyBool, PyBoolMethods}; use crate::IntoPyObject; use crate::Python; diff --git a/src/types/float.rs b/src/types/float.rs index 3c2d6643d18..50f43ac456e 100644 --- a/src/types/float.rs +++ b/src/types/float.rs @@ -126,10 +126,10 @@ impl<'py> IntoPyObject<'py> for &f64 { } } -impl<'py> FromPyObject<'py> for f64 { +impl<'py> FromPyObject<'_, 'py> for f64 { // PyFloat_AsDouble returns -1.0 upon failure #![allow(clippy::float_cmp)] - fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { // On non-limited API, .value() uses PyFloat_AS_DOUBLE which // allows us to have an optimized fast path for the case when // we have exactly a `float` object (it's not worth going through @@ -204,8 +204,8 @@ impl<'py> IntoPyObject<'py> for &f32 { } } -impl<'py> FromPyObject<'py> for f32 { - fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult { +impl<'py> FromPyObject<'_, 'py> for f32 { + fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { Ok(obj.extract::()? as f32) } diff --git a/src/types/sequence.rs b/src/types/sequence.rs index bc2643dcf8e..113ee17f169 100644 --- a/src/types/sequence.rs +++ b/src/types/sequence.rs @@ -1,3 +1,4 @@ +use crate::conversion::FromPyObjectOwned; use crate::err::{self, DowncastError, PyErr, PyResult}; use crate::exceptions::PyTypeError; use crate::ffi_ptr_ext::FfiPtrExt; @@ -331,11 +332,11 @@ impl<'py> PySequenceMethods<'py> for Bound<'py, PySequence> { } } -impl<'py, T> FromPyObject<'py> for Vec +impl<'py, T> FromPyObject<'_, 'py> for Vec where - T: FromPyObject<'py>, + T: FromPyObjectOwned<'py>, { - fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { if obj.is_instance_of::() { return Err(PyTypeError::new_err("Can't extract `str` to `Vec`")); } @@ -348,9 +349,9 @@ where } } -fn extract_sequence<'py, T>(obj: &Bound<'py, PyAny>) -> PyResult> +fn extract_sequence<'py, T>(obj: Borrowed<'_, 'py, PyAny>) -> PyResult> where - T: FromPyObject<'py>, + T: FromPyObjectOwned<'py>, { // Types that pass `PySequence_Check` usually implement enough of the sequence protocol // to support this function and if not, we will only fail extraction safely. @@ -358,7 +359,7 @@ where if ffi::PySequence_Check(obj.as_ptr()) != 0 { obj.downcast_unchecked::() } else { - return Err(DowncastError::new(obj, "Sequence").into()); + return Err(DowncastError::new_from_borrowed(obj, "Sequence").into()); } }; diff --git a/src/types/tuple.rs b/src/types/tuple.rs index 216a376d833..7d5ca449d62 100644 --- a/src/types/tuple.rs +++ b/src/types/tuple.rs @@ -505,7 +505,7 @@ impl IntoPy> for &'_ Bound<'_, PyTuple> { } #[cold] -fn wrong_tuple_length(t: &Bound<'_, PyTuple>, expected_length: usize) -> PyErr { +fn wrong_tuple_length(t: Borrowed<'_, '_, PyTuple>, expected_length: usize) -> PyErr { let msg = format!( "expected tuple of length {}, but got tuple of length {}", expected_length, @@ -573,8 +573,8 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+ } } - impl<'py, $($T: FromPyObject<'py>),+> FromPyObject<'py> for ($($T,)+) { - fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult + impl<'a, 'py, $($T: FromPyObject<'a, 'py>),+> FromPyObject<'a, 'py> for ($($T,)+) { + fn extract(obj: Borrowed<'a, 'py, PyAny>) -> PyResult { let t = obj.downcast::()?; if t.len() == $length { 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 bd2b588df32..d6f2bec4a7e 100644 --- a/tests/ui/invalid_cancel_handle.stderr +++ b/tests/ui/invalid_cancel_handle.stderr @@ -38,44 +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 `FromPyObjectBound<'_, '_>` - = note: required for `CancelHandle` to implement `PyFunctionArgument<'_, '_>` + = note: required for `CancelHandle` to implement `FromPyObject<'_, '_>` + = 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>> - = note: required for `CancelHandle` to implement `FromPyObject<'_>` - = note: required for `CancelHandle` to implement `FromPyObjectBound<'_, '_>` - = note: required for `CancelHandle` to implement `PyFunctionArgument<'_, '_>` + = 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 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) {}