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
// Argument supplied directly to the `#[pyclass]` annotation.
#[pyclass(name = "SomeName", subclass)]
struct MyClass { }
struct MyClass {}
// Argument supplied as a separate annotation.
#[pyclass]
#[pyo3(name = "SomeName", subclass)]
struct MyClass { }
struct MyClass {}
```
[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
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
#[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))]
fn function_only_supported_before_python_3_8() { }
fn function_only_supported_before_python_3_8() {}
#[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.
@ -98,11 +98,10 @@ PyO3 provides the APIs [`Python::version()`] and [`Python::version_info()`] to q
use pyo3::Python;
Python::with_gil(|py| {
// PyO3 supports Python 3.7 and up.
assert!(py.version_info() >= (3, 7));
assert!(py.version_info() >= (3, 7, 0));
// PyO3 supports Python 3.7 and up.
assert!(py.version_info() >= (3, 7));
assert!(py.version_info() >= (3, 7, 0));
});
```
[`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::*;
#[pyclass]
struct Integer{
inner: i32
struct Integer {
inner: i32,
}
// A "tuple" struct
@ -166,7 +166,7 @@ struct MyClass {
num: i32,
}
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
assert_eq!(obj_ref.num, 3);
@ -204,7 +204,7 @@ fn return_myclass() -> Py<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 obj_ref = cell.borrow(); // Get PyRef<T>
assert_eq!(obj_ref.num, 1);
@ -279,7 +279,7 @@ impl SubClass {
}
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)
}
}
@ -293,13 +293,12 @@ struct SubSubClass {
impl SubSubClass {
#[new]
fn new() -> PyClassInitializer<Self> {
PyClassInitializer::from(SubClass::new())
.add_subclass(SubSubClass{val3: 20})
PyClassInitializer::from(SubClass::new()).add_subclass(SubSubClass { val3: 20 })
}
fn method3(self_: PyRef<'_, Self>) -> PyResult<usize> {
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)
}
}
@ -426,7 +425,7 @@ For simple cases where a member variable is just read and written with no side e
#[pyclass]
struct MyClass {
#[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
# use pyo3::prelude::*;
#[pyclass(subclass)]
enum BadBase{
enum BadBase {
Var1,
}
```
@ -949,7 +948,7 @@ enum BadBase{
struct Base;
#[pyclass(extends=Base)]
enum BadSubclass{
enum BadSubclass {
Var1,
}
```

View File

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

View File

@ -40,10 +40,10 @@ print(n)
### String representations
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
contained inside `Number`.
`__repr__` and `__str__` methods inside a `#[pymethods]` block. We do this by accessing the value
contained inside `Number`.
```rust
```rust
# use pyo3::prelude::*;
#
# #[pyclass]
@ -114,13 +114,13 @@ impl Number {
> ```rust
> # use pyo3::prelude::*;
> #[pyclass]
> struct NotHashable { }
> struct NotHashable {}
>
> #[pymethods]
> impl NotHashable {
> #[classattr]
> const __hash__: Option<Py<PyAny>> = None;
>}
> #[classattr]
> const __hash__: Option<Py<PyAny>> = None;
> }
> ```
### Comparisons

View File

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

View File

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

View File

@ -131,7 +131,6 @@ fn my_async_module(py: Python<'_>, m: &PyModule) -> PyResult<()> {
Ok(())
}
```
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| {
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',)");
});
```
@ -47,7 +51,6 @@ fn mymodule(py: Python<'_>, m: &PyModule) -> PyResult<()> {
Ok(())
}
```
## Raising an exception
@ -115,7 +118,6 @@ fn tell(file: &PyAny) -> PyResult<u64> {
Ok(x) => x.extract::<u64>(),
}
}
```
[`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::*;
#[pyclass]
#[derive(Clone)]
struct Inner { /* fields omitted */ }
struct Inner {/* fields omitted */}
#[pyclass]
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::*;
#[pyclass]
#[derive(Clone)]
struct Inner { /* fields omitted */ }
struct Inner {/* fields omitted */}
#[pyclass]
struct Outer {

View File

@ -138,14 +138,14 @@ This allows to use [`#[derive(Serialize, Deserialize)`](https://serde.rs/derive.
#[pyclass]
#[derive(Serialize, Deserialize)]
struct Permission {
name: String
name: String,
}
#[pyclass]
#[derive(Serialize, Deserialize)]
struct User {
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]
#[pyo3(name = "no_args")]
fn no_args_py() -> usize { 42 }
fn no_args_py() -> usize {
42
}
#[pymodule]
fn module_with_functions(py: Python<'_>, m: &PyModule) -> PyResult<()> {
@ -185,8 +187,6 @@ fn add(a: u64, b: u64) -> u64 {
/// sub(a, b, /)
/// --
///
///
#[pyfunction]
fn sub(a: u64, b: u64) -> u64 {
a - b
@ -255,7 +255,6 @@ use pyo3::prelude::*;
#[pymodule]
fn my_extension(py: Python<'_>, m: &PyModule) -> PyResult<()> {
#[pyfn(m)]
fn double(x: usize) -> usize {
x * 2
@ -273,7 +272,6 @@ use pyo3::prelude::*;
#[pymodule]
fn my_extension(py: Python<'_>, m: &PyModule) -> PyResult<()> {
#[pyfunction]
fn double(x: usize) -> usize {
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> {
if &addr == "0.0.0.0"{
if &addr == "0.0.0.0" {
Err(CustomIOError)
} else {
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::types::PyDict;
#[pyfunction(kwds="**")]
#[pyfunction(kwds = "**")]
fn num_kwds(kwds: Option<&PyDict>) -> usize {
kwds.map_or(0, |dict| dict.len())
}
@ -166,12 +166,7 @@ impl MyClass {
MyClass { num }
}
#[args(
num = "10",
py_args = "*",
name = "\"Hello\"",
py_kwargs = "**"
)]
#[args(num = "10", py_args = "*", name = "\"Hello\"", py_kwargs = "**")]
fn method(
&mut self,
num: i32,

View File

@ -44,7 +44,6 @@ impl Mapping {
// ...
// 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:
@ -155,7 +154,7 @@ use pyo3::class::{PyBasicProtocol, PyIterProtocol};
use pyo3::types::PyString;
#[pyclass]
struct MyClass { }
struct MyClass {}
#[pyproto]
impl PyBasicProtocol for MyClass {
@ -179,7 +178,7 @@ use pyo3::prelude::*;
use pyo3::types::PyString;
#[pyclass]
struct MyClass { }
struct MyClass {}
#[pymethods]
impl MyClass {
@ -343,7 +342,7 @@ use pyo3::prelude::*;
use pyo3::class::basic::PyBasicProtocol;
#[pyclass]
struct MyClass { }
struct MyClass {}
#[pyproto]
impl PyBasicProtocol for MyClass {
@ -359,7 +358,7 @@ After:
use pyo3::prelude::*;
#[pyclass]
struct MyClass { }
struct MyClass {}
#[pymethods]
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
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(())
# }).unwrap();
```
@ -568,7 +570,7 @@ There can be two fixes:
#[pyclass]
struct NotThreadSafe {
shared_bools: Rc<RefCell<Vec<bool>>>,
closure: Box<dyn Fn()>
closure: Box<dyn Fn()>,
}
```
@ -581,7 +583,7 @@ There can be two fixes:
#[pyclass]
struct ThreadSafe {
shared_bools: Arc<Mutex<Vec<bool>>>,
closure: Box<dyn Fn() + Send>
closure: Box<dyn Fn() + Send>,
}
```
@ -679,10 +681,10 @@ struct MyClass {}
#[pymethods]
impl MyClass {
#[new]
fn new(obj: &PyRawObject) {
obj.init(MyClass { })
}
#[new]
fn new(obj: &PyRawObject) {
obj.init(MyClass {})
}
}
```
@ -694,10 +696,10 @@ struct MyClass {}
#[pymethods]
impl MyClass {
#[new]
fn new() -> Self {
MyClass {}
}
#[new]
fn new() -> Self {
MyClass {}
}
}
```
@ -720,7 +722,7 @@ Here is an example.
#[pyclass]
struct Names {
names: Vec<String>
names: Vec<String>,
}
#[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
# }
#
#
fn search_sequential(contents: &str, needle: &str) -> usize {
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
# }
#
#
# fn search_sequential(contents: &str, needle: &str) -> usize {
# contents.lines().map(|line| count_line(line, needle)).sum()
# }

View File

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

View File

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

View File

@ -25,13 +25,13 @@ The argument of the function can be any model that implements the `Model` trait
```rust
# #![allow(dead_code)]
pub trait Model {
fn set_variables(&mut self, inputs: &Vec<f64>);
fn compute(&mut self);
fn get_results(&self) -> Vec<f64>;
fn set_variables(&mut self, inputs: &Vec<f64>);
fn compute(&mut self);
fn get_results(&self) -> Vec<f64>;
}
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:
@ -152,7 +152,7 @@ Now we add the PyO3 annotations to the trait implementation:
```rust,ignore
#[pymethods]
impl Model for UserModel {
// the previous trait implementation
// the previous trait implementation
}
```
@ -411,7 +411,10 @@ impl Model for UserModel {
.unwrap();
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()
})
@ -472,7 +475,7 @@ pub trait Model {
}
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]
@ -538,7 +541,10 @@ impl Model for UserModel {
.unwrap();
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()
})

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};
# #[pyclass] #[derive(Clone)] struct MyClass { }
# 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
let _: &PyCell<MyClass> = obj.downcast()?;
@ -238,7 +238,7 @@ so it also exposes all of the methods on `PyAny`.
# use pyo3::prelude::*;
# #[pyclass] struct MyClass { }
# 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()
let py_ref: PyRef<'_, MyClass> = cell.try_borrow()?;
@ -258,7 +258,7 @@ let _: &mut MyClass = &mut *py_ref_mut;
# use pyo3::prelude::*;
# #[pyclass] struct MyClass { }
# 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
let _ = cell.repr()?;

View File

@ -2,6 +2,7 @@ import os
import re
import subprocess
import sys
import tempfile
import time
from glob import glob
from pathlib import Path
@ -231,6 +232,58 @@ def build_guide(session: nox.Session):
_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")
def address_sanitizer(session: nox.Session):
_run(