guide: tidy up doctests
This commit is contained in:
parent
576818dc0c
commit
2bd64c4962
|
@ -47,6 +47,7 @@ rustversion = "1.0"
|
||||||
# 1.0.0 requires Rust 1.50
|
# 1.0.0 requires Rust 1.50
|
||||||
proptest = { version = "0.10.1", default-features = false, features = ["std"] }
|
proptest = { version = "0.10.1", default-features = false, features = ["std"] }
|
||||||
send_wrapper = "0.5"
|
send_wrapper = "0.5"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0.61"
|
serde_json = "1.0.61"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
|
|
@ -14,7 +14,7 @@ is linked at the end.
|
||||||
|
|
||||||
An example crate containing this pyclass can be found [here](https://github.com/PyO3/pyo3/tree/main/examples/decorator)
|
An example crate containing this pyclass can be found [here](https://github.com/PyO3/pyo3/tree/main/examples/decorator)
|
||||||
|
|
||||||
```rust
|
```rust,ignore
|
||||||
{{#include ../../../examples/decorator/src/lib.rs}}
|
{{#include ../../../examples/decorator/src/lib.rs}}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -115,6 +115,11 @@ Enables (de)serialization of Py<T> objects via [serde](https://serde.rs/).
|
||||||
This allows to use [`#[derive(Serialize, Deserialize)`](https://serde.rs/derive.html) on structs that hold references to `#[pyclass]` instances
|
This allows to use [`#[derive(Serialize, Deserialize)`](https://serde.rs/derive.html) on structs that hold references to `#[pyclass]` instances
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
# #[cfg(feature = "serde")]
|
||||||
|
# #[allow(dead_code)]
|
||||||
|
# mod serde_only {
|
||||||
|
# use pyo3::prelude::*;
|
||||||
|
# use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
|
@ -128,4 +133,5 @@ struct User {
|
||||||
username: String,
|
username: String,
|
||||||
permissions: Vec<Py<Permission>>
|
permissions: Vec<Py<Permission>>
|
||||||
}
|
}
|
||||||
|
# }
|
||||||
```
|
```
|
||||||
|
|
|
@ -23,11 +23,16 @@ held. (If PyO3 could not assume this, every PyO3 API would need to take a
|
||||||
very simple and easy-to-understand programs like this:
|
very simple and easy-to-understand programs like this:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
# use pyo3::prelude::*;
|
||||||
|
# use pyo3::types::PyString;
|
||||||
|
# fn main() -> PyResult<()> {
|
||||||
Python::with_gil(|py| -> PyResult<()> {
|
Python::with_gil(|py| -> PyResult<()> {
|
||||||
let hello: &PyString = py.eval("\"Hello World!\"", None, None)?.extract()?;
|
let hello: &PyString = py.eval("\"Hello World!\"", None, None)?.extract()?;
|
||||||
println!("Python says: {}", hello);
|
println!("Python says: {}", hello);
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
# Ok(())
|
||||||
|
# }
|
||||||
```
|
```
|
||||||
|
|
||||||
Internally, calling `Python::with_gil()` or `Python::acquire_gil()` creates a
|
Internally, calling `Python::with_gil()` or `Python::acquire_gil()` creates a
|
||||||
|
@ -39,6 +44,9 @@ it owns are decreased, releasing them to the Python garbage collector. Most
|
||||||
of the time we don't have to think about this, but consider the following:
|
of the time we don't have to think about this, but consider the following:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
# use pyo3::prelude::*;
|
||||||
|
# use pyo3::types::PyString;
|
||||||
|
# fn main() -> PyResult<()> {
|
||||||
Python::with_gil(|py| -> PyResult<()> {
|
Python::with_gil(|py| -> PyResult<()> {
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
let hello: &PyString = py.eval("\"Hello World!\"", None, None)?.extract()?;
|
let hello: &PyString = py.eval("\"Hello World!\"", None, None)?.extract()?;
|
||||||
|
@ -47,6 +55,8 @@ Python::with_gil(|py| -> PyResult<()> {
|
||||||
// There are 10 copies of `hello` on Python's heap here.
|
// There are 10 copies of `hello` on Python's heap here.
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
# Ok(())
|
||||||
|
# }
|
||||||
```
|
```
|
||||||
|
|
||||||
We might assume that the `hello` variable's memory is freed at the end of each
|
We might assume that the `hello` variable's memory is freed at the end of each
|
||||||
|
@ -62,6 +72,9 @@ In general we don't want unbounded memory growth during loops! One workaround
|
||||||
is to acquire and release the GIL with each iteration of the loop.
|
is to acquire and release the GIL with each iteration of the loop.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
# use pyo3::prelude::*;
|
||||||
|
# use pyo3::types::PyString;
|
||||||
|
# fn main() -> PyResult<()> {
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
Python::with_gil(|py| -> PyResult<()> {
|
Python::with_gil(|py| -> PyResult<()> {
|
||||||
let hello: &PyString = py.eval("\"Hello World!\"", None, None)?.extract()?;
|
let hello: &PyString = py.eval("\"Hello World!\"", None, None)?.extract()?;
|
||||||
|
@ -69,6 +82,8 @@ for _ in 0..10 {
|
||||||
Ok(())
|
Ok(())
|
||||||
})?; // only one copy of `hello` at a time
|
})?; // only one copy of `hello` at a time
|
||||||
}
|
}
|
||||||
|
# Ok(())
|
||||||
|
# }
|
||||||
```
|
```
|
||||||
|
|
||||||
It might not be practical or performant to acquire and release the GIL so many
|
It might not be practical or performant to acquire and release the GIL so many
|
||||||
|
@ -76,6 +91,9 @@ times. Another workaround is to work with the `GILPool` object directly, but
|
||||||
this is unsafe.
|
this is unsafe.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
# use pyo3::prelude::*;
|
||||||
|
# use pyo3::types::PyString;
|
||||||
|
# fn main() -> PyResult<()> {
|
||||||
Python::with_gil(|py| -> PyResult<()> {
|
Python::with_gil(|py| -> PyResult<()> {
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
let pool = unsafe { py.new_pool() };
|
let pool = unsafe { py.new_pool() };
|
||||||
|
@ -85,6 +103,8 @@ Python::with_gil(|py| -> PyResult<()> {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
# Ok(())
|
||||||
|
# }
|
||||||
```
|
```
|
||||||
|
|
||||||
The unsafe method `Python::new_pool` allows you to create a nested `GILPool`
|
The unsafe method `Python::new_pool` allows you to create a nested `GILPool`
|
||||||
|
@ -112,11 +132,16 @@ What happens to the memory when the last `Py<PyAny>` is dropped and its
|
||||||
reference count reaches zero? It depends whether or not we are holding the GIL.
|
reference count reaches zero? It depends whether or not we are holding the GIL.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
# use pyo3::prelude::*;
|
||||||
|
# use pyo3::types::PyString;
|
||||||
|
# fn main() -> PyResult<()> {
|
||||||
Python::with_gil(|py| -> PyResult<()> {
|
Python::with_gil(|py| -> PyResult<()> {
|
||||||
let hello: Py<PyString> = py.eval("\"Hello World!\"", None, None)?.extract())?;
|
let hello: Py<PyString> = py.eval("\"Hello World!\"", None, None)?.extract()?;
|
||||||
println!("Python says: {}", hello.as_ref(py));
|
println!("Python says: {}", hello.as_ref(py));
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
})?;
|
||||||
|
# Ok(())
|
||||||
|
# }
|
||||||
```
|
```
|
||||||
|
|
||||||
At the end of the `Python::with_gil()` closure `hello` is dropped, and then the
|
At the end of the `Python::with_gil()` closure `hello` is dropped, and then the
|
||||||
|
@ -129,8 +154,11 @@ This example wasn't very interesting. We could have just used a GIL-bound
|
||||||
we are *not* holding the GIL?
|
we are *not* holding the GIL?
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
# use pyo3::prelude::*;
|
||||||
|
# use pyo3::types::PyString;
|
||||||
|
# fn main() -> PyResult<()> {
|
||||||
let hello: Py<PyString> = Python::with_gil(|py| {
|
let hello: Py<PyString> = Python::with_gil(|py| {
|
||||||
py.eval("\"Hello World!\"", None, None)?.extract())
|
py.eval("\"Hello World!\"", None, None)?.extract()
|
||||||
})?;
|
})?;
|
||||||
// Do some stuff...
|
// Do some stuff...
|
||||||
// Now sometime later in the program we want to access `hello`.
|
// Now sometime later in the program we want to access `hello`.
|
||||||
|
@ -142,7 +170,10 @@ drop(hello); // Memory *not* released here.
|
||||||
// Sometime later we need the GIL again for something...
|
// Sometime later we need the GIL again for something...
|
||||||
Python::with_gil(|py|
|
Python::with_gil(|py|
|
||||||
// Memory for `hello` is released here.
|
// Memory for `hello` is released here.
|
||||||
|
# ()
|
||||||
);
|
);
|
||||||
|
# Ok(())
|
||||||
|
# }
|
||||||
```
|
```
|
||||||
|
|
||||||
When `hello` is dropped *nothing* happens to the pointed-to memory on Python's
|
When `hello` is dropped *nothing* happens to the pointed-to memory on Python's
|
||||||
|
@ -154,8 +185,11 @@ We can avoid the delay in releasing memory if we are careful to drop the
|
||||||
`Py<Any>` while the GIL is held.
|
`Py<Any>` while the GIL is held.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
# use pyo3::prelude::*;
|
||||||
|
# use pyo3::types::PyString;
|
||||||
|
# fn main() -> PyResult<()> {
|
||||||
let hello: Py<PyString> = Python::with_gil(|py| {
|
let hello: Py<PyString> = Python::with_gil(|py| {
|
||||||
py.eval("\"Hello World!\"", None, None)?.extract())
|
py.eval("\"Hello World!\"", None, None)?.extract()
|
||||||
})?;
|
})?;
|
||||||
// Do some stuff...
|
// Do some stuff...
|
||||||
// Now sometime later in the program:
|
// Now sometime later in the program:
|
||||||
|
@ -163,6 +197,8 @@ Python::with_gil(|py| {
|
||||||
println!("Python says: {}", hello.as_ref(py));
|
println!("Python says: {}", hello.as_ref(py));
|
||||||
drop(hello); // Memory released here.
|
drop(hello); // Memory released here.
|
||||||
});
|
});
|
||||||
|
# Ok(())
|
||||||
|
# }
|
||||||
```
|
```
|
||||||
|
|
||||||
We could also have used `Py::into_ref()`, which consumes `self`, instead of
|
We could also have used `Py::into_ref()`, which consumes `self`, instead of
|
||||||
|
@ -172,8 +208,11 @@ that rather than being released immediately, the memory will not be released
|
||||||
until the GIL is dropped.
|
until the GIL is dropped.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
# use pyo3::prelude::*;
|
||||||
|
# use pyo3::types::PyString;
|
||||||
|
# fn main() -> PyResult<()> {
|
||||||
let hello: Py<PyString> = Python::with_gil(|py| {
|
let hello: Py<PyString> = Python::with_gil(|py| {
|
||||||
py.eval("\"Hello World!\"", None, None)?.extract())
|
py.eval("\"Hello World!\"", None, None)?.extract()
|
||||||
})?;
|
})?;
|
||||||
// Do some stuff...
|
// Do some stuff...
|
||||||
// Now sometime later in the program:
|
// Now sometime later in the program:
|
||||||
|
@ -183,4 +222,6 @@ Python::with_gil(|py| {
|
||||||
// Do more stuff...
|
// Do more stuff...
|
||||||
// Memory released here at end of `with_gil()` closure.
|
// Memory released here at end of `with_gil()` closure.
|
||||||
});
|
});
|
||||||
|
# Ok(())
|
||||||
|
# }
|
||||||
```
|
```
|
||||||
|
|
72
src/lib.rs
72
src/lib.rs
|
@ -417,48 +417,42 @@ pub mod doc_test {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! doctest {
|
macro_rules! doctests {
|
||||||
($path:expr, $mod:ident) => {
|
($($path:expr => $mod:ident),* $(,)?) => {
|
||||||
doctest_impl!(include_str!(concat!("../", $path)), $mod);
|
$(doctest_impl!(include_str!(concat!("../", $path)), $mod);)*
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
doctest!("README.md", readme_md);
|
doctests! {
|
||||||
doctest!("guide/src/advanced.md", guide_advanced_md);
|
"README.md" => readme_md,
|
||||||
doctest!(
|
"guide/src/advanced.md" => guide_advanced_md,
|
||||||
"guide/src/building_and_distribution.md",
|
"guide/src/building_and_distribution.md" => guide_building_and_distribution_md,
|
||||||
guide_building_and_distribution_md
|
"guide/src/building_and_distribution/multiple_python_versions.md" => guide_bnd_multiple_python_versions_md,
|
||||||
);
|
"guide/src/class.md" => guide_class_md,
|
||||||
doctest!("guide/src/class.md", guide_class_md);
|
"guide/src/class/call.md" => guide_class_call,
|
||||||
doctest!("guide/src/class/protocols.md", guide_class_protocols_md);
|
"guide/src/class/object.md" => guide_class_object,
|
||||||
doctest!("guide/src/conversions.md", guide_conversions_md);
|
"guide/src/class/numeric.md" => guide_class_numeric,
|
||||||
doctest!(
|
"guide/src/class/protocols.md" => guide_class_protocols_md,
|
||||||
"guide/src/conversions/tables.md",
|
"guide/src/conversions.md" => guide_conversions_md,
|
||||||
guide_conversions_tables_md
|
"guide/src/conversions/tables.md" => guide_conversions_tables_md,
|
||||||
);
|
"guide/src/conversions/traits.md" => guide_conversions_traits_md,
|
||||||
|
"guide/src/debugging.md" => guide_debugging_md,
|
||||||
|
|
||||||
doctest!(
|
// deliberate choice not to test guide/ecosystem because those pages depend on external
|
||||||
"guide/src/conversions/traits.md",
|
// crates such as pyo3_asyncio.
|
||||||
guide_conversions_traits_md
|
|
||||||
);
|
|
||||||
doctest!("guide/src/debugging.md", guide_debugging_md);
|
|
||||||
doctest!("guide/src/exception.md", guide_exception_md);
|
|
||||||
doctest!("guide/src/function.md", guide_function_md);
|
|
||||||
doctest!("guide/src/migration.md", guide_migration_md);
|
|
||||||
doctest!("guide/src/module.md", guide_module_md);
|
|
||||||
doctest!("guide/src/parallelism.md", guide_parallelism_md);
|
|
||||||
doctest!("guide/src/python_from_rust.md", guide_python_from_rust_md);
|
|
||||||
doctest!("guide/src/rust_cpython.md", guide_rust_cpython_md);
|
|
||||||
doctest!("guide/src/trait_bounds.md", guide_trait_bounds_md);
|
|
||||||
doctest!("guide/src/types.md", guide_types_md);
|
|
||||||
doctest!("guide/src/faq.md", faq);
|
|
||||||
doctest!(
|
|
||||||
"guide/src/python_typing_hints.md",
|
|
||||||
guide_python_typing_hints
|
|
||||||
);
|
|
||||||
doctest!("guide/src/class/object.md", guide_class_object);
|
|
||||||
doctest!("guide/src/class/numeric.md", guide_class_numeric);
|
|
||||||
|
|
||||||
// deliberate choice not to test guide/ecosystem because those pages depend on external crates
|
"guide/src/exception.md" => guide_exception_md,
|
||||||
// such as pyo3_asyncio.
|
"guide/src/faq.md" => guide_faq_md,
|
||||||
|
"guide/src/features.md" => guide_features_md,
|
||||||
|
"guide/src/function.md" => guide_function_md,
|
||||||
|
"guide/src/memory.md" => guide_memory_md,
|
||||||
|
"guide/src/migration.md" => guide_migration_md,
|
||||||
|
"guide/src/module.md" => guide_module_md,
|
||||||
|
"guide/src/parallelism.md" => guide_parallelism_md,
|
||||||
|
"guide/src/python_from_rust.md" => guide_python_from_rust_md,
|
||||||
|
"guide/src/python_typing_hints.md" => guide_python_typing_hints_md,
|
||||||
|
"guide/src/rust_cpython.md" => guide_rust_cpython_md,
|
||||||
|
"guide/src/trait_bounds.md" => guide_trait_bounds_md,
|
||||||
|
"guide/src/types.md" => guide_types_md,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue