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

docs: update Python function section of the guide #3925

Merged
merged 2 commits into from
Mar 3, 2024
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 14 additions & 15 deletions guide/src/function.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ The `#[pyo3]` attribute can be used to modify properties of the generated Python

- <a name="pass_module" ></a> `#[pyo3(pass_module)]`

Set this option to make PyO3 pass the containing module as the first argument to the function. It is then possible to use the module in the function body. The first argument **must** be of type `&PyModule`.
Set this option to make PyO3 pass the containing module as the first argument to the function. It is then possible to use the module in the function body. The first argument **must** be of type `&Bound<'_, PyModule>`, `Bound<'_, PyModule>`, or `Py<PyModule>`.

The following example creates a function `pyfunction_with_module` which returns the containing module's name (i.e. `module_with_fn`):

Expand All @@ -103,14 +103,14 @@ The `#[pyo3]` attribute can be used on individual arguments to modify properties

- <a name="from_py_with"></a> `#[pyo3(from_py_with = "...")]`

Set this on an option to specify a custom function to convert the function argument from Python to the desired Rust type, instead of using the default `FromPyObject` extraction. The function signature must be `fn(&PyAny) -> PyResult<T>` where `T` is the Rust type of the argument.
Set this on an option to specify a custom function to convert the function argument from Python to the desired Rust type, instead of using the default `FromPyObject` extraction. The function signature must be `fn(&Bound<'_, PyAny>) -> PyResult<T>` where `T` is the Rust type of the argument.

The following example uses `from_py_with` to convert the input Python object to its length:

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

fn get_length(obj: &PyAny) -> PyResult<usize> {
fn get_length(obj: &Bound<'_, PyAny>) -> PyResult<usize> {
let length = obj.len()?;
Ok(length)
}
Expand All @@ -121,7 +121,7 @@ The `#[pyo3]` attribute can be used on individual arguments to modify properties
}

# Python::with_gil(|py| {
# let f = pyo3::wrap_pyfunction!(object_length)(py).unwrap();
# let f = pyo3::wrap_pyfunction_bound!(object_length)(py).unwrap();
# assert_eq!(f.call1((vec![1, 2, 3],)).unwrap().extract::<usize>().unwrap(), 3);
# });
```
Expand All @@ -134,11 +134,11 @@ You can pass Python `def`'d functions and built-in functions to Rust functions [
corresponds to regular Python functions while [`PyCFunction`] describes built-ins such as
`repr()`.

You can also use [`PyAny::is_callable`] to check if you have a callable object. `is_callable` will
return `true` for functions (including lambdas), methods and objects with a `__call__` method.
You can call the object with [`PyAny::call`] with the args as first parameter and the kwargs
(or `None`) as second parameter. There are also [`PyAny::call0`] with no args and [`PyAny::call1`]
with only positional args.
You can also use [`Bound<'_, PyAny>::is_callable`] to check if you have a callable object. `is_callable`
will return `true` for functions (including lambdas), methods and objects with a `__call__` method.
You can call the object with [`Bound<'_, PyAny>::call`] with the args as first parameter and the kwargs
(or `None`) as second parameter. There are also [`Bound<'_, PyAny>::call0`] with no args and
[`Bound<'_, PyAny>::call1`] with only positional args.

### Calling Rust functions in Python

Expand All @@ -149,11 +149,10 @@ The ways to convert a Rust function into a Python object vary depending on the f
- use a `#[pyclass]` struct which stores the function as a field and implement `__call__` to call the stored function.
- use `PyCFunction::new_closure` to create an object directly from the function.

[`PyAny::is_callable`]: {{#PYO3_DOCS_URL}}/pyo3/struct.PyAny.html#tymethod.is_callable
[`PyAny::call`]: {{#PYO3_DOCS_URL}}/pyo3/struct.PyAny.html#tymethod.call
[`PyAny::call0`]: {{#PYO3_DOCS_URL}}/pyo3/struct.PyAny.html#tymethod.call0
[`PyAny::call1`]: {{#PYO3_DOCS_URL}}/pyo3/struct.PyAny.html#tymethod.call1
[`PyObject`]: {{#PYO3_DOCS_URL}}/pyo3/type.PyObject.html
[`Bound<'_, PyAny>::is_callable`]: {{#PYO3_DOCS_URL}}/pyo3/prelude/trait.PyAnyMethods.html#tymethod.is_callable
[`Bound<'_, PyAny>::call`]: {{#PYO3_DOCS_URL}}/pyo3/prelude/trait.PyAnyMethods.html#tymethod.call
[`Bound<'_, PyAny>::call0`]: {{#PYO3_DOCS_URL}}/pyo3/prelude/trait.PyAnyMethods.html#tymethod.call0
[`Bound<'_, PyAny>::call1`]: {{#PYO3_DOCS_URL}}/pyo3/prelude/trait.PyAnyMethods.html#tymethod.call1
[`wrap_pyfunction!`]: {{#PYO3_DOCS_URL}}/pyo3/macro.wrap_pyfunction.html
[`PyFunction`]: {{#PYO3_DOCS_URL}}/pyo3/types/struct.PyFunction.html
[`PyCFunction`]: {{#PYO3_DOCS_URL}}/pyo3/types/struct.PyCFunction.html
Expand All @@ -180,7 +179,7 @@ An example of `#[pyfn]` is below:
use pyo3::prelude::*;

#[pymodule]
fn my_extension(py: Python<'_>, m: &PyModule) -> PyResult<()> {
fn my_extension(m: &Bound<'_, PyModule>) -> PyResult<()> {
#[pyfn(m)]
fn double(x: usize) -> usize {
x * 2
Expand Down
10 changes: 4 additions & 6 deletions guide/src/function/error_handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ fn check_positive(x: i32) -> PyResult<()> {
#
# fn main(){
# Python::with_gil(|py|{
# let fun = pyo3::wrap_pyfunction!(check_positive, py).unwrap();
# let fun = pyo3::wrap_pyfunction_bound!(check_positive, py).unwrap();
# fun.call1((-1,)).unwrap_err();
# fun.call1((1,)).unwrap();
# });
Expand Down Expand Up @@ -72,7 +72,7 @@ fn parse_int(x: &str) -> Result<usize, ParseIntError> {

# fn main() {
# Python::with_gil(|py| {
# let fun = pyo3::wrap_pyfunction!(parse_int, py).unwrap();
# let fun = pyo3::wrap_pyfunction_bound!(parse_int, py).unwrap();
# let value: usize = fun.call1(("5",)).unwrap().extract().unwrap();
# assert_eq!(value, 5);
# });
Expand Down Expand Up @@ -132,7 +132,7 @@ fn connect(s: String) -> Result<(), CustomIOError> {

fn main() {
Python::with_gil(|py| {
let fun = pyo3::wrap_pyfunction!(connect, py).unwrap();
let fun = pyo3::wrap_pyfunction_bound!(connect, py).unwrap();
let err = fun.call1(("0.0.0.0",)).unwrap_err();
assert!(err.is_instance_of::<PyOSError>(py));
});
Expand Down Expand Up @@ -224,7 +224,7 @@ fn wrapped_get_x() -> Result<i32, MyOtherError> {

# fn main() {
# Python::with_gil(|py| {
# let fun = pyo3::wrap_pyfunction!(wrapped_get_x, py).unwrap();
# let fun = pyo3::wrap_pyfunction_bound!(wrapped_get_x, py).unwrap();
# let value: usize = fun.call0().unwrap().extract().unwrap();
# assert_eq!(value, 5);
# });
Expand All @@ -234,8 +234,6 @@ fn wrapped_get_x() -> Result<i32, MyOtherError> {

[`From`]: https://doc.rust-lang.org/stable/std/convert/trait.From.html
[`Result<T, E>`]: https://doc.rust-lang.org/stable/std/result/enum.Result.html

[`PyResult<T>`]: {{#PYO3_DOCS_URL}}/pyo3/prelude/type.PyResult.html
[`PyResult<T>`]: {{#PYO3_DOCS_URL}}/pyo3/prelude/type.PyResult.html
[`PyErr`]: {{#PYO3_DOCS_URL}}/pyo3/struct.PyErr.html
[`pyo3::exceptions`]: {{#PYO3_DOCS_URL}}/pyo3/exceptions/index.html
22 changes: 11 additions & 11 deletions guide/src/function/signature.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use pyo3::types::PyDict;

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

Expand All @@ -31,8 +31,8 @@ Just like in Python, the following constructs can be part of the signature::

* `/`: positional-only arguments separator, each parameter defined before `/` is a positional-only parameter.
* `*`: var arguments separator, each parameter defined after `*` is a keyword-only parameter.
* `*args`: "args" is var args. Type of the `args` parameter has to be `&PyTuple`.
* `**kwargs`: "kwargs" receives keyword arguments. The type of the `kwargs` parameter has to be `Option<&PyDict>`.
* `*args`: "args" is var args. Type of the `args` parameter has to be `&Bound<'_, PyTuple>`.
* `**kwargs`: "kwargs" receives keyword arguments. The type of the `kwargs` parameter has to be `Option<&Bound<'_, PyDict>>`.
* `arg=Value`: arguments with default 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
Expand All @@ -59,9 +59,9 @@ impl MyClass {
fn method(
&mut self,
num: i32,
py_args: &PyTuple,
py_args: &Bound<'_, PyTuple>,
name: &str,
py_kwargs: Option<&PyDict>,
py_kwargs: Option<&Bound<'_, PyDict>>,
) -> String {
let num_before = self.num;
self.num = num;
Expand Down Expand Up @@ -136,7 +136,7 @@ fn increment(x: u64, amount: Option<u64>) -> u64 {
#
# fn main() -> PyResult<()> {
# Python::with_gil(|py| {
# let fun = pyo3::wrap_pyfunction!(increment, py)?;
# let fun = pyo3::wrap_pyfunction_bound!(increment, py)?;
#
# let inspect = PyModule::import_bound(py, "inspect")?.getattr("signature")?;
# let sig: String = inspect
Expand Down Expand Up @@ -164,7 +164,7 @@ fn increment(x: u64, amount: Option<u64>) -> u64 {
#
# fn main() -> PyResult<()> {
# Python::with_gil(|py| {
# let fun = pyo3::wrap_pyfunction!(increment, py)?;
# let fun = pyo3::wrap_pyfunction_bound!(increment, py)?;
#
# let inspect = PyModule::import_bound(py, "inspect")?.getattr("signature")?;
# let sig: String = inspect
Expand Down Expand Up @@ -204,7 +204,7 @@ fn add(a: u64, b: u64) -> u64 {
#
# fn main() -> PyResult<()> {
# Python::with_gil(|py| {
# let fun = pyo3::wrap_pyfunction!(add, py)?;
# let fun = pyo3::wrap_pyfunction_bound!(add, py)?;
#
# let doc: String = fun.getattr("__doc__")?.extract()?;
# assert_eq!(doc, "This function adds two unsigned 64-bit integers.");
Expand Down Expand Up @@ -252,7 +252,7 @@ fn add(a: u64, b: u64) -> u64 {
#
# fn main() -> PyResult<()> {
# Python::with_gil(|py| {
# let fun = pyo3::wrap_pyfunction!(add, py)?;
# let fun = pyo3::wrap_pyfunction_bound!(add, py)?;
#
# let doc: String = fun.getattr("__doc__")?.extract()?;
# assert_eq!(doc, "This function adds two unsigned 64-bit integers.");
Expand All @@ -269,7 +269,7 @@ fn add(a: u64, b: u64) -> u64 {
# }
```

PyO3 will include the contents of the annotation unmodified as the `__text_signature`. Below shows how IPython will now present this (see the default value of 0 for b):
PyO3 will include the contents of the annotation unmodified as the `__text_signature__`. Below shows how IPython will now present this (see the default value of 0 for b):

```text
>>> pyo3_test.add.__text_signature__
Expand All @@ -294,7 +294,7 @@ fn add(a: u64, b: u64) -> u64 {
#
# fn main() -> PyResult<()> {
# Python::with_gil(|py| {
# let fun = pyo3::wrap_pyfunction!(add, py)?;
# let fun = pyo3::wrap_pyfunction_bound!(add, py)?;
#
# let doc: String = fun.getattr("__doc__")?.extract()?;
# assert_eq!(doc, "This function adds two unsigned 64-bit integers.");
Expand Down
Loading