pyo3/tests/test_various.rs

207 lines
5 KiB
Rust
Raw Normal View History

2021-12-03 00:03:32 +00:00
#![cfg(feature = "macros")]
use pyo3::prelude::*;
use pyo3::py_run;
use pyo3::types::{PyDict, PyTuple};
use std::fmt;
2023-09-24 12:34:53 +00:00
#[path = "../src/tests/common.rs"]
mod common;
#[pyclass]
struct MutRefArg {
n: i32,
}
#[pymethods]
impl MutRefArg {
2021-02-11 21:37:38 +00:00
fn get(&self) -> i32 {
self.n
}
2022-03-23 07:07:28 +00:00
fn set_other(&self, mut other: PyRefMut<'_, MutRefArg>) {
other.n = 100;
}
}
#[test]
fn mut_ref_arg() {
2022-07-19 17:34:23 +00:00
Python::with_gil(|py| {
let inst1 = Py::new(py, MutRefArg { n: 0 }).unwrap();
let inst2 = Py::new(py, MutRefArg { n: 0 }).unwrap();
2022-07-19 17:34:23 +00:00
py_run!(py, inst1 inst2, "inst1.set_other(inst2)");
let inst2 = inst2.bind(py).borrow();
2022-07-19 17:34:23 +00:00
assert_eq!(inst2.n, 100);
});
}
2018-09-08 22:19:55 +00:00
#[pyclass]
struct PyUsize {
#[pyo3(get)]
2018-09-08 22:19:55 +00:00
pub value: usize,
}
#[pyfunction]
2021-02-11 21:37:38 +00:00
fn get_zero() -> PyUsize {
PyUsize { value: 0 }
2018-09-08 22:19:55 +00:00
}
#[test]
/// Checks that we can use return a custom class in arbitrary function and use those functions
/// both in rust and python
fn return_custom_class() {
2022-07-19 17:34:23 +00:00
Python::with_gil(|py| {
// Using from rust
assert_eq!(get_zero().value, 0);
2018-09-08 22:19:55 +00:00
2022-07-19 17:34:23 +00:00
// Using from python
let get_zero = wrap_pyfunction_bound!(get_zero)(py).unwrap();
2022-07-19 17:34:23 +00:00
py_assert!(py, get_zero, "get_zero().value == 0");
});
2018-09-08 22:19:55 +00:00
}
#[test]
fn intopytuple_primitive() {
2022-07-19 17:34:23 +00:00
Python::with_gil(|py| {
let tup = (1, 2, "foo");
py_assert!(py, tup, "tup == (1, 2, 'foo')");
py_assert!(py, tup, "tup[0] == 1");
py_assert!(py, tup, "tup[1] == 2");
py_assert!(py, tup, "tup[2] == 'foo'");
});
}
#[pyclass]
struct SimplePyClass {}
#[test]
fn intopytuple_pyclass() {
2022-07-19 17:34:23 +00:00
Python::with_gil(|py| {
let tup = (
Py::new(py, SimplePyClass {}).unwrap(),
Py::new(py, SimplePyClass {}).unwrap(),
2022-07-19 17:34:23 +00:00
);
py_assert!(py, tup, "type(tup[0]).__name__ == 'SimplePyClass'");
py_assert!(py, tup, "type(tup[0]).__name__ == type(tup[1]).__name__");
py_assert!(py, tup, "tup[0] != tup[1]");
});
}
#[test]
fn pytuple_primitive_iter() {
2022-07-19 17:34:23 +00:00
Python::with_gil(|py| {
2023-12-26 15:07:32 +00:00
let tup = PyTuple::new_bound(py, [1u32, 2, 3].iter());
2022-07-19 17:34:23 +00:00
py_assert!(py, tup, "tup == (1, 2, 3)");
});
}
#[test]
fn pytuple_pyclass_iter() {
2022-07-19 17:34:23 +00:00
Python::with_gil(|py| {
2023-12-26 15:07:32 +00:00
let tup = PyTuple::new_bound(
2022-07-19 17:34:23 +00:00
py,
[
Py::new(py, SimplePyClass {}).unwrap(),
Py::new(py, SimplePyClass {}).unwrap(),
2022-07-19 17:34:23 +00:00
]
.iter(),
);
py_assert!(py, tup, "type(tup[0]).__name__ == 'SimplePyClass'");
py_assert!(py, tup, "type(tup[0]).__name__ == type(tup[0]).__name__");
py_assert!(py, tup, "tup[0] != tup[1]");
});
}
2020-01-30 13:13:42 +00:00
#[pyclass(dict, module = "test_module")]
struct PickleSupport {}
#[pymethods]
impl PickleSupport {
#[new]
2019-12-14 14:16:39 +00:00
fn new() -> PickleSupport {
PickleSupport {}
}
2019-05-25 13:12:33 +00:00
pub fn __reduce__<'py>(
slf: &Bound<'py, Self>,
2019-05-25 13:12:33 +00:00
py: Python<'py>,
2023-12-26 15:07:32 +00:00
) -> PyResult<(PyObject, Bound<'py, PyTuple>, PyObject)> {
let cls = slf.to_object(py).getattr(py, "__class__")?;
let dict = slf.to_object(py).getattr(py, "__dict__")?;
2023-12-26 15:07:32 +00:00
Ok((cls, PyTuple::empty_bound(py), dict))
}
}
fn add_module(module: Bound<'_, PyModule>) -> PyResult<()> {
PyModule::import_bound(module.py(), "sys")?
.dict()
.get_item("modules")
.unwrap()
.unwrap()
2020-02-15 15:24:38 +00:00
.downcast::<PyDict>()?
.set_item(module.name()?, module)
}
#[test]
#[cfg_attr(all(Py_LIMITED_API, not(Py_3_10)), ignore)]
fn test_pickle() {
2022-07-19 17:34:23 +00:00
Python::with_gil(|py| {
let module = PyModule::new_bound(py, "test_module").unwrap();
2022-07-19 17:34:23 +00:00
module.add_class::<PickleSupport>().unwrap();
add_module(module).unwrap();
let inst = Py::new(py, PickleSupport {}).unwrap();
2022-07-19 17:34:23 +00:00
py_run!(
py,
inst,
r#"
inst.a = 1
assert inst.__dict__ == {'a': 1}
import pickle
inst2 = pickle.loads(pickle.dumps(inst))
assert inst2.__dict__ == {'a': 1}
"#
2022-07-19 17:34:23 +00:00
);
});
}
/// Testing https://github.com/PyO3/pyo3/issues/1106. A result type that
/// implements `From<MyError> for PyErr` should be automatically converted
/// when using `#[pyfunction]`.
///
/// This only makes sure that valid `Result` types do work. For an invalid
/// enum type, see `ui/invalid_result_conversion.py`.
#[derive(Debug)]
struct MyError {
pub descr: &'static str,
}
impl fmt::Display for MyError {
2022-03-23 07:07:28 +00:00
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "My error message: {}", self.descr)
}
}
/// Important for the automatic conversion to `PyErr`.
impl From<MyError> for PyErr {
fn from(err: MyError) -> pyo3::PyErr {
2020-08-25 19:33:36 +00:00
pyo3::exceptions::PyOSError::new_err(err.to_string())
}
}
#[pyfunction]
fn result_conversion_function() -> Result<(), MyError> {
Err(MyError {
descr: "something went wrong",
})
}
#[test]
fn test_result_conversion() {
2022-07-19 17:34:23 +00:00
Python::with_gil(|py| {
wrap_pyfunction_bound!(result_conversion_function)(py).unwrap();
2022-07-19 17:34:23 +00:00
});
}