Clean up doctests, deny some lints (#1900)
* Clean up doctests, deny some lints * Apply suggestions from review. * replace \" with ' * Fix some more doc examples * Fix formatting * Fix some more things * Remove unused parentheses * Only test class sig on supported abi/platforms * Only test class signature on correct versions * Fix tests to compile on msrv * msrv strikes yet again * Add feedback * Pin `half` to 1.7.1 on msrv
This commit is contained in:
parent
ae873698d4
commit
8a57fe2d20
|
@ -183,6 +183,7 @@ jobs:
|
|||
cargo update -p indexmap --precise 1.6.2
|
||||
cargo update -p hashbrown:0.11.2 --precise 0.9.1
|
||||
cargo update -p bitflags --precise 1.2.1
|
||||
cargo update -p half --precise 1.7.1
|
||||
|
||||
- name: Build docs
|
||||
run: cargo doc --no-deps --no-default-features --features "${{ steps.settings.outputs.all_additive_features }}"
|
||||
|
|
|
@ -121,7 +121,7 @@ use pyo3::types::IntoPyDict;
|
|||
fn main() -> PyResult<()> {
|
||||
Python::with_gil(|py| {
|
||||
let sys = py.import("sys")?;
|
||||
let version: String = sys.get("version")?.extract()?;
|
||||
let version: String = sys.getattr("version")?.extract()?;
|
||||
|
||||
let locals = [("os", py.import("os")?)].into_py_dict(py);
|
||||
let code = "os.getenv('USER') or os.getenv('USERNAME') or 'Unknown'";
|
||||
|
|
|
@ -25,11 +25,12 @@ To define a custom Python class, a Rust struct needs to be annotated with the
|
|||
`#[pyclass]` attribute.
|
||||
|
||||
```rust
|
||||
# #![allow(dead_code)]
|
||||
# use pyo3::prelude::*;
|
||||
#[pyclass]
|
||||
struct MyClass {
|
||||
# #[pyo3(get)]
|
||||
num: i32,
|
||||
debug: bool,
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -45,8 +46,8 @@ Custom Python classes can then be added to a module using `add_class()`.
|
|||
# use pyo3::prelude::*;
|
||||
# #[pyclass]
|
||||
# struct MyClass {
|
||||
# #[allow(dead_code)]
|
||||
# num: i32,
|
||||
# debug: bool,
|
||||
# }
|
||||
#[pymodule]
|
||||
fn mymodule(_py: Python, m: &PyModule) -> PyResult<()> {
|
||||
|
@ -78,15 +79,13 @@ For users who are not very familiar with `RefCell`, here is a reminder of Rust's
|
|||
|
||||
```rust
|
||||
# use pyo3::prelude::*;
|
||||
# use pyo3::types::PyDict;
|
||||
#[pyclass]
|
||||
struct MyClass {
|
||||
#[pyo3(get)]
|
||||
num: i32,
|
||||
debug: bool,
|
||||
}
|
||||
Python::with_gil(|py| {
|
||||
let obj = PyCell::new(py, MyClass { num: 3, debug: true }).unwrap();
|
||||
let obj = PyCell::new(py, MyClass { num: 3}).unwrap();
|
||||
{
|
||||
let obj_ref = obj.borrow(); // Get PyRef
|
||||
assert_eq!(obj_ref.num, 3);
|
||||
|
@ -117,10 +116,13 @@ lifetime, and therefore needs a `Python<'_>` token to access.
|
|||
struct MyClass {
|
||||
num: i32,
|
||||
}
|
||||
|
||||
fn return_myclass() -> Py<MyClass> {
|
||||
Python::with_gil(|py| Py::new(py, MyClass { num: 1 }).unwrap())
|
||||
}
|
||||
|
||||
let obj = return_myclass();
|
||||
|
||||
Python::with_gil(|py|{
|
||||
let cell = obj.as_ref(py); // Py<MyClass>::as_ref returns &PyCell<MyClass>
|
||||
let obj_ref = cell.borrow(); // Get PyRef<T>
|
||||
|
@ -157,6 +159,7 @@ attribute. Only Python's `__new__` method can be specified, `__init__` is not av
|
|||
# use pyo3::prelude::*;
|
||||
#[pyclass]
|
||||
struct MyClass {
|
||||
# #[allow(dead_code)]
|
||||
num: i32,
|
||||
}
|
||||
|
||||
|
@ -174,6 +177,7 @@ Alternatively, if your `new` method may fail you can return `PyResult<Self>`.
|
|||
# use pyo3::prelude::*;
|
||||
#[pyclass]
|
||||
struct MyClass {
|
||||
# #[allow(dead_code)]
|
||||
num: i32,
|
||||
}
|
||||
|
||||
|
@ -323,7 +327,7 @@ impl DictWithCounter {
|
|||
```
|
||||
|
||||
If `SubClass` does not provide a baseclass initialization, the compilation fails.
|
||||
```compile_fail
|
||||
```rust,compile_fail
|
||||
# use pyo3::prelude::*;
|
||||
|
||||
#[pyclass]
|
||||
|
@ -498,8 +502,8 @@ gets injected by the method wrapper, e.g.
|
|||
# use pyo3::prelude::*;
|
||||
# #[pyclass]
|
||||
# struct MyClass {
|
||||
# #[allow(dead_code)]
|
||||
# num: i32,
|
||||
# debug: bool,
|
||||
# }
|
||||
#[pymethods]
|
||||
impl MyClass {
|
||||
|
@ -522,8 +526,8 @@ This is the equivalent of the Python decorator `@classmethod`.
|
|||
# use pyo3::types::PyType;
|
||||
# #[pyclass]
|
||||
# struct MyClass {
|
||||
# #[allow(dead_code)]
|
||||
# num: i32,
|
||||
# debug: bool,
|
||||
# }
|
||||
#[pymethods]
|
||||
impl MyClass {
|
||||
|
@ -552,8 +556,8 @@ To create a static method for a custom class, the method needs to be annotated w
|
|||
# use pyo3::prelude::*;
|
||||
# #[pyclass]
|
||||
# struct MyClass {
|
||||
# #[allow(dead_code)]
|
||||
# num: i32,
|
||||
# debug: bool,
|
||||
# }
|
||||
#[pymethods]
|
||||
impl MyClass {
|
||||
|
@ -706,19 +710,17 @@ use pyo3::types::{PyDict, PyTuple};
|
|||
# #[pyclass]
|
||||
# struct MyClass {
|
||||
# num: i32,
|
||||
# debug: bool,
|
||||
# }
|
||||
#[pymethods]
|
||||
impl MyClass {
|
||||
#[new]
|
||||
#[args(num = "-1", debug = "true")]
|
||||
fn new(num: i32, debug: bool) -> Self {
|
||||
MyClass { num, debug }
|
||||
#[args(num = "-1")]
|
||||
fn new(num: i32) -> Self {
|
||||
MyClass { num }
|
||||
}
|
||||
|
||||
#[args(
|
||||
num = "10",
|
||||
debug = "true",
|
||||
py_args = "*",
|
||||
name = "\"Hello\"",
|
||||
py_kwargs = "**"
|
||||
|
@ -726,23 +728,20 @@ impl MyClass {
|
|||
fn method(
|
||||
&mut self,
|
||||
num: i32,
|
||||
debug: bool,
|
||||
name: &str,
|
||||
py_args: &PyTuple,
|
||||
py_kwargs: Option<&PyDict>,
|
||||
) -> PyResult<String> {
|
||||
self.debug = debug;
|
||||
self.num = num;
|
||||
Ok(format!(
|
||||
"py_args={:?}, py_kwargs={:?}, name={}, num={}, debug={}",
|
||||
py_args, py_kwargs, name, self.num, self.debug
|
||||
"py_args={:?}, py_kwargs={:?}, name={}, num={}",
|
||||
py_args, py_kwargs, name, self.num
|
||||
))
|
||||
}
|
||||
|
||||
fn make_change(&mut self, num: i32, debug: bool) -> PyResult<String> {
|
||||
fn make_change(&mut self, num: i32) -> PyResult<String> {
|
||||
self.num = num;
|
||||
self.debug = debug;
|
||||
Ok(format!("num={}, debug={}", self.num, self.debug))
|
||||
Ok(format!("num={}", self.num))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -754,14 +753,13 @@ mc = mymodule.MyClass()
|
|||
print(mc.method(44, False, "World", 666, x=44, y=55))
|
||||
print(mc.method(num=-1, name="World"))
|
||||
print(mc.make_change(44, False))
|
||||
print(mc.make_change(debug=False, num=-1))
|
||||
```
|
||||
Produces output:
|
||||
```text
|
||||
py_args=('World', 666), py_kwargs=Some({'x': 44, 'y': 55}), name=Hello, num=44, debug=false
|
||||
py_args=(), py_kwargs=None, name=World, num=-1, debug=true
|
||||
num=44, debug=false
|
||||
num=-1, debug=false
|
||||
py_args=('World', 666), py_kwargs=Some({'x': 44, 'y': 55}), name=Hello, num=44
|
||||
py_args=(), py_kwargs=None, name=World, num=-1
|
||||
num=44
|
||||
num=-1
|
||||
```
|
||||
|
||||
## Implementation details
|
||||
|
@ -781,8 +779,8 @@ The `#[pyclass]` macro expands to roughly the code seen below. The `PyClassImplC
|
|||
|
||||
/// Class for demonstration
|
||||
struct MyClass {
|
||||
# #[allow(dead_code)]
|
||||
num: i32,
|
||||
debug: bool,
|
||||
}
|
||||
|
||||
unsafe impl pyo3::PyTypeInfo for MyClass {
|
||||
|
@ -812,7 +810,7 @@ impl pyo3::IntoPy<PyObject> for MyClass {
|
|||
}
|
||||
|
||||
impl pyo3::class::impl_::PyClassImpl for MyClass {
|
||||
const DOC: &'static str = "Class for demonstration";
|
||||
const DOC: &'static str = "Class for demonstration\u{0}";
|
||||
const IS_GC: bool = false;
|
||||
const IS_BASETYPE: bool = false;
|
||||
const IS_SUBCLASS: bool = false;
|
||||
|
|
|
@ -300,7 +300,7 @@ use pyo3::PyIterProtocol;
|
|||
|
||||
#[pyclass]
|
||||
struct MyIterator {
|
||||
iter: Box<Iterator<Item = PyObject> + Send>,
|
||||
iter: Box<dyn Iterator<Item = PyObject> + Send>,
|
||||
}
|
||||
|
||||
#[pyproto]
|
||||
|
|
|
@ -32,6 +32,9 @@ structs is not supported.
|
|||
|
||||
#### Deriving [`FromPyObject`] for structs
|
||||
|
||||
The derivation generates code that will attempt to access the attribute `my_string` on
|
||||
the Python object, i.e. `obj.getattr("my_string")`, and call `extract()` on the attribute.
|
||||
|
||||
```
|
||||
use pyo3::prelude::*;
|
||||
|
||||
|
@ -39,20 +42,50 @@ use pyo3::prelude::*;
|
|||
struct RustyStruct {
|
||||
my_string: String,
|
||||
}
|
||||
#
|
||||
# fn main() -> PyResult<()> {
|
||||
# Python::with_gil(|py| -> PyResult<()> {
|
||||
# let module = PyModule::from_code(
|
||||
# py,
|
||||
# "class Foo:
|
||||
# def __init__(self):
|
||||
# self.my_string = 'test'",
|
||||
# "",
|
||||
# "",
|
||||
# )?;
|
||||
#
|
||||
# let class = module.getattr("Foo")?;
|
||||
# let instance = class.call0()?;
|
||||
# let rustystruct: RustyStruct = instance.extract()?;
|
||||
# assert_eq!(rustystruct.my_string, "test");
|
||||
# Ok(())
|
||||
# })
|
||||
# }
|
||||
```
|
||||
|
||||
The derivation generates code that will per default access the attribute `my_string` on
|
||||
the Python object, i.e. `obj.getattr("my_string")`, and call `extract()` on the attribute.
|
||||
It is also possible to access the value on the Python object through `obj.get_item("my_string")`
|
||||
by setting the attribute `pyo3(item)` on the field:
|
||||
By setting the `#[pyo3(item)]` attribute on the field, PyO3 will attempt to extract the value by calling the `get_item` method on the Python object.
|
||||
|
||||
```
|
||||
use pyo3::prelude::*;
|
||||
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
struct RustyStruct {
|
||||
#[pyo3(item)]
|
||||
my_string: String,
|
||||
}
|
||||
#
|
||||
# use pyo3::types::PyDict;
|
||||
# fn main() -> PyResult<()> {
|
||||
# Python::with_gil(|py| -> PyResult<()> {
|
||||
# let dict = PyDict::new(py);
|
||||
# dict.set_item("my_string", "test")?;
|
||||
#
|
||||
# let rustystruct: RustyStruct = dict.extract()?;
|
||||
# assert_eq!(rustystruct.my_string, "test");
|
||||
# Ok(())
|
||||
# })
|
||||
# }
|
||||
```
|
||||
|
||||
The argument passed to `getattr` and `get_item` can also be configured:
|
||||
|
@ -67,6 +100,28 @@ struct RustyStruct {
|
|||
#[pyo3(attribute("name"))]
|
||||
string_attr: String,
|
||||
}
|
||||
#
|
||||
# fn main() -> PyResult<()> {
|
||||
# Python::with_gil(|py| -> PyResult<()> {
|
||||
# let module = PyModule::from_code(
|
||||
# py,
|
||||
# "class Foo(dict):
|
||||
# def __init__(self):
|
||||
# self.name = 'test'
|
||||
# self['key'] = 'test2'",
|
||||
# "",
|
||||
# "",
|
||||
# )?;
|
||||
#
|
||||
# let class = module.getattr("Foo")?;
|
||||
# let instance = class.call0()?;
|
||||
# let rustystruct: RustyStruct = instance.extract()?;
|
||||
# assert_eq!(rustystruct.string_attr, "test");
|
||||
# assert_eq!(rustystruct.string_in_mapping, "test2");
|
||||
#
|
||||
# Ok(())
|
||||
# })
|
||||
# }
|
||||
```
|
||||
|
||||
This tries to extract `string_attr` from the attribute `name` and `string_in_mapping`
|
||||
|
@ -85,6 +140,19 @@ use pyo3::prelude::*;
|
|||
|
||||
#[derive(FromPyObject)]
|
||||
struct RustyTuple(String, String);
|
||||
|
||||
# use pyo3::types::PyTuple;
|
||||
# fn main() -> PyResult<()> {
|
||||
# Python::with_gil(|py| -> PyResult<()> {
|
||||
# let tuple = PyTuple::new(py, vec!["test", "test2"]);
|
||||
#
|
||||
# let rustytuple: RustyTuple = tuple.extract()?;
|
||||
# assert_eq!(rustytuple.0, "test");
|
||||
# assert_eq!(rustytuple.1, "test2");
|
||||
#
|
||||
# Ok(())
|
||||
# })
|
||||
# }
|
||||
```
|
||||
|
||||
Tuple structs with a single field are treated as wrapper types which are described in the
|
||||
|
@ -95,6 +163,18 @@ use pyo3::prelude::*;
|
|||
|
||||
#[derive(FromPyObject)]
|
||||
struct RustyTuple((String,));
|
||||
|
||||
# use pyo3::types::PyTuple;
|
||||
# fn main() -> PyResult<()> {
|
||||
# Python::with_gil(|py| -> PyResult<()> {
|
||||
# let tuple = PyTuple::new(py, vec!["test"]);
|
||||
#
|
||||
# let rustytuple: RustyTuple = tuple.extract()?;
|
||||
# assert_eq!((rustytuple.0).0, "test");
|
||||
#
|
||||
# Ok(())
|
||||
# })
|
||||
# }
|
||||
```
|
||||
|
||||
#### Deriving [`FromPyObject`] for wrapper types
|
||||
|
@ -115,6 +195,21 @@ struct RustyTransparentTupleStruct(String);
|
|||
struct RustyTransparentStruct {
|
||||
inner: String,
|
||||
}
|
||||
|
||||
# use pyo3::types::PyString;
|
||||
# fn main() -> PyResult<()> {
|
||||
# Python::with_gil(|py| -> PyResult<()> {
|
||||
# let s = PyString::new(py, "test");
|
||||
#
|
||||
# let tup: RustyTransparentTupleStruct = s.extract()?;
|
||||
# assert_eq!(tup.0, "test");
|
||||
#
|
||||
# let stru: RustyTransparentStruct = s.extract()?;
|
||||
# assert_eq!(stru.inner, "test");
|
||||
#
|
||||
# Ok(())
|
||||
# })
|
||||
# }
|
||||
```
|
||||
|
||||
#### Deriving [`FromPyObject`] for enums
|
||||
|
@ -132,6 +227,7 @@ attribute can be applied to single-field-variants.
|
|||
use pyo3::prelude::*;
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
# #[derive(Debug)]
|
||||
enum RustyEnum<'a> {
|
||||
Int(usize), // input is a positive int
|
||||
String(String), // input is a string
|
||||
|
@ -151,23 +247,178 @@ enum RustyEnum<'a> {
|
|||
#[pyo3(transparent)]
|
||||
CatchAll(&'a PyAny), // This extraction never fails
|
||||
}
|
||||
#
|
||||
# use pyo3::types::{PyBytes, PyString};
|
||||
# fn main() -> PyResult<()> {
|
||||
# Python::with_gil(|py| -> PyResult<()> {
|
||||
# {
|
||||
# let thing = 42_u8.to_object(py);
|
||||
# let rust_thing: RustyEnum = thing.extract(py)?;
|
||||
#
|
||||
# assert_eq!(
|
||||
# 42,
|
||||
# match rust_thing {
|
||||
# RustyEnum::Int(i) => i,
|
||||
# other => unreachable!("Error extracting: {:?}", other),
|
||||
# }
|
||||
# );
|
||||
# }
|
||||
# {
|
||||
# let thing = PyString::new(py, "text");
|
||||
# let rust_thing: RustyEnum = thing.extract()?;
|
||||
#
|
||||
# assert_eq!(
|
||||
# "text",
|
||||
# match rust_thing {
|
||||
# RustyEnum::String(i) => i,
|
||||
# other => unreachable!("Error extracting: {:?}", other),
|
||||
# }
|
||||
# );
|
||||
# }
|
||||
# {
|
||||
# let thing = (32_u8, 73_u8).to_object(py);
|
||||
# let rust_thing: RustyEnum = thing.extract(py)?;
|
||||
#
|
||||
# assert_eq!(
|
||||
# (32, 73),
|
||||
# match rust_thing {
|
||||
# RustyEnum::IntTuple(i, j) => (i, j),
|
||||
# other => unreachable!("Error extracting: {:?}", other),
|
||||
# }
|
||||
# );
|
||||
# }
|
||||
# {
|
||||
# let thing = ("foo", 73_u8).to_object(py);
|
||||
# let rust_thing: RustyEnum = thing.extract(py)?;
|
||||
#
|
||||
# assert_eq!(
|
||||
# (String::from("foo"), 73),
|
||||
# match rust_thing {
|
||||
# RustyEnum::StringIntTuple(i, j) => (i, j),
|
||||
# other => unreachable!("Error extracting: {:?}", other),
|
||||
# }
|
||||
# );
|
||||
# }
|
||||
# {
|
||||
# let module = PyModule::from_code(
|
||||
# py,
|
||||
# "class Foo(dict):
|
||||
# def __init__(self):
|
||||
# self.x = 0
|
||||
# self.y = 1
|
||||
# self.z = 2",
|
||||
# "",
|
||||
# "",
|
||||
# )?;
|
||||
#
|
||||
# let class = module.getattr("Foo")?;
|
||||
# let instance = class.call0()?;
|
||||
# let rust_thing: RustyEnum = instance.extract()?;
|
||||
#
|
||||
# assert_eq!(
|
||||
# (0, 1, 2),
|
||||
# match rust_thing {
|
||||
# RustyEnum::Coordinates3d { x, y, z } => (x, y, z),
|
||||
# other => unreachable!("Error extracting: {:?}", other),
|
||||
# }
|
||||
# );
|
||||
# }
|
||||
#
|
||||
# {
|
||||
# let module = PyModule::from_code(
|
||||
# py,
|
||||
# "class Foo(dict):
|
||||
# def __init__(self):
|
||||
# self.x = 3
|
||||
# self.y = 4",
|
||||
# "",
|
||||
# "",
|
||||
# )?;
|
||||
#
|
||||
# let class = module.getattr("Foo")?;
|
||||
# let instance = class.call0()?;
|
||||
# let rust_thing: RustyEnum = instance.extract()?;
|
||||
#
|
||||
# assert_eq!(
|
||||
# (3, 4),
|
||||
# match rust_thing {
|
||||
# RustyEnum::Coordinates2d { a, b } => (a, b),
|
||||
# other => unreachable!("Error extracting: {:?}", other),
|
||||
# }
|
||||
# );
|
||||
# }
|
||||
#
|
||||
# {
|
||||
# let thing = PyBytes::new(py, b"text");
|
||||
# let rust_thing: RustyEnum = thing.extract()?;
|
||||
#
|
||||
# assert_eq!(
|
||||
# b"text",
|
||||
# match rust_thing {
|
||||
# RustyEnum::CatchAll(i) => i.downcast::<PyBytes>()?.as_bytes(),
|
||||
# other => unreachable!("Error extracting: {:?}", other),
|
||||
# }
|
||||
# );
|
||||
# }
|
||||
# Ok(())
|
||||
# })
|
||||
# }
|
||||
```
|
||||
|
||||
If none of the enum variants match, a `PyValueError` containing the names of the
|
||||
If none of the enum variants match, a `PyTypeError` containing the names of the
|
||||
tested variants is returned. The names reported in the error message can be customized
|
||||
through the `pyo3(annotation = "name")` attribute, e.g. to use conventional Python type
|
||||
through the `#[pyo3(annotation = "name")]` attribute, e.g. to use conventional Python type
|
||||
names:
|
||||
|
||||
```
|
||||
use pyo3::prelude::*;
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
# #[derive(Debug)]
|
||||
enum RustyEnum {
|
||||
#[pyo3(transparent, annotation = "str")]
|
||||
String(String),
|
||||
#[pyo3(transparent, annotation = "int")]
|
||||
Int(isize),
|
||||
}
|
||||
#
|
||||
# fn main() -> PyResult<()> {
|
||||
# Python::with_gil(|py| -> PyResult<()> {
|
||||
# {
|
||||
# let thing = 42_u8.to_object(py);
|
||||
# let rust_thing: RustyEnum = thing.extract(py)?;
|
||||
#
|
||||
# assert_eq!(
|
||||
# 42,
|
||||
# match rust_thing {
|
||||
# RustyEnum::Int(i) => i,
|
||||
# other => unreachable!("Error extracting: {:?}", other),
|
||||
# }
|
||||
# );
|
||||
# }
|
||||
#
|
||||
# {
|
||||
# let thing = "foo".to_object(py);
|
||||
# let rust_thing: RustyEnum = thing.extract(py)?;
|
||||
#
|
||||
# assert_eq!(
|
||||
# "foo",
|
||||
# match rust_thing {
|
||||
# RustyEnum::String(i) => i,
|
||||
# other => unreachable!("Error extracting: {:?}", other),
|
||||
# }
|
||||
# );
|
||||
# }
|
||||
#
|
||||
# {
|
||||
# let thing = b"foo".to_object(py);
|
||||
# let error = thing.extract::<RustyEnum>(py).unwrap_err();
|
||||
# assert!(error.is_instance::<pyo3::exceptions::PyTypeError>(py));
|
||||
# }
|
||||
#
|
||||
# Ok(())
|
||||
# })
|
||||
# }
|
||||
```
|
||||
|
||||
If the input is neither a string nor an integer, the error message will be:
|
||||
|
|
|
@ -49,7 +49,35 @@ fn mymodule(py: Python, m: &PyModule) -> PyResult<()> {
|
|||
|
||||
## Raising an exception
|
||||
|
||||
To raise an exception, first you need to obtain an exception type and construct a new [`PyErr`], then call the [`PyErr::restore`]({{#PYO3_DOCS_URL}}/pyo3/struct.PyErr.html#method.restore) method to write the exception back to the Python interpreter's global state.
|
||||
To raise an exception from `pyfunction`s and `pymethods`, you should return an `Err(PyErr)`.
|
||||
If returned to Python code, this [`PyErr`] will then be raised as a Python exception. Many PyO3 APIs also return [`PyResult`].
|
||||
|
||||
If a Rust type exists for the exception, then it is possible to use the `new_err` method.
|
||||
For example, each standard exception defined in the `pyo3::exceptions` module
|
||||
has a corresponding Rust type and exceptions defined by [`create_exception!`] and [`import_exception!`] macro have Rust types as well.
|
||||
|
||||
```rust
|
||||
use pyo3::exceptions::PyZeroDivisionError;
|
||||
use pyo3::prelude::*;
|
||||
|
||||
#[pyfunction]
|
||||
fn divide(a: i32, b: i32) -> PyResult<i32> {
|
||||
match a.checked_div(b) {
|
||||
Some(q) => Ok(q),
|
||||
None => Err(PyZeroDivisionError::new_err("division by zero")),
|
||||
}
|
||||
}
|
||||
#
|
||||
# fn main(){
|
||||
# Python::with_gil(|py|{
|
||||
# let fun = pyo3::wrap_pyfunction!(divide, py).unwrap();
|
||||
# fun.call1((1,0)).unwrap_err();
|
||||
# fun.call1((1,1)).unwrap();
|
||||
# });
|
||||
# }
|
||||
```
|
||||
|
||||
You can also manually write and fetch errors in the Python interpreter's global state:
|
||||
|
||||
```rust
|
||||
use pyo3::{Python, PyErr};
|
||||
|
@ -62,32 +90,12 @@ Python::with_gil(|py| {
|
|||
});
|
||||
```
|
||||
|
||||
From `pyfunction`s and `pyclass` methods, returning an `Err(PyErr)` is enough;
|
||||
PyO3 will handle restoring the exception on the Python interpreter side.
|
||||
|
||||
If you already have a Python exception instance, you can simply call [`PyErr::from_instance`].
|
||||
|
||||
```rust,ignore
|
||||
PyErr::from_instance(py, err).restore(py);
|
||||
```
|
||||
|
||||
If a Rust type exists for the exception, then it is possible to use the `new_err` method.
|
||||
For example, each standard exception defined in the `pyo3::exceptions` module
|
||||
has a corresponding Rust type, exceptions defined by [`create_exception!`] and [`import_exception!`] macro
|
||||
have Rust types as well.
|
||||
|
||||
```rust
|
||||
# use pyo3::exceptions::PyValueError;
|
||||
# use pyo3::prelude::*;
|
||||
# fn check_for_error() -> bool {false}
|
||||
fn my_func(arg: PyObject) -> PyResult<()> {
|
||||
if check_for_error() {
|
||||
Err(PyValueError::new_err("argument is wrong"))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Checking exception types
|
||||
|
||||
|
@ -128,47 +136,62 @@ which is an alias for the type `Result<T, PyErr>`.
|
|||
A [`PyErr`] represents a Python exception. Errors within the PyO3 library are also exposed as
|
||||
Python exceptions.
|
||||
|
||||
If your code has a custom error type e.g. `MyError`, adding an implementation of
|
||||
`std::convert::From<MyError> for PyErr` is usually enough. PyO3 will then automatically convert
|
||||
your error to a Python exception when needed.
|
||||
If your code has a custom error type, adding an implementation of `std::convert::From<MyError> for PyErr`
|
||||
is usually enough. PyO3 will then automatically convert your error to a Python exception when needed.
|
||||
|
||||
The following code snippet defines a Rust error named `CustomIOError`. In its `From<CustomIOError> for PyErr`
|
||||
implementation it returns a `PyErr` representing Python's `OSError`.
|
||||
|
||||
```rust
|
||||
# use pyo3::prelude::*;
|
||||
# use pyo3::exceptions::PyOSError;
|
||||
# use std::error::Error;
|
||||
# use std::fmt;
|
||||
#
|
||||
# #[derive(Debug)]
|
||||
# struct CustomIOError;
|
||||
#
|
||||
# impl Error for CustomIOError {}
|
||||
#
|
||||
# impl fmt::Display for CustomIOError {
|
||||
# fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
# write!(f, "Oh no!")
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# fn bind(_addr: &str) -> Result<(), CustomIOError> {
|
||||
# Err(CustomIOError)
|
||||
# }
|
||||
use pyo3::exceptions::PyOSError;
|
||||
use pyo3::prelude::*;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CustomIOError;
|
||||
|
||||
impl std::error::Error for CustomIOError {}
|
||||
|
||||
impl fmt::Display for CustomIOError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Oh no!")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<CustomIOError> for PyErr {
|
||||
fn from(err: CustomIOError) -> PyErr {
|
||||
PyOSError::new_err(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Connection { /* ... */}
|
||||
|
||||
fn bind(addr: String) -> Result<Connection, CustomIOError> {
|
||||
if &addr == "0.0.0.0"{
|
||||
Err(CustomIOError)
|
||||
} else {
|
||||
Ok(Connection{ /* ... */})
|
||||
}
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn connect(s: String) -> Result<bool, CustomIOError> {
|
||||
bind("127.0.0.1:80")?;
|
||||
Ok(true)
|
||||
fn connect(s: String) -> Result<(), CustomIOError> {
|
||||
bind(s)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
Python::with_gil(|py| {
|
||||
let fun = pyo3::wrap_pyfunction!(connect, py).unwrap();
|
||||
let err = fun.call1(("0.0.0.0",)).unwrap_err();
|
||||
assert!(err.is_instance::<PyOSError>(py));
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
The code snippet above will raise an `OSError` in Python if `bind()` returns a `CustomIOError`.
|
||||
|
||||
The `std::convert::From<T>` trait is implemented for most of the Rust standard library's error
|
||||
types so the `?` operator can be used.
|
||||
This has been implemented for most of Rust's standard library errors, so that you can use the `?`
|
||||
("try") operator with them. The following code snippet will raise a `ValueError` in Python if
|
||||
`String::parse()` returns an error.
|
||||
|
||||
```rust
|
||||
use pyo3::prelude::*;
|
||||
|
@ -176,10 +199,27 @@ use pyo3::prelude::*;
|
|||
fn parse_int(s: String) -> PyResult<usize> {
|
||||
Ok(s.parse::<usize>()?)
|
||||
}
|
||||
#
|
||||
# use pyo3::exceptions::PyValueError;
|
||||
#
|
||||
# fn main() {
|
||||
# Python::with_gil(|py| {
|
||||
# assert_eq!(parse_int(String::from("1")).unwrap(), 1);
|
||||
# assert_eq!(parse_int(String::from("1337")).unwrap(), 1337);
|
||||
#
|
||||
# assert!(parse_int(String::from("-1"))
|
||||
# .unwrap_err()
|
||||
# .is_instance::<PyValueError>(py));
|
||||
# assert!(parse_int(String::from("foo"))
|
||||
# .unwrap_err()
|
||||
# .is_instance::<PyValueError>(py));
|
||||
# assert!(parse_int(String::from("13.37"))
|
||||
# .unwrap_err()
|
||||
# .is_instance::<PyValueError>(py));
|
||||
# })
|
||||
# }
|
||||
```
|
||||
|
||||
The code snippet above will raise a `ValueError` in Python if `String::parse()` returns an error.
|
||||
|
||||
If lazy construction of the Python exception instance is desired, the
|
||||
[`PyErrArguments`]({{#PYO3_DOCS_URL}}/pyo3/trait.PyErrArguments.html)
|
||||
trait can be implemented. In that case, actual exception argument creation is delayed
|
||||
|
@ -192,6 +232,7 @@ The `import_exception!` macro allows importing a specific exception class and de
|
|||
for that exception.
|
||||
|
||||
```rust
|
||||
#![allow(dead_code)]
|
||||
use pyo3::prelude::*;
|
||||
|
||||
mod io {
|
||||
|
@ -199,8 +240,6 @@ mod io {
|
|||
}
|
||||
|
||||
fn tell(file: &PyAny) -> PyResult<u64> {
|
||||
use pyo3::exceptions::*;
|
||||
|
||||
match file.call_method0("tell") {
|
||||
Err(_) => Err(io::UnsupportedOperation::new_err("not supported: tell")),
|
||||
Ok(x) => x.extract::<u64>(),
|
||||
|
@ -216,6 +255,7 @@ defines exceptions for several standard library modules.
|
|||
[`import_exception!`]: {{#PYO3_DOCS_URL}}/pyo3/macro.import_exception.html
|
||||
|
||||
[`PyErr`]: {{#PYO3_DOCS_URL}}/pyo3/struct.PyErr.html
|
||||
[`PyResult`]: {{#PYO3_DOCS_URL}}/pyo3/type.PyResult.html
|
||||
[`PyErr::from_instance`]: {{#PYO3_DOCS_URL}}/pyo3/struct.PyErr.html#method.from_instance
|
||||
[`Python::is_instance`]: {{#PYO3_DOCS_URL}}/pyo3/struct.Python.html#method.is_instance
|
||||
[`PyAny::is_instance`]: {{#PYO3_DOCS_URL}}/pyo3/struct.PyAny.html#method.is_instance
|
||||
|
|
|
@ -102,8 +102,6 @@ fn module_with_functions(py: Python, m: &PyModule) -> PyResult<()> {
|
|||
m.add_function(wrap_pyfunction!(num_kwds, m)?).unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
# fn main() {}
|
||||
```
|
||||
|
||||
## Making the function signature available to Python
|
||||
|
@ -123,16 +121,34 @@ use pyo3::prelude::*;
|
|||
fn add(a: u64, b: u64) -> u64 {
|
||||
a + b
|
||||
}
|
||||
#
|
||||
# fn main() -> PyResult<()> {
|
||||
# Python::with_gil(|py| {
|
||||
# let fun = pyo3::wrap_pyfunction!(add, py)?;
|
||||
#
|
||||
# let doc: String = fun.getattr("__doc__")?.extract()?;
|
||||
# assert_eq!(doc, "This function adds two unsigned 64-bit integers.");
|
||||
#
|
||||
# let inspect = PyModule::import(py, "inspect")?.getattr("signature")?;
|
||||
# let sig: String = inspect
|
||||
# .call1((fun,))?
|
||||
# .call_method0("__str__")?
|
||||
# .extract()?;
|
||||
# assert_eq!(sig, "(a, b, /)");
|
||||
#
|
||||
# Ok(())
|
||||
# })
|
||||
# }
|
||||
```
|
||||
|
||||
This also works for classes and methods:
|
||||
|
||||
```rust
|
||||
# #![allow(dead_code)]
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::PyType;
|
||||
|
||||
// it works even if the item is not documented:
|
||||
|
||||
#[pyclass]
|
||||
#[pyo3(text_signature = "(c, d, /)")]
|
||||
struct MyClass {}
|
||||
|
@ -161,6 +177,69 @@ impl MyClass {
|
|||
e + f
|
||||
}
|
||||
}
|
||||
#
|
||||
# fn main() -> PyResult<()> {
|
||||
# Python::with_gil(|py| {
|
||||
# let inspect = PyModule::import(py, "inspect")?.getattr("signature")?;
|
||||
# let module = PyModule::new(py, "my_module")?;
|
||||
# module.add_class::<MyClass>()?;
|
||||
# let class = module.getattr("MyClass")?;
|
||||
#
|
||||
# if cfg!(not(Py_LIMITED_API)) || py.version_info() >= (3, 10) {
|
||||
# let doc: String = class.getattr("__doc__")?.extract()?;
|
||||
# assert_eq!(doc, "");
|
||||
#
|
||||
# let sig: String = inspect
|
||||
# .call1((class,))?
|
||||
# .call_method0("__str__")?
|
||||
# .extract()?;
|
||||
# assert_eq!(sig, "(c, d, /)");
|
||||
# } else {
|
||||
# let doc: String = class.getattr("__doc__")?.extract()?;
|
||||
# assert_eq!(doc, "");
|
||||
#
|
||||
# inspect.call1((class,)).expect_err("`text_signature` on classes is not compatible with compilation in `abi3` mode until Python 3.10 or greater");
|
||||
# }
|
||||
#
|
||||
# {
|
||||
# let method = class.getattr("my_method")?;
|
||||
#
|
||||
# assert!(method.getattr("__doc__")?.is_none());
|
||||
#
|
||||
# let sig: String = inspect
|
||||
# .call1((method,))?
|
||||
# .call_method0("__str__")?
|
||||
# .extract()?;
|
||||
# assert_eq!(sig, "(self, /, e, f)");
|
||||
# }
|
||||
#
|
||||
# {
|
||||
# let method = class.getattr("my_class_method")?;
|
||||
#
|
||||
# assert!(method.getattr("__doc__")?.is_none());
|
||||
#
|
||||
# let sig: String = inspect
|
||||
# .call1((method,))?
|
||||
# .call_method0("__str__")?
|
||||
# .extract()?;
|
||||
# assert_eq!(sig, "(cls, e, f)");
|
||||
# }
|
||||
#
|
||||
# {
|
||||
# let method = class.getattr("my_static_method")?;
|
||||
#
|
||||
# assert!(method.getattr("__doc__")?.is_none());
|
||||
#
|
||||
# let sig: String = inspect
|
||||
# .call1((method,))?
|
||||
# .call_method0("__str__")?
|
||||
# .extract()?;
|
||||
# assert_eq!(sig, "(e, f)");
|
||||
# }
|
||||
#
|
||||
# Ok(())
|
||||
# })
|
||||
# }
|
||||
```
|
||||
|
||||
Note that `text_signature` on classes is not compatible with compilation in
|
||||
|
@ -176,6 +255,7 @@ formatted like in the following example. Please note that the newline after the
|
|||
generated signatures when those are added in a future version of PyO3.
|
||||
|
||||
```rust
|
||||
# #![allow(dead_code)]
|
||||
use pyo3::prelude::*;
|
||||
|
||||
/// add(a, b, /)
|
||||
|
@ -262,8 +342,6 @@ fn pyfunction_with_module(module: &PyModule) -> PyResult<&str> {
|
|||
fn module_with_fn(py: Python, m: &PyModule) -> PyResult<()> {
|
||||
m.add_function(wrap_pyfunction!(pyfunction_with_module, m)?)
|
||||
}
|
||||
|
||||
# fn main() {}
|
||||
```
|
||||
|
||||
If `pass_module` is set, the first argument **must** be the `&PyModule`. It is then possible to use the module
|
||||
|
|
|
@ -146,7 +146,7 @@ let result: PyResult<()> = PyErr::new::<TypeError, _>("error message").into();
|
|||
|
||||
After (also using the new reworked exception types; see the following section):
|
||||
```rust
|
||||
# use pyo3::{PyErr, PyResult, exceptions::PyTypeError};
|
||||
# use pyo3::{PyResult, exceptions::PyTypeError};
|
||||
let result: PyResult<()> = Err(PyTypeError::new_err("error message"));
|
||||
```
|
||||
|
||||
|
@ -292,6 +292,7 @@ There can be two fixes:
|
|||
|
||||
After:
|
||||
```rust
|
||||
# #![allow(dead_code)]
|
||||
use pyo3::prelude::*;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
|
@ -323,6 +324,7 @@ There can be two fixes:
|
|||
|
||||
After:
|
||||
```rust
|
||||
# #![allow(dead_code)]
|
||||
use pyo3::prelude::*;
|
||||
|
||||
#[pyclass(unsendable)]
|
||||
|
@ -433,6 +435,7 @@ rules of references.
|
|||
Here is an example.
|
||||
```rust
|
||||
# use pyo3::prelude::*;
|
||||
|
||||
#[pyclass]
|
||||
struct Names {
|
||||
names: Vec<String>
|
||||
|
|
|
@ -92,9 +92,7 @@ pub(crate) fn register(py: Python, m: &PyModule) -> PyResult<()> {
|
|||
}
|
||||
|
||||
#[pyclass]
|
||||
struct SomeClass {
|
||||
x: usize,
|
||||
}
|
||||
struct SomeClass {/* ... */}
|
||||
# }
|
||||
|
||||
// src/osutil.rs
|
||||
|
|
|
@ -20,38 +20,43 @@ Both of these APIs take `args` and `kwargs` arguments (for positional and keywor
|
|||
|
||||
For convenience the [`Py<T>`](types.html#pyt-and-pyobject) smart pointer also exposes these same six API methods, but needs a `Python` token as an additional first argument to prove the GIL is held.
|
||||
|
||||
The example below shows a calling Python functions behind a `PyObject` (aka `Py<PyAny>`) reference:
|
||||
The example below calls a Python function behind a `PyObject` (aka `Py<PyAny>`) reference:
|
||||
|
||||
```rust
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::{PyDict, PyTuple};
|
||||
use pyo3::types::PyTuple;
|
||||
|
||||
struct SomeObject;
|
||||
impl SomeObject {
|
||||
fn new(py: Python) -> PyObject {
|
||||
PyDict::new(py).to_object(py)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
fn main() -> PyResult<()> {
|
||||
let arg1 = "arg1";
|
||||
let arg2 = "arg2";
|
||||
let arg3 = "arg3";
|
||||
|
||||
Python::with_gil(|py| {
|
||||
let obj = SomeObject::new(py);
|
||||
let fun: Py<PyAny> = PyModule::from_code(
|
||||
py,
|
||||
"def example(*args, **kwargs):
|
||||
if args != ():
|
||||
print('called with args', args)
|
||||
if kwargs != {}:
|
||||
print('called with kwargs', kwargs)
|
||||
if args == () and kwargs == {}:
|
||||
print('called with no arguments')",
|
||||
"",
|
||||
"",
|
||||
)?.getattr("example")?.into();
|
||||
|
||||
// call object without empty arguments
|
||||
obj.call0(py);
|
||||
fun.call0(py)?;
|
||||
|
||||
// call object with PyTuple
|
||||
let args = PyTuple::new(py, &[arg1, arg2, arg3]);
|
||||
obj.call1(py, args);
|
||||
fun.call1(py, args)?;
|
||||
|
||||
// pass arguments as rust tuple
|
||||
let args = (arg1, arg2, arg3);
|
||||
obj.call1(py, args);
|
||||
});
|
||||
fun.call1(py, args)?;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -61,39 +66,45 @@ For the `call` and `call_method` APIs, `kwargs` can be `None` or `Some(&PyDict)`
|
|||
|
||||
```rust
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::{IntoPyDict, PyDict};
|
||||
use pyo3::types::IntoPyDict;
|
||||
use std::collections::HashMap;
|
||||
|
||||
struct SomeObject;
|
||||
|
||||
impl SomeObject {
|
||||
fn new(py: Python) -> PyObject {
|
||||
PyDict::new(py).to_object(py)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
fn main() -> PyResult<()> {
|
||||
let key1 = "key1";
|
||||
let val1 = 1;
|
||||
let key2 = "key2";
|
||||
let val2 = 2;
|
||||
|
||||
Python::with_gil(|py| {
|
||||
let obj = SomeObject::new(py);
|
||||
let fun: Py<PyAny> = PyModule::from_code(
|
||||
py,
|
||||
"def example(*args, **kwargs):
|
||||
if args != ():
|
||||
print('called with args', args)
|
||||
if kwargs != {}:
|
||||
print('called with kwargs', kwargs)
|
||||
if args == () and kwargs == {}:
|
||||
print('called with no arguments')",
|
||||
"",
|
||||
"",
|
||||
)?.getattr("example")?.into();
|
||||
|
||||
|
||||
// call object with PyDict
|
||||
let kwargs = [(key1, val1)].into_py_dict(py);
|
||||
obj.call(py, (), Some(kwargs));
|
||||
fun.call(py, (), Some(kwargs))?;
|
||||
|
||||
// pass arguments as Vec
|
||||
let kwargs = vec![(key1, val1), (key2, val2)];
|
||||
obj.call(py, (), Some(kwargs.into_py_dict(py)));
|
||||
fun.call(py, (), Some(kwargs.into_py_dict(py)))?;
|
||||
|
||||
// pass arguments as HashMap
|
||||
let mut kwargs = HashMap::<&str, i32>::new();
|
||||
kwargs.insert(key1, 1);
|
||||
obj.call(py, (), Some(kwargs.into_py_dict(py)));
|
||||
});
|
||||
fun.call(py, (), Some(kwargs.into_py_dict(py)))?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -128,7 +139,6 @@ and return the evaluated value as a `&PyAny` object.
|
|||
|
||||
```rust
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::IntoPyDict;
|
||||
|
||||
# fn main() -> Result<(), ()> {
|
||||
Python::with_gil(|py| {
|
||||
|
|
|
@ -23,6 +23,7 @@ Let's say we have a function `solve` that operates on a model and mutates its st
|
|||
The argument of the function can be any model that implements the `Model` trait :
|
||||
|
||||
```rust
|
||||
# #![allow(dead_code)]
|
||||
pub trait Model {
|
||||
fn set_variables(&mut self, inputs: &Vec<f64>);
|
||||
fn compute(&mut self);
|
||||
|
@ -117,6 +118,7 @@ Now that this bit is implemented, let's expose the model wrapper to Python.
|
|||
Let's add the PyO3 annotations and add a constructor:
|
||||
|
||||
```rust
|
||||
# #![allow(dead_code)]
|
||||
# pub trait Model {
|
||||
# fn set_variables(&mut self, inputs: &Vec<f64>);
|
||||
# fn compute(&mut self);
|
||||
|
|
|
@ -66,7 +66,7 @@ a list:
|
|||
|
||||
```rust
|
||||
# use pyo3::prelude::*;
|
||||
# use pyo3::{Py, Python, PyAny, PyResult, types::PyList};
|
||||
# use pyo3::types::PyList;
|
||||
# Python::with_gil(|py| -> PyResult<()> {
|
||||
let obj: &PyAny = PyList::empty(py);
|
||||
|
||||
|
@ -86,7 +86,7 @@ For a `&PyAny` object reference `any` where the underlying object is a `#[pyclas
|
|||
|
||||
```rust
|
||||
# use pyo3::prelude::*;
|
||||
# use pyo3::{Py, Python, PyAny, PyResult, types::PyList};
|
||||
# use pyo3::{Py, Python, PyAny, PyResult};
|
||||
# #[pyclass] #[derive(Clone)] struct MyClass { }
|
||||
# Python::with_gil(|py| -> PyResult<()> {
|
||||
let obj: &PyAny = Py::new(py, MyClass { })?.into_ref(py);
|
||||
|
@ -236,7 +236,6 @@ so it also exposes all of the methods on `PyAny`.
|
|||
|
||||
```rust
|
||||
# use pyo3::prelude::*;
|
||||
# use pyo3::types::PyList;
|
||||
# #[pyclass] struct MyClass { }
|
||||
# Python::with_gil(|py| -> PyResult<()> {
|
||||
let cell: &PyCell<MyClass> = PyCell::new(py, MyClass { })?;
|
||||
|
@ -257,7 +256,6 @@ let _: &mut MyClass = &mut *py_ref_mut;
|
|||
|
||||
```rust
|
||||
# use pyo3::prelude::*;
|
||||
# use pyo3::types::PyList;
|
||||
# #[pyclass] struct MyClass { }
|
||||
# Python::with_gil(|py| -> PyResult<()> {
|
||||
let cell: &PyCell<MyClass> = PyCell::new(py, MyClass { })?;
|
||||
|
|
|
@ -431,7 +431,6 @@ impl Parse for FieldPyO3Attribute {
|
|||
|
||||
impl FieldPyO3Attributes {
|
||||
/// Extract the field attributes.
|
||||
///
|
||||
fn from_attrs(attrs: &[Attribute]) -> Result<Self> {
|
||||
let mut getter = None;
|
||||
let mut from_py_with = None;
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
//! Context manager api
|
||||
//! Trait and support implementation for context manager api
|
||||
//!
|
||||
|
||||
use crate::callback::IntoPyCallbackOutput;
|
||||
use crate::{PyClass, PyObject};
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
|
||||
//! Python GC support
|
||||
//!
|
||||
|
||||
use crate::{ffi, AsPyPointer, PyCell, PyClass, Python};
|
||||
use std::os::raw::{c_int, c_void};
|
||||
|
|
|
@ -17,13 +17,13 @@ use crate::{ffi, IntoPy, IntoPyPointer, PyClass, PyObject, Python};
|
|||
/// the integers 1 to 5, before raising `StopIteration("Ended")`.
|
||||
///
|
||||
/// ```rust
|
||||
/// use pyo3::class::iter::IterNextOutput;
|
||||
/// use pyo3::prelude::*;
|
||||
/// use pyo3::PyIterProtocol;
|
||||
/// use pyo3::class::iter::IterNextOutput;
|
||||
///
|
||||
/// #[pyclass]
|
||||
/// struct Iter {
|
||||
/// count: usize
|
||||
/// count: usize,
|
||||
/// }
|
||||
///
|
||||
/// #[pyproto]
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
//! https://docs.python.org/3/c-api/typeobj.html#async-object-structures)
|
||||
//!
|
||||
//! [PEP-0492](https://www.python.org/dev/peps/pep-0492/)
|
||||
//!
|
||||
|
||||
use crate::callback::IntoPyCallbackOutput;
|
||||
use crate::derive_utils::TryFromPyCell;
|
||||
|
|
|
@ -17,7 +17,7 @@ use std::ptr::NonNull;
|
|||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use pyo3::{AsPyPointer, prelude::*};
|
||||
/// use pyo3::{prelude::*, AsPyPointer};
|
||||
/// Python::with_gil(|py| {
|
||||
/// let dict = pyo3::types::PyDict::new(py);
|
||||
/// // All native object wrappers implement AsPyPointer!!!
|
||||
|
@ -147,8 +147,8 @@ where
|
|||
///
|
||||
/// #[pyclass]
|
||||
/// struct Number {
|
||||
/// #[pyo3(get, set)]
|
||||
/// value: i32,
|
||||
/// #[pyo3(get, set)]
|
||||
/// value: i32,
|
||||
/// }
|
||||
/// ```
|
||||
/// Python code will see this as an instance of the `Number` class with a `value` attribute.
|
||||
|
@ -161,14 +161,14 @@ where
|
|||
/// use pyo3::prelude::*;
|
||||
///
|
||||
/// struct Number {
|
||||
/// value: i32,
|
||||
/// value: i32,
|
||||
/// }
|
||||
///
|
||||
/// impl IntoPy<PyObject> for Number {
|
||||
/// fn into_py(self, py: Python) -> PyObject {
|
||||
/// // delegates to i32's IntoPy implementation.
|
||||
/// self.value.into_py(py)
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// Python code will see this as an `int` object.
|
||||
|
@ -183,7 +183,7 @@ where
|
|||
/// enum Value {
|
||||
/// Integer(i32),
|
||||
/// String(String),
|
||||
/// None
|
||||
/// None,
|
||||
/// }
|
||||
///
|
||||
/// impl IntoPy<PyObject> for Value {
|
||||
|
@ -191,10 +191,22 @@ where
|
|||
/// match self {
|
||||
/// Self::Integer(val) => val.into_py(py),
|
||||
/// Self::String(val) => val.into_py(py),
|
||||
/// Self::None => py.None()
|
||||
/// Self::None => py.None(),
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// # fn main() {
|
||||
/// # Python::with_gil(|py| {
|
||||
/// # let v = Value::Integer(73).into_py(py);
|
||||
/// # let v = v.extract::<i32>(py).unwrap();
|
||||
/// #
|
||||
/// # let v = Value::String("foo".into()).into_py(py);
|
||||
/// # let v = v.extract::<String>(py).unwrap();
|
||||
/// #
|
||||
/// # let v = Value::None.into_py(py);
|
||||
/// # let v = v.extract::<Option<Vec<i32>>>(py).unwrap();
|
||||
/// # });
|
||||
/// # }
|
||||
/// ```
|
||||
/// Python code will see this as any of the `int`, `string` or `None` objects.
|
||||
#[cfg_attr(docsrs, doc(alias = "IntoPyCallbackOutput"))]
|
||||
|
|
|
@ -35,7 +35,6 @@
|
|||
//! Using [indexmap](https://docs.rs/indexmap) to return a dictionary with some statistics
|
||||
//! about a list of numbers. Because of the insertion order guarantees, the Python code will
|
||||
//! always print the same result, matching users' expectations about Python's dict.
|
||||
//!
|
||||
//! ```rust
|
||||
//! use indexmap::{indexmap, IndexMap};
|
||||
//! use pyo3::prelude::*;
|
||||
|
@ -43,7 +42,7 @@
|
|||
//! fn median(data: &Vec<i32>) -> f32 {
|
||||
//! let sorted_data = data.clone().sort();
|
||||
//! let mid = data.len() / 2;
|
||||
//! if (data.len() % 2 == 0) {
|
||||
//! if data.len() % 2 == 0 {
|
||||
//! data[mid] as f32
|
||||
//! }
|
||||
//! else {
|
||||
|
|
|
@ -35,7 +35,6 @@
|
|||
//! Using [`BigInt`] to correctly increment an arbitrary precision integer.
|
||||
//! This is not possible with Rust's native integers if the Python integer is too large,
|
||||
//! in which case it will fail its conversion and raise `OverflowError`.
|
||||
//!
|
||||
//! ```rust
|
||||
//! use num_bigint::BigInt;
|
||||
//! use pyo3::prelude::*;
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
//!
|
||||
//! Using [num-complex](https://docs.rs/num-complex) and [nalgebra](https://docs.rs/nalgebra)
|
||||
//! to create a pyfunction that calculates the eigenvalues of a 2x2 matrix.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! # // not tested because nalgebra isn't supported on msrv
|
||||
//! # // please file an issue if it breaks!
|
||||
|
|
|
@ -114,10 +114,10 @@ impl PyErr {
|
|||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// use pyo3::{Python, PyErr, IntoPy, exceptions::PyTypeError, types::PyType};
|
||||
/// use pyo3::{exceptions::PyTypeError, types::PyType, IntoPy, PyErr, Python};
|
||||
/// Python::with_gil(|py| {
|
||||
/// // Case #1: Exception instance
|
||||
/// let err = PyErr::from_instance(PyTypeError::new_err("some type error",).instance(py));
|
||||
/// let err = PyErr::from_instance(PyTypeError::new_err("some type error").instance(py));
|
||||
/// assert_eq!(err.to_string(), "TypeError: some type error");
|
||||
///
|
||||
/// // Case #2: Exception type
|
||||
|
@ -126,7 +126,10 @@ impl PyErr {
|
|||
///
|
||||
/// // Case #3: Invalid exception value
|
||||
/// let err = PyErr::from_instance("foo".into_py(py).as_ref(py));
|
||||
/// assert_eq!(err.to_string(), "TypeError: exceptions must derive from BaseException");
|
||||
/// assert_eq!(
|
||||
/// err.to_string(),
|
||||
/// "TypeError: exceptions must derive from BaseException"
|
||||
/// );
|
||||
/// });
|
||||
/// ```
|
||||
pub fn from_instance(obj: &PyAny) -> PyErr {
|
||||
|
@ -159,9 +162,10 @@ impl PyErr {
|
|||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// use pyo3::{Python, PyErr, exceptions::PyTypeError, types::PyType};
|
||||
/// use pyo3::{exceptions::PyTypeError, types::PyType, PyErr, Python};
|
||||
///
|
||||
/// Python::with_gil(|py| {
|
||||
/// let err = PyTypeError::new_err(("some type error",));
|
||||
/// let err: PyErr = PyTypeError::new_err(("some type error",));
|
||||
/// assert_eq!(err.ptype(py), PyType::new::<PyTypeError>(py));
|
||||
/// });
|
||||
/// ```
|
||||
|
@ -174,10 +178,12 @@ impl PyErr {
|
|||
/// The object will be normalized first if needed.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use pyo3::{Python, PyErr, exceptions::PyTypeError, types::PyType};
|
||||
/// use pyo3::{exceptions::PyTypeError, PyErr, Python};
|
||||
///
|
||||
/// Python::with_gil(|py| {
|
||||
/// let err = PyTypeError::new_err(("some type error",));
|
||||
/// let err: PyErr = PyTypeError::new_err(("some type error",));
|
||||
/// assert!(err.is_instance::<PyTypeError>(py));
|
||||
/// assert_eq!(err.pvalue(py).to_string(), "some type error");
|
||||
/// });
|
||||
|
@ -192,7 +198,8 @@ impl PyErr {
|
|||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// use pyo3::{Python, PyErr, exceptions::PyTypeError, types::PyType};
|
||||
/// use pyo3::{exceptions::PyTypeError, Python};
|
||||
///
|
||||
/// Python::with_gil(|py| {
|
||||
/// let err = PyTypeError::new_err(("some type error",));
|
||||
/// assert_eq!(err.ptraceback(py), None);
|
||||
|
@ -403,9 +410,9 @@ impl PyErr {
|
|||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// use pyo3::{Python, PyErr, exceptions::PyTypeError, types::PyType};
|
||||
/// use pyo3::{exceptions::PyTypeError, PyErr, Python};
|
||||
/// Python::with_gil(|py| {
|
||||
/// let err = PyTypeError::new_err(("some type error",));
|
||||
/// let err: PyErr = PyTypeError::new_err(("some type error",));
|
||||
/// let err_clone = err.clone_ref(py);
|
||||
/// assert_eq!(err.ptype(py), err_clone.ptype(py));
|
||||
/// assert_eq!(err.pvalue(py), err_clone.pvalue(py));
|
||||
|
|
|
@ -80,7 +80,7 @@ mod bufferinfo {
|
|||
/* Flags for getting buffers */
|
||||
pub const PyBUF_SIMPLE: c_int = 0;
|
||||
pub const PyBUF_WRITABLE: c_int = 0x0001;
|
||||
/* we used to include an E, backwards compatible alias */
|
||||
/* we used to include an E, backwards compatible alias */
|
||||
pub const PyBUF_WRITEABLE: c_int = PyBUF_WRITABLE;
|
||||
pub const PyBUF_FORMAT: c_int = 0x0004;
|
||||
pub const PyBUF_ND: c_int = 0x0008;
|
||||
|
|
22
src/gil.rs
22
src/gil.rs
|
@ -63,13 +63,10 @@ pub(crate) fn gil_is_acquired() -> bool {
|
|||
/// ```rust
|
||||
/// use pyo3::prelude::*;
|
||||
///
|
||||
/// # #[allow(clippy::needless_doctest_main)]
|
||||
/// fn main() {
|
||||
/// pyo3::prepare_freethreaded_python();
|
||||
/// Python::with_gil(|py| {
|
||||
/// py.run("print('Hello World')", None, None)
|
||||
/// });
|
||||
/// }
|
||||
/// # fn main() -> PyResult<()>{
|
||||
/// pyo3::prepare_freethreaded_python();
|
||||
/// Python::with_gil(|py| py.run("print('Hello World')", None, None))
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(not(PyPy))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(PyPy))))]
|
||||
|
@ -131,14 +128,11 @@ pub fn prepare_freethreaded_python() {
|
|||
/// ```rust
|
||||
/// use pyo3::prelude::*;
|
||||
///
|
||||
/// # #[allow(clippy::needless_doctest_main)]
|
||||
/// fn main() {
|
||||
/// unsafe {
|
||||
/// pyo3::with_embedded_python_interpreter(|py| {
|
||||
/// py.run("print('Hello World')", None, None)
|
||||
/// });
|
||||
/// }
|
||||
/// # fn main() -> PyResult<()>{
|
||||
/// unsafe {
|
||||
/// pyo3::with_embedded_python_interpreter(|py| py.run("print('Hello World')", None, None))
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(not(PyPy))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(PyPy))))]
|
||||
|
|
|
@ -84,44 +84,74 @@ pub unsafe trait PyNativeType: Sized {
|
|||
/// [`Py`]`<T>` can be used to get around this by converting `dict` into a GIL-independent reference:
|
||||
///
|
||||
/// ```rust
|
||||
/// # use pyo3::prelude::*;
|
||||
/// # use pyo3::types::PyDict;
|
||||
/// #
|
||||
/// use pyo3::prelude::*;
|
||||
/// use pyo3::types::PyDict;
|
||||
///
|
||||
/// #[pyclass]
|
||||
/// struct Foo {
|
||||
/// inner: Py<PyDict>,
|
||||
/// }
|
||||
///
|
||||
/// #[pymethods]
|
||||
/// impl Foo {
|
||||
/// fn new() -> Foo {
|
||||
/// #[new]
|
||||
/// fn __new__() -> Foo {
|
||||
/// Python::with_gil(|py| {
|
||||
/// let dict: Py<PyDict> = PyDict::new(py).into();
|
||||
/// Foo { inner: dict }
|
||||
/// })
|
||||
/// }
|
||||
/// }
|
||||
/// #
|
||||
/// # fn main() -> PyResult<()> {
|
||||
/// # Python::with_gil(|py| {
|
||||
/// # let m = pyo3::types::PyModule::new(py, "test")?;
|
||||
/// # m.add_class::<Foo>()?;
|
||||
/// #
|
||||
/// # let foo: &PyCell<Foo> = pyo3::PyTryFrom::try_from(m.getattr("Foo")?.call0()?)?;
|
||||
/// # let dict = &foo.borrow().inner;
|
||||
/// # let dict: &PyDict = dict.as_ref(py);
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # })
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// This can also be done with other pyclasses:
|
||||
/// ```rust
|
||||
/// # use pyo3::prelude::*;
|
||||
/// #
|
||||
/// use pyo3::prelude::*;
|
||||
///
|
||||
/// #[pyclass]
|
||||
/// struct Bar {/* fields omitted */}
|
||||
/// struct Bar {/* ... */}
|
||||
///
|
||||
/// #[pyclass]
|
||||
/// struct Foo {
|
||||
/// inner: Py<Bar>,
|
||||
/// }
|
||||
///
|
||||
/// #[pymethods]
|
||||
/// impl Foo {
|
||||
/// fn new() -> PyResult<Foo> {
|
||||
/// #[new]
|
||||
/// fn __new__() -> PyResult<Foo> {
|
||||
/// Python::with_gil(|py| {
|
||||
/// let bar: Py<Bar> = Py::new(py, Bar {})?;
|
||||
/// Ok(Foo { inner: bar })
|
||||
/// })
|
||||
/// }
|
||||
/// }
|
||||
/// #
|
||||
/// # fn main() -> PyResult<()> {
|
||||
/// # Python::with_gil(|py| {
|
||||
/// # let m = pyo3::types::PyModule::new(py, "test")?;
|
||||
/// # m.add_class::<Foo>()?;
|
||||
/// #
|
||||
/// # let foo: &PyCell<Foo> = pyo3::PyTryFrom::try_from(m.getattr("Foo")?.call0()?)?;
|
||||
/// # let bar = &foo.borrow().inner;
|
||||
/// # let bar: &Bar = &*bar.borrow(py);
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # })
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// # Example: Shared ownership of Python objects
|
||||
|
@ -134,9 +164,9 @@ pub unsafe trait PyNativeType: Sized {
|
|||
/// [`Py::clone_ref`] will be faster if you happen to be already holding the GIL.
|
||||
///
|
||||
/// ```rust
|
||||
/// use pyo3::conversion::AsPyPointer;
|
||||
/// use pyo3::prelude::*;
|
||||
/// use pyo3::types::PyDict;
|
||||
/// use pyo3::conversion::AsPyPointer;
|
||||
///
|
||||
/// # fn main() {
|
||||
/// Python::with_gil(|py| {
|
||||
|
@ -259,10 +289,10 @@ where
|
|||
/// # use pyo3::prelude::*;
|
||||
/// #
|
||||
/// #[pyclass]
|
||||
/// struct MyClass { }
|
||||
/// struct MyClass {}
|
||||
///
|
||||
/// Python::with_gil(|py| {
|
||||
/// let my_class: Py<MyClass> = Py::new(py, MyClass { }).unwrap();
|
||||
/// let my_class: Py<MyClass> = Py::new(py, MyClass {}).unwrap();
|
||||
/// let my_class_cell: &PyCell<MyClass> = my_class.as_ref(py);
|
||||
/// assert!(my_class_cell.try_borrow().is_ok());
|
||||
/// });
|
||||
|
@ -304,7 +334,7 @@ where
|
|||
///
|
||||
/// ```rust
|
||||
/// # use pyo3::prelude::*;
|
||||
/// #
|
||||
/// # #[allow(dead_code)] // This is just to show it compiles.
|
||||
/// fn new_py_any<'py>(py: Python<'py>, value: impl IntoPy<Py<PyAny>>) -> &'py PyAny {
|
||||
/// let obj: Py<PyAny> = value.into_py(py);
|
||||
///
|
||||
|
@ -441,9 +471,9 @@ impl<T> Py<T> {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use pyo3::conversion::AsPyPointer;
|
||||
/// use pyo3::prelude::*;
|
||||
/// use pyo3::types::PyDict;
|
||||
/// use pyo3::conversion::AsPyPointer;
|
||||
///
|
||||
/// # fn main() {
|
||||
/// Python::with_gil(|py| {
|
||||
|
|
12
src/lib.rs
12
src/lib.rs
|
@ -8,6 +8,9 @@
|
|||
rustdoc::bare_urls
|
||||
)
|
||||
)]
|
||||
// Deny some lints in doctests.
|
||||
// Use `#[allow(...)]` locally to override.
|
||||
#![doc(test(attr(deny(warnings), allow(unused_variables, unused_assignments))))]
|
||||
|
||||
//! Rust bindings to the Python interpreter.
|
||||
//!
|
||||
|
@ -143,7 +146,6 @@
|
|||
//! ```
|
||||
//!
|
||||
//! **`src/lib.rs`**
|
||||
//!
|
||||
//! ```rust
|
||||
//! use pyo3::prelude::*;
|
||||
//!
|
||||
|
@ -165,7 +167,6 @@
|
|||
//! With those two files in place, now `maturin` needs to be installed. This can be done using
|
||||
//! Python's package manager `pip`. First, load up a new Python `virtualenv`, and install `maturin`
|
||||
//! into it:
|
||||
//!
|
||||
//! ```bash
|
||||
//! $ cd string_sum
|
||||
//! $ python -m venv .env
|
||||
|
@ -174,7 +175,6 @@
|
|||
//! ```
|
||||
//!
|
||||
//! Now build and execute the module:
|
||||
//!
|
||||
//! ```bash
|
||||
//! $ maturin develop
|
||||
//! # lots of progress output as maturin runs the compilation...
|
||||
|
@ -195,13 +195,11 @@
|
|||
//! some example code which runs an embedded Python interpreter.
|
||||
//!
|
||||
//! To install the Python shared library on Ubuntu:
|
||||
//!
|
||||
//! ```bash
|
||||
//! sudo apt install python3-dev
|
||||
//! ```
|
||||
//!
|
||||
//! Start a new project with `cargo new` and add `pyo3` to the `Cargo.toml` like this:
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies.pyo3]
|
||||
// workaround for `extended_key_value_attributes`: https://github.com/rust-lang/rust/issues/82768#issuecomment-803935643
|
||||
|
@ -212,7 +210,6 @@
|
|||
//! ```
|
||||
//!
|
||||
//! Example program displaying the value of `sys.version` and the current user name:
|
||||
//!
|
||||
//! ```rust
|
||||
//! use pyo3::prelude::*;
|
||||
//! use pyo3::types::IntoPyDict;
|
||||
|
@ -220,7 +217,7 @@
|
|||
//! fn main() -> PyResult<()> {
|
||||
//! Python::with_gil(|py| {
|
||||
//! let sys = py.import("sys")?;
|
||||
//! let version: String = sys.get("version")?.extract()?;
|
||||
//! let version: String = sys.getattr("version")?.extract()?;
|
||||
//!
|
||||
//! let locals = [("os", py.import("os")?)].into_py_dict(py);
|
||||
//! let code = "os.getenv('USER') or os.getenv('USERNAME') or 'Unknown'";
|
||||
|
@ -390,6 +387,7 @@ pub mod doc_test {
|
|||
"guide/src/conversions/tables.md",
|
||||
guide_conversions_tables_md
|
||||
);
|
||||
|
||||
doctest!(
|
||||
"guide/src/conversions/traits.md",
|
||||
guide_conversions_traits_md
|
||||
|
|
|
@ -97,12 +97,14 @@ macro_rules! wrap_pymodule {
|
|||
/// #[pymethods]
|
||||
/// impl MyClass {
|
||||
/// #[new]
|
||||
/// fn new() -> Self { MyClass {} }
|
||||
/// fn new() -> Self {
|
||||
/// MyClass {}
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// Python::with_gil(|py| {
|
||||
/// let locals = [("C", py.get_type::<MyClass>())].into_py_dict(py);
|
||||
/// pyo3::py_run!(py, *locals, "c = C()");
|
||||
/// let locals = [("C", py.get_type::<MyClass>())].into_py_dict(py);
|
||||
/// pyo3::py_run!(py, *locals, "c = C()");
|
||||
/// });
|
||||
/// ```
|
||||
#[macro_export]
|
||||
|
|
|
@ -15,9 +15,9 @@ use std::cell::UnsafeCell;
|
|||
/// between threads:
|
||||
///
|
||||
/// ```
|
||||
/// use pyo3::once_cell::GILOnceCell;
|
||||
/// use pyo3::prelude::*;
|
||||
/// use pyo3::types::PyList;
|
||||
/// use pyo3::once_cell::GILOnceCell;
|
||||
///
|
||||
/// static LIST_CELL: GILOnceCell<Py<PyList>> = GILOnceCell::new();
|
||||
///
|
||||
|
|
|
@ -107,6 +107,16 @@
|
|||
//! fn swap_numbers(a: &mut Number, b: &mut Number) {
|
||||
//! std::mem::swap(&mut a.inner, &mut b.inner);
|
||||
//! }
|
||||
//! # use pyo3::AsPyPointer;
|
||||
//! # fn main() {
|
||||
//! # Python::with_gil(|py|{
|
||||
//! # let n = Py::new(py, Number{inner: 35}).unwrap();
|
||||
//! # let n2 = n.clone_ref(py);
|
||||
//! # assert_eq!(n.as_ptr(), n2.as_ptr());
|
||||
//! # let fun = pyo3::wrap_pyfunction!(swap_numbers, py).unwrap();
|
||||
//! # fun.call1((n, n2)).expect_err("Managed to create overlapping mutable references. Note: this is undefined behaviour.");
|
||||
//! # });
|
||||
//! # }
|
||||
//! ```
|
||||
//! When users pass in the same `Number` as both arguments, one of the mutable borrows will
|
||||
//! fail and raise a `RuntimeError`:
|
||||
|
@ -119,7 +129,7 @@
|
|||
//! ```
|
||||
//!
|
||||
//! It is better to write that function like this:
|
||||
//! ```
|
||||
//! ```rust
|
||||
//! # use pyo3::prelude::*;
|
||||
//! # #[pyclass]
|
||||
//! # pub struct Number {
|
||||
|
@ -128,12 +138,36 @@
|
|||
//! #[pyfunction]
|
||||
//! fn swap_numbers(a: &PyCell<Number>, b: &PyCell<Number>) {
|
||||
//! // Check that the pointers are unequal
|
||||
//! if a.as_ref() != b.as_ref() {
|
||||
//! if a.as_ptr() != b.as_ptr() {
|
||||
//! std::mem::swap(&mut a.borrow_mut().inner, &mut b.borrow_mut().inner);
|
||||
//! } else {
|
||||
//! // Do nothing - they are the same object, so don't need swapping.
|
||||
//! }
|
||||
//! }
|
||||
//! # use pyo3::AsPyPointer;
|
||||
//! # fn main() {
|
||||
//! # // With duplicate numbers
|
||||
//! # Python::with_gil(|py|{
|
||||
//! # let n = Py::new(py, Number{inner: 35}).unwrap();
|
||||
//! # let n2 = n.clone_ref(py);
|
||||
//! # assert_eq!(n.as_ptr(), n2.as_ptr());
|
||||
//! # let fun = pyo3::wrap_pyfunction!(swap_numbers, py).unwrap();
|
||||
//! # fun.call1((n, n2)).unwrap();
|
||||
//! # });
|
||||
//! #
|
||||
//! # // With two different numbers
|
||||
//! # Python::with_gil(|py|{
|
||||
//! # let n = Py::new(py, Number{inner: 35}).unwrap();
|
||||
//! # let n2 = Py::new(py, Number{inner: 42}).unwrap();
|
||||
//! # assert_ne!(n.as_ptr(), n2.as_ptr());
|
||||
//! # let fun = pyo3::wrap_pyfunction!(swap_numbers, py).unwrap();
|
||||
//! # fun.call1((&n, &n2)).unwrap();
|
||||
//! # let n: u32 = n.borrow(py).inner;
|
||||
//! # let n2: u32 = n2.borrow(py).inner;
|
||||
//! # assert_eq!(n, 42);
|
||||
//! # assert_eq!(n2, 35);
|
||||
//! # });
|
||||
//! # }
|
||||
//! ```
|
||||
//! See the [guide] for more information.
|
||||
//!
|
||||
|
@ -607,18 +641,18 @@ where
|
|||
/// #[pyclass(extends=Base1, subclass)]
|
||||
/// struct Base2 {
|
||||
/// name2: &'static str,
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[pyclass(extends=Base2)]
|
||||
/// struct Sub {
|
||||
/// name3: &'static str,
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[pymethods]
|
||||
/// impl Sub {
|
||||
/// #[new]
|
||||
/// fn new() -> PyClassInitializer<Self> {
|
||||
/// PyClassInitializer::from(Base1{ name1: "base1" })
|
||||
/// PyClassInitializer::from(Base1 { name1: "base1" })
|
||||
/// .add_subclass(Base2 { name2: "base2" })
|
||||
/// .add_subclass(Self { name3: "sub" })
|
||||
/// }
|
||||
|
|
|
@ -85,7 +85,6 @@ impl<T: PyTypeInfo> PyObjectInit<T> for PyNativeTypeInitializer<T> {
|
|||
/// ```
|
||||
/// # use pyo3::prelude::*;
|
||||
/// # use pyo3::py_run;
|
||||
/// # use pyo3::types::IntoPyDict;
|
||||
/// #[pyclass(subclass)]
|
||||
/// struct BaseClass {
|
||||
/// #[pyo3(get)]
|
||||
|
@ -108,7 +107,9 @@ impl<T: PyTypeInfo> PyObjectInit<T> for PyNativeTypeInitializer<T> {
|
|||
/// fn new() -> PyClassInitializer<Self> {
|
||||
/// PyClassInitializer::from(BaseClass { basename: "base" })
|
||||
/// .add_subclass(SubClass { subname: "sub" })
|
||||
/// .add_subclass(SubSubClass { subsubname: "subsub" })
|
||||
/// .add_subclass(SubSubClass {
|
||||
/// subsubname: "subsub",
|
||||
/// })
|
||||
/// }
|
||||
/// }
|
||||
/// Python::with_gil(|py| {
|
||||
|
@ -141,19 +142,20 @@ impl<T: PyClass> PyClassInitializer<T> {
|
|||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use pyo3::prelude::*;
|
||||
/// #[pyclass]
|
||||
/// use pyo3::prelude::*;
|
||||
///
|
||||
/// #[pyclass(subclass)]
|
||||
/// struct BaseClass {
|
||||
/// value: u32,
|
||||
/// #[pyo3(get)]
|
||||
/// value: i32,
|
||||
/// }
|
||||
///
|
||||
/// impl BaseClass {
|
||||
/// fn new(value: i32) -> PyResult<Self> {
|
||||
/// Ok(Self {
|
||||
/// value: std::convert::TryFrom::try_from(value)?,
|
||||
/// })
|
||||
/// Ok(Self { value })
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[pyclass(extends=BaseClass)]
|
||||
/// struct SubClass {}
|
||||
///
|
||||
|
@ -165,6 +167,22 @@ impl<T: PyClass> PyClassInitializer<T> {
|
|||
/// Ok(base_init.add_subclass(SubClass {}))
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// fn main() -> PyResult<()> {
|
||||
/// Python::with_gil(|py| {
|
||||
/// let m = PyModule::new(py, "example")?;
|
||||
/// m.add_class::<SubClass>()?;
|
||||
/// m.add_class::<BaseClass>()?;
|
||||
///
|
||||
/// let instance = m.getattr("SubClass")?.call1((92,))?;
|
||||
///
|
||||
/// // `SubClass` does not have a `value` attribute, but `BaseClass` does.
|
||||
/// let n = instance.getattr("value")?.extract::<i32>()?;
|
||||
/// assert_eq!(n, 92);
|
||||
///
|
||||
/// Ok(())
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
pub fn add_subclass<S>(self, subclass_value: S) -> PyClassInitializer<S>
|
||||
where
|
||||
|
|
|
@ -180,11 +180,14 @@ impl Python<'_> {
|
|||
/// # Examples
|
||||
/// ```
|
||||
/// use pyo3::prelude::*;
|
||||
///
|
||||
/// # fn main() -> PyResult<()> {
|
||||
/// Python::with_gil(|py| -> PyResult<()> {
|
||||
/// let x: i32 = py.eval("5", None, None)?.extract()?;
|
||||
/// assert_eq!(x, 5);
|
||||
/// Ok(())
|
||||
/// });
|
||||
/// })
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn with_gil<F, R>(f: F) -> R
|
||||
|
@ -250,18 +253,18 @@ impl<'p> Python<'p> {
|
|||
/// Temporarily releases the `GIL`, thus allowing other Python threads to run.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use pyo3::prelude::*; use pyo3::types::IntoPyDict;
|
||||
/// use pyo3::exceptions::PyRuntimeError;
|
||||
/// use std::sync::Arc;
|
||||
/// use std::thread;
|
||||
///
|
||||
/// #[pyfunction]
|
||||
/// fn parallel_count(py: Python<'_>, strings: Vec<String>, query: String) -> PyResult<usize> {
|
||||
/// let query = query.chars().next().unwrap();
|
||||
/// py.allow_threads(move || {
|
||||
/// let threads: Vec<_> = strings
|
||||
/// .into_iter()
|
||||
/// .map(|s| thread::spawn(move || s.chars().filter(|&c| c == query).count()))
|
||||
/// .map(|s| std::thread::spawn(move || s.chars().filter(|&c| c == query).count()))
|
||||
/// .collect();
|
||||
/// let mut sum = 0;
|
||||
/// for t in threads {
|
||||
|
@ -273,12 +276,17 @@ impl<'p> Python<'p> {
|
|||
///
|
||||
/// Python::with_gil(|py| {
|
||||
/// let m = PyModule::new(py, "pcount").unwrap();
|
||||
/// m.add_function(wrap_pyfunction!(parallel_count, m).unwrap()).unwrap();
|
||||
/// m.add_function(wrap_pyfunction!(parallel_count, m).unwrap())
|
||||
/// .unwrap();
|
||||
/// let locals = [("pcount", m)].into_py_dict(py);
|
||||
/// pyo3::py_run!(py, *locals, r#"
|
||||
/// pyo3::py_run!(
|
||||
/// py,
|
||||
/// *locals,
|
||||
/// r#"
|
||||
/// s = ["Flow", "my", "tears", "the", "Policeman", "Said"]
|
||||
/// assert pcount.parallel_count(s, "a") == 3
|
||||
/// "#);
|
||||
/// "#
|
||||
/// );
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
|
@ -341,8 +349,9 @@ impl<'p> Python<'p> {
|
|||
/// If `locals` is `None`, it defaults to the value of `globals`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use pyo3::{types::{PyBytes, PyDict}, prelude::*};
|
||||
/// # use pyo3::prelude::*;
|
||||
/// # Python::with_gil(|py| {
|
||||
/// let result = py.eval("[i * 10 for i in range(5)]", None, None).unwrap();
|
||||
/// let res: Vec<i64> = result.extract().unwrap();
|
||||
|
@ -365,7 +374,10 @@ impl<'p> Python<'p> {
|
|||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use pyo3::{types::{PyBytes, PyDict}, prelude::*};
|
||||
/// use pyo3::{
|
||||
/// prelude::*,
|
||||
/// types::{PyBytes, PyDict},
|
||||
/// };
|
||||
/// Python::with_gil(|py| {
|
||||
/// let locals = PyDict::new(py);
|
||||
/// py.run(
|
||||
|
@ -374,8 +386,8 @@ impl<'p> Python<'p> {
|
|||
/// s = 'Hello Rust!'
|
||||
/// ret = base64.b64encode(s.encode('utf-8'))
|
||||
/// "#,
|
||||
/// None,
|
||||
/// Some(locals),
|
||||
/// None,
|
||||
/// Some(locals),
|
||||
/// )
|
||||
/// .unwrap();
|
||||
/// let ret = locals.get_item("ret").unwrap();
|
||||
|
@ -631,7 +643,9 @@ impl<'p> Python<'p> {
|
|||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # #![allow(dead_code)] // this example is quite impractical to test
|
||||
/// use pyo3::prelude::*;
|
||||
///
|
||||
/// # fn main(){
|
||||
/// #[pyfunction]
|
||||
/// fn loop_forever(py: Python) -> PyResult<()> {
|
||||
|
@ -699,7 +713,7 @@ impl<'p> Python<'p> {
|
|||
///
|
||||
/// // It is recommended to *always* immediately set py to the pool's Python, to help
|
||||
/// // avoid creating references with invalid lifetimes.
|
||||
/// let py = unsafe { pool.python() };
|
||||
/// let py = pool.python();
|
||||
///
|
||||
/// // do stuff...
|
||||
/// # break; // Exit the loop so that doctest terminates!
|
||||
|
|
|
@ -219,25 +219,21 @@ impl PyAny {
|
|||
/// Depending on the value of `compare_op`, this is equivalent to one of the
|
||||
/// following Python expressions:
|
||||
///
|
||||
/// <div style="width:1px">
|
||||
///
|
||||
/// | `compare_op` | <span style="white-space: pre">Python expression</span> |
|
||||
/// | `compare_op` | Python expression |
|
||||
/// | :---: | :----: |
|
||||
/// | [`CompareOp::Eq`] | <span style="white-space: pre">`self == other`</span> |
|
||||
/// | [`CompareOp::Ne`] | <span style="white-space: pre">`self != other`</span> |
|
||||
/// | [`CompareOp::Lt`] | <span style="white-space: pre">`self < other`</span> |
|
||||
/// | [`CompareOp::Le`] | <span style="white-space: pre">`self <= other`</span> |
|
||||
/// | [`CompareOp::Gt`] | <span style="white-space: pre">`self > other`</span> |
|
||||
/// | [`CompareOp::Ge`] | <span style="white-space: pre">`self >= other`</span> |
|
||||
///
|
||||
/// </div>
|
||||
/// | [`CompareOp::Eq`] | `self == other` |
|
||||
/// | [`CompareOp::Ne`] | `self != other` |
|
||||
/// | [`CompareOp::Lt`] | `self < other` |
|
||||
/// | [`CompareOp::Le`] | `self <= other` |
|
||||
/// | [`CompareOp::Gt`] | `self > other` |
|
||||
/// | [`CompareOp::Ge`] | `self >= other` |
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use pyo3::class::basic::CompareOp;
|
||||
/// use pyo3::prelude::*;
|
||||
/// use pyo3::types::PyInt;
|
||||
/// use pyo3::class::basic::CompareOp;
|
||||
///
|
||||
/// # fn main() -> PyResult<()> {
|
||||
/// Python::with_gil(|py| -> PyResult<()> {
|
||||
|
@ -245,7 +241,7 @@ impl PyAny {
|
|||
/// let b: &PyInt = 42_u8.into_py(py).into_ref(py).downcast()?;
|
||||
/// assert!(a.rich_compare(b, CompareOp::Le)?.is_true()?);
|
||||
/// Ok(())
|
||||
/// })?;
|
||||
/// })?;
|
||||
/// # Ok(())}
|
||||
/// ```
|
||||
pub fn rich_compare<O>(&self, other: O, compare_op: CompareOp) -> PyResult<&PyAny>
|
||||
|
@ -274,10 +270,10 @@ impl PyAny {
|
|||
///
|
||||
/// # fn main() -> PyResult<()> {
|
||||
/// Python::with_gil(|py| -> PyResult<()> {
|
||||
/// let builtins = PyModule::import(py, "builtins")?;
|
||||
/// let print = builtins.getattr("print")?;
|
||||
/// assert!(print.is_callable());
|
||||
/// Ok(())
|
||||
/// let builtins = PyModule::import(py, "builtins")?;
|
||||
/// let print = builtins.getattr("print")?;
|
||||
/// assert!(print.is_callable());
|
||||
/// Ok(())
|
||||
/// })?;
|
||||
/// # Ok(())}
|
||||
/// ```
|
||||
|
@ -363,7 +359,7 @@ impl PyAny {
|
|||
/// Python::with_gil(|py| -> PyResult<()> {
|
||||
/// let module = PyModule::import(py, "operator")?;
|
||||
/// let add = module.getattr("add")?;
|
||||
/// let args = (1,2);
|
||||
/// let args = (1, 2);
|
||||
/// let value = add.call1(args)?;
|
||||
/// assert_eq!(value.extract::<i32>()?, 3);
|
||||
/// Ok(())
|
||||
|
@ -391,8 +387,7 @@ impl PyAny {
|
|||
///
|
||||
/// ```rust
|
||||
/// use pyo3::prelude::*;
|
||||
/// use pyo3::types::{PyDict, PyList};
|
||||
/// use crate::pyo3::types::IntoPyDict;
|
||||
/// use pyo3::types::{IntoPyDict, PyList};
|
||||
///
|
||||
/// # fn main() -> PyResult<()> {
|
||||
/// Python::with_gil(|py| -> PyResult<()> {
|
||||
|
|
|
@ -28,8 +28,11 @@ impl PyByteArray {
|
|||
/// * If `init` returns `Ok(())`, `new_with` will return `Ok(&PyByteArray)`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use pyo3::{prelude::*, types::PyByteArray};
|
||||
///
|
||||
/// # fn main() -> PyResult<()> {
|
||||
/// Python::with_gil(|py| -> PyResult<()> {
|
||||
/// let py_bytearray = PyByteArray::new_with(py, 10, |bytes: &mut [u8]| {
|
||||
/// bytes.copy_from_slice(b"Hello Rust");
|
||||
|
@ -38,7 +41,8 @@ impl PyByteArray {
|
|||
/// let bytearray: &[u8] = unsafe { py_bytearray.as_bytes() };
|
||||
/// assert_eq!(bytearray, b"Hello Rust");
|
||||
/// Ok(())
|
||||
/// });
|
||||
/// })
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn new_with<F>(py: Python, len: usize, init: F) -> PyResult<&PyByteArray>
|
||||
where
|
||||
|
|
|
@ -34,8 +34,11 @@ impl PyBytes {
|
|||
/// * If `init` returns `Ok(())`, `new_with` will return `Ok(&PyBytes)`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use pyo3::{prelude::*, types::PyBytes};
|
||||
///
|
||||
/// # fn main() -> PyResult<()> {
|
||||
/// Python::with_gil(|py| -> PyResult<()> {
|
||||
/// let py_bytes = PyBytes::new_with(py, 10, |bytes: &mut [u8]| {
|
||||
/// bytes.copy_from_slice(b"Hello Rust");
|
||||
|
@ -44,7 +47,8 @@ impl PyBytes {
|
|||
/// let bytes: &[u8] = FromPyObject::extract(py_bytes)?;
|
||||
/// assert_eq!(bytes, b"Hello Rust");
|
||||
/// Ok(())
|
||||
/// });
|
||||
/// })
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn new_with<F>(py: Python, len: usize, init: F) -> PyResult<&PyBytes>
|
||||
where
|
||||
|
|
|
@ -11,18 +11,19 @@ use crate::{PyDowncastError, PyTryFrom};
|
|||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use pyo3::prelude::*;
|
||||
/// use pyo3::types::PyIterator;
|
||||
/// use pyo3::prelude::*;
|
||||
///
|
||||
/// # fn main() -> PyResult<()> {
|
||||
/// Python::with_gil(|py| -> PyResult<()> {
|
||||
/// Python::with_gil(|py| -> PyResult<()> {
|
||||
/// let list = py.eval("iter([1, 2, 3, 4])", None, None)?;
|
||||
/// let numbers: PyResult<Vec<usize>> = list.iter()?.map(|i| i.and_then(PyAny::extract::<usize>)).collect();
|
||||
/// let numbers: PyResult<Vec<usize>> = list
|
||||
/// .iter()?
|
||||
/// .map(|i| i.and_then(PyAny::extract::<usize>))
|
||||
/// .collect();
|
||||
/// let sum: usize = numbers?.iter().sum();
|
||||
/// assert_eq!(sum, 10);
|
||||
/// Ok(())
|
||||
/// });
|
||||
/// # Ok(())
|
||||
/// })
|
||||
/// # }
|
||||
/// ```
|
||||
#[repr(transparent)]
|
||||
|
|
|
@ -64,7 +64,6 @@ impl PyModule {
|
|||
/// ```
|
||||
///
|
||||
/// This is equivalent to the following Python expression:
|
||||
///
|
||||
/// ```python
|
||||
/// import antigravity
|
||||
/// ```
|
||||
|
@ -99,12 +98,7 @@ impl PyModule {
|
|||
///
|
||||
/// # fn main() -> PyResult<()> {
|
||||
/// Python::with_gil(|py| -> PyResult<()> {
|
||||
/// let module = PyModule::from_code(
|
||||
/// py,
|
||||
/// "print(__file__, __name__)",
|
||||
/// "my_file",
|
||||
/// "my_module"
|
||||
/// )?;
|
||||
/// let module = PyModule::from_code(py, "print(__file__, __name__)", "my_file", "my_module")?;
|
||||
/// Ok(())
|
||||
/// })?;
|
||||
/// # Ok(())}
|
||||
|
@ -254,7 +248,6 @@ impl PyModule {
|
|||
/// ```
|
||||
///
|
||||
/// Python code can see this class as such:
|
||||
///
|
||||
/// ```python
|
||||
/// from my_module import Foo
|
||||
///
|
||||
|
@ -262,7 +255,6 @@ impl PyModule {
|
|||
/// ```
|
||||
///
|
||||
/// This will result in the following output:
|
||||
///
|
||||
/// ```text
|
||||
/// Foo is <class 'builtins.Foo'>
|
||||
/// ```
|
||||
|
|
|
@ -110,13 +110,16 @@ impl PyTuple {
|
|||
/// # Example
|
||||
/// ```
|
||||
/// use pyo3::{prelude::*, types::PyTuple};
|
||||
///
|
||||
/// # fn main() -> PyResult<()> {
|
||||
/// Python::with_gil(|py| -> PyResult<()> {
|
||||
/// let ob = (1, 2, 3).to_object(py);
|
||||
/// let tuple = <PyTuple as PyTryFrom>::try_from(ob.as_ref(py)).unwrap();
|
||||
/// let obj = tuple.get_item(0);
|
||||
/// assert_eq!(obj.unwrap().extract::<i32>().unwrap(), 1);
|
||||
/// Ok(())
|
||||
/// });
|
||||
/// })
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn get_item(&self, index: usize) -> PyResult<&PyAny> {
|
||||
unsafe {
|
||||
|
|
Loading…
Reference in New Issue