Skip to content

Commit

Permalink
guide: use sentence case, move function signatures to own section
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt committed Aug 23, 2022
1 parent af60a35 commit 580e747
Show file tree
Hide file tree
Showing 15 changed files with 179 additions and 127 deletions.
29 changes: 15 additions & 14 deletions guide/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,36 @@

---

- [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)
- [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
68 changes: 20 additions & 48 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
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
2 changes: 1 addition & 1 deletion guide/src/exception.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Python Exceptions
# Python exceptions

## Defining a new exception

Expand Down
2 changes: 1 addition & 1 deletion guide/src/faq.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Frequently Asked Questions / Troubleshooting
# Frequently Asked Questions and troubleshooting

## I'm experiencing deadlocks using PyO3 with lazy_static or once_cell!

Expand Down
2 changes: 1 addition & 1 deletion guide/src/features.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Features Reference
# Features reference

PyO3 provides a number of Cargo features to customise functionality. This chapter of the guide provides detail on each of them.

Expand Down
33 changes: 8 additions & 25 deletions guide/src/function.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Python Functions
# Python functions

The `#[pyfunction]` attribute is used to define a Python function from a Rust function. Once defined, the function needs to be added to a [module](./module.md) using the `wrap_pyfunction!` macro.

Expand All @@ -19,17 +19,20 @@ fn my_extension(py: Python<'_>, m: &PyModule) -> PyResult<()> {
}
```

This chapter of the guide explains full usage of the `#[pyfunction]` attribute. The following topics are covered:
This chapter of the guide explains full usage of the `#[pyfunction]` attribute. In this first section, the following topics are covered:

- [Function options](#function-options)
- [`#[pyo3(name = "...")]`](#name)
- [`#[pyo3(text_signature = "...")]`](#text_signature)
- [`#[pyo3(pass_module)]`](#pass_module)
- [Argument parsing](#argument-parsing)
- [`#[pyo3(from_py_with = "...")]`](#from_py_with)
- [Per-argument options](#per-argument-options)
- [Advanced function patterns](#advanced-function-patterns)
- [`#[pyfn]` shorthand](#pyfn-shorthand)

There are also additional sections on the following topics:

- [Function Signatures](./function/signature.md)

## Function options

The `#[pyo3]` attribute can be used to modify properties of the generated Python function. It can take any combination of the following options:
Expand Down Expand Up @@ -118,27 +121,7 @@ The `#[pyo3]` attribute can be used to modify properties of the generated Python
}
```

## Argument parsing

The `#[pyfunction]` attribute supports specifying details of argument parsing. The details are given in the section ["Method arguments" of the Classes chapter](class.md#method-arguments). Here is an example for a function that accepts arbitrary keyword arguments (`**kwargs` in Python syntax) and returns the number that was passed:

```rust
use pyo3::prelude::*;
use pyo3::types::PyDict;

#[pyfunction(kwds="**")]
fn num_kwds(kwds: Option<&PyDict>) -> usize {
kwds.map_or(0, |dict| dict.len())
}

#[pymodule]
fn module_with_functions(py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(num_kwds, m)?).unwrap();
Ok(())
}
```

### Per-argument options
## Per-argument options

The `#[pyo3]` attribute can be used on individual arguments to modify properties of them in the generated function. It can take any combination of the following options:

Expand Down
97 changes: 97 additions & 0 deletions guide/src/function/signature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Function signatures

The `#[pyfunction]` attribute also accepts parameters to control how the generated Python function accepts arguments. Just like in Python, arguments can be positional-only, keyword-only, or accept either. `*args` lists and `**kwargs` dicts can also be accepted. These parameters also work for `#[pymethods]` which will be introduced in the [Python Classes](../class.md) section of the guide.

Like Python, by default PyO3 accepts all arguments as either positional or keyword arguments. The extra arguments to `#[pyfunction]` modify this behaviour. For example, below is a function that accepts arbitrary keyword arguments (`**kwargs` in Python syntax) and returns the number that was passed:

```rust
use pyo3::prelude::*;
use pyo3::types::PyDict;

#[pyfunction(kwds="**")]
fn num_kwds(kwds: Option<&PyDict>) -> usize {
kwds.map_or(0, |dict| dict.len())
}

#[pymodule]
fn module_with_functions(py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(num_kwds, m)?).unwrap();
Ok(())
}
```

The following parameters can be passed to the `#[pyfunction]` attribute:

* `"/"`: 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:
```rust
# use pyo3::prelude::*;
use pyo3::types::{PyDict, PyTuple};
#
# #[pyclass]
# struct MyClass {
# num: i32,
# }
#[pymethods]
impl MyClass {
#[new]
#[args(num = "-1")]
fn new(num: i32) -> Self {
MyClass { num }
}

#[args(
num = "10",
py_args = "*",
name = "\"Hello\"",
py_kwargs = "**"
)]
fn method(
&mut self,
num: i32,
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> {
self.num = num;
Ok(format!("num={}", self.num))
}
}
```
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
```

0 comments on commit 580e747

Please sign in to comment.