Add a nox task to rustfmt code in the guide.

Also apply it. Two caveats:

1) needs nightly rustfmt to be available
2) not all reformat diffs have been applied; using best judgment for readability.
This commit is contained in:
Georg Brandl 2022-11-20 14:58:41 +01:00
parent 8ca41be87b
commit a7a53d6c0d
22 changed files with 183 additions and 123 deletions

View File

@ -24,12 +24,12 @@ more accompanying `#[pyo3(...)]` annotations, e.g.:
```rust,ignore ```rust,ignore
// Argument supplied directly to the `#[pyclass]` annotation. // Argument supplied directly to the `#[pyclass]` annotation.
#[pyclass(name = "SomeName", subclass)] #[pyclass(name = "SomeName", subclass)]
struct MyClass { } struct MyClass {}
// Argument supplied as a separate annotation. // Argument supplied as a separate annotation.
#[pyclass] #[pyclass]
#[pyo3(name = "SomeName", subclass)] #[pyo3(name = "SomeName", subclass)]
struct MyClass { } struct MyClass {}
``` ```
[params-1]: https://docs.rs/pyo3/latest/pyo3/struct.PyAny.html [params-1]: https://docs.rs/pyo3/latest/pyo3/struct.PyAny.html

View File

@ -115,7 +115,7 @@ The easiest way to set the correct linker arguments is to add a [`build.rs`](htt
```rust,ignore ```rust,ignore
fn main() { fn main() {
pyo3_build_config::add_extension_module_link_args(); pyo3_build_config::add_extension_module_link_args();
} }
``` ```

View File

@ -14,13 +14,13 @@ This allows us to write code like the following
```rust,ignore ```rust,ignore
#[cfg(Py_3_7)] #[cfg(Py_3_7)]
fn function_only_supported_on_python_3_7_and_up() { } fn function_only_supported_on_python_3_7_and_up() {}
#[cfg(not(Py_3_8))] #[cfg(not(Py_3_8))]
fn function_only_supported_before_python_3_8() { } fn function_only_supported_before_python_3_8() {}
#[cfg(not(Py_LIMITED_API))] #[cfg(not(Py_LIMITED_API))]
fn function_incompatible_with_abi3_feature() { } fn function_incompatible_with_abi3_feature() {}
``` ```
The following sections first show how to add these `#[cfg]` flags to your build process, and then cover some common patterns flags in a little more detail. The following sections first show how to add these `#[cfg]` flags to your build process, and then cover some common patterns flags in a little more detail.
@ -98,11 +98,10 @@ PyO3 provides the APIs [`Python::version()`] and [`Python::version_info()`] to q
use pyo3::Python; use pyo3::Python;
Python::with_gil(|py| { Python::with_gil(|py| {
// PyO3 supports Python 3.7 and up. // PyO3 supports Python 3.7 and up.
assert!(py.version_info() >= (3, 7)); assert!(py.version_info() >= (3, 7));
assert!(py.version_info() >= (3, 7, 0)); assert!(py.version_info() >= (3, 7, 0));
}); });
``` ```
[`Python::version()`]: {{#PYO3_DOCS_URL}}/pyo3/struct.Python.html#method.version [`Python::version()`]: {{#PYO3_DOCS_URL}}/pyo3/struct.Python.html#method.version

View File

@ -26,8 +26,8 @@ To define a custom Python class, add the `#[pyclass]` attribute to a Rust struct
use pyo3::prelude::*; use pyo3::prelude::*;
#[pyclass] #[pyclass]
struct Integer{ struct Integer {
inner: i32 inner: i32,
} }
// A "tuple" struct // A "tuple" struct
@ -166,7 +166,7 @@ struct MyClass {
num: i32, num: i32,
} }
Python::with_gil(|py| { Python::with_gil(|py| {
let obj = PyCell::new(py, MyClass { num: 3}).unwrap(); let obj = PyCell::new(py, MyClass { num: 3 }).unwrap();
{ {
let obj_ref = obj.borrow(); // Get PyRef let obj_ref = obj.borrow(); // Get PyRef
assert_eq!(obj_ref.num, 3); assert_eq!(obj_ref.num, 3);
@ -204,7 +204,7 @@ fn return_myclass() -> Py<MyClass> {
let obj = return_myclass(); let obj = return_myclass();
Python::with_gil(|py|{ Python::with_gil(|py| {
let cell = obj.as_ref(py); // Py<MyClass>::as_ref returns &PyCell<MyClass> let cell = obj.as_ref(py); // Py<MyClass>::as_ref returns &PyCell<MyClass>
let obj_ref = cell.borrow(); // Get PyRef<T> let obj_ref = cell.borrow(); // Get PyRef<T>
assert_eq!(obj_ref.num, 1); assert_eq!(obj_ref.num, 1);
@ -279,7 +279,7 @@ impl SubClass {
} }
fn method2(self_: PyRef<'_, Self>) -> PyResult<usize> { fn method2(self_: PyRef<'_, Self>) -> PyResult<usize> {
let super_ = self_.as_ref(); // Get &BaseClass let super_ = self_.as_ref(); // Get &BaseClass
super_.method().map(|x| x * self_.val2) super_.method().map(|x| x * self_.val2)
} }
} }
@ -293,13 +293,12 @@ struct SubSubClass {
impl SubSubClass { impl SubSubClass {
#[new] #[new]
fn new() -> PyClassInitializer<Self> { fn new() -> PyClassInitializer<Self> {
PyClassInitializer::from(SubClass::new()) PyClassInitializer::from(SubClass::new()).add_subclass(SubSubClass { val3: 20 })
.add_subclass(SubSubClass{val3: 20})
} }
fn method3(self_: PyRef<'_, Self>) -> PyResult<usize> { fn method3(self_: PyRef<'_, Self>) -> PyResult<usize> {
let v = self_.val3; let v = self_.val3;
let super_ = self_.into_super(); // Get PyRef<'_, SubClass> let super_ = self_.into_super(); // Get PyRef<'_, SubClass>
SubClass::method2(super_).map(|x| x * v) SubClass::method2(super_).map(|x| x * v)
} }
} }
@ -426,7 +425,7 @@ For simple cases where a member variable is just read and written with no side e
#[pyclass] #[pyclass]
struct MyClass { struct MyClass {
#[pyo3(get, set)] #[pyo3(get, set)]
num: i32 num: i32,
} }
``` ```
@ -937,7 +936,7 @@ You may not use enums as a base class or let enums inherit from other classes.
```rust,compile_fail ```rust,compile_fail
# use pyo3::prelude::*; # use pyo3::prelude::*;
#[pyclass(subclass)] #[pyclass(subclass)]
enum BadBase{ enum BadBase {
Var1, Var1,
} }
``` ```
@ -949,7 +948,7 @@ enum BadBase{
struct Base; struct Base;
#[pyclass(extends=Base)] #[pyclass(extends=Base)]
enum BadSubclass{ enum BadSubclass {
Var1, Var1,
} }
``` ```

View File

@ -395,7 +395,6 @@ fn my_module(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
# Ok(()) # Ok(())
# }) # })
# } # }
``` ```
## Appendix: Writing some unsafe code ## Appendix: Writing some unsafe code

View File

@ -40,10 +40,10 @@ print(n)
### String representations ### String representations
It can't even print an user-readable representation of itself! We can fix that by defining the It can't even print an user-readable representation of itself! We can fix that by defining the
`__repr__` and `__str__` methods inside a `#[pymethods]` block. We do this by accessing the value `__repr__` and `__str__` methods inside a `#[pymethods]` block. We do this by accessing the value
contained inside `Number`. contained inside `Number`.
```rust ```rust
# use pyo3::prelude::*; # use pyo3::prelude::*;
# #
# #[pyclass] # #[pyclass]
@ -114,13 +114,13 @@ impl Number {
> ```rust > ```rust
> # use pyo3::prelude::*; > # use pyo3::prelude::*;
> #[pyclass] > #[pyclass]
> struct NotHashable { } > struct NotHashable {}
> >
> #[pymethods] > #[pymethods]
> impl NotHashable { > impl NotHashable {
> #[classattr] > #[classattr]
> const __hash__: Option<Py<PyAny>> = None; > const __hash__: Option<Py<PyAny>> = None;
>} > }
> ``` > ```
### Comparisons ### Comparisons

View File

@ -48,7 +48,7 @@ given signatures should be interpreted as follows:
# use pyo3::prelude::*; # use pyo3::prelude::*;
# #
#[pyclass] #[pyclass]
struct NotHashable { } struct NotHashable {}
#[pymethods] #[pymethods]
impl NotHashable { impl NotHashable {
@ -229,7 +229,7 @@ Use the `#[pyclass(sequence)]` annotation to instruct PyO3 to fill the `sq_lengt
# use pyo3::prelude::*; # use pyo3::prelude::*;
# #
#[pyclass] #[pyclass]
struct NoContains { } struct NoContains {}
#[pymethods] #[pymethods]
impl NoContains { impl NoContains {

View File

@ -62,7 +62,7 @@ struct RustyStruct {
# "", # "",
# "", # "",
# )?; # )?;
# #
# let class = module.getattr("Foo")?; # let class = module.getattr("Foo")?;
# let instance = class.call0()?; # let instance = class.call0()?;
# let rustystruct: RustyStruct = instance.extract()?; # let rustystruct: RustyStruct = instance.extract()?;
@ -77,7 +77,6 @@ By setting the `#[pyo3(item)]` attribute on the field, PyO3 will attempt to extr
```rust ```rust
use pyo3::prelude::*; use pyo3::prelude::*;
#[derive(FromPyObject)] #[derive(FromPyObject)]
struct RustyStruct { struct RustyStruct {
#[pyo3(item)] #[pyo3(item)]
@ -89,7 +88,7 @@ struct RustyStruct {
# Python::with_gil(|py| -> PyResult<()> { # Python::with_gil(|py| -> PyResult<()> {
# let dict = PyDict::new(py); # let dict = PyDict::new(py);
# dict.set_item("my_string", "test")?; # dict.set_item("my_string", "test")?;
# #
# let rustystruct: RustyStruct = dict.extract()?; # let rustystruct: RustyStruct = dict.extract()?;
# assert_eq!(rustystruct.my_string, "test"); # assert_eq!(rustystruct.my_string, "test");
# Ok(()) # Ok(())
@ -109,7 +108,7 @@ struct RustyStruct {
#[pyo3(attribute("name"))] #[pyo3(attribute("name"))]
string_attr: String, string_attr: String,
} }
# #
# fn main() -> PyResult<()> { # fn main() -> PyResult<()> {
# Python::with_gil(|py| -> PyResult<()> { # Python::with_gil(|py| -> PyResult<()> {
# let module = PyModule::from_code( # let module = PyModule::from_code(
@ -121,13 +120,13 @@ struct RustyStruct {
# "", # "",
# "", # "",
# )?; # )?;
# #
# let class = module.getattr("Foo")?; # let class = module.getattr("Foo")?;
# let instance = class.call0()?; # let instance = class.call0()?;
# let rustystruct: RustyStruct = instance.extract()?; # let rustystruct: RustyStruct = instance.extract()?;
# assert_eq!(rustystruct.string_attr, "test"); # assert_eq!(rustystruct.string_attr, "test");
# assert_eq!(rustystruct.string_in_mapping, "test2"); # assert_eq!(rustystruct.string_in_mapping, "test2");
# #
# Ok(()) # Ok(())
# }) # })
# } # }
@ -154,11 +153,11 @@ struct RustyTuple(String, String);
# fn main() -> PyResult<()> { # fn main() -> PyResult<()> {
# Python::with_gil(|py| -> PyResult<()> { # Python::with_gil(|py| -> PyResult<()> {
# let tuple = PyTuple::new(py, vec!["test", "test2"]); # let tuple = PyTuple::new(py, vec!["test", "test2"]);
# #
# let rustytuple: RustyTuple = tuple.extract()?; # let rustytuple: RustyTuple = tuple.extract()?;
# assert_eq!(rustytuple.0, "test"); # assert_eq!(rustytuple.0, "test");
# assert_eq!(rustytuple.1, "test2"); # assert_eq!(rustytuple.1, "test2");
# #
# Ok(()) # Ok(())
# }) # })
# } # }
@ -177,10 +176,10 @@ struct RustyTuple((String,));
# fn main() -> PyResult<()> { # fn main() -> PyResult<()> {
# Python::with_gil(|py| -> PyResult<()> { # Python::with_gil(|py| -> PyResult<()> {
# let tuple = PyTuple::new(py, vec!["test"]); # let tuple = PyTuple::new(py, vec!["test"]);
# #
# let rustytuple: RustyTuple = tuple.extract()?; # let rustytuple: RustyTuple = tuple.extract()?;
# assert_eq!((rustytuple.0).0, "test"); # assert_eq!((rustytuple.0).0, "test");
# #
# Ok(()) # Ok(())
# }) # })
# } # }
@ -209,13 +208,13 @@ struct RustyTransparentStruct {
# fn main() -> PyResult<()> { # fn main() -> PyResult<()> {
# Python::with_gil(|py| -> PyResult<()> { # Python::with_gil(|py| -> PyResult<()> {
# let s = PyString::new(py, "test"); # let s = PyString::new(py, "test");
# #
# let tup: RustyTransparentTupleStruct = s.extract()?; # let tup: RustyTransparentTupleStruct = s.extract()?;
# assert_eq!(tup.0, "test"); # assert_eq!(tup.0, "test");
# #
# let stru: RustyTransparentStruct = s.extract()?; # let stru: RustyTransparentStruct = s.extract()?;
# assert_eq!(stru.inner, "test"); # assert_eq!(stru.inner, "test");
# #
# Ok(()) # Ok(())
# }) # })
# } # }
@ -256,14 +255,14 @@ enum RustyEnum<'a> {
#[pyo3(transparent)] #[pyo3(transparent)]
CatchAll(&'a PyAny), // This extraction never fails CatchAll(&'a PyAny), // This extraction never fails
} }
# #
# use pyo3::types::{PyBytes, PyString}; # use pyo3::types::{PyBytes, PyString};
# fn main() -> PyResult<()> { # fn main() -> PyResult<()> {
# Python::with_gil(|py| -> PyResult<()> { # Python::with_gil(|py| -> PyResult<()> {
# { # {
# let thing = 42_u8.to_object(py); # let thing = 42_u8.to_object(py);
# let rust_thing: RustyEnum<'_> = thing.extract(py)?; # let rust_thing: RustyEnum<'_> = thing.extract(py)?;
# #
# assert_eq!( # assert_eq!(
# 42, # 42,
# match rust_thing { # match rust_thing {
@ -275,7 +274,7 @@ enum RustyEnum<'a> {
# { # {
# let thing = PyString::new(py, "text"); # let thing = PyString::new(py, "text");
# let rust_thing: RustyEnum<'_> = thing.extract()?; # let rust_thing: RustyEnum<'_> = thing.extract()?;
# #
# assert_eq!( # assert_eq!(
# "text", # "text",
# match rust_thing { # match rust_thing {
@ -287,7 +286,7 @@ enum RustyEnum<'a> {
# { # {
# let thing = (32_u8, 73_u8).to_object(py); # let thing = (32_u8, 73_u8).to_object(py);
# let rust_thing: RustyEnum<'_> = thing.extract(py)?; # let rust_thing: RustyEnum<'_> = thing.extract(py)?;
# #
# assert_eq!( # assert_eq!(
# (32, 73), # (32, 73),
# match rust_thing { # match rust_thing {
@ -299,7 +298,7 @@ enum RustyEnum<'a> {
# { # {
# let thing = ("foo", 73_u8).to_object(py); # let thing = ("foo", 73_u8).to_object(py);
# let rust_thing: RustyEnum<'_> = thing.extract(py)?; # let rust_thing: RustyEnum<'_> = thing.extract(py)?;
# #
# assert_eq!( # assert_eq!(
# (String::from("foo"), 73), # (String::from("foo"), 73),
# match rust_thing { # match rust_thing {
@ -319,11 +318,11 @@ enum RustyEnum<'a> {
# "", # "",
# "", # "",
# )?; # )?;
# #
# let class = module.getattr("Foo")?; # let class = module.getattr("Foo")?;
# let instance = class.call0()?; # let instance = class.call0()?;
# let rust_thing: RustyEnum<'_> = instance.extract()?; # let rust_thing: RustyEnum<'_> = instance.extract()?;
# #
# assert_eq!( # assert_eq!(
# (0, 1, 2), # (0, 1, 2),
# match rust_thing { # match rust_thing {
@ -332,7 +331,7 @@ enum RustyEnum<'a> {
# } # }
# ); # );
# } # }
# #
# { # {
# let module = PyModule::from_code( # let module = PyModule::from_code(
# py, # py,
@ -343,11 +342,11 @@ enum RustyEnum<'a> {
# "", # "",
# "", # "",
# )?; # )?;
# #
# let class = module.getattr("Foo")?; # let class = module.getattr("Foo")?;
# let instance = class.call0()?; # let instance = class.call0()?;
# let rust_thing: RustyEnum<'_> = instance.extract()?; # let rust_thing: RustyEnum<'_> = instance.extract()?;
# #
# assert_eq!( # assert_eq!(
# (3, 4), # (3, 4),
# match rust_thing { # match rust_thing {
@ -356,11 +355,11 @@ enum RustyEnum<'a> {
# } # }
# ); # );
# } # }
# #
# { # {
# let thing = PyBytes::new(py, b"text"); # let thing = PyBytes::new(py, b"text");
# let rust_thing: RustyEnum<'_> = thing.extract()?; # let rust_thing: RustyEnum<'_> = thing.extract()?;
# #
# assert_eq!( # assert_eq!(
# b"text", # b"text",
# match rust_thing { # match rust_thing {
@ -396,7 +395,7 @@ enum RustyEnum {
# { # {
# let thing = 42_u8.to_object(py); # let thing = 42_u8.to_object(py);
# let rust_thing: RustyEnum = thing.extract(py)?; # let rust_thing: RustyEnum = thing.extract(py)?;
# #
# assert_eq!( # assert_eq!(
# 42, # 42,
# match rust_thing { # match rust_thing {
@ -405,11 +404,11 @@ enum RustyEnum {
# } # }
# ); # );
# } # }
# #
# { # {
# let thing = "foo".to_object(py); # let thing = "foo".to_object(py);
# let rust_thing: RustyEnum = thing.extract(py)?; # let rust_thing: RustyEnum = thing.extract(py)?;
# #
# assert_eq!( # assert_eq!(
# "foo", # "foo",
# match rust_thing { # match rust_thing {
@ -418,13 +417,13 @@ enum RustyEnum {
# } # }
# ); # );
# } # }
# #
# { # {
# let thing = b"foo".to_object(py); # let thing = b"foo".to_object(py);
# let error = thing.extract::<RustyEnum>(py).unwrap_err(); # let error = thing.extract::<RustyEnum>(py).unwrap_err();
# assert!(error.is_instance_of::<pyo3::exceptions::PyTypeError>(py)); # assert!(error.is_instance_of::<pyo3::exceptions::PyTypeError>(py));
# } # }
# #
# Ok(()) # Ok(())
# }) # })
# } # }

View File

@ -131,7 +131,6 @@ fn my_async_module(py: Python<'_>, m: &PyModule) -> PyResult<()> {
Ok(()) Ok(())
} }
``` ```
If you want to use `tokio` instead, here's what your module should look like: If you want to use `tokio` instead, here's what your module should look like:

View File

@ -25,7 +25,11 @@ create_exception!(mymodule, CustomError, PyException);
Python::with_gil(|py| { Python::with_gil(|py| {
let ctx = [("CustomError", py.get_type::<CustomError>())].into_py_dict(py); let ctx = [("CustomError", py.get_type::<CustomError>())].into_py_dict(py);
pyo3::py_run!(py, *ctx, "assert str(CustomError) == \"<class 'mymodule.CustomError'>\""); pyo3::py_run!(
py,
*ctx,
"assert str(CustomError) == \"<class 'mymodule.CustomError'>\""
);
pyo3::py_run!(py, *ctx, "assert CustomError('oops').args == ('oops',)"); pyo3::py_run!(py, *ctx, "assert CustomError('oops').args == ('oops',)");
}); });
``` ```
@ -47,7 +51,6 @@ fn mymodule(py: Python<'_>, m: &PyModule) -> PyResult<()> {
Ok(()) Ok(())
} }
``` ```
## Raising an exception ## Raising an exception
@ -115,7 +118,6 @@ fn tell(file: &PyAny) -> PyResult<u64> {
Ok(x) => x.extract::<u64>(), Ok(x) => x.extract::<u64>(),
} }
} }
``` ```
[`pyo3::exceptions`]({{#PYO3_DOCS_URL}}/pyo3/exceptions/index.html) [`pyo3::exceptions`]({{#PYO3_DOCS_URL}}/pyo3/exceptions/index.html)

View File

@ -86,7 +86,7 @@ You may have a nested struct similar to this:
# use pyo3::prelude::*; # use pyo3::prelude::*;
#[pyclass] #[pyclass]
#[derive(Clone)] #[derive(Clone)]
struct Inner { /* fields omitted */ } struct Inner {/* fields omitted */}
#[pyclass] #[pyclass]
struct Outer { struct Outer {
@ -126,7 +126,7 @@ If you don't want that cloning to happen, a workaround is to allocate the field
# use pyo3::prelude::*; # use pyo3::prelude::*;
#[pyclass] #[pyclass]
#[derive(Clone)] #[derive(Clone)]
struct Inner { /* fields omitted */ } struct Inner {/* fields omitted */}
#[pyclass] #[pyclass]
struct Outer { struct Outer {

View File

@ -138,14 +138,14 @@ This allows to use [`#[derive(Serialize, Deserialize)`](https://serde.rs/derive.
#[pyclass] #[pyclass]
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
struct Permission { struct Permission {
name: String name: String,
} }
#[pyclass] #[pyclass]
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
struct User { struct User {
username: String, username: String,
permissions: Vec<Py<Permission>> permissions: Vec<Py<Permission>>,
} }
# } # }
``` ```

View File

@ -50,7 +50,9 @@ The `#[pyo3]` attribute can be used to modify properties of the generated Python
#[pyfunction] #[pyfunction]
#[pyo3(name = "no_args")] #[pyo3(name = "no_args")]
fn no_args_py() -> usize { 42 } fn no_args_py() -> usize {
42
}
#[pymodule] #[pymodule]
fn module_with_functions(py: Python<'_>, m: &PyModule) -> PyResult<()> { fn module_with_functions(py: Python<'_>, m: &PyModule) -> PyResult<()> {
@ -185,8 +187,6 @@ fn add(a: u64, b: u64) -> u64 {
/// sub(a, b, /) /// sub(a, b, /)
/// -- /// --
///
///
#[pyfunction] #[pyfunction]
fn sub(a: u64, b: u64) -> u64 { fn sub(a: u64, b: u64) -> u64 {
a - b a - b
@ -255,7 +255,6 @@ use pyo3::prelude::*;
#[pymodule] #[pymodule]
fn my_extension(py: Python<'_>, m: &PyModule) -> PyResult<()> { fn my_extension(py: Python<'_>, m: &PyModule) -> PyResult<()> {
#[pyfn(m)] #[pyfn(m)]
fn double(x: usize) -> usize { fn double(x: usize) -> usize {
x * 2 x * 2
@ -273,7 +272,6 @@ use pyo3::prelude::*;
#[pymodule] #[pymodule]
fn my_extension(py: Python<'_>, m: &PyModule) -> PyResult<()> { fn my_extension(py: Python<'_>, m: &PyModule) -> PyResult<()> {
#[pyfunction] #[pyfunction]
fn double(x: usize) -> usize { fn double(x: usize) -> usize {
x * 2 x * 2

View File

@ -112,10 +112,10 @@ impl std::convert::From<CustomIOError> for PyErr {
} }
} }
pub struct Connection { /* ... */} pub struct Connection {/* ... */}
fn bind(addr: String) -> Result<Connection, CustomIOError> { fn bind(addr: String) -> Result<Connection, CustomIOError> {
if &addr == "0.0.0.0"{ if &addr == "0.0.0.0" {
Err(CustomIOError) Err(CustomIOError)
} else { } else {
Ok(Connection{ /* ... */}) Ok(Connection{ /* ... */})

View File

@ -120,7 +120,7 @@ Below are the same examples as above which using the deprecated syntax:
use pyo3::prelude::*; use pyo3::prelude::*;
use pyo3::types::PyDict; use pyo3::types::PyDict;
#[pyfunction(kwds="**")] #[pyfunction(kwds = "**")]
fn num_kwds(kwds: Option<&PyDict>) -> usize { fn num_kwds(kwds: Option<&PyDict>) -> usize {
kwds.map_or(0, |dict| dict.len()) kwds.map_or(0, |dict| dict.len())
} }
@ -166,12 +166,7 @@ impl MyClass {
MyClass { num } MyClass { num }
} }
#[args( #[args(num = "10", py_args = "*", name = "\"Hello\"", py_kwargs = "**")]
num = "10",
py_args = "*",
name = "\"Hello\"",
py_kwargs = "**"
)]
fn method( fn method(
&mut self, &mut self,
num: i32, num: i32,

View File

@ -44,7 +44,6 @@ impl Mapping {
// ... // ...
// truncated implementation of this mapping pyclass - basically a wrapper around a HashMap // truncated implementation of this mapping pyclass - basically a wrapper around a HashMap
} }
``` ```
You must register the class with `collections.abc.Mapping` before the downcast will work: You must register the class with `collections.abc.Mapping` before the downcast will work:
@ -155,7 +154,7 @@ use pyo3::class::{PyBasicProtocol, PyIterProtocol};
use pyo3::types::PyString; use pyo3::types::PyString;
#[pyclass] #[pyclass]
struct MyClass { } struct MyClass {}
#[pyproto] #[pyproto]
impl PyBasicProtocol for MyClass { impl PyBasicProtocol for MyClass {
@ -179,7 +178,7 @@ use pyo3::prelude::*;
use pyo3::types::PyString; use pyo3::types::PyString;
#[pyclass] #[pyclass]
struct MyClass { } struct MyClass {}
#[pymethods] #[pymethods]
impl MyClass { impl MyClass {
@ -343,7 +342,7 @@ use pyo3::prelude::*;
use pyo3::class::basic::PyBasicProtocol; use pyo3::class::basic::PyBasicProtocol;
#[pyclass] #[pyclass]
struct MyClass { } struct MyClass {}
#[pyproto] #[pyproto]
impl PyBasicProtocol for MyClass { impl PyBasicProtocol for MyClass {
@ -359,7 +358,7 @@ After:
use pyo3::prelude::*; use pyo3::prelude::*;
#[pyclass] #[pyclass]
struct MyClass { } struct MyClass {}
#[pymethods] #[pymethods]
impl MyClass { impl MyClass {
@ -457,7 +456,10 @@ assert_eq!(err.to_string(), "TypeError: error message");
// Now possible to interact with exception instances, new for PyO3 0.12 // Now possible to interact with exception instances, new for PyO3 0.12
let instance: &PyBaseException = err.instance(py); let instance: &PyBaseException = err.instance(py);
assert_eq!(instance.getattr("__class__")?, PyTypeError::type_object(py).as_ref()); assert_eq!(
instance.getattr("__class__")?,
PyTypeError::type_object(py).as_ref()
);
# Ok(()) # Ok(())
# }).unwrap(); # }).unwrap();
``` ```
@ -568,7 +570,7 @@ There can be two fixes:
#[pyclass] #[pyclass]
struct NotThreadSafe { struct NotThreadSafe {
shared_bools: Rc<RefCell<Vec<bool>>>, shared_bools: Rc<RefCell<Vec<bool>>>,
closure: Box<dyn Fn()> closure: Box<dyn Fn()>,
} }
``` ```
@ -581,7 +583,7 @@ There can be two fixes:
#[pyclass] #[pyclass]
struct ThreadSafe { struct ThreadSafe {
shared_bools: Arc<Mutex<Vec<bool>>>, shared_bools: Arc<Mutex<Vec<bool>>>,
closure: Box<dyn Fn() + Send> closure: Box<dyn Fn() + Send>,
} }
``` ```
@ -679,10 +681,10 @@ struct MyClass {}
#[pymethods] #[pymethods]
impl MyClass { impl MyClass {
#[new] #[new]
fn new(obj: &PyRawObject) { fn new(obj: &PyRawObject) {
obj.init(MyClass { }) obj.init(MyClass {})
} }
} }
``` ```
@ -694,10 +696,10 @@ struct MyClass {}
#[pymethods] #[pymethods]
impl MyClass { impl MyClass {
#[new] #[new]
fn new() -> Self { fn new() -> Self {
MyClass {} MyClass {}
} }
} }
``` ```
@ -720,7 +722,7 @@ Here is an example.
#[pyclass] #[pyclass]
struct Names { struct Names {
names: Vec<String> names: Vec<String>,
} }
#[pymethods] #[pymethods]

View File

@ -43,7 +43,7 @@ But let's assume you have a long running Rust function which you would like to e
# } # }
# total # total
# } # }
# #
fn search_sequential(contents: &str, needle: &str) -> usize { fn search_sequential(contents: &str, needle: &str) -> usize {
contents.lines().map(|line| count_line(line, needle)).sum() contents.lines().map(|line| count_line(line, needle)).sum()
} }
@ -63,7 +63,7 @@ To enable parallel execution of this function, the [`Python::allow_threads`] met
# } # }
# total # total
# } # }
# #
# fn search_sequential(contents: &str, needle: &str) -> usize { # fn search_sequential(contents: &str, needle: &str) -> usize {
# contents.lines().map(|line| count_line(line, needle)).sum() # contents.lines().map(|line| count_line(line, needle)).sum()
# } # }

View File

@ -43,7 +43,9 @@ fn main() -> PyResult<()> {
print('called with no arguments')", print('called with no arguments')",
"", "",
"", "",
)?.getattr("example")?.into(); )?
.getattr("example")?
.into();
// call object without any arguments // call object without any arguments
fun.call0(py)?; fun.call0(py)?;
@ -87,8 +89,9 @@ fn main() -> PyResult<()> {
print('called with no arguments')", print('called with no arguments')",
"", "",
"", "",
)?.getattr("example")?.into(); )?
.getattr("example")?
.into();
// call object with PyDict // call object with PyDict
let kwargs = [(key1, val1)].into_py_dict(py); let kwargs = [(key1, val1)].into_py_dict(py);
@ -104,7 +107,7 @@ fn main() -> PyResult<()> {
fun.call(py, (), Some(kwargs.into_py_dict(py)))?; fun.call(py, (), Some(kwargs.into_py_dict(py)))?;
Ok(()) Ok(())
}) })
} }
``` ```
@ -124,7 +127,10 @@ use pyo3::prelude::*;
fn main() -> PyResult<()> { fn main() -> PyResult<()> {
Python::with_gil(|py| { Python::with_gil(|py| {
let builtins = PyModule::import(py, "builtins")?; let builtins = PyModule::import(py, "builtins")?;
let total: i32 = builtins.getattr("sum")?.call1((vec![1, 2, 3],))?.extract()?; let total: i32 = builtins
.getattr("sum")?
.call1((vec![1, 2, 3],))?
.extract()?;
assert_eq!(total, 6); assert_eq!(total, 6);
Ok(()) Ok(())
}) })
@ -142,9 +148,11 @@ use pyo3::prelude::*;
# fn main() -> Result<(), ()> { # fn main() -> Result<(), ()> {
Python::with_gil(|py| { Python::with_gil(|py| {
let result = py.eval("[i * 10 for i in range(5)]", None, None).map_err(|e| { let result = py
e.print_and_set_sys_last_vars(py); .eval("[i * 10 for i in range(5)]", None, None)
})?; .map_err(|e| {
e.print_and_set_sys_last_vars(py);
})?;
let res: Vec<i64> = result.extract().unwrap(); let res: Vec<i64> = result.extract().unwrap();
assert_eq!(res, vec![0, 10, 20, 30, 40]); assert_eq!(res, vec![0, 10, 20, 30, 40]);
Ok(()) Ok(())
@ -228,7 +236,8 @@ def leaky_relu(x, slope=0.01):
let kwargs = [("slope", 0.2)].into_py_dict(py); let kwargs = [("slope", 0.2)].into_py_dict(py);
let lrelu_result: f64 = activators let lrelu_result: f64 = activators
.getattr("leaky_relu")?.call((-1.0,), Some(kwargs))? .getattr("leaky_relu")?
.call((-1.0,), Some(kwargs))?
.extract()?; .extract()?;
assert_eq!(lrelu_result, -0.2); assert_eq!(lrelu_result, -0.2);
# Ok(()) # Ok(())

View File

@ -27,7 +27,7 @@ use pyo3::prelude::*;
#[pyclass] #[pyclass]
struct MyClass { struct MyClass {
num: u32, num: u32,
} }
#[pymethods] #[pymethods]

View File

@ -25,13 +25,13 @@ The argument of the function can be any model that implements the `Model` trait
```rust ```rust
# #![allow(dead_code)] # #![allow(dead_code)]
pub trait Model { pub trait Model {
fn set_variables(&mut self, inputs: &Vec<f64>); fn set_variables(&mut self, inputs: &Vec<f64>);
fn compute(&mut self); fn compute(&mut self);
fn get_results(&self) -> Vec<f64>; fn get_results(&self) -> Vec<f64>;
} }
pub fn solve<T: Model>(model: &mut T) { pub fn solve<T: Model>(model: &mut T) {
println!("Magic solver that mutates the model into a resolved state"); println!("Magic solver that mutates the model into a resolved state");
} }
``` ```
Let's assume we have the following constraints: Let's assume we have the following constraints:
@ -152,7 +152,7 @@ Now we add the PyO3 annotations to the trait implementation:
```rust,ignore ```rust,ignore
#[pymethods] #[pymethods]
impl Model for UserModel { impl Model for UserModel {
// the previous trait implementation // the previous trait implementation
} }
``` ```
@ -411,7 +411,10 @@ impl Model for UserModel {
.unwrap(); .unwrap();
if py_result.get_type().name().unwrap() != "list" { if py_result.get_type().name().unwrap() != "list" {
panic!("Expected a list for the get_results() method signature, got {}", py_result.get_type().name().unwrap()); panic!(
"Expected a list for the get_results() method signature, got {}",
py_result.get_type().name().unwrap()
);
} }
py_result.extract() py_result.extract()
}) })
@ -472,7 +475,7 @@ pub trait Model {
} }
pub fn solve<T: Model>(model: &mut T) { pub fn solve<T: Model>(model: &mut T) {
println!("Magic solver that mutates the model into a resolved state"); println!("Magic solver that mutates the model into a resolved state");
} }
#[pyfunction] #[pyfunction]
@ -538,7 +541,10 @@ impl Model for UserModel {
.unwrap(); .unwrap();
if py_result.get_type().name().unwrap() != "list" { if py_result.get_type().name().unwrap() != "list" {
panic!("Expected a list for the get_results() method signature, got {}", py_result.get_type().name().unwrap()); panic!(
"Expected a list for the get_results() method signature, got {}",
py_result.get_type().name().unwrap()
);
} }
py_result.extract() py_result.extract()
}) })

View File

@ -89,7 +89,7 @@ For a `&PyAny` object reference `any` where the underlying object is a `#[pyclas
# use pyo3::{Py, Python, PyAny, PyResult}; # use pyo3::{Py, Python, PyAny, PyResult};
# #[pyclass] #[derive(Clone)] struct MyClass { } # #[pyclass] #[derive(Clone)] struct MyClass { }
# Python::with_gil(|py| -> PyResult<()> { # Python::with_gil(|py| -> PyResult<()> {
let obj: &PyAny = Py::new(py, MyClass { })?.into_ref(py); let obj: &PyAny = Py::new(py, MyClass {})?.into_ref(py);
// To &PyCell<MyClass> with PyAny::downcast // To &PyCell<MyClass> with PyAny::downcast
let _: &PyCell<MyClass> = obj.downcast()?; let _: &PyCell<MyClass> = obj.downcast()?;
@ -238,7 +238,7 @@ so it also exposes all of the methods on `PyAny`.
# use pyo3::prelude::*; # use pyo3::prelude::*;
# #[pyclass] struct MyClass { } # #[pyclass] struct MyClass { }
# Python::with_gil(|py| -> PyResult<()> { # Python::with_gil(|py| -> PyResult<()> {
let cell: &PyCell<MyClass> = PyCell::new(py, MyClass { })?; let cell: &PyCell<MyClass> = PyCell::new(py, MyClass {})?;
// To PyRef<T> with .borrow() or .try_borrow() // To PyRef<T> with .borrow() or .try_borrow()
let py_ref: PyRef<'_, MyClass> = cell.try_borrow()?; let py_ref: PyRef<'_, MyClass> = cell.try_borrow()?;
@ -258,7 +258,7 @@ let _: &mut MyClass = &mut *py_ref_mut;
# use pyo3::prelude::*; # use pyo3::prelude::*;
# #[pyclass] struct MyClass { } # #[pyclass] struct MyClass { }
# Python::with_gil(|py| -> PyResult<()> { # Python::with_gil(|py| -> PyResult<()> {
let cell: &PyCell<MyClass> = PyCell::new(py, MyClass { })?; let cell: &PyCell<MyClass> = PyCell::new(py, MyClass {})?;
// Use methods from PyAny on PyCell<T> with Deref implementation // Use methods from PyAny on PyCell<T> with Deref implementation
let _ = cell.repr()?; let _ = cell.repr()?;

View File

@ -2,6 +2,7 @@ import os
import re import re
import subprocess import subprocess
import sys import sys
import tempfile
import time import time
from glob import glob from glob import glob
from pathlib import Path from pathlib import Path
@ -231,6 +232,58 @@ def build_guide(session: nox.Session):
_run(session, "mdbook", "build", "-d", "../target/guide", "guide", *session.posargs) _run(session, "mdbook", "build", "-d", "../target/guide", "guide", *session.posargs)
@nox.session(name="format-guide", venv_backend="none")
def format_guide(session: nox.Session):
fence_line = "//! ```\n"
for path in Path("guide").glob("**/*.md"):
session.log("Working on %s", path)
content = path.read_text()
lines = iter(path.read_text().splitlines(True))
new_lines = []
for line in lines:
new_lines.append(line)
if not re.search("```rust(,.*)?$", line):
continue
# Found a code block fence, gobble up its lines and write to temp. file
prefix = line[: line.index("```")]
with tempfile.NamedTemporaryFile("w", delete=False) as file:
tempname = file.name
file.write(fence_line)
for line in lines:
if line == prefix + "```\n":
break
file.write(("//! " + line[len(prefix) :]).rstrip() + "\n")
file.write(fence_line)
# Format it (needs nightly rustfmt for `format_code_in_doc_comments`)
_run(
session,
"rustfmt",
"+nightly",
"--config",
"format_code_in_doc_comments=true",
"--config",
"reorder_imports=false",
tempname,
)
# Re-read the formatted file, add its lines, and delete it
with open(tempname, "r") as file:
for line in file:
if line == fence_line:
continue
new_lines.append((prefix + line[4:]).rstrip() + "\n")
os.unlink(tempname)
new_lines.append(prefix + "```\n")
path.write_text("".join(new_lines))
@nox.session(name="address-sanitizer", venv_backend="none") @nox.session(name="address-sanitizer", venv_backend="none")
def address_sanitizer(session: nox.Session): def address_sanitizer(session: nox.Session):
_run( _run(