pyo3/tests/test_frompyobject.rs

407 lines
11 KiB
Rust
Raw Normal View History

2020-08-24 22:00:12 +00:00
use pyo3::exceptions::PyValueError;
use pyo3::prelude::*;
use pyo3::types::{PyDict, PyString, PyTuple};
2020-08-25 19:33:36 +00:00
use pyo3::PyMappingProtocol;
2020-08-24 22:00:12 +00:00
#[macro_use]
mod common;
#[derive(Debug, FromPyObject)]
pub struct A<'a> {
#[pyo3(attribute)]
2020-08-24 22:00:12 +00:00
s: String,
#[pyo3(item)]
2020-08-24 22:00:12 +00:00
t: &'a PyString,
#[pyo3(attribute("foo"))]
2020-08-24 22:00:12 +00:00
p: &'a PyAny,
}
#[pyclass]
pub struct PyA {
#[pyo3(get)]
s: String,
#[pyo3(get)]
foo: Option<String>,
}
#[pyproto]
impl PyMappingProtocol for PyA {
fn __getitem__(&self, key: String) -> pyo3::PyResult<String> {
if key == "t" {
Ok("bar".into())
} else {
2020-08-25 19:33:36 +00:00
Err(PyValueError::new_err("Failed"))
2020-08-24 22:00:12 +00:00
}
}
}
#[test]
fn test_named_fields_struct() {
let gil = Python::acquire_gil();
let py = gil.python();
let pya = PyA {
s: "foo".into(),
foo: None,
};
let py_c = Py::new(py, pya).unwrap();
let a: A = FromPyObject::extract(py_c.as_ref(py)).expect("Failed to extract A from PyA");
assert_eq!(a.s, "foo");
assert_eq!(a.t.to_string_lossy(), "bar");
assert!(a.p.is_none());
}
#[derive(Debug, FromPyObject)]
#[pyo3(transparent)]
2020-08-24 22:00:12 +00:00
pub struct B {
test: String,
}
#[test]
fn test_transparent_named_field_struct() {
let gil = Python::acquire_gil();
let py = gil.python();
let test = "test".into_py(py);
let b: B = FromPyObject::extract(test.as_ref(py)).expect("Failed to extract B from String");
assert_eq!(b.test, "test");
let test: PyObject = 1.into_py(py);
let b = B::extract(test.as_ref(py));
assert!(b.is_err())
}
#[derive(Debug, FromPyObject)]
#[pyo3(transparent)]
2020-08-24 22:00:12 +00:00
pub struct D<T> {
test: T,
}
#[test]
fn test_generic_transparent_named_field_struct() {
let gil = Python::acquire_gil();
let py = gil.python();
let test = "test".into_py(py);
let d: D<String> =
D::extract(test.as_ref(py)).expect("Failed to extract D<String> from String");
assert_eq!(d.test, "test");
let test = 1usize.into_py(py);
let d: D<usize> = D::extract(test.as_ref(py)).expect("Failed to extract D<usize> from String");
assert_eq!(d.test, 1);
}
#[derive(Debug, FromPyObject)]
pub struct E<T, T2> {
test: T,
test2: T2,
}
#[pyclass]
2021-05-31 22:23:54 +00:00
#[derive(Clone)]
2020-08-24 22:00:12 +00:00
pub struct PyE {
#[pyo3(get)]
test: String,
#[pyo3(get)]
test2: usize,
}
#[test]
fn test_generic_named_fields_struct() {
let gil = Python::acquire_gil();
let py = gil.python();
let pye = PyE {
test: "test".into(),
test2: 2,
}
.into_py(py);
let e: E<String, usize> =
E::extract(pye.as_ref(py)).expect("Failed to extract E<String, usize> from PyE");
assert_eq!(e.test, "test");
assert_eq!(e.test2, 2);
let e = E::<usize, usize>::extract(pye.as_ref(py));
assert!(e.is_err());
}
#[derive(Debug, FromPyObject)]
pub struct C {
#[pyo3(attribute("test"))]
2020-08-24 22:00:12 +00:00
test: String,
}
#[test]
fn test_named_field_with_ext_fn() {
let gil = Python::acquire_gil();
let py = gil.python();
let pyc = PyE {
test: "foo".into(),
test2: 0,
}
.into_py(py);
let c = C::extract(pyc.as_ref(py)).expect("Failed to extract C from PyE");
assert_eq!(c.test, "foo");
}
2021-05-31 22:23:54 +00:00
#[derive(Debug, FromPyObject)]
2020-08-24 22:00:12 +00:00
pub struct Tuple(String, usize);
#[test]
fn test_tuple_struct() {
let gil = Python::acquire_gil();
let py = gil.python();
let tup = PyTuple::new(py, &[1.into_py(py), "test".into_py(py)]);
let tup = Tuple::extract(tup.as_ref());
assert!(tup.is_err());
let tup = PyTuple::new(py, &["test".into_py(py), 1.into_py(py)]);
let tup = Tuple::extract(tup.as_ref()).expect("Failed to extract Tuple from PyTuple");
assert_eq!(tup.0, "test");
assert_eq!(tup.1, 1);
}
#[derive(FromPyObject)]
pub struct TransparentTuple(String);
#[test]
fn test_transparent_tuple_struct() {
let gil = Python::acquire_gil();
let py = gil.python();
let tup: PyObject = 1.into_py(py);
let tup = TransparentTuple::extract(tup.as_ref(py));
2020-08-24 22:00:12 +00:00
assert!(tup.is_err());
let test = "test".into_py(py);
let tup = TransparentTuple::extract(test.as_ref(py))
2020-08-24 22:00:12 +00:00
.expect("Failed to extract TransparentTuple from PyTuple");
assert_eq!(tup.0, "test");
}
2021-05-31 22:23:54 +00:00
#[pyclass]
struct PyBaz {
#[pyo3(get)]
tup: (String, String),
#[pyo3(get)]
e: PyE,
}
#[derive(Debug, FromPyObject)]
struct Baz<U, T> {
e: E<U, T>,
tup: Tuple,
}
#[test]
fn test_struct_nested_type_errors() {
let gil = Python::acquire_gil();
let py = gil.python();
let pybaz = PyBaz {
tup: ("test".into(), "test".into()),
e: PyE {
test: "foo".into(),
test2: 0,
},
}
.into_py(py);
let test: PyResult<Baz<String, usize>> = FromPyObject::extract(pybaz.as_ref(py));
assert!(test.is_err());
2021-05-31 22:23:54 +00:00
assert_eq!(
"TypeError: failed to extract field Baz.tup\n\nCaused by:\n TypeError: failed to extract field Tuple.1\n\nCaused by:\n TypeError: 'str' object cannot be interpreted as an integer\n\n",
test.unwrap_err().to_string()
2021-05-31 22:23:54 +00:00
);
}
#[test]
fn test_struct_nested_type_errors_with_generics() {
let gil = Python::acquire_gil();
let py = gil.python();
let pybaz = PyBaz {
tup: ("test".into(), "test".into()),
e: PyE {
test: "foo".into(),
test2: 0,
},
}
.into_py(py);
let test: PyResult<Baz<usize, String>> = FromPyObject::extract(pybaz.as_ref(py));
assert!(test.is_err());
2021-05-31 22:23:54 +00:00
assert_eq!(
"TypeError: failed to extract field Baz.e\n\nCaused by:\n TypeError: failed to extract field E.test\n\nCaused by:\n TypeError: \'str\' object cannot be interpreted as an integer\n\n",
test.unwrap_err().to_string()
);
}
#[derive(Debug, FromPyObject)]
#[pyo3(transparent)]
pub struct TransparentStruct {
a: String,
}
#[test]
fn test_transparent_struct_error_message() {
let gil = Python::acquire_gil();
let py = gil.python();
let tup: PyObject = 1.into_py(py);
let tup = TransparentStruct::extract(tup.as_ref(py));
assert!(tup.is_err());
assert_eq!(
"TypeError: failed to extract field TransparentStruct.a\n\nCaused by:\n TypeError: \'int\' object cannot be converted to \'PyString\'\n",
tup.unwrap_err().to_string()
);
}
2020-08-24 22:00:12 +00:00
#[derive(Debug, FromPyObject)]
pub enum Foo<'a> {
TupleVar(usize, String),
StructVar {
test: &'a PyString,
},
#[pyo3(transparent)]
2020-08-24 22:00:12 +00:00
TransparentTuple(usize),
#[pyo3(transparent)]
2020-08-24 22:00:12 +00:00
TransparentStructVar {
a: Option<String>,
},
StructVarGetAttrArg {
#[pyo3(attribute("bla"))]
2020-08-24 22:00:12 +00:00
a: bool,
},
StructWithGetItem {
#[pyo3(item)]
2020-08-24 22:00:12 +00:00
a: String,
},
StructWithGetItemArg {
#[pyo3(item("foo"))]
2020-08-24 22:00:12 +00:00
a: String,
},
#[pyo3(transparent)]
2020-08-24 22:00:12 +00:00
CatchAll(&'a PyAny),
}
#[pyclass]
pub struct PyBool {
#[pyo3(get)]
bla: bool,
}
#[test]
fn test_enum() {
let gil = Python::acquire_gil();
let py = gil.python();
let tup = PyTuple::new(py, &[1.into_py(py), "test".into_py(py)]);
let f = Foo::extract(tup.as_ref()).expect("Failed to extract Foo from tuple");
match f {
Foo::TupleVar(test, test2) => {
assert_eq!(test, 1);
assert_eq!(test2, "test");
}
_ => panic!("Expected extracting Foo::TupleVar, got {:?}", f),
}
let pye = PyE {
test: "foo".into(),
test2: 0,
}
.into_py(py);
let f = Foo::extract(pye.as_ref(py)).expect("Failed to extract Foo from PyE");
match f {
Foo::StructVar { test } => assert_eq!(test.to_string_lossy(), "foo"),
_ => panic!("Expected extracting Foo::StructVar, got {:?}", f),
}
let int: PyObject = 1.into_py(py);
let f = Foo::extract(int.as_ref(py)).expect("Failed to extract Foo from int");
match f {
Foo::TransparentTuple(test) => assert_eq!(test, 1),
_ => panic!("Expected extracting Foo::TransparentTuple, got {:?}", f),
}
let none = py.None();
let f = Foo::extract(none.as_ref(py)).expect("Failed to extract Foo from int");
match f {
Foo::TransparentStructVar { a } => assert!(a.is_none()),
_ => panic!("Expected extracting Foo::TransparentStructVar, got {:?}", f),
}
let pybool = PyBool { bla: true }.into_py(py);
let f = Foo::extract(pybool.as_ref(py)).expect("Failed to extract Foo from PyBool");
match f {
Foo::StructVarGetAttrArg { a } => assert!(a),
_ => panic!("Expected extracting Foo::StructVarGetAttrArg, got {:?}", f),
}
let dict = PyDict::new(py);
dict.set_item("a", "test").expect("Failed to set item");
let f = Foo::extract(dict.as_ref()).expect("Failed to extract Foo from dict");
match f {
Foo::StructWithGetItem { a } => assert_eq!(a, "test"),
_ => panic!("Expected extracting Foo::StructWithGetItem, got {:?}", f),
}
let dict = PyDict::new(py);
dict.set_item("foo", "test").expect("Failed to set item");
let f = Foo::extract(dict.as_ref()).expect("Failed to extract Foo from dict");
match f {
Foo::StructWithGetItemArg { a } => assert_eq!(a, "test"),
_ => panic!("Expected extracting Foo::StructWithGetItemArg, got {:?}", f),
}
let dict = PyDict::new(py);
let f = Foo::extract(dict.as_ref()).expect("Failed to extract Foo from dict");
match f {
Foo::CatchAll(any) => {
let d = <&PyDict>::extract(any).expect("Expected pydict");
assert!(d.is_empty());
}
_ => panic!("Expected extracting Foo::CatchAll, got {:?}", f),
}
}
2020-08-25 19:33:36 +00:00
#[derive(Debug, FromPyObject)]
2020-08-24 22:00:12 +00:00
pub enum Bar {
#[pyo3(annotation = "str")]
2020-08-24 22:00:12 +00:00
A(String),
#[pyo3(annotation = "uint")]
2020-08-24 22:00:12 +00:00
B(usize),
#[pyo3(annotation = "int", transparent)]
2020-08-24 22:00:12 +00:00
C(isize),
}
#[test]
fn test_err_rename() {
let gil = Python::acquire_gil();
let py = gil.python();
let dict = PyDict::new(py);
let f = Bar::extract(dict.as_ref());
assert!(f.is_err());
2020-08-25 19:33:36 +00:00
assert_eq!(
f.unwrap_err().to_string(),
"TypeError: Failed to extract type Bar\n\nCaused by:\n TypeError: \'dict\' object cannot be converted to \'Union[str, uint, int]\'\n\nTypeError: failed to extract inner field of Bar :: A\n\nCaused by:\n TypeError: \'dict\' object cannot be converted to \'PyString\'\n\nTypeError: failed to extract inner field of Bar :: B\n\nCaused by:\n TypeError: \'dict\' object cannot be interpreted as an integer\n\nTypeError: failed to extract inner field of Bar :: C\n\nCaused by:\n TypeError: \'dict\' object cannot be interpreted as an integer\n\n"
2020-08-25 19:33:36 +00:00
);
2020-08-24 22:00:12 +00:00
}
#[derive(Debug, FromPyObject)]
pub struct Zap {
#[pyo3(item)]
name: String,
#[pyo3(from_py_with = "PyAny::len", item("my_object"))]
some_object_length: usize,
}
#[test]
fn test_from_py_with() {
Python::with_gil(|py| {
let py_zap = py
.eval(
r#"{"name": "whatever", "my_object": [1, 2, 3]}"#,
None,
None,
)
.expect("failed to create dict");
let zap = Zap::extract(py_zap).unwrap();
assert_eq!(zap.name, "whatever");
assert_eq!(zap.some_object_length, 3usize);
});
}