guide: tidy up doctests

This commit is contained in:
David Hewitt 2022-03-04 22:41:25 +00:00
parent 576818dc0c
commit 2bd64c4962
5 changed files with 87 additions and 45 deletions

View file

@ -47,6 +47,7 @@ rustversion = "1.0"
# 1.0.0 requires Rust 1.50
proptest = { version = "0.10.1", default-features = false, features = ["std"] }
send_wrapper = "0.5"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.61"
[build-dependencies]

View file

@ -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)
```rust
```rust,ignore
{{#include ../../../examples/decorator/src/lib.rs}}
```

View file

@ -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
```rust
# #[cfg(feature = "serde")]
# #[allow(dead_code)]
# mod serde_only {
# use pyo3::prelude::*;
# use serde::{Deserialize, Serialize};
#[pyclass]
#[derive(Serialize, Deserialize)]
@ -128,4 +133,5 @@ struct User {
username: String,
permissions: Vec<Py<Permission>>
}
# }
```

View file

@ -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:
```rust
# use pyo3::prelude::*;
# use pyo3::types::PyString;
# fn main() -> PyResult<()> {
Python::with_gil(|py| -> PyResult<()> {
let hello: &PyString = py.eval("\"Hello World!\"", None, None)?.extract()?;
println!("Python says: {}", hello);
Ok(())
})?;
# Ok(())
# }
```
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:
```rust
# use pyo3::prelude::*;
# use pyo3::types::PyString;
# fn main() -> PyResult<()> {
Python::with_gil(|py| -> PyResult<()> {
for _ in 0..10 {
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.
Ok(())
})?;
# Ok(())
# }
```
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.
```rust
# use pyo3::prelude::*;
# use pyo3::types::PyString;
# fn main() -> PyResult<()> {
for _ in 0..10 {
Python::with_gil(|py| -> PyResult<()> {
let hello: &PyString = py.eval("\"Hello World!\"", None, None)?.extract()?;
@ -69,6 +82,8 @@ for _ in 0..10 {
Ok(())
})?; // only one copy of `hello` at a time
}
# Ok(())
# }
```
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.
```rust
# use pyo3::prelude::*;
# use pyo3::types::PyString;
# fn main() -> PyResult<()> {
Python::with_gil(|py| -> PyResult<()> {
for _ in 0..10 {
let pool = unsafe { py.new_pool() };
@ -85,6 +103,8 @@ Python::with_gil(|py| -> PyResult<()> {
}
Ok(())
})?;
# Ok(())
# }
```
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.
```rust
# use pyo3::prelude::*;
# use pyo3::types::PyString;
# fn main() -> 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));
Ok(())
});
})?;
# Ok(())
# }
```
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?
```rust
# use pyo3::prelude::*;
# use pyo3::types::PyString;
# fn main() -> PyResult<()> {
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...
// 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...
Python::with_gil(|py|
// Memory for `hello` is released here.
# ()
);
# Ok(())
# }
```
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.
```rust
# use pyo3::prelude::*;
# use pyo3::types::PyString;
# fn main() -> PyResult<()> {
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...
// Now sometime later in the program:
@ -163,6 +197,8 @@ Python::with_gil(|py| {
println!("Python says: {}", hello.as_ref(py));
drop(hello); // Memory released here.
});
# Ok(())
# }
```
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.
```rust
# use pyo3::prelude::*;
# use pyo3::types::PyString;
# fn main() -> PyResult<()> {
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...
// Now sometime later in the program:
@ -183,4 +222,6 @@ Python::with_gil(|py| {
// Do more stuff...
// Memory released here at end of `with_gil()` closure.
});
# Ok(())
# }
```

View file

@ -417,48 +417,42 @@ pub mod doc_test {
};
}
macro_rules! doctest {
($path:expr, $mod:ident) => {
doctest_impl!(include_str!(concat!("../", $path)), $mod);
macro_rules! doctests {
($($path:expr => $mod:ident),* $(,)?) => {
$(doctest_impl!(include_str!(concat!("../", $path)), $mod);)*
};
}
doctest!("README.md", readme_md);
doctest!("guide/src/advanced.md", guide_advanced_md);
doctest!(
"guide/src/building_and_distribution.md",
guide_building_and_distribution_md
);
doctest!("guide/src/class.md", guide_class_md);
doctest!("guide/src/class/protocols.md", guide_class_protocols_md);
doctest!("guide/src/conversions.md", guide_conversions_md);
doctest!(
"guide/src/conversions/tables.md",
guide_conversions_tables_md
);
doctests! {
"README.md" => readme_md,
"guide/src/advanced.md" => guide_advanced_md,
"guide/src/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,
"guide/src/class/call.md" => guide_class_call,
"guide/src/class/object.md" => guide_class_object,
"guide/src/class/numeric.md" => guide_class_numeric,
"guide/src/class/protocols.md" => guide_class_protocols_md,
"guide/src/conversions.md" => guide_conversions_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!(
"guide/src/conversions/traits.md",
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 such as pyo3_asyncio.
// deliberate choice not to test guide/ecosystem because those pages depend on external crates
// such as pyo3_asyncio.
"guide/src/exception.md" => guide_exception_md,
"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,
}
}