Un-ignore and expand on doc examples
This commit is contained in:
parent
f34b92a368
commit
f1e5d4c9a1
|
@ -48,6 +48,7 @@ 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"
|
||||
rayon = "1.0.2"
|
||||
|
||||
[build-dependencies]
|
||||
pyo3-build-config = { path = "pyo3-build-config", version = "0.16.4", features = ["resolve-config"] }
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
# Used in PyModule examples.
|
||||
|
||||
class Blah:
|
||||
pass
|
|
@ -212,13 +212,13 @@ The known complications are:
|
|||
|
||||
Significantly different compiler versions may see errors like this:
|
||||
|
||||
```ignore
|
||||
```text
|
||||
lto1: fatal error: bytecode stream in file 'rust-numpy/target/release/deps/libpyo3-6a7fb2ed970dbf26.rlib' generated with LTO version 6.0 instead of the expected 6.2
|
||||
```
|
||||
|
||||
Mismatching flags may lead to errors like this:
|
||||
|
||||
```ignore
|
||||
```text
|
||||
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/libpython3.9.a(zlibmodule.o): relocation R_X86_64_32 against `.data' can not be used when making a PIE object; recompile with -fPIE
|
||||
```
|
||||
|
||||
|
|
|
@ -53,31 +53,31 @@ After these steps you are ready to annotate your code!
|
|||
|
||||
The `#[cfg]` flags added by `pyo3-build-cfg` can be combined with all of Rust's logic in the `#[cfg]` attribute to create very precise conditional code generation. The following are some common patterns implemented using these flags:
|
||||
|
||||
```rust,ignore
|
||||
```text
|
||||
#[cfg(Py_3_7)]
|
||||
```
|
||||
|
||||
This `#[cfg]` marks code that will only be present on Python 3.7 and upwards. There are similar options `Py_3_8`, `Py_3_9`, `Py_3_10` and so on for each minor version.
|
||||
|
||||
```rust,ignore
|
||||
```text
|
||||
#[cfg(not(Py_3_7))]
|
||||
```
|
||||
|
||||
This `#[cfg]` marks code that will only be present on Python versions before (but not including) Python 3.7.
|
||||
|
||||
```rust,ignore
|
||||
```text
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
```
|
||||
|
||||
This `#[cfg]` marks code that is only available when building for the unlimited Python API (i.e. PyO3's `abi3` feature is not enabled). This might be useful if you want to ship your extension module as an `abi3` wheel and also allow users to compile it from source to make use of optimizations only possible with the unlimited API.
|
||||
|
||||
```rust,ignore
|
||||
```text
|
||||
#[cfg(any(Py_3_9, not(Py_LIMITED_API)))]
|
||||
```
|
||||
|
||||
This `#[cfg]` marks code which is available when running Python 3.9 or newer, or when using the unlimited API with an older Python version. Patterns like this are commonly seen on Python APIs which were added to the limited Python API in a specific minor version.
|
||||
|
||||
```rust,ignore
|
||||
```text
|
||||
#[cfg(PyPy)]
|
||||
```
|
||||
|
||||
|
@ -93,10 +93,16 @@ There's no way to detect your user doing that at compile time, so instead you ne
|
|||
|
||||
PyO3 provides the APIs [`Python::version()`] and [`Python::version_info()`] to query the running Python version. This allows you to do the following, for example:
|
||||
|
||||
```rust,ignore
|
||||
if py.version_info() >= (3, 9) {
|
||||
// run this code only if Python 3.9 or up
|
||||
}
|
||||
|
||||
```rust
|
||||
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));
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
[`Python::version()`]: {{#PYO3_DOCS_URL}}/pyo3/struct.Python.html#method.version
|
||||
|
|
|
@ -606,9 +606,24 @@ Python::with_gil(|py| {
|
|||
|
||||
Note that unlike class variables defined in Python code, class attributes defined in Rust cannot
|
||||
be mutated at all:
|
||||
```rust,ignore
|
||||
// Would raise a `TypeError: can't set attributes of built-in/extension type 'MyClass'`
|
||||
pyo3::py_run!(py, my_class, "my_class.my_attribute = 'foo'")
|
||||
```rust,should_panic
|
||||
# use pyo3::prelude::*;
|
||||
# #[pyclass]
|
||||
# struct MyClass {}
|
||||
# #[pymethods]
|
||||
# impl MyClass {
|
||||
# #[classattr]
|
||||
# fn my_attribute() -> String {
|
||||
# "hello".to_string()
|
||||
# }
|
||||
# }
|
||||
#
|
||||
Python::with_gil(|py| {
|
||||
let my_class = py.get_type::<MyClass>();
|
||||
|
||||
// Would raise a `TypeError: can't set attributes of built-in/extension type 'MyClass'`
|
||||
pyo3::py_run!(py, my_class, "my_class.my_attribute = 'foo'")
|
||||
});
|
||||
```
|
||||
|
||||
If the class attribute is defined with `const` code only, one can also annotate associated
|
||||
|
|
|
@ -8,8 +8,17 @@ The easiest way to convert a Python object to a Rust value is using
|
|||
`.extract()`. It returns a `PyResult` with a type error if the conversion
|
||||
fails, so usually you will use something like
|
||||
|
||||
```ignore
|
||||
let v: Vec<i32> = obj.extract()?;
|
||||
```rust
|
||||
# use pyo3::prelude::*;
|
||||
# use pyo3::types::PyList;
|
||||
# fn main() -> PyResult<()> {
|
||||
# Python::with_gil(|py| {
|
||||
# let list = PyList::new(py, b"foo");
|
||||
let v: Vec<i32> = list.extract()?;
|
||||
# assert_eq!(&v, &[102, 111, 111]);
|
||||
# Ok(())
|
||||
# })
|
||||
# }
|
||||
```
|
||||
|
||||
This method is available for many Python object types, and can produce a wide
|
||||
|
|
|
@ -33,9 +33,12 @@ Python::with_gil(|py| {
|
|||
When using PyO3 to create an extension module, you can add the new exception to
|
||||
the module like this, so that it is importable from Python:
|
||||
|
||||
```rust,ignore
|
||||
```rust
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::PyModule;
|
||||
use pyo3::exceptions::PyException;
|
||||
|
||||
create_exception!(mymodule, CustomError, PyException);
|
||||
pyo3::create_exception!(mymodule, CustomError, PyException);
|
||||
|
||||
#[pymodule]
|
||||
fn mymodule(py: Python<'_>, m: &PyModule) -> PyResult<()> {
|
||||
|
@ -77,7 +80,7 @@ fn divide(a: i32, b: i32) -> PyResult<i32> {
|
|||
# }
|
||||
```
|
||||
|
||||
You can also manually write and fetch errors in the Python interpreter's global state:
|
||||
You can manually write and fetch errors in the Python interpreter's global state:
|
||||
|
||||
```rust
|
||||
use pyo3::{Python, PyErr};
|
||||
|
@ -90,12 +93,7 @@ Python::with_gil(|py| {
|
|||
});
|
||||
```
|
||||
|
||||
If you already have a Python exception object, you can simply call [`PyErr::from_value`].
|
||||
|
||||
```rust,ignore
|
||||
PyErr::from_value(py, err).restore(py);
|
||||
```
|
||||
|
||||
If you already have a Python exception object, you can use [`PyErr::from_value`] to create a `PyErr` from it.
|
||||
|
||||
## Checking exception types
|
||||
|
||||
|
|
|
@ -207,7 +207,7 @@ fn sub(a: u64, b: u64) -> u64 {
|
|||
|
||||
When annotated like this, signatures are also correctly displayed in IPython.
|
||||
|
||||
```ignore
|
||||
```text
|
||||
>>> pyo3_test.add?
|
||||
Signature: pyo3_test.add(a, b, /)
|
||||
Docstring: This function adds two unsigned 64-bit integers.
|
||||
|
|
|
@ -46,7 +46,7 @@ To migrate, update trait bounds and imports from `PyTypeObject` to `PyTypeInfo`.
|
|||
|
||||
Before:
|
||||
|
||||
```rust,ignore
|
||||
```rust,compile_fail
|
||||
use pyo3::Python;
|
||||
use pyo3::type_object::PyTypeObject;
|
||||
use pyo3::types::PyType;
|
||||
|
@ -85,7 +85,7 @@ Migration from `#[pyproto]` to `#[pymethods]` is straightforward; copying the ex
|
|||
|
||||
Before:
|
||||
|
||||
```rust,ignore
|
||||
```rust,compile_fail
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::class::{PyBasicProtocol, PyIterProtocol};
|
||||
use pyo3::types::PyString;
|
||||
|
@ -110,7 +110,7 @@ impl PyIterProtocol for MyClass {
|
|||
|
||||
After
|
||||
|
||||
```rust,ignore
|
||||
```rust,compile_fail
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::PyString;
|
||||
|
||||
|
@ -274,7 +274,7 @@ To migrate just move the affected methods from a `#[pyproto]` to a `#[pymethods]
|
|||
|
||||
Before:
|
||||
|
||||
```rust,ignore
|
||||
```rust,compile_fail
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::class::basic::PyBasicProtocol;
|
||||
|
||||
|
@ -358,7 +358,7 @@ Exception types](#exception-types-have-been-reworked)).
|
|||
This implementation was redundant. Just construct the `Result::Err` variant directly.
|
||||
|
||||
Before:
|
||||
```rust,ignore
|
||||
```rust,compile_fail
|
||||
let result: PyResult<()> = PyErr::new::<TypeError, _>("error message").into();
|
||||
```
|
||||
|
||||
|
@ -376,13 +376,13 @@ makes it possible to interact with Python exception objects.
|
|||
|
||||
The new types also have names starting with the "Py" prefix. For example, before:
|
||||
|
||||
```rust,ignore
|
||||
```rust,compile_fail
|
||||
let err: PyErr = TypeError::py_err("error message");
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```rust,ignore
|
||||
```rust,compile_fail
|
||||
# use pyo3::{PyErr, PyResult, Python, type_object::PyTypeObject};
|
||||
# use pyo3::exceptions::{PyBaseException, PyTypeError};
|
||||
# Python::with_gil(|py| -> PyResult<()> {
|
||||
|
@ -407,7 +407,7 @@ Now there is only one way to define the conversion, `IntoPy`, so downstream crat
|
|||
adjust accordingly.
|
||||
|
||||
Before:
|
||||
```rust,ignore
|
||||
```rust,compile_fail
|
||||
# use pyo3::prelude::*;
|
||||
struct MyPyObjectWrapper(PyObject);
|
||||
|
||||
|
@ -433,7 +433,7 @@ impl IntoPy<PyObject> for MyPyObjectWrapper {
|
|||
Similarly, code which was using the `FromPy` trait can be trivially rewritten to use `IntoPy`.
|
||||
|
||||
Before:
|
||||
```rust,ignore
|
||||
```rust,compile_fail
|
||||
# use pyo3::prelude::*;
|
||||
# Python::with_gil(|py| {
|
||||
let obj = PyObject::from_py(1.234, py);
|
||||
|
@ -461,7 +461,7 @@ This should require no code changes except removing `use pyo3::AsPyRef` for code
|
|||
`pyo3::prelude::*`.
|
||||
|
||||
Before:
|
||||
```rust,ignore
|
||||
```rust,compile_fail
|
||||
use pyo3::{AsPyRef, Py, types::PyList};
|
||||
# pyo3::Python::with_gil(|py| {
|
||||
let list_py: Py<PyList> = PyList::empty(py).into();
|
||||
|
@ -722,7 +722,7 @@ If `T` implements `Clone`, you can extract `T` itself.
|
|||
In addition, you can also extract `&PyCell<T>`, though you rarely need it.
|
||||
|
||||
Before:
|
||||
```ignore
|
||||
```compile_fail
|
||||
let obj: &PyAny = create_obj();
|
||||
let obj_ref: &MyClass = obj.extract().unwrap();
|
||||
let obj_ref_mut: &mut MyClass = obj.extract().unwrap();
|
||||
|
@ -775,7 +775,9 @@ impl PySequenceProtocol for ByteSequence {
|
|||
```
|
||||
|
||||
After:
|
||||
```rust,ignore
|
||||
```rust
|
||||
# #[cfg(feature = "pyproto")]
|
||||
# {
|
||||
# use pyo3::prelude::*;
|
||||
# use pyo3::class::PySequenceProtocol;
|
||||
#[pyclass]
|
||||
|
@ -790,6 +792,7 @@ impl PySequenceProtocol for ByteSequence {
|
|||
Ok(Self { elements })
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
[`FromPyObject`]: {{#PYO3_DOCS_URL}}/pyo3/conversion/trait.FromPyObject.html
|
||||
|
|
|
@ -3,7 +3,25 @@
|
|||
CPython has the infamous [Global Interpreter Lock](https://docs.python.org/3/glossary.html#term-global-interpreter-lock), which prevents several threads from executing Python bytecode in parallel. This makes threading in Python a bad fit for [CPU-bound](https://stackoverflow.com/questions/868568/) tasks and often forces developers to accept the overhead of multiprocessing.
|
||||
|
||||
In PyO3 parallelism can be easily achieved in Rust-only code. Let's take a look at our [word-count](https://github.com/PyO3/pyo3/blob/main/examples/word-count/src/lib.rs) example, where we have a `search` function that utilizes the [rayon](https://github.com/rayon-rs/rayon) crate to count words in parallel.
|
||||
```rust, ignore
|
||||
```rust,no_run
|
||||
# #![allow(dead_code)]
|
||||
use pyo3::prelude::*;
|
||||
|
||||
// These traits let us use `par_lines` and `map`.
|
||||
use rayon::str::ParallelString;
|
||||
use rayon::iter::ParallelIterator;
|
||||
|
||||
/// Count the occurrences of needle in line, case insensitive
|
||||
fn count_line(line: &str, needle: &str) -> usize {
|
||||
let mut total = 0;
|
||||
for word in line.split(' ') {
|
||||
if word == needle {
|
||||
total += 1;
|
||||
}
|
||||
}
|
||||
total
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn search(contents: &str, needle: &str) -> usize {
|
||||
contents
|
||||
|
@ -14,14 +32,41 @@ fn search(contents: &str, needle: &str) -> usize {
|
|||
```
|
||||
|
||||
But let's assume you have a long running Rust function which you would like to execute several times in parallel. For the sake of example let's take a sequential version of the word count:
|
||||
```rust, ignore
|
||||
```rust,no_run
|
||||
# #![allow(dead_code)]
|
||||
# fn count_line(line: &str, needle: &str) -> usize {
|
||||
# let mut total = 0;
|
||||
# for word in line.split(' ') {
|
||||
# if word == needle {
|
||||
# total += 1;
|
||||
# }
|
||||
# }
|
||||
# total
|
||||
# }
|
||||
#
|
||||
fn search_sequential(contents: &str, needle: &str) -> usize {
|
||||
contents.lines().map(|line| count_line(line, needle)).sum()
|
||||
}
|
||||
```
|
||||
|
||||
To enable parallel execution of this function, the [`Python::allow_threads`] method can be used to temporarily release the GIL, thus allowing other Python threads to run. We then have a function exposed to the Python runtime which calls `search_sequential` inside a closure passed to [`Python::allow_threads`] to enable true parallelism:
|
||||
```rust, ignore
|
||||
```rust,no_run
|
||||
# #![allow(dead_code)]
|
||||
# use pyo3::prelude::*;
|
||||
#
|
||||
# fn count_line(line: &str, needle: &str) -> usize {
|
||||
# let mut total = 0;
|
||||
# for word in line.split(' ') {
|
||||
# if word == needle {
|
||||
# total += 1;
|
||||
# }
|
||||
# }
|
||||
# total
|
||||
# }
|
||||
#
|
||||
# fn search_sequential(contents: &str, needle: &str) -> usize {
|
||||
# contents.lines().map(|line| count_line(line, needle)).sum()
|
||||
# }
|
||||
#[pyfunction]
|
||||
fn search_sequential_allow_threads(py: Python<'_>, contents: &str, needle: &str) -> usize {
|
||||
py.allow_threads(|| search_sequential(contents, needle))
|
||||
|
@ -59,7 +104,7 @@ We are using `pytest-benchmark` to benchmark four word count functions:
|
|||
The benchmark script can be found [here](https://github.com/PyO3/pyo3/blob/main/examples/word-count/tests/test_word_count.py), and we can run `nox` in the `word-count` folder to benchmark these functions.
|
||||
|
||||
While the results of the benchmark of course depend on your machine, the relative results should be similar to this (mid 2020):
|
||||
```ignore
|
||||
```text
|
||||
-------------------------------------------------------------------------------------------------- benchmark: 4 tests -------------------------------------------------------------------------------------------------
|
||||
Name (time in ms) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations
|
||||
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
|
|
@ -311,7 +311,7 @@ from anywhere as long as your `app.py` is in the expected directory (in this exa
|
|||
that directory is `/usr/share/python_app`).
|
||||
|
||||
`src/main.rs`:
|
||||
```ignore
|
||||
```no_run
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::PyList;
|
||||
use std::fs;
|
||||
|
|
|
@ -210,7 +210,7 @@ macro_rules! callback_body {
|
|||
///
|
||||
/// For example this pyfunction:
|
||||
///
|
||||
/// ```ignore
|
||||
/// ```no_compile
|
||||
/// fn foo(&self) -> &Bar {
|
||||
/// &self.bar
|
||||
/// }
|
||||
|
@ -218,7 +218,7 @@ macro_rules! callback_body {
|
|||
///
|
||||
/// It is wrapped in proc macros with handle_panic like so:
|
||||
///
|
||||
/// ```ignore
|
||||
/// ```no_compile
|
||||
/// pyo3::callback::handle_panic(|_py| {
|
||||
/// let _slf = #slf;
|
||||
/// pyo3::callback::convert(_py, #foo)
|
||||
|
@ -227,7 +227,7 @@ macro_rules! callback_body {
|
|||
///
|
||||
/// If callback_body was used instead:
|
||||
///
|
||||
/// ```ignore
|
||||
/// ```no_compile
|
||||
/// pyo3::callback_body!(py, {
|
||||
/// let _slf = #slf;
|
||||
/// #foo
|
||||
|
|
|
@ -77,15 +77,40 @@ impl PyErr {
|
|||
/// If an error occurs during normalization (for example if `T` is not a Python type which
|
||||
/// extends from `BaseException`), then a different error may be produced during normalization.
|
||||
///
|
||||
/// # Example
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// return Err(PyErr::new::<exceptions::PyTypeError, _>("Error message"));
|
||||
/// ```
|
||||
/// use pyo3::prelude::*;
|
||||
/// use pyo3::exceptions::PyTypeError;
|
||||
///
|
||||
/// #[pyfunction]
|
||||
/// fn always_throws() -> PyResult<()> {
|
||||
/// Err(PyErr::new::<PyTypeError, _>("Error message"))
|
||||
/// }
|
||||
/// #
|
||||
/// # Python::with_gil(|py| {
|
||||
/// # let fun = pyo3::wrap_pyfunction!(always_throws, py).unwrap();
|
||||
/// # let err = fun.call0().expect_err("called a function that should always return an error but the return value was Ok");
|
||||
/// # assert!(err.is_instance_of::<PyTypeError>(py))
|
||||
/// # });
|
||||
/// ```
|
||||
///
|
||||
/// In most cases, you can use a concrete exception's constructor instead, which is equivalent:
|
||||
/// ```ignore
|
||||
/// return Err(exceptions::PyTypeError::new_err("Error message"));
|
||||
/// In most cases, you can use a concrete exception's constructor instead:
|
||||
///
|
||||
/// ```
|
||||
/// use pyo3::prelude::*;
|
||||
/// use pyo3::exceptions::PyTypeError;
|
||||
///
|
||||
/// #[pyfunction]
|
||||
/// fn always_throws() -> PyResult<()> {
|
||||
/// Err(PyTypeError::new_err("Error message"))
|
||||
/// }
|
||||
/// #
|
||||
/// # Python::with_gil(|py| {
|
||||
/// # let fun = pyo3::wrap_pyfunction!(always_throws, py).unwrap();
|
||||
/// # let err = fun.call0().expect_err("called a function that should always return an error but the return value was Ok");
|
||||
/// # assert!(err.is_instance_of::<PyTypeError>(py))
|
||||
/// # });
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn new<T, A>(args: A) -> PyErr
|
||||
|
|
12
src/gil.rs
12
src/gil.rs
|
@ -97,14 +97,16 @@ pub fn prepare_freethreaded_python() {
|
|||
/// - The return value of the closure must not contain any Python value, _including_ `PyResult`.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// use pyo3::prelude::*;
|
||||
///
|
||||
/// # fn main() -> PyResult<()>{
|
||||
/// ```rust
|
||||
/// unsafe {
|
||||
/// pyo3::with_embedded_python_interpreter(|py| py.run("print('Hello World')", None, None))
|
||||
/// pyo3::with_embedded_python_interpreter(|py| {
|
||||
/// if let Err(e) = py.run("print('Hello World')", None, None){
|
||||
/// // We must make sure to not return a `PyErr`!
|
||||
/// e.print(py);
|
||||
/// }
|
||||
/// });
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(not(PyPy))]
|
||||
pub unsafe fn with_embedded_python_interpreter<F, R>(f: F) -> R
|
||||
|
|
|
@ -8,17 +8,17 @@
|
|||
rustdoc::bare_urls
|
||||
)
|
||||
)]
|
||||
#![warn(elided_lifetimes_in_paths, unused_lifetimes)]
|
||||
#![warn(rust_2018_idioms, unused_lifetimes)]
|
||||
// Deny some lints in doctests.
|
||||
// Use `#[allow(...)]` locally to override.
|
||||
#![doc(test(attr(
|
||||
deny(
|
||||
elided_lifetimes_in_paths,
|
||||
rust_2018_idioms,
|
||||
unused_lifetimes,
|
||||
rust_2021_prelude_collisions,
|
||||
warnings
|
||||
),
|
||||
allow(unused_variables, unused_assignments)
|
||||
allow(unused_variables, unused_assignments, unused_extern_crates)
|
||||
)))]
|
||||
|
||||
//! Rust bindings to the Python interpreter.
|
||||
|
|
|
@ -41,15 +41,39 @@
|
|||
//! The [`#[pymethods]`](crate::pymethods) proc macro will generate this wrapper function (and more),
|
||||
//! using [`PyCell`] under the hood:
|
||||
//!
|
||||
//! ```ignore
|
||||
//! ```rust
|
||||
//! # use pyo3::prelude::*;
|
||||
//! # #[pyclass]
|
||||
//! # struct Number {
|
||||
//! # inner: u32,
|
||||
//! # }
|
||||
//! #
|
||||
//! # #[pymethods]
|
||||
//! # impl Number {
|
||||
//! # fn increment(&mut self) {
|
||||
//! # self.inner += 1;
|
||||
//! # }
|
||||
//! # }
|
||||
//! #
|
||||
//! // This function is exported to Python.
|
||||
//! unsafe extern "C" fn __wrap(slf: *mut PyObject, _args: *mut PyObject) -> *mut PyObject {
|
||||
//! pyo3::callback::handle_panic(|py| {
|
||||
//! let cell: &PyCell<Number> = py.from_borrowed_ptr(slf);
|
||||
//! let mut _ref: PyRefMut<Number> = cell.try_borrow_mut()?;
|
||||
//! let slf: &mut Number = &mut _ref;
|
||||
//! pyo3::callback::convert(py, Number::increment(slf))
|
||||
//! })
|
||||
//! unsafe extern "C" fn __wrap(
|
||||
//! _slf: *mut ::pyo3::ffi::PyObject,
|
||||
//! _args: *mut ::pyo3::ffi::PyObject,
|
||||
//! ) -> *mut ::pyo3::ffi::PyObject {
|
||||
//! use :: pyo3 as _pyo3;
|
||||
//! let gil = _pyo3::GILPool::new();
|
||||
//! let _py = gil.python();
|
||||
//! _pyo3::callback::panic_result_into_callback_output(
|
||||
//! _py,
|
||||
//! ::std::panic::catch_unwind(move || -> _pyo3::PyResult<_> {
|
||||
//! let _cell = _py
|
||||
//! .from_borrowed_ptr::<_pyo3::PyAny>(_slf)
|
||||
//! .downcast::<_pyo3::PyCell<Number>>()?;
|
||||
//! let mut _ref = _cell.try_borrow_mut()?;
|
||||
//! let _slf: &mut Number = &mut *_ref;
|
||||
//! _pyo3::callback::convert(_py, Number::increment(_slf))
|
||||
//! }),
|
||||
//! )
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
|
|
|
@ -92,43 +92,44 @@ impl PyModule {
|
|||
/// - Any Python exceptions are raised while initializing the module.
|
||||
/// - Any of the arguments cannot be converted to [`CString`](std::ffi::CString)s.
|
||||
///
|
||||
/// # Examples
|
||||
/// # Example: bundle in a file at compile time with [`include_str!`][1]:
|
||||
///
|
||||
/// Include a file at compile time by using [`std::include_str` macro][1]:
|
||||
///
|
||||
/// ```ignore
|
||||
/// ```rust
|
||||
/// use pyo3::prelude::*;
|
||||
///
|
||||
/// # fn main() -> PyResult<()> {
|
||||
/// let code = include_str!("../example.py");
|
||||
/// Python::with_gil(|py| -> PyResult<()> {
|
||||
/// PyModule::from_code(py, code, "example", "example")?;
|
||||
/// Ok(())
|
||||
/// })?;
|
||||
/// Ok(())
|
||||
/// // This path is resolved relative to this file.
|
||||
/// let code = include_str!("../../assets/script.py");
|
||||
///
|
||||
/// Python::with_gil(|py| -> PyResult<()> {
|
||||
/// PyModule::from_code(py, code, "example", "example")?;
|
||||
/// Ok(())
|
||||
/// })?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// Load a file at runtime by using [`std::fs::read_to_string`][2] function. It is recommended
|
||||
/// to use an absolute path to your Python files because then your binary can be run from
|
||||
/// anywhere:
|
||||
/// # Example: Load a file at runtime with [`std::fs::read_to_string`][2].
|
||||
///
|
||||
/// ```ignore
|
||||
/// use std::fs;
|
||||
/// ```rust
|
||||
/// use pyo3::prelude::*;
|
||||
///
|
||||
/// # fn main() -> PyResult<()> {
|
||||
/// let code = fs::read_to_string("/some/absolute/path/to/example.py")?;
|
||||
/// Python::with_gil(|py| -> PyResult<()> {
|
||||
/// PyModule::from_code(py, &code, "example", "example")?;
|
||||
/// Ok(())
|
||||
/// })?;
|
||||
/// Ok(())
|
||||
/// // This path is resolved by however the platform resolves paths,
|
||||
/// // which also makes this less portable. Consider using `include_str`
|
||||
/// // if you just want to bundle a script with your module.
|
||||
/// let code = std::fs::read_to_string("assets/script.py")?;
|
||||
///
|
||||
/// Python::with_gil(|py| -> PyResult<()> {
|
||||
/// PyModule::from_code(py, &code, "example", "example")?;
|
||||
/// Ok(())
|
||||
/// })?;
|
||||
/// Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [1]: https://doc.rust-lang.org/std/macro.include_str.html
|
||||
/// [2]: https://doc.rust-lang.org/std/fs/fn.read_to_string.html
|
||||
/// [1]: std::include_str
|
||||
/// [2]: std::fs::read_to_string
|
||||
pub fn from_code<'p>(
|
||||
py: Python<'p>,
|
||||
code: &str,
|
||||
|
|
Loading…
Reference in New Issue