Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pyproto: remove deprecated feature #2587

Merged
merged 1 commit into from Sep 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion Architecture.md
Expand Up @@ -154,7 +154,7 @@ implemented in Python, such as GC support.
## 5. Procedural macros to simplify usage for users.

[`pyo3-macros`] provides five proc-macro APIs: `pymodule`, `pyfunction`, `pyclass`,
`pymethods`, and `#[derive(FromPyObject)]`. (And a deprecated `pyproto` macro.)
`pymethods`, and `#[derive(FromPyObject)]`.
[`pyo3-macros-backend`] has the actual implementations of these APIs.
[`src/derive_utils.rs`] contains some utilities used in code generated by these proc-macros,
such as parsing function arguments.
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Expand Up @@ -6,6 +6,12 @@ PyO3 versions, please see the [migration guide](https://pyo3.rs/latest/migration
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Removed

- Remove the deprecated `pyproto` feature, `#[pyproto]` macro, and all accompanying APIs. [#2587](https://github.com/PyO3/pyo3/pull/2587)

## [0.17.1] - 2022-08-28

### Fixed
Expand Down
4 changes: 0 additions & 4 deletions Cargo.toml
Expand Up @@ -64,9 +64,6 @@ macros = ["pyo3-macros", "indoc", "unindent"]
# Enables multiple #[pymethods] per #[pyclass]
multiple-pymethods = ["inventory", "pyo3-macros/multiple-pymethods"]

# Enables deprecated #[pyproto] macro
pyproto = ["pyo3-macros/pyproto"]

# Use this feature when building an extension module.
# It tells the linker to keep the python symbols unresolved,
# so that the module can also be used with statically linked python interpreters.
Expand Down Expand Up @@ -97,7 +94,6 @@ nightly = []
# This is mostly intended for testing purposes - activating *all* of these isn't particularly useful.
full = [
"macros",
"pyproto",
# "multiple-pymethods", # TODO re-add this when MSRV is greater than 1.62
"num-bigint",
"num-complex",
Expand Down
4 changes: 2 additions & 2 deletions guide/src/class.md
Expand Up @@ -916,9 +916,9 @@ This simple technique works for the case when there is zero or one implementatio
The `#[pyclass]` macro expands to roughly the code seen below. The `PyClassImplCollector` is the type used internally by PyO3 for dtolnay specialization:

```rust
# #[cfg(not(any(feature = "multiple-pymethods", feature = "pyproto")))] {
# #[cfg(not(feature = "multiple-pymethods"))] {
# use pyo3::prelude::*;
// Note: the implementation differs slightly with the `pyproto` or `multiple-pymethods` features enabled.
// Note: the implementation differs slightly with the `multiple-pymethods` feature enabled.
struct MyClass {
# #[allow(dead_code)]
num: i32,
Expand Down
219 changes: 1 addition & 218 deletions guide/src/class/protocols.md
Expand Up @@ -2,12 +2,7 @@

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 "magic" methods, such as `__str__` or `__repr__`. Because of the double-underscores surrounding their name, these are also known as "dunder" methods.

In the Python C-API which PyO3 is implemented upon, many of these magic methods have to be placed into special "slots" on the class type object, as covered in the previous section. There are two ways in which this can be done:

- In `#[pymethods]`, if the name of the method is a recognised magic method, PyO3 will place it in the type object automatically.
- [Deprecated since PyO3 0.16] In special traits combined with the `#[pyproto]` attribute.

(There are also many magic methods which don't have a special slot, such as `__dir__`. These methods can be implemented as normal in `#[pymethods]`.)
In the Python C-API which PyO3 is implemented upon, many of these magic methods have to be placed into special "slots" on the class type object, as covered in the previous section.

If a function name in `#[pymethods]` is a recognised magic method, it will be automatically placed into the correct slot in the Python type object. The function name is taken from the usual rules for naming `#[pymethods]`: the `#[pyo3(name = "...")]` attribute is used if present, otherwise the Rust function name is used.

Expand Down Expand Up @@ -405,217 +400,5 @@ impl ClassWithGCSupport {
```

[`IterNextOutput`]: {{#PYO3_DOCS_URL}}/pyo3/class/iter/enum.IterNextOutput.html


### `#[pyproto]` traits

PyO3 can use the `#[pyproto]` attribute in combination with special traits to implement the magic methods which need slots. The special traits are listed below. See also the [documentation for the `pyo3::class` module]({{#PYO3_DOCS_URL}}/pyo3/class/index.html).

Due to complexity in the implementation and usage, these traits were deprecated in PyO3 0.16 in favour of the `#[pymethods]` solution.

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.

#### Basic object customization

The [`PyObjectProtocol`] trait provides several basic customizations.

* `fn __str__(&self) -> PyResult<impl ToPyObject<ObjectType=PyString>>`
* `fn __repr__(&self) -> PyResult<impl ToPyObject<ObjectType=PyString>>`
* `fn __hash__(&self) -> PyResult<impl PrimInt>`
* `fn __richcmp__(&self, other: impl FromPyObject, op: CompareOp) -> PyResult<impl ToPyObject>`
* `fn __getattr__(&self, name: impl FromPyObject) -> PyResult<impl IntoPy<PyObject>>`
* `fn __setattr__(&mut self, name: impl FromPyObject, value: impl FromPyObject) -> PyResult<()>`
* `fn __delattr__(&mut self, name: impl FromPyObject) -> PyResult<()>`
* `fn __bool__(&self) -> PyResult<bool>`

#### Emulating numeric types

The [`PyNumberProtocol`] trait can be implemented to emulate [numeric types](https://docs.python.org/3/reference/datamodel.html#emulating-numeric-types).

* `fn __add__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
* `fn __sub__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
* `fn __mul__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
* `fn __matmul__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
* `fn __truediv__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
* `fn __floordiv__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
* `fn __mod__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
* `fn __divmod__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
* `fn __pow__(lhs: impl FromPyObject, rhs: impl FromPyObject, modulo: Option<impl FromPyObject>) -> PyResult<impl ToPyObject>`
* `fn __lshift__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
* `fn __rshift__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
* `fn __and__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
* `fn __or__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
* `fn __xor__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`

These methods are called to implement the binary arithmetic operations.

The reflected operations are also available:

* `fn __radd__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
* `fn __rsub__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
* `fn __rmul__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
* `fn __rmatmul__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
* `fn __rtruediv__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
* `fn __rfloordiv__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
* `fn __rmod__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
* `fn __rdivmod__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
* `fn __rpow__(lhs: impl FromPyObject, rhs: impl FromPyObject, modulo: Option<impl FromPyObject>) -> PyResult<impl ToPyObject>`
* `fn __rlshift__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
* `fn __rrshift__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
* `fn __rand__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
* `fn __ror__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
* `fn __rxor__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`

The code generated for these methods expect that all arguments match the
signature, or raise a TypeError.

* `fn __iadd__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
* `fn __isub__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
* `fn __imul__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
* `fn __imatmul__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
* `fn __itruediv__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
* `fn __ifloordiv__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
* `fn __imod__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
* `fn __ipow__(&'p mut self, other: impl FromPyObject, modulo: impl FromPyObject) -> PyResult<()>` on Python 3.8^
* `fn __ipow__(&'p mut self, other: impl FromPyObject) -> PyResult<()>` on Python 3.7 see https://bugs.python.org/issue36379
* `fn __ilshift__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
* `fn __irshift__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
* `fn __iand__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
* `fn __ior__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
* `fn __ixor__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`


The following methods implement the unary arithmetic operations:

* `fn __neg__(&'p self) -> PyResult<impl ToPyObject>`
* `fn __pos__(&'p self) -> PyResult<impl ToPyObject>`
* `fn __abs__(&'p self) -> PyResult<impl ToPyObject>`
* `fn __invert__(&'p self) -> PyResult<impl ToPyObject>`

Support for coercions:

* `fn __int__(&'p self) -> PyResult<impl ToPyObject>`
* `fn __float__(&'p self) -> PyResult<impl ToPyObject>`
* `fn __index__(&'p self) -> PyResult<impl ToPyObject>`

#### Emulating sequential containers (such as lists or tuples)

The [`PySequenceProtocol`] trait can be implemented to emulate
[sequential container types](https://docs.python.org/3/reference/datamodel.html#emulating-container-types).

For a sequence, the keys are the integers _k_ for which _0 <= k < N_,
where _N_ is the length of the sequence.

* `fn __len__(&self) -> PyResult<usize>`

Implements the built-in function `len()` for the sequence.

* `fn __getitem__(&self, idx: isize) -> PyResult<impl ToPyObject>`

Implements evaluation of the `self[idx]` element.
If the `idx` value is outside the set of indexes for the sequence, `IndexError` should be raised.

*Note:* Negative integer indexes are handled as follows: if `__len__()` is defined,
it is called and the sequence length is used to compute a positive index,
which is passed to `__getitem__()`.
If `__len__()` is not defined, the index is passed as is to the function.

* `fn __setitem__(&mut self, idx: isize, value: impl FromPyObject) -> PyResult<()>`

Implements assignment to the `self[idx]` element. Same note as for `__getitem__()`.
Should only be implemented if sequence elements can be replaced.

* `fn __delitem__(&mut self, idx: isize) -> PyResult<()>`

Implements deletion of the `self[idx]` element. Same note as for `__getitem__()`.
Should only be implemented if sequence elements can be deleted.

* `fn __contains__(&self, item: impl FromPyObject) -> PyResult<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.

* `fn __concat__(&self, other: impl FromPyObject) -> PyResult<impl ToPyObject>`

Concatenates two sequences.
Used by the `+` operator, after trying the numeric addition via
the `PyNumberProtocol` trait method.

* `fn __repeat__(&self, count: isize) -> PyResult<impl ToPyObject>`

Repeats the sequence `count` times.
Used by the `*` operator, after trying the numeric multiplication via
the `PyNumberProtocol` trait method.

* `fn __inplace_concat__(&mut self, other: impl FromPyObject) -> PyResult<Self>`

Concatenates two sequences in place. Returns the modified first operand.
Used by the `+=` operator, after trying the numeric in place addition via
the `PyNumberProtocol` trait method.

* `fn __inplace_repeat__(&mut self, count: isize) -> PyResult<Self>`

Repeats the sequence `count` times in place. Returns the modified first operand.
Used by the `*=` operator, after trying the numeric in place multiplication via
the `PyNumberProtocol` trait method.

#### Emulating mapping containers (such as dictionaries)

The [`PyMappingProtocol`] trait allows to emulate
[mapping container types](https://docs.python.org/3/reference/datamodel.html#emulating-container-types).

For a mapping, the keys may be Python objects of arbitrary type.

* `fn __len__(&self) -> PyResult<usize>`

Implements the built-in function `len()` for the mapping.

* `fn __getitem__(&self, key: impl FromPyObject) -> PyResult<impl ToPyObject>`

Implements evaluation of the `self[key]` element.
If `key` is of an inappropriate type, `TypeError` may be raised;
if `key` is missing (not in the container), `KeyError` should be raised.

* `fn __setitem__(&mut self, key: impl FromPyObject, value: impl FromPyObject) -> PyResult<()>`

Implements assignment to the `self[key]` element or insertion of a new `key`
mapping to `value`.
Should only be implemented if the mapping support changes to the values for keys,
or if new keys can be added.
The same exceptions should be raised for improper key values as
for the `__getitem__()` method.

* `fn __delitem__(&mut self, key: impl FromPyObject) -> PyResult<()>`

Implements deletion of the `self[key]` element.
Should only be implemented if the mapping supports removal of keys.
The same exceptions should be raised for improper key values as
for the `__getitem__()` method.

#### Iterator Types

Iterators can be defined using the [`PyIterProtocol`] trait.
It includes two methods `__iter__` and `__next__`:
* `fn __iter__(slf: PyRefMut<'_, Self>) -> PyResult<impl IntoPy<PyObject>>`
* `fn __next__(slf: PyRefMut<'_, Self>) -> PyResult<Option<impl IntoPy<PyObject>>>`

These two methods can be take either `PyRef<'_, Self>` or `PyRefMut<'_, Self>` as their
first argument, so that mutable borrow can be avoided if needed.

For details, look at the `#[pymethods]` regarding iterator methods.

#### Garbage Collector Integration

Implement the [`PyGCProtocol`] trait for your struct.
For details, look at the `#[pymethods]` regarding GC methods.

[`PyGCProtocol`]: {{#PYO3_DOCS_URL}}/pyo3/class/gc/trait.PyGCProtocol.html
[`PyMappingProtocol`]: {{#PYO3_DOCS_URL}}/pyo3/class/mapping/trait.PyMappingProtocol.html
[`PyNumberProtocol`]: {{#PYO3_DOCS_URL}}/pyo3/class/number/trait.PyNumberProtocol.html
[`PyObjectProtocol`]: {{#PYO3_DOCS_URL}}/pyo3/class/basic/trait.PyObjectProtocol.html
[`PySequenceProtocol`]: {{#PYO3_DOCS_URL}}/pyo3/class/sequence/trait.PySequenceProtocol.html
[`PyIterProtocol`]: {{#PYO3_DOCS_URL}}/pyo3/class/iter/trait.PyIterProtocol.html
[`PySequence`]: {{#PYO3_DOCS_URL}}/pyo3/types/struct.PySequence.html
[`CompareOp::matches`]: {{#PYO3_DOCS_URL}}/pyo3/pyclass/enum.CompareOp.html#method.matches
4 changes: 0 additions & 4 deletions guide/src/features.md
Expand Up @@ -75,10 +75,6 @@ Most users should only need a single `#[pymethods]` per `#[pyclass]`. In additio

See [the `#[pyclass]` implementation details](class.md#implementation-details) for more information.

### `pyproto`

This feature enables the `#[pyproto]` macro, which is a deprecated alternative to `#[pymethods]` for defining magic methods such as `__eq__`.

### `nightly`

The `nightly` feature needs the nightly Rust compiler. This allows PyO3 to use the auto_traits and negative_impls features to fix the `Python::allow_threads` function.
Expand Down
1 change: 0 additions & 1 deletion pyo3-macros-backend/Cargo.toml
Expand Up @@ -23,5 +23,4 @@ default-features = false
features = ["derive", "parsing", "printing", "clone-impls", "full", "extra-traits"]

[features]
pyproto = []
abi3 = []