Merge branch 'master' into capybara
This commit is contained in:
commit
57048bc5fc
|
@ -25,6 +25,9 @@ spin = "0.4.6"
|
|||
num-traits = "0.2"
|
||||
pyo3cls = { path = "pyo3cls", version = "^0.2.1" }
|
||||
|
||||
[dev-dependencies]
|
||||
docmatic = "^0.1.2"
|
||||
|
||||
[build-dependencies]
|
||||
regex = "0.2"
|
||||
version_check = "0.1"
|
||||
|
|
|
@ -13,12 +13,15 @@ extern crate pyo3;
|
|||
|
||||
## Define new class
|
||||
|
||||
To define python custom class, rust struct needs to be annotated with `#[py::class]` attribute.
|
||||
To define python custom class, rust struct needs to be annotated with `#[class]` attribute.
|
||||
|
||||
```rust
|
||||
use pyo3::prelude::*;
|
||||
# #![feature(proc_macro, specialization, const_fn)]
|
||||
# extern crate pyo3;
|
||||
# use pyo3::prelude::*;
|
||||
use pyo3::py::class;
|
||||
|
||||
#[py::class]
|
||||
#[class]
|
||||
struct MyClass {
|
||||
num: i32,
|
||||
debug: bool,
|
||||
|
@ -44,11 +47,11 @@ are generated only if struct contains `PyToken` attribute.
|
|||
|
||||
TODO - continue
|
||||
|
||||
## py::class macro
|
||||
## class macro
|
||||
|
||||
Python class generation is powered by [Procedural Macros](https://doc.rust-lang.org/book/first-edition/procedural-macros.html).
|
||||
To define python custom class, rust struct needs to be annotated with `#[py::class]` attribute.
|
||||
`py::class` macro accepts following parameters:
|
||||
To define python custom class, rust struct needs to be annotated with `#[class]` attribute.
|
||||
`class` macro accepts following parameters:
|
||||
|
||||
* `name=XXX` - customize class name visible to python code. By default struct name is used as
|
||||
a class name.
|
||||
|
@ -72,14 +75,29 @@ To declare constructor, you need to define class method and annotate it with `#[
|
|||
attribute. Only python `__new__` method can be specified, `__init__` is not available.
|
||||
|
||||
```rust
|
||||
#[py::methods]
|
||||
# #![feature(proc_macro, specialization, const_fn)]
|
||||
#
|
||||
# extern crate pyo3;
|
||||
# use pyo3::prelude::*;
|
||||
# use pyo3::py::class;
|
||||
#
|
||||
# #[class]
|
||||
# struct MyClass {
|
||||
# num: i32,
|
||||
# debug: bool,
|
||||
# token: PyToken,
|
||||
# }
|
||||
#
|
||||
use pyo3::py::methods;
|
||||
|
||||
#[methods]
|
||||
impl MyClass {
|
||||
|
||||
#[new]
|
||||
fn __new__(obj: &PyRawObject, ...) -> PyResult<()> {
|
||||
fn __new__(obj: &PyRawObject, num: Option<i32>) -> PyResult<()> {
|
||||
obj.init(|token| {
|
||||
MyClass {
|
||||
num: 10,
|
||||
num: num.unwrap_or(10),
|
||||
debug: false,
|
||||
token: token
|
||||
}
|
||||
|
@ -93,7 +111,7 @@ Some rules of `new` method
|
|||
* If no method marked with `#[new]` is declared, object instances can only be created
|
||||
from Rust, but not from Python.
|
||||
* The first parameter is the raw object, custom `new` method must initialize object
|
||||
with value of struct using `init` method. Type of the object may be the type object of
|
||||
with value of struct using `init` method. Type of the object may be the type object of
|
||||
a derived class declared in Python.
|
||||
* The first parameter implicitly has type `&PyRawObject`.
|
||||
* For details on `parameter-list`, see the documentation of `Method arguments` section.
|
||||
|
@ -104,42 +122,48 @@ Some rules of `new` method
|
|||
## Inheritance
|
||||
|
||||
By default `PyObject` is used as default base class. To override default base class
|
||||
`base` parameter for `py::class` needs to be used. Value is full path to base class.
|
||||
`base` parameter for `class` needs to be used. Value is full path to base class.
|
||||
`__new__` method accepts `PyRawObject` object. `obj` instance must be initialized
|
||||
with value of custom class struct. Subclass must call parent's `__new__` method.
|
||||
|
||||
```rust
|
||||
|
||||
#[py::class]
|
||||
# #![feature(proc_macro, specialization, const_fn)]
|
||||
# extern crate pyo3;
|
||||
# use pyo3::prelude::*;
|
||||
# use pyo3::py::*;
|
||||
#
|
||||
#[class]
|
||||
struct BaseClass {
|
||||
val1: usize
|
||||
val1: usize,
|
||||
token: PyToken,
|
||||
}
|
||||
|
||||
#[py::methods]
|
||||
#[methods]
|
||||
impl BaseClass {
|
||||
#[new]
|
||||
fn __new__(obj: &PyRawObject) -> PyResult<()> {
|
||||
obj.init(|t| BaseClass{val1: 10})
|
||||
obj.init(|token| BaseClass{val1: 10, token})
|
||||
}
|
||||
|
||||
pub fn method(&self) -> PyResult<() {
|
||||
|
||||
pub fn method(&self) -> PyResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[py::class(base=BaseClass)]
|
||||
#[class(base=BaseClass)]
|
||||
struct SubClass {
|
||||
val2: usize
|
||||
val2: usize,
|
||||
token: PyToken,
|
||||
}
|
||||
|
||||
#[py::methods]
|
||||
#[methods]
|
||||
impl SubClass {
|
||||
#[new]
|
||||
fn __new__(obj: &PyRawObject) -> PyResult<()> {
|
||||
obj.init(|t| SubClass{val2: 10})
|
||||
obj.init(|token| SubClass{val2: 10, token});
|
||||
BaseClass::__new__(obj)
|
||||
}
|
||||
|
||||
|
||||
fn method2(&self) -> PyResult<()> {
|
||||
self.get_base().method()
|
||||
}
|
||||
|
@ -153,11 +177,21 @@ base class.
|
|||
## Object properties
|
||||
|
||||
Descriptor methods can be defined in
|
||||
`#[py::methods]` `impl` block only and has to be annotated with `#[getter]` or `[setter]`
|
||||
`#[methods]` `impl` block only and has to be annotated with `#[getter]` or `[setter]`
|
||||
attributes. i.e.
|
||||
|
||||
```rust
|
||||
#[py::methods]
|
||||
# #![feature(proc_macro, specialization, const_fn)]
|
||||
# extern crate pyo3;
|
||||
# use pyo3::prelude::*;
|
||||
# use pyo3::py::*;
|
||||
# #[class]
|
||||
# struct MyClass {
|
||||
# num: i32,
|
||||
# token: PyToken,
|
||||
# }
|
||||
#
|
||||
#[methods]
|
||||
impl MyClass {
|
||||
|
||||
#[getter]
|
||||
|
@ -175,7 +209,17 @@ Descriptor name becomes function name with prefix removed. This is useful in cas
|
|||
rust's special keywords like `type`.
|
||||
|
||||
```rust
|
||||
#[py::methods]
|
||||
# #![feature(proc_macro, specialization, const_fn)]
|
||||
# extern crate pyo3;
|
||||
# use pyo3::prelude::*;
|
||||
# use pyo3::py::*;
|
||||
# #[class]
|
||||
# struct MyClass {
|
||||
# num: i32,
|
||||
# token: PyToken,
|
||||
# }
|
||||
#
|
||||
#[methods]
|
||||
impl MyClass {
|
||||
|
||||
#[getter]
|
||||
|
@ -185,7 +229,7 @@ impl MyClass {
|
|||
|
||||
#[setter]
|
||||
fn set_num(&mut self, value: i32) -> PyResult<()> {
|
||||
self.num = value
|
||||
self.num = value;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -197,7 +241,17 @@ Also both `#[getter]` and `#[setter]` attributes accepts one parameter.
|
|||
If parameter is specified, it is used and property name. i.e.
|
||||
|
||||
```rust
|
||||
#[py::methods]
|
||||
# #![feature(proc_macro, specialization, const_fn)]
|
||||
# extern crate pyo3;
|
||||
# use pyo3::prelude::*;
|
||||
# use pyo3::py::*;
|
||||
# #[class]
|
||||
# struct MyClass {
|
||||
# num: i32,
|
||||
# token: PyToken,
|
||||
# }
|
||||
#
|
||||
#[methods]
|
||||
impl MyClass {
|
||||
|
||||
#[getter(number)]
|
||||
|
@ -207,7 +261,7 @@ impl MyClass {
|
|||
|
||||
#[setter(number)]
|
||||
fn set_num(&mut self, value: i32) -> PyResult<()> {
|
||||
self.num = value
|
||||
self.num = value;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -218,7 +272,11 @@ In this case property `number` is defined. And it is available from python code
|
|||
For simple cases you can also define getters and setters in your Rust struct field definition, for example:
|
||||
|
||||
```rust
|
||||
#[py:class]
|
||||
# #![feature(proc_macro, specialization, const_fn)]
|
||||
# extern crate pyo3;
|
||||
# use pyo3::prelude::*;
|
||||
# use pyo3::py::*;
|
||||
#[class]
|
||||
struct MyClass {
|
||||
#[prop(get, set)]
|
||||
num: i32
|
||||
|
@ -230,12 +288,22 @@ Then it is available from Python code as `self.num`.
|
|||
## Instance methods
|
||||
|
||||
To define python compatible method, `impl` block for struct has to be annotated
|
||||
with `#[py::methods]` attribute. `pyo3` library generates python compatible
|
||||
with `#[methods]` attribute. `pyo3` library generates python compatible
|
||||
wrappers for all functions in this block with some variations, like descriptors,
|
||||
class method static methods, etc.
|
||||
|
||||
```rust
|
||||
#[py::methods]
|
||||
# #![feature(proc_macro, specialization, const_fn)]
|
||||
# extern crate pyo3;
|
||||
# use pyo3::prelude::*;
|
||||
# use pyo3::py::*;
|
||||
# #[class]
|
||||
# struct MyClass {
|
||||
# num: i32,
|
||||
# token: PyToken,
|
||||
# }
|
||||
#
|
||||
#[methods]
|
||||
impl MyClass {
|
||||
|
||||
fn method1(&self) -> PyResult<i32> {
|
||||
|
@ -243,7 +311,7 @@ impl MyClass {
|
|||
}
|
||||
|
||||
fn set_method(&mut self, value: i32) -> PyResult<()> {
|
||||
self.num = value
|
||||
self.num = value;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -256,9 +324,19 @@ The return type must be `PyResult<T>` for some `T` that implements `IntoPyObject
|
|||
get injected by method wrapper. i.e
|
||||
|
||||
```rust
|
||||
#[py::methods]
|
||||
impl MyClass {
|
||||
# #![feature(proc_macro, specialization, const_fn)]
|
||||
# extern crate pyo3;
|
||||
# use pyo3::prelude::*;
|
||||
# use pyo3::py::*;
|
||||
# #[class]
|
||||
# struct MyClass {
|
||||
# num: i32,
|
||||
# debug: bool,
|
||||
# token: PyToken,
|
||||
# }
|
||||
|
||||
#[methods]
|
||||
impl MyClass {
|
||||
fn method2(&self, py: Python) -> PyResult<i32> {
|
||||
Ok(10)
|
||||
}
|
||||
|
@ -273,9 +351,19 @@ To specify class method for custom class, method needs to be annotated
|
|||
with`#[classmethod]` attribute.
|
||||
|
||||
```rust
|
||||
#[py::methods]
|
||||
impl MyClass {
|
||||
# #![feature(proc_macro, specialization, const_fn)]
|
||||
# extern crate pyo3;
|
||||
# use pyo3::prelude::*;
|
||||
# use pyo3::py::*;
|
||||
# #[class]
|
||||
# struct MyClass {
|
||||
# num: i32,
|
||||
# debug: bool,
|
||||
# token: PyToken,
|
||||
# }
|
||||
|
||||
#[methods]
|
||||
impl MyClass {
|
||||
#[classmethod]
|
||||
fn cls_method(cls: &PyType) -> PyResult<i32> {
|
||||
Ok(10)
|
||||
|
@ -298,9 +386,19 @@ with `#[staticmethod]` attribute. The return type must be `PyResult<T>`
|
|||
for some `T` that implements `IntoPyObject`.
|
||||
|
||||
```rust
|
||||
#[py::methods]
|
||||
impl MyClass {
|
||||
# #![feature(proc_macro, specialization, const_fn)]
|
||||
# extern crate pyo3;
|
||||
# use pyo3::prelude::*;
|
||||
# use pyo3::py::*;
|
||||
# #[class]
|
||||
# struct MyClass {
|
||||
# num: i32,
|
||||
# debug: bool,
|
||||
# token: PyToken,
|
||||
# }
|
||||
|
||||
#[methods]
|
||||
impl MyClass {
|
||||
#[staticmethod]
|
||||
fn static_method(param1: i32, param2: &str) -> PyResult<i32> {
|
||||
Ok(10)
|
||||
|
@ -314,9 +412,19 @@ To specify custom `__call__` method for custom class, call method needs to be an
|
|||
with `#[call]` attribute. Arguments of the method are specified same as for instance method.
|
||||
|
||||
```rust
|
||||
#[py::methods]
|
||||
impl MyClass {
|
||||
# #![feature(proc_macro, specialization, const_fn)]
|
||||
# extern crate pyo3;
|
||||
# use pyo3::prelude::*;
|
||||
# use pyo3::py::*;
|
||||
# #[class]
|
||||
# struct MyClass {
|
||||
# num: i32,
|
||||
# debug: bool,
|
||||
# token: PyToken,
|
||||
# }
|
||||
|
||||
#[methods]
|
||||
impl MyClass {
|
||||
#[call]
|
||||
#[args(args="*")]
|
||||
fn __call__(&self, args: &PyTuple) -> PyResult<i32> {
|
||||
|
@ -350,9 +458,19 @@ Each parameter could one of following type:
|
|||
|
||||
Example:
|
||||
```rust
|
||||
#[py::methods]
|
||||
# #![feature(proc_macro, specialization, const_fn)]
|
||||
# extern crate pyo3;
|
||||
# use pyo3::prelude::*;
|
||||
# use pyo3::py::*;
|
||||
# #[class]
|
||||
# struct MyClass {
|
||||
# num: i32,
|
||||
# debug: bool,
|
||||
# token: PyToken,
|
||||
# }
|
||||
#
|
||||
#[methods]
|
||||
impl MyClass {
|
||||
|
||||
#[args(arg1=true, args="*", arg2=10, kwargs="**")]
|
||||
fn method(&self, arg1: bool, args: &PyTuple, arg2: i32, kwargs: Option<&PyDict>) -> PyResult<i32> {
|
||||
Ok(1)
|
||||
|
@ -367,7 +485,7 @@ Python object model defines several protocols for different object behavior,
|
|||
like sequence, mapping or number protocols. pyo3 library defines separate trait for each
|
||||
of them. To provide specific python object behavior you need to implement specific trait
|
||||
for your struct. Important note, each protocol implementation block has to be annotated
|
||||
with `#[py::proto]` attribute.
|
||||
with `#[proto]` attribute.
|
||||
|
||||
### Basic object customization
|
||||
|
||||
|
@ -444,17 +562,19 @@ These correspond to the slots `tp_traverse` and `tp_clear` in the Python C API.
|
|||
as every cycle must contain at least one mutable reference.
|
||||
Example:
|
||||
```rust
|
||||
#![feature(proc_macro, specialization)]
|
||||
#![feature(proc_macro, specialization, const_fn)]
|
||||
extern crate pyo3;
|
||||
|
||||
use pyo3::{py, PyObject, PyGCProtocol, PyVisit, PyTraverseError};
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::py::*;
|
||||
|
||||
#[py::class]
|
||||
#[class]
|
||||
struct ClassWithGCSupport {
|
||||
obj: Option<PyObject>,
|
||||
token: PyToken,
|
||||
}
|
||||
|
||||
#[py::proto]
|
||||
#[proto]
|
||||
impl PyGCProtocol for ClassWithGCSupport {
|
||||
fn __traverse__(&self, visit: PyVisit) -> Result<(), PyTraverseError> {
|
||||
if let Some(ref obj) = self.obj {
|
||||
|
@ -472,10 +592,10 @@ impl PyGCProtocol for ClassWithGCSupport {
|
|||
}
|
||||
```
|
||||
|
||||
Special protocol trait implementation has to be annotated with `#[py::proto]` attribute.
|
||||
Special protocol trait implementation has to be annotated with `#[proto]` attribute.
|
||||
|
||||
It is also possible to enable gc for custom class using `gc` parameter for `py::class` annotation.
|
||||
i.e. `#[py::class(gc)]`. In that case instances of custom class participate in python garbage
|
||||
It is also possible to enable gc for custom class using `gc` parameter for `class` annotation.
|
||||
i.e. `#[class(gc)]`. In that case instances of custom class participate in python garbage
|
||||
collector, and it is possible to track them with `gc` module methods.
|
||||
|
||||
### Iterator Types
|
||||
|
@ -490,18 +610,20 @@ It includes two methods `__iter__` and `__next__`:
|
|||
|
||||
Example:
|
||||
```rust
|
||||
#![feature(proc_macro, specialization)]
|
||||
#![feature(proc_macro, specialization, const_fn)]
|
||||
extern crate pyo3;
|
||||
|
||||
use pyo3::{py, PyObject, PyResult, PyIterProtocol};
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::py::*;
|
||||
|
||||
#[py::class]
|
||||
#[class]
|
||||
struct MyIterator {
|
||||
iter: Box<Iterator<Item=PyObject> + Send>
|
||||
iter: Box<Iterator<Item=PyObject> + Send>,
|
||||
token: PyToken,
|
||||
}
|
||||
|
||||
#[py::proto]
|
||||
impl PyIterProtocol {
|
||||
#[proto]
|
||||
impl PyIterProtocol for MyIterator {
|
||||
|
||||
fn __iter__(&mut self) -> PyResult<PyObject> {
|
||||
Ok(self.into())
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
## `ToPyObject` and `IntoPyObject` trait
|
||||
|
||||
[`ToPyObject`][ToPyObject] trait is a conversion trait that allows various objects to be converted into [`PyObject`][PyObject]. [`IntoPyObject`][IntoPyObject] serves the same purpose except it consumes `self`.
|
||||
[`ToPyObject`] trait is a conversion trait that allows various objects to be converted into [`PyObject`][PyObject]. [`IntoPyObject`][IntoPyObject] serves the same purpose except it consumes `self`.
|
||||
|
||||
## `IntoPyTuple` trait
|
||||
|
||||
|
@ -14,7 +14,6 @@ For example, [`IntoPyTuple`][IntoPyTuple] trait is implemented for `()` so that
|
|||
|
||||
```rust
|
||||
extern crate pyo3;
|
||||
|
||||
use pyo3::{Python, IntoPyTuple};
|
||||
|
||||
fn main() {
|
||||
|
@ -39,66 +38,89 @@ Both methods accept `args` and `kwargs` arguments. `args` argument is generate o
|
|||
[`IntoPyTuple`][IntoPyTuple] trait. So args could be `PyTuple` instance or
|
||||
rust tuple with up to 10 elements. Or `NoArgs` object which represents empty tuple object.
|
||||
|
||||
```rust,ignore
|
||||
```rust
|
||||
extern crate pyo3;
|
||||
|
||||
use pyo3::prelude::*;
|
||||
|
||||
# struct SomeObject;
|
||||
# impl SomeObject {
|
||||
# fn new(py: Python) -> PyObject {
|
||||
# pyo3::PyDict::new(py).to_object(py)
|
||||
# }
|
||||
# }
|
||||
#
|
||||
fn main() {
|
||||
# let arg1 = "arg1";
|
||||
# let arg2 = "arg2";
|
||||
# let arg3 = "arg3";
|
||||
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
||||
let obj = SomeObject::new();
|
||||
|
||||
|
||||
let obj = SomeObject::new(py);
|
||||
|
||||
// call object without empty arguments
|
||||
obj.call(NoArgs, NoArg);
|
||||
|
||||
obj.call0(py);
|
||||
|
||||
// call object with PyTuple
|
||||
let args = PyTuple::new(py, &[arg1, arg2, arg3]);
|
||||
obj.call(args, NoArg);
|
||||
obj.call1(py, args);
|
||||
|
||||
// pass arguments as rust tuple
|
||||
let args = (arg1, arg2, arg3);
|
||||
obj.call(args, NoArg);
|
||||
obj.call1(py, args);
|
||||
}
|
||||
```
|
||||
|
||||
`kwargs` argument is generate over
|
||||
[`IntoPyDictPointer`][IntoPyDictPointer] trait. `HashMap` or `BTreeMap` could be used as
|
||||
keyword arguments. rust tuple with up to 10 elements where each element is tuple with size 2
|
||||
could be used as kwargs as well. Or `NoArgs` object can be used to indicate that
|
||||
could be used as kwargs as well. Or `NoArgs` object can be used to indicate that
|
||||
no keywords arguments are provided.
|
||||
|
||||
```rust,ignore
|
||||
```rust
|
||||
extern crate pyo3;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use pyo3::prelude::*;
|
||||
|
||||
# struct SomeObject;
|
||||
# impl SomeObject {
|
||||
# fn new(py: Python) -> PyObject {
|
||||
# pyo3::PyDict::new(py).to_object(py)
|
||||
# }
|
||||
# }
|
||||
fn main() {
|
||||
# let key1 = "key1";
|
||||
# let val1 = 1;
|
||||
# let key2 = "key2";
|
||||
# let val2 = 2;
|
||||
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
||||
let obj = SomeObject::new();
|
||||
|
||||
|
||||
let obj = SomeObject::new(py);
|
||||
|
||||
// call object with PyDict
|
||||
let kwargs = PyDict::new(py);
|
||||
kwargs.set_item(key, value);
|
||||
obj.call(NoArg, kwargs);
|
||||
kwargs.set_item(key1, val1);
|
||||
obj.call(py, NoArgs, kwargs);
|
||||
|
||||
// pass arguments as rust tuple
|
||||
let kwargs = ((key1, val1), (key2, val2), (key3, val3));
|
||||
obj.call(args, kwargs);
|
||||
let kwargs = ((key1, val1), (key2, val2));
|
||||
obj.call(py, NoArgs, kwargs);
|
||||
|
||||
// pass arguments as HashMap
|
||||
let mut kwargs = HashMap::<i32, i32>::new();
|
||||
kwargs.insert(1, 1);
|
||||
obj.call(args, kwargs);
|
||||
let mut kwargs = HashMap::<&str, i32>::new();
|
||||
kwargs.insert(key1, 1);
|
||||
obj.call(py, NoArgs, kwargs);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
TODO
|
||||
|
||||
[ToPyObject]: https://pyo3.github.io/pyo3/pyo3/trait.ToPyObject.html
|
||||
[`ToPyObject`]: https://pyo3.github.io/pyo3/pyo3/trait.ToPyObject.html
|
||||
[IntoPyObject]: https://pyo3.github.io/pyo3/pyo3/trait.IntoPyObject.html
|
||||
[PyObject]: https://pyo3.github.io/pyo3/pyo3/struct.PyObject.html
|
||||
[IntoPyTuple]: https://pyo3.github.io/pyo3/pyo3/trait.IntoPyTuple.html
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
You can use the `py_exception!` macro to define a new exception type:
|
||||
|
||||
```rust
|
||||
#[macro_use] extern crate pyo3;
|
||||
|
||||
py_exception!(module, MyError);
|
||||
```
|
||||
|
||||
|
@ -25,7 +27,7 @@ fn main() {
|
|||
let py = gil.python();
|
||||
let ctx = PyDict::new(py);
|
||||
|
||||
ctx.set_item(py, "CustomError", py.get_type::<CustomError>()).unwrap();
|
||||
ctx.set_item("CustomError", py.get_type::<CustomError>()).unwrap();
|
||||
|
||||
py.run("assert str(CustomError) == \"<class 'mymodule.CustomError'>\"", None, Some(&ctx)).unwrap();
|
||||
py.run("assert CustomError('oops').args == ('oops',)", None, Some(&ctx)).unwrap();
|
||||
|
@ -52,7 +54,7 @@ fn main() {
|
|||
|
||||
If you already have a Python exception instance, you can simply call [`PyErr::from_instance()`](https://pyo3.github.io/pyo3/pyo3/struct.PyErr.html#method.from_instance).
|
||||
|
||||
```rust
|
||||
```rust,ignore
|
||||
PyErr::from_instance(py, err).restore(py);
|
||||
```
|
||||
|
||||
|
@ -62,7 +64,9 @@ has corresponding rust type, exceptions defined by `py_exception!` and `import_e
|
|||
have rust type as well.
|
||||
|
||||
```rust
|
||||
|
||||
# extern crate pyo3;
|
||||
# use pyo3::prelude::*;
|
||||
# fn check_for_error() -> bool {false}
|
||||
fn my_func(arg: PyObject) -> PyResult<()> {
|
||||
if check_for_error() {
|
||||
Err(exc::ValueError::new("argument is wrong"))
|
||||
|
@ -70,7 +74,6 @@ fn my_func(arg: PyObject) -> PyResult<()> {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Check exception type
|
||||
|
@ -86,10 +89,10 @@ use pyo3::{Python, PyBool, PyList};
|
|||
fn main() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
assert!(py.is_instance::<PyBool>(py.True().as_ref()).unwrap());
|
||||
assert!(py.is_instance::<PyBool, _>(PyBool::new(py, true)).unwrap());
|
||||
let list = PyList::new(py, &[1, 2, 3, 4]);
|
||||
assert!(!py.is_instance::<PyBool>(list.as_ref()).unwrap());
|
||||
assert!(py.is_instance::<PyList>(list.as_ref()).unwrap());
|
||||
assert!(!py.is_instance::<PyBool, _>(list.as_ref()).unwrap());
|
||||
assert!(py.is_instance::<PyList, _>(list.as_ref()).unwrap());
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -98,7 +101,14 @@ fn main() {
|
|||
To check the type of an exception, you can simply do:
|
||||
|
||||
```rust
|
||||
let ret = py.is_instance::<exc::TypeError>(&err.instance(py)).expect("Error calling is_instance");
|
||||
# extern crate pyo3;
|
||||
# use pyo3::prelude::*;
|
||||
# fn main() {
|
||||
# let gil = Python::acquire_gil();
|
||||
# let py = gil.python();
|
||||
# let err = exc::TypeError::new(NoArgs);
|
||||
err.is_instance::<exc::TypeError>(py);
|
||||
# }
|
||||
```
|
||||
|
||||
## Handle Rust Error
|
||||
|
@ -120,10 +130,12 @@ more complex arguments are required [`PyErrArgument`](https://pyo3.github.io/pyo
|
|||
trait can be implemented. In that case actual exception arguments creation get delayed
|
||||
until `Python` object is available.
|
||||
|
||||
```rust
|
||||
use std;
|
||||
```rust,ignore
|
||||
#![feature(proc_macro, specialization)]
|
||||
extern crate pyo3;
|
||||
|
||||
use std::net::TcpListener;
|
||||
use pyo3::{PyErr, PyResult, ToPyErr, exc};
|
||||
use pyo3::{PyErr, PyResult, exc};
|
||||
|
||||
impl std::convert::From<std::io::Error> for PyErr {
|
||||
fn from(err: std::io::Error) -> PyErr {
|
||||
|
@ -133,7 +145,6 @@ impl std::convert::From<std::io::Error> for PyErr {
|
|||
|
||||
fn connect(s: String) -> PyResult<bool> {
|
||||
TcpListener::bind("127.0.0.1:80")?;
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
```
|
||||
|
@ -144,7 +155,8 @@ The code snippet above will raise `OSError` in Python if `TcpListener::bind()` r
|
|||
types so `try!` macro or `?` operator can be used.
|
||||
|
||||
```rust
|
||||
use pyo3::*;
|
||||
# extern crate pyo3;
|
||||
use pyo3::prelude::*;
|
||||
|
||||
fn parse_int(s: String) -> PyResult<usize> {
|
||||
Ok(s.parse::<usize>()?)
|
||||
|
@ -156,21 +168,26 @@ The code snippet above will raise `ValueError` in Python if `String::parse()` re
|
|||
|
||||
## Using exceptions defined in python code
|
||||
|
||||
It is possible to use exception defined in python code as native rust types.
|
||||
It is possible to use exception defined in python code as native rust types.
|
||||
`import_exception!` macro allows to import specific exception class and defined zst type
|
||||
for that exception.
|
||||
|
||||
```rust
|
||||
use pyo3::{PyErr, PyResult, exc};
|
||||
#[macro_use] extern crate pyo3;
|
||||
use pyo3::prelude::*;
|
||||
|
||||
import_exception!(asyncio, CancelledError)
|
||||
import_exception!(io, UnsupportedOperation);
|
||||
|
||||
fn cancel(fut: PyFuture) -> PyResult<()> {
|
||||
if fut.cancelled() {
|
||||
Err(CancelledError.into())
|
||||
}
|
||||
fn tell(file: PyObject) -> PyResult<u64> {
|
||||
use pyo3::exc::*;
|
||||
|
||||
Ok(())
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
||||
match file.call_method0(py, "tell") {
|
||||
Err(_) => Err(UnsupportedOperation::new("not supported: tell")),
|
||||
Ok(x) => x.extract::<u64>(py),
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
|
|
@ -40,7 +40,7 @@ fn init_mod(py: Python, m: &PyModule) -> PyResult<()> {
|
|||
// Note that the `#[pyfn()]` annotation automatically converts the arguments from
|
||||
// Python objects to Rust values; and the Rust return value back into a Python object.
|
||||
#[pyfn(m, "sum_as_string")]
|
||||
fn sum_as_string_py(_: Python, a:i64, b:i64) -> PyResult<String> {
|
||||
fn sum_as_string_py(_py: Python, a:i64, b:i64) -> PyResult<String> {
|
||||
let out = sum_as_string(a, b);
|
||||
Ok(out)
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ Example program displaying the value of `sys.version`:
|
|||
```rust
|
||||
extern crate pyo3;
|
||||
|
||||
use pyo3::{Python, PyDict, PyResult};
|
||||
use pyo3::prelude::*;
|
||||
|
||||
fn main() {
|
||||
let gil = Python::acquire_gil();
|
||||
|
@ -76,7 +76,7 @@ features = ["extension-module"]
|
|||
#![feature(proc_macro, specialization)]
|
||||
|
||||
extern crate pyo3;
|
||||
use pyo3::{py, PyResult, Python, PyModule};
|
||||
use pyo3::prelude::*;
|
||||
|
||||
use pyo3::py::modinit as pymodinit;
|
||||
|
||||
|
@ -90,7 +90,7 @@ fn init_mod(py: Python, m: &PyModule) -> PyResult<()> {
|
|||
// pyo3 aware function. All of our python interface could be declared in a separate module.
|
||||
// Note that the `#[pyfn()]` annotation automatically converts the arguments from
|
||||
// Python objects to Rust values; and the Rust return value back into a Python object.
|
||||
fn sum_as_string_py(_: Python, a:i64, b:i64) -> PyResult<String> {
|
||||
fn sum_as_string_py(_py: Python, a:i64, b:i64) -> PyResult<String> {
|
||||
let out = sum_as_string(a, b);
|
||||
Ok(out)
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ use ffi;
|
|||
static NO_PY_METHODS: &'static [PyMethodDefType] = &[];
|
||||
|
||||
/// `PyMethodDefType` represents different types of python callable objects.
|
||||
/// It is used by `#[py::methods]` and `#[py::proto]` annotations.
|
||||
/// It is used by `#[pymethods]` and `#[pyproto]` annotations.
|
||||
#[derive(Debug)]
|
||||
pub enum PyMethodDefType {
|
||||
/// Represents class `__new__` method
|
||||
|
|
|
@ -116,12 +116,15 @@ mod test {
|
|||
let py = gil.python();
|
||||
|
||||
let err: PyErr = gaierror.into();
|
||||
let socket = py.import("socket").map_err(|e| e.print(py)).expect("could not import socket");
|
||||
|
||||
let d = PyDict::new(py);
|
||||
d.set_item("socket", py.import("socket").map_err(|e| e.print(py)).unwrap()).unwrap();
|
||||
d.set_item("exc", err).unwrap();
|
||||
d.set_item("socket", socket).map_err(|e| e.print(py)).expect("could not setitem");
|
||||
d.set_item("exc", err).map_err(|e| e.print(py)).expect("could not setitem");
|
||||
|
||||
py.run("assert isinstance(exc, socket.gaierror)", None, Some(d)).unwrap();
|
||||
py.run("assert isinstance(exc, socket.gaierror)", None, Some(d))
|
||||
.map_err(|e| e.print(py))
|
||||
.expect("assertion failed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -130,11 +133,14 @@ mod test {
|
|||
let py = gil.python();
|
||||
|
||||
let err: PyErr = MessageError.into();
|
||||
let email = py.import("email").map_err(|e| e.print(py)).expect("could not import email");
|
||||
|
||||
let d = PyDict::new(py);
|
||||
d.set_item("email", py.import("email").map_err(|e| e.print(py)).unwrap()).unwrap();
|
||||
d.set_item("exc", err).unwrap();
|
||||
d.set_item("email", email).map_err(|e| e.print(py)).expect("could not setitem");
|
||||
d.set_item("exc", err).map_err(|e| e.print(py)).expect("could not setitem");
|
||||
|
||||
py.run("assert isinstance(exc, email.errors.MessageError)", None, Some(d)).unwrap();
|
||||
py.run("assert isinstance(exc, email.errors.MessageError)", None, Some(d))
|
||||
.map_err(|e| e.print(py))
|
||||
.expect("assertion failed");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,12 +103,15 @@ impl<'a, T: ?Sized> PyTypeInfo for &'a T where T: PyTypeInfo {
|
|||
///
|
||||
/// Example of custom class implementation with `__new__` method:
|
||||
/// ```rust,ignore
|
||||
/// #[py::class]
|
||||
/// use pyo3::py::class as pyclass;
|
||||
/// use pyo3::py::methods as pymethods;
|
||||
///
|
||||
/// #[pyclass]
|
||||
/// struct MyClass {
|
||||
/// token: PyToken
|
||||
/// }
|
||||
///
|
||||
/// #[py::methods]
|
||||
/// #[pymethods]
|
||||
/// impl MyClass {
|
||||
/// #[new]
|
||||
/// fn __new__(obj: &PyRawObject) -> PyResult<()> {
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
extern crate docmatic;
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::default::Default;
|
||||
|
||||
fn assert_file<P: AsRef<Path>>(path: P) {
|
||||
let mut doc = docmatic::Assert::default();
|
||||
if cfg!(windows) {
|
||||
doc.library_path(option_env!("PYTHON").map(|py| PathBuf::from(py).join("libs")).unwrap());
|
||||
}
|
||||
doc.test_file(path.as_ref())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_guide() {
|
||||
let guide_path = PathBuf::from("guide").join("src");
|
||||
for entry in guide_path.read_dir().unwrap() {
|
||||
assert_file(entry.unwrap().path())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_readme() {
|
||||
assert_file("README.md")
|
||||
}
|
Loading…
Reference in New Issue