docs: update Python function section of the guide (#3925)
* docs: update Python function section of the guide * update `pass_module` types Co-authored-by: David Hewitt <mail@davidhewitt.dev> --------- Co-authored-by: David Hewitt <mail@davidhewitt.dev>
This commit is contained in:
parent
2e56f659ed
commit
00eb014623
|
@ -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`):
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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);
|
||||
# });
|
||||
```
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
# });
|
||||
|
@ -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);
|
||||
# });
|
||||
|
@ -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));
|
||||
});
|
||||
|
@ -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);
|
||||
# });
|
||||
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.");
|
||||
|
@ -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.");
|
||||
|
@ -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__
|
||||
|
@ -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.");
|
||||
|
|
Loading…
Reference in New Issue