Merge pull request #2350 from mejrs/ignore-less
Expand on AsPyPointer docs and un-ignore doc examples
This commit is contained in:
commit
bc8641c790
|
@ -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,5 @@
|
|||
# Used in PyModule examples.
|
||||
|
||||
|
||||
class Blah:
|
||||
pass
|
|
@ -213,13 +213,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
|
||||
|
|
|
@ -604,13 +604,6 @@ 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'")
|
||||
```
|
||||
|
||||
If the class attribute is defined with `const` code only, one can also annotate associated
|
||||
constants:
|
||||
|
||||
|
|
|
@ -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,10 @@ impl PySequenceProtocol for ByteSequence {
|
|||
```
|
||||
|
||||
After:
|
||||
```rust,ignore
|
||||
```rust
|
||||
# #[allow(deprecated)]
|
||||
# #[cfg(feature = "pyproto")]
|
||||
# {
|
||||
# use pyo3::prelude::*;
|
||||
# use pyo3::class::PySequenceProtocol;
|
||||
#[pyclass]
|
||||
|
@ -790,6 +793,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
|
||||
|
|
|
@ -10,30 +10,88 @@ use crate::{
|
|||
};
|
||||
use std::ptr::NonNull;
|
||||
|
||||
/// This trait represents that **we can do zero-cost conversion from the object
|
||||
/// to a FFI pointer**.
|
||||
/// Returns a borrowed pointer to a Python object.
|
||||
///
|
||||
/// This trait is implemented for types that internally wrap a pointer to a Python object.
|
||||
/// The returned pointer will be valid for as long as `self` is. It may be null depending on the
|
||||
/// implementation.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use pyo3::{prelude::*, AsPyPointer};
|
||||
/// ```rust
|
||||
/// use pyo3::prelude::*;
|
||||
/// use pyo3::AsPyPointer;
|
||||
/// use pyo3::types::PyString;
|
||||
/// use pyo3::ffi;
|
||||
///
|
||||
/// Python::with_gil(|py| {
|
||||
/// let dict = pyo3::types::PyDict::new(py);
|
||||
/// // All native object wrappers implement AsPyPointer!!!
|
||||
/// assert_ne!(dict.as_ptr(), std::ptr::null_mut());
|
||||
/// let s: Py<PyString> = "foo".into_py(py);
|
||||
/// let ptr = s.as_ptr();
|
||||
///
|
||||
/// let is_really_a_pystring = unsafe { ffi::PyUnicode_CheckExact(ptr) };
|
||||
/// assert_eq!(is_really_a_pystring, 1);
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// It is your responsibility to make sure that the underlying Python object is not dropped too
|
||||
/// early. For example, the following code will cause undefined behavior:
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use pyo3::prelude::*;
|
||||
/// # use pyo3::AsPyPointer;
|
||||
/// # use pyo3::ffi;
|
||||
/// #
|
||||
/// Python::with_gil(|py| {
|
||||
/// let ptr: *mut ffi::PyObject = 0xabad1dea_u32.into_py(py).as_ptr();
|
||||
///
|
||||
/// let isnt_a_pystring = unsafe {
|
||||
/// // `ptr` is dangling, this is UB
|
||||
/// ffi::PyUnicode_CheckExact(ptr)
|
||||
/// };
|
||||
/// # assert_eq!(isnt_a_pystring, 0);
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// This happens because the pointer returned by `as_ptr` does not carry any lifetime information
|
||||
/// and the Python object is dropped immediately after the `0xabad1dea_u32.into_py(py).as_ptr()`
|
||||
/// expression is evaluated. To fix the problem, bind Python object to a local variable like earlier
|
||||
/// to keep the Python object alive until the end of its scope.
|
||||
pub trait AsPyPointer {
|
||||
/// Retrieves the underlying FFI pointer (as a borrowed pointer).
|
||||
/// Returns the underlying FFI pointer as a borrowed pointer.
|
||||
fn as_ptr(&self) -> *mut ffi::PyObject;
|
||||
}
|
||||
|
||||
/// This trait allows retrieving the underlying FFI pointer from Python objects.
|
||||
/// Returns an owned pointer to a Python object.
|
||||
///
|
||||
/// The returned pointer will be valid until you decrease its reference count. It may be null
|
||||
/// depending on the implementation.
|
||||
/// It is your responsibility to decrease the reference count of the pointer to avoid leaking memory.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use pyo3::prelude::*;
|
||||
/// use pyo3::IntoPyPointer;
|
||||
/// use pyo3::types::PyString;
|
||||
/// use pyo3::ffi;
|
||||
///
|
||||
/// Python::with_gil(|py| {
|
||||
/// let s: Py<PyString> = "foo".into_py(py);
|
||||
/// let ptr = s.into_ptr();
|
||||
///
|
||||
/// let is_really_a_pystring = unsafe { ffi::PyUnicode_CheckExact(ptr) };
|
||||
/// assert_eq!(is_really_a_pystring, 1);
|
||||
///
|
||||
/// // Because we took ownership of the pointer,
|
||||
/// // we must manually decrement it to avoid leaking memory
|
||||
/// unsafe { ffi::Py_DECREF(ptr) };
|
||||
/// });
|
||||
/// ```
|
||||
pub trait IntoPyPointer {
|
||||
/// Retrieves the underlying FFI pointer. Whether pointer owned or borrowed
|
||||
/// depends on implementation.
|
||||
/// Returns the underlying FFI pointer as an owned pointer.
|
||||
///
|
||||
/// If `self` has ownership of the underlying pointer, it will "steal" ownership of it.
|
||||
fn into_ptr(self) -> *mut ffi::PyObject;
|
||||
}
|
||||
|
||||
|
@ -188,17 +246,32 @@ pub trait IntoPy<T>: Sized {
|
|||
fn into_py(self, py: Python<'_>) -> T;
|
||||
}
|
||||
|
||||
/// `FromPyObject` is implemented by various types that can be extracted from
|
||||
/// a Python object reference.
|
||||
/// Extract a type from a Python object.
|
||||
///
|
||||
/// Normal usage is through the helper methods `Py::extract` or `PyAny::extract`:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// let obj: Py<PyAny> = ...;
|
||||
/// let value: &TargetType = obj.extract(py)?;
|
||||
/// Normal usage is through the `extract` methods on [`Py`] and [`PyAny`], which forward to this trait.
|
||||
///
|
||||
/// let any: &PyAny = ...;
|
||||
/// let value: &TargetType = any.extract()?;
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use pyo3::prelude::*;
|
||||
/// use pyo3::types::PyString;
|
||||
///
|
||||
/// # fn main() -> PyResult<()> {
|
||||
/// Python::with_gil(|py| {
|
||||
/// let obj: Py<PyString> = PyString::new(py, "blah").into();
|
||||
///
|
||||
/// // Straight from an owned reference
|
||||
/// let s: &str = obj.extract(py)?;
|
||||
/// # assert_eq!(s, "blah");
|
||||
///
|
||||
/// // Or from a borrowed reference
|
||||
/// let obj: &PyString = obj.as_ref(py);
|
||||
/// let s: &str = obj.extract()?;
|
||||
/// # assert_eq!(s, "blah");
|
||||
/// # Ok(())
|
||||
/// })
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// Note: depending on the implementation, the lifetime of the extracted result may
|
||||
|
|
|
@ -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