Skip to content

Commit

Permalink
Merge pull request #2580 from davidhewitt/docs-bonanza
Browse files Browse the repository at this point in the history
docs: pile of tweaks and additions
  • Loading branch information
davidhewitt committed Aug 23, 2022
2 parents f574168 + f753790 commit bc03d6a
Show file tree
Hide file tree
Showing 20 changed files with 465 additions and 293 deletions.
30 changes: 16 additions & 14 deletions guide/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,37 @@

---

- [Getting Started](getting_started.md)
- [Python Modules](module.md)
- [Python Functions](function.md)
- [Python Classes](class.md)
- [Getting started](getting_started.md)
- [Python modules](module.md)
- [Python functions](function.md)
- [Function signatures](function/signature.md)
- [Error handling](function/error_handling.md)
- [Python classes](class.md)
- [Class customizations](class/protocols.md)
- [Basic object customization](class/object.md)
- [Emulating numeric types](class/numeric.md)
- [Emulating callable objects](class/call.md)
- [Type Conversions](conversions.md)
- [Type conversions](conversions.md)
- [Mapping of Rust types to Python types](conversions/tables.md)]
- [Conversion traits](conversions/traits.md)]
- [Python Exceptions](exception.md)
- [Python exceptions](exception.md)
- [Calling Python from Rust](python_from_rust.md)
- [GIL, mutability and object types](types.md)
- [Parallelism](parallelism.md)
- [Debugging](debugging.md)
- [Features Reference](features.md)
- [Memory Management](memory.md)
- [Advanced Topics](advanced.md)
- [Building and Distribution](building_and_distribution.md)
- [Features reference](features.md)
- [Memory management](memory.md)
- [Advanced topics](advanced.md)
- [Building and distribution](building_and_distribution.md)
- [Supporting multiple Python versions](building_and_distribution/multiple_python_versions.md)
- [Useful Crates](ecosystem.md)
- [Useful crates](ecosystem.md)
- [Logging](ecosystem/logging.md)
- [Async / Await](ecosystem/async-await.md)
- [FAQ & Troubleshooting](faq.md)
- [Using `async` and `await`](ecosystem/async-await.md)
- [FAQ and troubleshooting](faq.md)

---

[Appendix A: Migration Guide](migration.md)
[Appendix A: Migration guide](migration.md)
[Appendix B: PyO3 and rust-cpython](rust_cpython.md)
[Appendix C: Trait bounds](trait_bounds.md)
[Appendix D: Python typing hints](python_typing_hints.md)
Expand Down
2 changes: 1 addition & 1 deletion guide/src/advanced.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ PyO3 exposes much of Python's C API through the `ffi` module.

The C API is naturally unsafe and requires you to manage reference counts, errors and specific invariants yourself. Please refer to the [C API Reference Manual](https://docs.python.org/3/c-api/) and [The Rustonomicon](https://doc.rust-lang.org/nightly/nomicon/ffi.html) before using any function from that API.

## Memory Management
## Memory management

PyO3's `&PyAny` "owned references" and `Py<PyAny>` smart pointers are used to
access memory stored in Python's heap. This memory sometimes lives for longer
Expand Down
2 changes: 1 addition & 1 deletion guide/src/building_and_distribution.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Building and Distribution
# Building and distribution

This chapter of the guide goes into detail on how to build and distribute projects using PyO3. The way to achieve this is very different depending on whether the project is a Python module implemented in Rust, or a Rust binary embedding Python. For both types of project there are also common problems such as the Python version to build for and the [linker](https://en.wikipedia.org/wiki/Linker_(computing)) arguments to use.

Expand Down
96 changes: 34 additions & 62 deletions guide/src/class.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Python Classes
# Python classes

PyO3 exposes a group of attributes powered by Rust's proc macro system for defining Python classes as Rust structs.

Expand Down Expand Up @@ -614,29 +614,10 @@ impl MyClass {

## Method arguments

By default, PyO3 uses function signatures to determine which arguments are required. Then it scans
the incoming `args` and `kwargs` parameters. If it can not find all required
parameters, it raises a `TypeError` exception. It is possible to override the default behavior
with the `#[args(...)]` attribute. This attribute accepts a comma separated list of parameters in
the form of `attr_name="default value"`. Each parameter has to match the method parameter by name.

Each parameter can be one of the following types:

* `"/"`: positional-only arguments separator, each parameter defined before `"/"` is a
positional-only parameter.
Corresponds to python's `def meth(arg1, arg2, ..., /, argN..)`.
* `"*"`: var arguments separator, each parameter defined after `"*"` is a keyword-only parameter.
Corresponds to python's `def meth(*, arg1.., arg2=..)`.
* `args="*"`: "args" is var args, corresponds to Python's `def meth(*args)`. Type of the `args`
parameter has to be `&PyTuple`.
* `kwargs="**"`: "kwargs" receives keyword arguments, corresponds to Python's `def meth(**kwargs)`.
The type of the `kwargs` parameter has to be `Option<&PyDict>`.
* `arg="Value"`: arguments with default value. Corresponds to Python's `def meth(arg=Value)`.
If the `arg` argument is defined after var arguments, it is treated as a keyword-only argument.
Note that `Value` has to be valid rust code, PyO3 just inserts it into the generated
code unmodified.

Example:
Similar to `#[pyfunction]`, the `#[args]` attribute can be used to specify the way that `#[pymethods]` accept arguments. Consult the documentation for [`function signatures`](./function/signature.md) to see the parameters this attribute accepts.

The following example defines a class `MyClass` with a method `method`. This method has an `#[args]` attribute which sets default values for `num` and `name`, and indicates that `py_args` should collect all extra positional arguments and `py_kwargs` all extra keyword arguments:

```rust
# use pyo3::prelude::*;
use pyo3::types::{PyDict, PyTuple};
Expand Down Expand Up @@ -665,35 +646,26 @@ impl MyClass {
name: &str,
py_args: &PyTuple,
py_kwargs: Option<&PyDict>,
) -> PyResult<String> {
self.num = num;
Ok(format!(
"py_args={:?}, py_kwargs={:?}, name={}, num={}",
py_args, py_kwargs, name, self.num
))
}

fn make_change(&mut self, num: i32) -> PyResult<String> {
) -> String {
let num_before = self.num;
self.num = num;
Ok(format!("num={}", self.num))
format!(
"py_args={:?}, py_kwargs={:?}, name={}, num={} num_before={}",
py_args, py_kwargs, name, self.num, num_before,
)
}
}
```
N.B. the position of the `"/"` and `"*"` arguments (if included) control the system of handling positional and keyword arguments. In Python:
```python
import mymodule

mc = mymodule.MyClass()
print(mc.method(44, False, "World", 666, x=44, y=55))
print(mc.method(num=-1, name="World"))
print(mc.make_change(44, False))
```
Produces output:
```text
py_args=('World', 666), py_kwargs=Some({'x': 44, 'y': 55}), name=Hello, num=44
py_args=(), py_kwargs=None, name=World, num=-1
num=44
num=-1
In Python this might be used like:

```python
>>> import mymodule
>>> mc = mymodule.MyClass()
>>> print(mc.method(44, False, "World", 666, x=44, y=55))
py_args=('World', 666), py_kwargs=Some({'x': 44, 'y': 55}), name=Hello, num=44, num_before=-1
>>> print(mc.method(num=-1, name="World"))
py_args=(), py_kwargs=None, name=World, num=-1, num_before=44
```

## Making class method signatures available to Python
Expand Down Expand Up @@ -951,8 +923,8 @@ struct MyClass {
# #[allow(dead_code)]
num: i32,
}
unsafe impl ::pyo3::type_object::PyTypeInfo for MyClass {
type AsRefTarget = ::pyo3::PyCell<Self>;
unsafe impl pyo3::type_object::PyTypeInfo for MyClass {
type AsRefTarget = pyo3::PyCell<Self>;
const NAME: &'static str = "MyClass";
const MODULE: ::std::option::Option<&'static str> = ::std::option::Option::None;
#[inline]
Expand All @@ -963,27 +935,27 @@ unsafe impl ::pyo3::type_object::PyTypeInfo for MyClass {
}
}

impl ::pyo3::PyClass for MyClass {
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, 'py> pyo3::impl_::extract_argument::PyFunctionArgument<'a, 'py> for &'a MyClass
{
type Holder = ::std::option::Option<::pyo3::PyRef<'py, MyClass>>;
type Holder = ::std::option::Option<pyo3::PyRef<'py, MyClass>>;

#[inline]
fn extract(obj: &'py ::pyo3::PyAny, holder: &'a mut Self::Holder) -> ::pyo3::PyResult<Self> {
::pyo3::impl_::extract_argument::extract_pyclass_ref(obj, holder)
fn extract(obj: &'py pyo3::PyAny, holder: &'a mut Self::Holder) -> pyo3::PyResult<Self> {
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, 'py> pyo3::impl_::extract_argument::PyFunctionArgument<'a, 'py> for &'a mut MyClass
{
type Holder = ::std::option::Option<::pyo3::PyRefMut<'py, MyClass>>;
type Holder = ::std::option::Option<pyo3::PyRefMut<'py, MyClass>>;

#[inline]
fn extract(obj: &'py ::pyo3::PyAny, holder: &'a mut Self::Holder) -> ::pyo3::PyResult<Self> {
::pyo3::impl_::extract_argument::extract_pyclass_ref_mut(obj, holder)
fn extract(obj: &'py pyo3::PyAny, holder: &'a mut Self::Holder) -> pyo3::PyResult<Self> {
pyo3::impl_::extract_argument::extract_pyclass_ref_mut(obj, holder)
}
}

Expand All @@ -1001,9 +973,9 @@ impl pyo3::impl_::pyclass::PyClassImpl for MyClass {
type BaseType = PyAny;
type ThreadChecker = pyo3::impl_::pyclass::ThreadCheckerStub<MyClass>;
type PyClassMutability = <<pyo3::PyAny as pyo3::impl_::pyclass::PyClassBaseType>::PyClassMutability as pyo3::impl_::pycell::PyClassMutability>::MutableChild;
type Dict = ::pyo3::impl_::pyclass::PyClassDummySlot;
type WeakRef = ::pyo3::impl_::pyclass::PyClassDummySlot;
type BaseNativeType = ::pyo3::PyAny;
type Dict = pyo3::impl_::pyclass::PyClassDummySlot;
type WeakRef = pyo3::impl_::pyclass::PyClassDummySlot;
type BaseNativeType = pyo3::PyAny;

fn items_iter() -> pyo3::impl_::pyclass::PyClassItemsIter {
use pyo3::impl_::pyclass::*;
Expand Down
38 changes: 31 additions & 7 deletions guide/src/class/object.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ It can't even print an user-readable representation of itself! We can fix that b

```rust
# use pyo3::prelude::*;
#
#
# #[pyclass]
# struct Number(i32);
#
#
#[pymethods]
impl Number {
// For `__repr__` we want to return a string that Python code could use to recreate
Expand Down Expand Up @@ -83,10 +83,10 @@ use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};

# use pyo3::prelude::*;
#
#
# #[pyclass]
# struct Number(i32);
#
#
#[pymethods]
impl Number {
fn __hash__(&self) -> u64 {
Expand All @@ -110,7 +110,7 @@ impl Number {
> 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]
Expand Down Expand Up @@ -174,16 +174,40 @@ impl Number {
It checks that the `std::cmp::Ordering` obtained from Rust's `Ord` matches
the given `CompareOp`.

Alternatively, 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::prelude::*;
#
# #[pyclass]
# struct Number(i32);
#
#[pymethods]
impl Number {
fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> PyObject {
match op {
CompareOp::Eq => (self.0 == other.0).into_py(py),
CompareOp::Ne => (self.0 != other.0).into_py(py),
_ => py.NotImplemented(),
}
}
}
```

### Truthyness

We'll consider `Number` to be `True` if it is nonzero:

```rust
# use pyo3::prelude::*;
#
#
# #[pyclass]
# struct Number(i32);
#
#
#[pymethods]
impl Number {
fn __bool__(&self) -> bool {
Expand Down
4 changes: 3 additions & 1 deletion guide/src/class/protocols.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ given signatures should be interpreted as follows:

- `__hash__(<self>) -> isize`

Objects that compare equal must have the same hash value.
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`).
<details>
<summary>Disabling Python's default hash</summary>
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:
Expand All @@ -67,6 +67,8 @@ given signatures should be interpreted as follows:

Overloads Python comparison operations (`==`, `!=`, `<`, `<=`, `>`, and `>=`).
The `CompareOp` argument indicates the comparison operation being performed.

_Note that implementing `__richcmp__` will cause Python not to generate a default `__hash__` implementation, so consider implementing `__hash__` when implementing `__richcmp__`._
<details>
<summary>Return type</summary>
The return type will normally be `PyResult<bool>`, but any Python object can be returned.
Expand Down
2 changes: 1 addition & 1 deletion guide/src/conversions.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Type Conversions
# Type conversions

In this portion of the guide we'll talk about the mapping of Python types to Rust types offered by PyO3, as well as the traits available to perform conversions between them.
2 changes: 1 addition & 1 deletion guide/src/ecosystem.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# The PyO3 Ecosystem
# The PyO3 ecosystem

This portion of the guide is dedicated to crates which are external to the main PyO3 project and provide additional functionality you might find useful.

Expand Down
2 changes: 1 addition & 1 deletion guide/src/ecosystem/async-await.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Async / Await
# Using `async` and `await`

If you are working with a Python library that makes use of async functions or wish to provide
Python bindings for an async Rust library, [`pyo3-asyncio`](https://github.com/awestlake87/pyo3-asyncio)
Expand Down

0 comments on commit bc03d6a

Please sign in to comment.