Merge pull request #2207 from davidhewitt/tidy-doctests
guide: tidy up doctests
This commit is contained in:
commit
0f1b8dd43f
|
@ -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]
|
||||
|
|
|
@ -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}}
|
||||
```
|
||||
|
||||
|
|
|
@ -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>>
|
||||
}
|
||||
# }
|
||||
```
|
||||
|
|
|
@ -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(())
|
||||
# }
|
||||
```
|
||||
|
|
72
src/lib.rs
72
src/lib.rs
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue