pyo3/tests/test_declarative_module.rs

171 lines
3.7 KiB
Rust

#![cfg(feature = "experimental-declarative-modules")]
use pyo3::create_exception;
use pyo3::exceptions::PyException;
use pyo3::prelude::*;
#[cfg(not(Py_LIMITED_API))]
use pyo3::types::PyBool;
#[path = "../src/tests/common.rs"]
mod common;
#[pyclass]
struct ValueClass {
value: usize,
}
#[pymethods]
impl ValueClass {
#[new]
fn new(value: usize) -> Self {
Self { value }
}
}
#[pyclass(module = "module")]
struct LocatedClass {}
#[pyfunction]
fn double(x: usize) -> usize {
x * 2
}
create_exception!(
declarative_module,
MyError,
PyException,
"Some description."
);
/// A module written using declarative syntax.
#[pymodule]
mod declarative_module {
#[pymodule_export]
use super::declarative_submodule;
#[pymodule_export]
// This is not a real constraint but to test cfg attribute support
#[cfg(not(Py_LIMITED_API))]
use super::LocatedClass;
use super::*;
#[pymodule_export]
use super::{declarative_module2, double, MyError, ValueClass as Value};
#[pymodule]
mod inner {
use super::*;
#[pyfunction]
fn triple(x: usize) -> usize {
x * 3
}
#[pyclass]
struct Struct;
#[pymethods]
impl Struct {
#[new]
fn new() -> Self {
Self
}
}
#[pyclass]
enum Enum {
A,
B,
}
}
#[pymodule_init]
fn init(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add("double2", m.getattr("double")?)
}
}
#[pyfunction]
fn double_value(v: &ValueClass) -> usize {
v.value * 2
}
#[pymodule]
mod declarative_submodule {
#[pymodule_export]
use super::{double, double_value};
}
#[pymodule]
#[pyo3(name = "declarative_module_renamed")]
mod declarative_module2 {
#[pymodule_export]
use super::double;
}
#[test]
fn test_declarative_module() {
Python::with_gil(|py| {
let m = pyo3::wrap_pymodule!(declarative_module)(py).into_bound(py);
py_assert!(
py,
m,
"m.__doc__ == 'A module written using declarative syntax.'"
);
py_assert!(py, m, "m.double(2) == 4");
py_assert!(py, m, "m.inner.triple(3) == 9");
py_assert!(py, m, "m.declarative_submodule.double(4) == 8");
py_assert!(
py,
m,
"m.declarative_submodule.double_value(m.ValueClass(1)) == 2"
);
py_assert!(py, m, "str(m.MyError('foo')) == 'foo'");
py_assert!(py, m, "m.declarative_module_renamed.double(2) == 4");
#[cfg(Py_LIMITED_API)]
py_assert!(py, m, "not hasattr(m, 'LocatedClass')");
#[cfg(not(Py_LIMITED_API))]
py_assert!(py, m, "hasattr(m, 'LocatedClass')");
py_assert!(py, m, "isinstance(m.inner.Struct(), m.inner.Struct)");
py_assert!(py, m, "isinstance(m.inner.Enum.A, m.inner.Enum)");
})
}
#[cfg(not(Py_LIMITED_API))]
#[pyclass(extends = PyBool)]
struct ExtendsBool;
#[cfg(not(Py_LIMITED_API))]
#[pymodule]
mod class_initialization_module {
#[pymodule_export]
use super::ExtendsBool;
}
#[test]
#[cfg(not(Py_LIMITED_API))]
fn test_class_initialization_fails() {
Python::with_gil(|py| {
let err = class_initialization_module::DEF
.make_module(py)
.unwrap_err();
assert_eq!(
err.to_string(),
"RuntimeError: An error occurred while initializing class ExtendsBool"
);
})
}
#[pymodule]
mod r#type {
#[pymodule_export]
use super::double;
}
#[test]
fn test_raw_ident_module() {
Python::with_gil(|py| {
let m = pyo3::wrap_pymodule!(r#type)(py).into_bound(py);
py_assert!(py, m, "m.double(2) == 4");
})
}