Skip to content

Commit

Permalink
Merge remote-tracking branch 'refs/remotes/upstream/main' into add-er…
Browse files Browse the repository at this point in the history
…ror-messages-for-unsupported-macro-features-on-compilation

# Conflicts:
#	tests/test_class_basics.rs
  • Loading branch information
codeguru42 committed Jun 16, 2024
2 parents 7177d53 + 0b2f19b commit 3c7e898
Show file tree
Hide file tree
Showing 70 changed files with 1,318 additions and 333 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ rust-version = "1.63"
cfg-if = "1.0"
libc = "0.2.62"
memoffset = "0.9"
once_cell = "1"

# ffi bindings to the python interpreter, split into a separate crate so they can be used independently
pyo3-ffi = { path = "pyo3-ffi", version = "=0.22.0-dev" }
Expand Down Expand Up @@ -171,7 +172,7 @@ used_underscore_binding = "warn"
[workspace.lints.rust]
elided_lifetimes_in_paths = "warn"
invalid_doc_attributes = "warn"
rust_2018_idioms = "warn"
rust_2018_idioms = { level = "warn", priority = -1 }
rust_2021_prelude_collisions = "warn"
unused_lifetimes = "warn"

Expand Down
1 change: 1 addition & 0 deletions guide/pyclass-parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
| `mapping` | Inform PyO3 that this class is a [`Mapping`][params-mapping], and so leave its implementation of sequence C-API slots empty. |
| <span style="white-space: pre">`module = "module_name"`</span> | Python code will see the class as being defined in this module. Defaults to `builtins`. |
| <span style="white-space: pre">`name = "python_name"`</span> | Sets the name that Python sees this class as. Defaults to the name of the Rust struct. |
| `ord` | Implements `__lt__`, `__gt__`, `__le__`, & `__ge__` using the `PartialOrd` implementation of the underlying Rust datatype. *Requires `eq`* |
| `rename_all = "renaming_rule"` | Applies renaming rules to every getters and setters of a struct, or every variants of an enum. Possible values are: "camelCase", "kebab-case", "lowercase", "PascalCase", "SCREAMING-KEBAB-CASE", "SCREAMING_SNAKE_CASE", "snake_case", "UPPERCASE". |
| `sequence` | Inform PyO3 that this class is a [`Sequence`][params-sequence], and so leave its C-API mapping length slot empty. |
| `set_all` | Generates setters for all fields of the pyclass. |
Expand Down
65 changes: 59 additions & 6 deletions guide/src/class.md
Original file line number Diff line number Diff line change
Expand Up @@ -327,8 +327,12 @@ explicitly.

To get a parent class from a child, use [`PyRef`] instead of `&self` for methods,
or [`PyRefMut`] instead of `&mut self`.
Then you can access a parent class by `self_.as_ref()` as `&Self::BaseClass`,
or by `self_.into_super()` as `PyRef<Self::BaseClass>`.
Then you can access a parent class by `self_.as_super()` as `&PyRef<Self::BaseClass>`,
or by `self_.into_super()` as `PyRef<Self::BaseClass>` (and similar for the `PyRefMut`
case). For convenience, `self_.as_ref()` can also be used to get `&Self::BaseClass`
directly; however, this approach does not let you access base clases higher in the
inheritance hierarchy, for which you would need to chain multiple `as_super` or
`into_super` calls.

```rust
# use pyo3::prelude::*;
Expand All @@ -345,7 +349,7 @@ impl BaseClass {
BaseClass { val1: 10 }
}

pub fn method(&self) -> PyResult<usize> {
pub fn method1(&self) -> PyResult<usize> {
Ok(self.val1)
}
}
Expand All @@ -363,8 +367,8 @@ impl SubClass {
}

fn method2(self_: PyRef<'_, Self>) -> PyResult<usize> {
let super_ = self_.as_ref(); // Get &BaseClass
super_.method().map(|x| x * self_.val2)
let super_ = self_.as_super(); // Get &PyRef<BaseClass>
super_.method1().map(|x| x * self_.val2)
}
}

Expand All @@ -381,11 +385,28 @@ impl SubSubClass {
}

fn method3(self_: PyRef<'_, Self>) -> PyResult<usize> {
let base = self_.as_super().as_super(); // Get &PyRef<'_, BaseClass>
base.method1().map(|x| x * self_.val3)
}

fn method4(self_: PyRef<'_, Self>) -> PyResult<usize> {
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) {
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>) {
self_.as_super().as_super().val1 *= 2;
self_.as_super().val2 *= 2;
self_.val3 *= 2;
}

#[staticmethod]
fn factory_method(py: Python<'_>, val: usize) -> PyResult<PyObject> {
let base = PyClassInitializer::from(BaseClass::new());
Expand All @@ -400,7 +421,13 @@ impl SubSubClass {
}
# Python::with_gil(|py| {
# let subsub = pyo3::Py::new(py, SubSubClass::new()).unwrap();
# pyo3::py_run!(py, subsub, "assert subsub.method3() == 3000");
# pyo3::py_run!(py, subsub, "assert subsub.method1() == 10");
# pyo3::py_run!(py, subsub, "assert subsub.method2() == 150");
# pyo3::py_run!(py, subsub, "assert subsub.method3() == 200");
# pyo3::py_run!(py, subsub, "assert subsub.method4() == 3000");
# pyo3::py_run!(py, subsub, "assert subsub.get_values() == (10, 15, 20)");
# pyo3::py_run!(py, subsub, "assert subsub.double_values() == None");
# pyo3::py_run!(py, subsub, "assert subsub.get_values() == (20, 30, 40)");
# let subsub = SubSubClass::factory_method(py, 2).unwrap();
# let subsubsub = SubSubClass::factory_method(py, 3).unwrap();
# let cls = py.get_type_bound::<SubSubClass>();
Expand Down Expand Up @@ -1160,6 +1187,32 @@ 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.*

```rust
# use pyo3::prelude::*;
#[pyclass(eq, ord)]
#[derive(PartialEq, PartialOrd)]
enum MyEnum{
A,
B,
C,
}

Python::with_gil(|py| {
let cls = py.get_type_bound::<MyEnum>();
let a = Py::new(py, MyEnum::A).unwrap();
let b = Py::new(py, MyEnum::B).unwrap();
let c = Py::new(py, MyEnum::C).unwrap();
pyo3::py_run!(py, cls a b c, r#"
assert (a < b) == True
assert (c <= b) == False
assert (c > a) == True
"#)
})
```

You may not use enums as a base class or let enums inherit from other classes.

```rust,compile_fail
Expand Down
10 changes: 10 additions & 0 deletions guide/src/class/object.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,16 @@ To implement `__eq__` using the Rust [`PartialEq`] trait implementation, the `eq
struct Number(i32);
```

To implement `__lt__`, `__le__`, `__gt__`, & `__ge__` using the Rust `PartialOrd` trait implementation, the `ord` option can be used. *Note: Requires `eq`.*

```rust
# use pyo3::prelude::*;
#
#[pyclass(eq, ord)]
#[derive(PartialEq, PartialOrd)]
struct Number(i32);
```

### Truthyness

We'll consider `Number` to be `True` if it is nonzero:
Expand Down
16 changes: 8 additions & 8 deletions guide/src/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ enum SimpleEnum {
</details>

## from 0.20.* to 0.21
<details open>
<details>
<summary><small>Click to expand</small></summary>

PyO3 0.21 introduces a new `Bound<'py, T>` smart pointer which replaces the existing "GIL Refs" API to interact with Python objects. For example, in PyO3 0.20 the reference `&'py PyAny` would be used to interact with Python objects. In PyO3 0.21 the updated type is `Bound<'py, PyAny>`. Making this change moves Rust ownership semantics out of PyO3's internals and into user code. This change fixes [a known soundness edge case of interaction with gevent](https://github.com/PyO3/pyo3/issues/3668) as well as improves CPU and [memory performance](https://github.com/PyO3/pyo3/issues/1056). For a full history of discussion see https://github.com/PyO3/pyo3/issues/3382.
Expand All @@ -100,7 +100,7 @@ The following sections are laid out in this order.
</details>

### Enable the `gil-refs` feature
<details open>
<details>
<summary><small>Click to expand</small></summary>

To make the transition for the PyO3 ecosystem away from the GIL Refs API as smooth as possible, in PyO3 0.21 no APIs consuming or producing GIL Refs have been altered. Instead, variants using `Bound<T>` smart pointers have been introduced, for example `PyTuple::new_bound` which returns `Bound<PyTuple>` is the replacement form of `PyTuple::new`. The GIL Ref APIs have been deprecated, but to make migration easier it is possible to disable these deprecation warnings by enabling the `gil-refs` feature.
Expand All @@ -127,7 +127,7 @@ pyo3 = { version = "0.21", features = ["gil-refs"] }
</details>

### `PyTypeInfo` and `PyTryFrom` have been adjusted
<details open>
<details>
<summary><small>Click to expand</small></summary>

The `PyTryFrom` trait has aged poorly, its `try_from` method now conflicts with `TryFrom::try_from` in the 2021 edition prelude. A lot of its functionality was also duplicated with `PyTypeInfo`.
Expand Down Expand Up @@ -170,7 +170,7 @@ Python::with_gil(|py| {
</details>

### `Iter(A)NextOutput` are deprecated
<details open>
<details>
<summary><small>Click to expand</small></summary>

The `__next__` and `__anext__` magic methods can now return any type convertible into Python objects directly just like all other `#[pymethods]`. The `IterNextOutput` used by `__next__` and `IterANextOutput` used by `__anext__` are subsequently deprecated. Most importantly, this change allows returning an awaitable from `__anext__` without non-sensically wrapping it into `Yield` or `Some`. Only the return types `Option<T>` and `Result<Option<T>, E>` are still handled in a special manner where `Some(val)` yields `val` and `None` stops iteration.
Expand Down Expand Up @@ -292,21 +292,21 @@ impl PyClassAsyncIter {
</details>

### `PyType::name` has been renamed to `PyType::qualname`
<details open>
<details>
<summary><small>Click to expand</small></summary>

`PyType::name` has been renamed to `PyType::qualname` to indicate that it does indeed return the [qualified name](https://docs.python.org/3/glossary.html#term-qualified-name), matching the `__qualname__` attribute. The newly added `PyType::name` yields the full name including the module name now which corresponds to `__module__.__name__` on the level of attributes.
</details>

### `PyCell` has been deprecated
<details open>
<details>
<summary><small>Click to expand</small></summary>

Interactions with Python objects implemented in Rust no longer need to go though `PyCell<T>`. Instead iteractions with Python object now consistently go through `Bound<T>` or `Py<T>` independently of whether `T` is native Python object or a `#[pyclass]` implemented in Rust. Use `Bound::new` or `Py::new` respectively to create and `Bound::borrow(_mut)` / `Py::borrow(_mut)` to borrow the Rust object.
</details>

### Migrating from the GIL Refs API to `Bound<T>`
<details open>
<details>
<summary><small>Click to expand</small></summary>

To minimise breakage of code using the GIL Refs API, the `Bound<T>` smart pointer has been introduced by adding complements to all functions which accept or return GIL Refs. This allows code to migrate by replacing the deprecated APIs with the new ones.
Expand Down Expand Up @@ -404,7 +404,7 @@ Despite a large amount of deprecations warnings produced by PyO3 to aid with the
</details>

### Deactivating the `gil-refs` feature
<details open>
<details>
<summary><small>Click to expand</small></summary>

As a final step of migration, deactivating the `gil-refs` feature will set up code for best performance and is intended to set up a forward-compatible API for PyO3 0.22.
Expand Down
35 changes: 33 additions & 2 deletions guide/src/module.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,39 @@ mod my_extension {
# }
```

The `#[pymodule]` macro automatically sets the `module` attribute of the `#[pyclass]` macros declared inside of it with its name.
For nested modules, the name of the parent module is automatically added.
In the following example, the `Unit` class will have for `module` `my_extension.submodule` because it is properly nested
but the `Ext` class will have for `module` the default `builtins` because it not nested.
```rust
# #[cfg(feature = "experimental-declarative-modules")]
# mod declarative_module_module_attr_test {
use pyo3::prelude::*;

#[pyclass]
struct Ext;

#[pymodule]
mod my_extension {
use super::*;

#[pymodule_export]
use super::Ext;

#[pymodule]
mod submodule {
use super::*;
// This is a submodule

#[pyclass] // This will be part of the module
struct Unit;
}
}
# }
```
It is possible to customize the `module` value for a `#[pymodule]` with the `#[pyo3(module = "MY_MODULE")]` option.

Some changes are planned to this feature before stabilization, like automatically
filling submodules into `sys.modules` to allow easier imports (see [issue #759](https://github.com/PyO3/pyo3/issues/759))
and filling the `module` argument of inlined `#[pyclass]` automatically with the proper module name.
filling submodules into `sys.modules` to allow easier imports (see [issue #759](https://github.com/PyO3/pyo3/issues/759)).
Macro names might also change.
See [issue #3900](https://github.com/PyO3/pyo3/issues/3900) to track this feature progress.
1 change: 1 addition & 0 deletions newsfragments/4178.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The global reference pool (to track pending reference count decrements) is now initialized lazily to avoid the overhead of taking a mutex upon function entry when the functionality is not actually used.
1 change: 1 addition & 0 deletions newsfragments/4201.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Remove CPython internal ffi call for complex number including: add, sub, mul, div, neg, abs, pow. Added PyAnyMethods::{abs, pos, neg}
1 change: 1 addition & 0 deletions newsfragments/4202.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add `#[pyclass(ord)]` to implement ordering based on `PartialOrd`.
1 change: 1 addition & 0 deletions newsfragments/4213.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Properly fills the `module=` attribute of declarative modules child `#[pymodule]` and `#[pyclass]`.
3 changes: 3 additions & 0 deletions newsfragments/4219.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- Added `as_super` methods to `PyRef` and `PyRefMut` for accesing the base class by reference
- Updated user guide to recommend `as_super` for referencing the base class instead of `as_ref`
- Added `pyo3::internal_tricks::ptr_from_mut` function for casting `&mut T` to `*mut T`
1 change: 1 addition & 0 deletions newsfragments/4236.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Declarative modules: do not discard doc comments on the `mod` node.
1 change: 1 addition & 0 deletions newsfragments/4237.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Respect the Python "limited API" when building for the `abi3` feature on PyPy or GraalPy.
1 change: 1 addition & 0 deletions newsfragments/4249.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Implement `PyModuleMethods::filename` on PyPy.
1 change: 1 addition & 0 deletions newsfragments/4251.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix `__dict__` attribute missing for `#[pyclass(dict)]` instances when building for `abi3` on Python 3.9.
Loading

0 comments on commit 3c7e898

Please sign in to comment.