pyproto: deprecate py_methods
This commit is contained in:
parent
88d86a65aa
commit
48823e22d6
|
@ -29,6 +29,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
- Deprecate `PyModule` methods `call`, `call0`, `call1` and `get`. [#1492](https://github.com/PyO3/pyo3/pull/1492)
|
- Deprecate `PyModule` methods `call`, `call0`, `call1` and `get`. [#1492](https://github.com/PyO3/pyo3/pull/1492)
|
||||||
- Add length information to `PyBufferError`s raised from `PyBuffer::copy_to_slice` and `PyBuffer::copy_from_slice`. [#1534](https://github.com/PyO3/pyo3/pull/1534)
|
- Add length information to `PyBufferError`s raised from `PyBuffer::copy_to_slice` and `PyBuffer::copy_from_slice`. [#1534](https://github.com/PyO3/pyo3/pull/1534)
|
||||||
- Automatically provide `-undefined` and `dynamic_lookup` linker arguments on macOS with `extension-module` feature. [#1539](https://github.com/PyO3/pyo3/pull/1539)
|
- Automatically provide `-undefined` and `dynamic_lookup` linker arguments on macOS with `extension-module` feature. [#1539](https://github.com/PyO3/pyo3/pull/1539)
|
||||||
|
- Deprecate `#[pyproto]` methods which are easier to implement as `#[pymethods]`: [#1560](https://github.com/PyO3/pyo3/pull/1560)
|
||||||
|
- `PyBasicProtocol::__bytes__` and `PyBasicProtocol::__format__`
|
||||||
|
- `PyContextProtocol::__enter__` and `PyContextProtocol::__exit__`
|
||||||
|
- `PyDescrProtocol::__delete__` and `PyDescrProtocol::__set_name__`
|
||||||
|
- `PyMappingProtocol::__reversed__`
|
||||||
|
- `PyNumberProtocol::__complex__` and `PyNumberProtocol::__round__`
|
||||||
|
- `PyAsyncProtocol::__aenter__` and `PyAsyncProtocol::__aexit__`
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
- Remove deprecated exception names `BaseException` etc. [#1426](https://github.com/PyO3/pyo3/pull/1426)
|
- Remove deprecated exception names `BaseException` etc. [#1426](https://github.com/PyO3/pyo3/pull/1426)
|
||||||
|
|
|
@ -31,10 +31,6 @@ impl PyObjectProtocol for MyClass {
|
||||||
fn __str__(&self) -> &'static str {
|
fn __str__(&self) -> &'static str {
|
||||||
"MyClass"
|
"MyClass"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn __bytes__(&self) -> &'static [u8] {
|
|
||||||
b"MyClass"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
## Class customizations
|
## Class customizations
|
||||||
|
|
||||||
Python's object model defines several protocols for different object behavior, like sequence,
|
PyO3 uses the `#[pyproto]` attribute in combination with special traits to implement certain protocol (aka `__dunder__`) methods of Python classes. The special traits are listed in this chapter of the guide. See also the [documentation for the `pyo3::class` module]({{#PYO3_DOCS_URL}}/pyo3/class/index.html).
|
||||||
mapping or number protocols. PyO3 defines separate traits for each of them. To provide specific
|
|
||||||
Python object behavior, you need to implement the specific trait for your struct. Important note,
|
|
||||||
each protocol implementation block has to be annotated with the `#[pyproto]` attribute.
|
|
||||||
|
|
||||||
All `#[pyproto]` methods which can be defined below can return `T` instead of `PyResult<T>` if the
|
Python's object model defines several protocols for different object behavior, such as the sequence, mapping, and number protocols. You may be familiar with implementing these protocols in Python classes by "dunder" methods, such as `__str__` or `__repr__`.
|
||||||
method implementation is infallible. In addition, if the return type is `()`, it can be omitted altogether.
|
|
||||||
|
In the Python C-API which PyO3 is dependent upon, many of these protocol methods have to be provided into special "slots" on the class type object. To fill these slots PyO3 uses the `#[pyproto]` attribute in combination with special traits.
|
||||||
|
|
||||||
|
All `#[pyproto]` methods can return `T` instead of `PyResult<T>` if the method implementation is infallible. In addition, if the return type is `()`, it can be omitted altogether.
|
||||||
|
|
||||||
|
There are many "dunder" methods which are not included in any of PyO3's protocol traits, such as `__dir__`. These methods can be implemented in `#[pymethods]` as already covered in the previous section.
|
||||||
|
|
||||||
### Basic object customization
|
### Basic object customization
|
||||||
|
|
||||||
|
@ -29,15 +31,6 @@ Each method corresponds to Python's `self.attr`, `self.attr = value` and `del se
|
||||||
|
|
||||||
Possible return types for `__str__` and `__repr__` are `PyResult<String>` or `PyResult<PyString>`.
|
Possible return types for `__str__` and `__repr__` are `PyResult<String>` or `PyResult<PyString>`.
|
||||||
|
|
||||||
* `fn __bytes__(&self) -> PyResult<PyBytes>`
|
|
||||||
|
|
||||||
Provides the conversion to `bytes`.
|
|
||||||
|
|
||||||
* `fn __format__(&self, format_spec: &str) -> PyResult<impl ToPyObject<ObjectType=PyString>>`
|
|
||||||
|
|
||||||
Special method that is used by the `format()` builtin and the `str.format()` method.
|
|
||||||
Possible return types are `PyResult<String>` or `PyResult<PyString>`.
|
|
||||||
|
|
||||||
#### Comparison operators
|
#### Comparison operators
|
||||||
|
|
||||||
* `fn __richcmp__(&self, other: impl FromPyObject, op: CompareOp) -> PyResult<impl ToPyObject>`
|
* `fn __richcmp__(&self, other: impl FromPyObject, op: CompareOp) -> PyResult<impl ToPyObject>`
|
||||||
|
@ -132,14 +125,12 @@ The following methods implement the unary arithmetic operations (`-`, `+`, `abs(
|
||||||
|
|
||||||
Support for coercions:
|
Support for coercions:
|
||||||
|
|
||||||
* `fn __complex__(&'p self) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __int__(&'p self) -> PyResult<impl ToPyObject>`
|
* `fn __int__(&'p self) -> PyResult<impl ToPyObject>`
|
||||||
* `fn __float__(&'p self) -> PyResult<impl ToPyObject>`
|
* `fn __float__(&'p self) -> PyResult<impl ToPyObject>`
|
||||||
|
|
||||||
Other:
|
Other:
|
||||||
|
|
||||||
* `fn __index__(&'p self) -> PyResult<impl ToPyObject>`
|
* `fn __index__(&'p self) -> PyResult<impl ToPyObject>`
|
||||||
* `fn __round__(&'p self, ndigits: Option<impl FromPyObject>) -> PyResult<impl ToPyObject>`
|
|
||||||
|
|
||||||
### Emulating sequential containers (such as lists or tuples)
|
### Emulating sequential containers (such as lists or tuples)
|
||||||
|
|
||||||
|
@ -237,12 +228,6 @@ For a mapping, the keys may be Python objects of arbitrary type.
|
||||||
The same exceptions should be raised for improper key values as
|
The same exceptions should be raised for improper key values as
|
||||||
for the `__getitem__()` method.
|
for the `__getitem__()` method.
|
||||||
|
|
||||||
* `fn __reversed__(&self) -> PyResult<impl ToPyObject>`
|
|
||||||
|
|
||||||
Called (if present) by the `reversed()` built-in to implement reverse iteration.
|
|
||||||
It should return a new iterator object that iterates over all the objects in
|
|
||||||
the container in reverse order.
|
|
||||||
|
|
||||||
### Garbage Collector Integration
|
### Garbage Collector Integration
|
||||||
|
|
||||||
If your type owns references to other Python objects, you will need to
|
If your type owns references to other Python objects, you will need to
|
||||||
|
|
|
@ -15,6 +15,47 @@ For projects embedding Python in Rust, PyO3 no longer automatically initalizes a
|
||||||
|
|
||||||
The limitation of the new default implementation is that it cannot support multiple `#[pymethods]` blocks for the same `#[pyclass]`. If you need this functionality, you must enable the `multiple-pymethods` feature which will switch `#[pymethods]` to the inventory-based implementation.
|
The limitation of the new default implementation is that it cannot support multiple `#[pymethods]` blocks for the same `#[pyclass]`. If you need this functionality, you must enable the `multiple-pymethods` feature which will switch `#[pymethods]` to the inventory-based implementation.
|
||||||
|
|
||||||
|
### Deprecated `#[pyproto]` methods
|
||||||
|
|
||||||
|
Some protocol (aka `__dunder__`) methods such as `__bytes__` and `__format__` have been possible to implement two ways in PyO3 for some time: via a `#[pyproto]` (e.g. `PyBasicProtocol` for the methods listed here), or by writing them directly in `#[pymethods]`. This is only true for a handful of the `#[pyproto]` methods (for technical reasons to do with the way PyO3 currently interacts with the Python C-API).
|
||||||
|
|
||||||
|
In the interest of having onle one way to do things, the `#[pyproto]` forms of these methods have been deprecated.
|
||||||
|
|
||||||
|
To migrate just move the affected methods from a `#[pyproto]` to a `#[pymethods]` block.
|
||||||
|
|
||||||
|
Before:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
use pyo3::prelude::*;
|
||||||
|
use pyo3::class::basic::PyBasicProtocol;
|
||||||
|
|
||||||
|
#[pyclass]
|
||||||
|
struct MyClass { }
|
||||||
|
|
||||||
|
#[pyproto]
|
||||||
|
impl PyBasicProtocol for MyClass {
|
||||||
|
fn __bytes__(&self) -> &'static [u8] {
|
||||||
|
b"hello, world"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
After:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
|
#[pyclass]
|
||||||
|
struct MyClass { }
|
||||||
|
|
||||||
|
#[pymethods]
|
||||||
|
impl MyClass {
|
||||||
|
fn __bytes__(&self) -> &'static [u8] {
|
||||||
|
b"hello, world"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## from 0.12.* to 0.13
|
## from 0.12.* to 0.13
|
||||||
|
|
||||||
### Minimum Rust version increased to Rust 1.45
|
### Minimum Rust version increased to Rust 1.45
|
||||||
|
|
|
@ -268,7 +268,7 @@ pub const DESCR: Proto = Proto {
|
||||||
methods: &[
|
methods: &[
|
||||||
MethodProto::new("__get__", "PyDescrGetProtocol").args(&["Receiver", "Inst", "Owner"]),
|
MethodProto::new("__get__", "PyDescrGetProtocol").args(&["Receiver", "Inst", "Owner"]),
|
||||||
MethodProto::new("__set__", "PyDescrSetProtocol").args(&["Receiver", "Inst", "Value"]),
|
MethodProto::new("__set__", "PyDescrSetProtocol").args(&["Receiver", "Inst", "Value"]),
|
||||||
MethodProto::new("__det__", "PyDescrDelProtocol")
|
MethodProto::new("__delete__", "PyDescrDelProtocol")
|
||||||
.args(&["Inst"])
|
.args(&["Inst"])
|
||||||
.has_self(),
|
.has_self(),
|
||||||
MethodProto::new("__set_name__", "PyDescrSetNameProtocol")
|
MethodProto::new("__set_name__", "PyDescrSetNameProtocol")
|
||||||
|
@ -276,7 +276,7 @@ pub const DESCR: Proto = Proto {
|
||||||
.has_self(),
|
.has_self(),
|
||||||
],
|
],
|
||||||
py_methods: &[
|
py_methods: &[
|
||||||
PyMethod::new("__del__", "PyDescrDelProtocolImpl"),
|
PyMethod::new("__delete__", "PyDescrDelProtocolImpl"),
|
||||||
PyMethod::new("__set_name__", "PyDescrNameProtocolImpl"),
|
PyMethod::new("__set_name__", "PyDescrNameProtocolImpl"),
|
||||||
],
|
],
|
||||||
slot_defs: &[
|
slot_defs: &[
|
||||||
|
|
|
@ -61,6 +61,10 @@ pub trait PyObjectProtocol<'p>: PyClass {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated(
|
||||||
|
since = "0.14.0",
|
||||||
|
note = "prefer implementing `__format__` in `#[pymethods]` instead of in a protocol"
|
||||||
|
)]
|
||||||
fn __format__(&'p self, format_spec: Self::Format) -> Self::Result
|
fn __format__(&'p self, format_spec: Self::Format) -> Self::Result
|
||||||
where
|
where
|
||||||
Self: PyObjectFormatProtocol<'p>,
|
Self: PyObjectFormatProtocol<'p>,
|
||||||
|
@ -75,6 +79,10 @@ pub trait PyObjectProtocol<'p>: PyClass {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated(
|
||||||
|
since = "0.14.0",
|
||||||
|
note = "prefer implementing `__bytes__` in `#[pymethods]` instead of in a protocol"
|
||||||
|
)]
|
||||||
fn __bytes__(&'p self) -> Self::Result
|
fn __bytes__(&'p self) -> Self::Result
|
||||||
where
|
where
|
||||||
Self: PyObjectBytesProtocol<'p>,
|
Self: PyObjectBytesProtocol<'p>,
|
||||||
|
|
|
@ -10,6 +10,10 @@ use crate::{PyClass, PyObject};
|
||||||
/// Context manager interface
|
/// Context manager interface
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
pub trait PyContextProtocol<'p>: PyClass {
|
pub trait PyContextProtocol<'p>: PyClass {
|
||||||
|
#[deprecated(
|
||||||
|
since = "0.14.0",
|
||||||
|
note = "prefer implementing `__enter__` in `#[pymethods]` instead of in a protocol"
|
||||||
|
)]
|
||||||
fn __enter__(&'p mut self) -> Self::Result
|
fn __enter__(&'p mut self) -> Self::Result
|
||||||
where
|
where
|
||||||
Self: PyContextEnterProtocol<'p>,
|
Self: PyContextEnterProtocol<'p>,
|
||||||
|
@ -17,6 +21,10 @@ pub trait PyContextProtocol<'p>: PyClass {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated(
|
||||||
|
since = "0.14.0",
|
||||||
|
note = "prefer implementing `__exit__` in `#[pymethods]` instead of in a protocol"
|
||||||
|
)]
|
||||||
fn __exit__(
|
fn __exit__(
|
||||||
&'p mut self,
|
&'p mut self,
|
||||||
exc_type: Option<Self::ExcType>,
|
exc_type: Option<Self::ExcType>,
|
||||||
|
|
|
@ -31,6 +31,10 @@ pub trait PyDescrProtocol<'p>: PyClass {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated(
|
||||||
|
since = "0.14.0",
|
||||||
|
note = "prefer implementing `__delete__` in `#[pymethods]` instead of in a protocol"
|
||||||
|
)]
|
||||||
fn __delete__(&'p self, instance: &'p PyAny) -> Self::Result
|
fn __delete__(&'p self, instance: &'p PyAny) -> Self::Result
|
||||||
where
|
where
|
||||||
Self: PyDescrDeleteProtocol<'p>,
|
Self: PyDescrDeleteProtocol<'p>,
|
||||||
|
@ -38,6 +42,10 @@ pub trait PyDescrProtocol<'p>: PyClass {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated(
|
||||||
|
since = "0.14.0",
|
||||||
|
note = "prefer implementing `__set_name__` in `#[pymethods]` instead of in a protocol"
|
||||||
|
)]
|
||||||
fn __set_name__(&'p self, instance: &'p PyAny) -> Self::Result
|
fn __set_name__(&'p self, instance: &'p PyAny) -> Self::Result
|
||||||
where
|
where
|
||||||
Self: PyDescrSetNameProtocol<'p>,
|
Self: PyDescrSetNameProtocol<'p>,
|
||||||
|
|
|
@ -37,6 +37,10 @@ pub trait PyMappingProtocol<'p>: PyClass {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated(
|
||||||
|
since = "0.14.0",
|
||||||
|
note = "prefer implementing `__reversed__` in `#[pymethods]` instead of in a protocol"
|
||||||
|
)]
|
||||||
fn __reversed__(&'p self) -> Self::Result
|
fn __reversed__(&'p self) -> Self::Result
|
||||||
where
|
where
|
||||||
Self: PyMappingReversedProtocol<'p>,
|
Self: PyMappingReversedProtocol<'p>,
|
||||||
|
|
|
@ -283,6 +283,10 @@ pub trait PyNumberProtocol<'p>: PyClass {
|
||||||
{
|
{
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
#[deprecated(
|
||||||
|
since = "0.14.0",
|
||||||
|
note = "prefer implementing `__complex__` in `#[pymethods]` instead of in a protocol"
|
||||||
|
)]
|
||||||
fn __complex__(&'p self) -> Self::Result
|
fn __complex__(&'p self) -> Self::Result
|
||||||
where
|
where
|
||||||
Self: PyNumberComplexProtocol<'p>,
|
Self: PyNumberComplexProtocol<'p>,
|
||||||
|
@ -307,6 +311,10 @@ pub trait PyNumberProtocol<'p>: PyClass {
|
||||||
{
|
{
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
#[deprecated(
|
||||||
|
since = "0.14.0",
|
||||||
|
note = "prefer implementing `__round__` in `#[pymethods]` instead of in a protocol"
|
||||||
|
)]
|
||||||
fn __round__(&'p self, ndigits: Option<Self::NDigits>) -> Self::Result
|
fn __round__(&'p self, ndigits: Option<Self::NDigits>) -> Self::Result
|
||||||
where
|
where
|
||||||
Self: PyNumberRoundProtocol<'p>,
|
Self: PyNumberRoundProtocol<'p>,
|
||||||
|
|
|
@ -39,6 +39,10 @@ pub trait PyAsyncProtocol<'p>: PyClass {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated(
|
||||||
|
since = "0.14.0",
|
||||||
|
note = "prefer implementing `__aenter__` in `#[pymethods]` instead of in a protocol"
|
||||||
|
)]
|
||||||
fn __aenter__(&'p mut self) -> Self::Result
|
fn __aenter__(&'p mut self) -> Self::Result
|
||||||
where
|
where
|
||||||
Self: PyAsyncAenterProtocol<'p>,
|
Self: PyAsyncAenterProtocol<'p>,
|
||||||
|
@ -46,6 +50,10 @@ pub trait PyAsyncProtocol<'p>: PyClass {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated(
|
||||||
|
since = "0.14.0",
|
||||||
|
note = "prefer implementing `__aexit__` in `#[pymethods]` instead of in a protocol"
|
||||||
|
)]
|
||||||
fn __aexit__(
|
fn __aexit__(
|
||||||
&'p mut self,
|
&'p mut self,
|
||||||
exc_type: Option<Self::ExcType>,
|
exc_type: Option<Self::ExcType>,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#![allow(deprecated)] // for deprecated protocol methods
|
||||||
|
|
||||||
use pyo3::class::basic::CompareOp;
|
use pyo3::class::basic::CompareOp;
|
||||||
use pyo3::class::*;
|
use pyo3::class::*;
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#![allow(deprecated)] // for deprecated protocol methods
|
||||||
|
|
||||||
use pyo3::class::{
|
use pyo3::class::{
|
||||||
PyAsyncProtocol, PyContextProtocol, PyDescrProtocol, PyIterProtocol, PyMappingProtocol,
|
PyAsyncProtocol, PyContextProtocol, PyDescrProtocol, PyIterProtocol, PyMappingProtocol,
|
||||||
PyObjectProtocol, PySequenceProtocol,
|
PyObjectProtocol, PySequenceProtocol,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#![allow(deprecated)] // for deprecated protocol methods
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use pyo3::exceptions::PyKeyError;
|
use pyo3::exceptions::PyKeyError;
|
||||||
|
|
Loading…
Reference in New Issue