Skip to content

Commit e4a86e6

Browse files
committed
add exception hierarchy pytests
1 parent b73c069 commit e4a86e6

File tree

3 files changed

+105
-0
lines changed

3 files changed

+105
-0
lines changed

pytests/src/exceptions.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
use pyo3::exceptions::PyException;
2+
use pyo3::prelude::*;
3+
use pyo3::types::{PyDict, PyTuple};
4+
5+
#[pyclass(extends=PyException, subclass)]
6+
struct CustomException {}
7+
8+
#[pymethods]
9+
impl CustomException {
10+
#[new]
11+
#[pyo3(signature = (*_args, **_kwargs))]
12+
fn new(_args: &PyTuple, _kwargs: Option<&PyDict>) -> PyClassInitializer<Self> {
13+
PyClassInitializer::from(CustomException {})
14+
}
15+
}
16+
17+
#[pyclass(extends=CustomException, subclass)]
18+
struct ExceptionSubclassA {}
19+
20+
#[pymethods]
21+
impl ExceptionSubclassA {
22+
#[new]
23+
#[pyo3(signature = (*args, **kwargs))]
24+
fn new(args: &PyTuple, kwargs: Option<&PyDict>) -> PyClassInitializer<Self> {
25+
CustomException::new(args, kwargs).add_subclass(Self {})
26+
}
27+
}
28+
29+
#[pyclass(extends=ExceptionSubclassA, subclass)]
30+
struct ExceptionSubclassAChild {}
31+
32+
#[pymethods]
33+
impl ExceptionSubclassAChild {
34+
#[new]
35+
#[pyo3(signature = (*args, **kwargs))]
36+
fn new(args: &PyTuple, kwargs: Option<&PyDict>) -> PyClassInitializer<Self> {
37+
ExceptionSubclassA::new(args, kwargs).add_subclass(Self {})
38+
}
39+
}
40+
41+
#[pyclass(extends=CustomException)]
42+
struct ExceptionSubclassB {}
43+
44+
#[pymethods]
45+
impl ExceptionSubclassB {
46+
#[new]
47+
#[pyo3(signature = (*args, **kwargs))]
48+
fn new(args: &PyTuple, kwargs: Option<&PyDict>) -> PyClassInitializer<Self> {
49+
CustomException::new(args, kwargs).add_subclass(Self {})
50+
}
51+
}
52+
53+
#[pyfunction]
54+
fn do_something(op: &str) -> PyResult<()> {
55+
match op {
56+
"success" => Ok(()),
57+
58+
"subclass_a" => Err(PyErr::new::<ExceptionSubclassA, _>("subclass_a")),
59+
"subclass_a_child" => Err(PyErr::new::<ExceptionSubclassAChild, _>("subclass_a_child")),
60+
"subclass_b" => Err(PyErr::new::<ExceptionSubclassB, _>("subclass_b")),
61+
_ => Err(PyErr::new::<CustomException, _>(format!(
62+
"unknown op `{}`",
63+
op
64+
))),
65+
}
66+
}
67+
68+
#[pymodule]
69+
pub fn exceptions(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
70+
m.add_class::<CustomException>()?;
71+
m.add_class::<ExceptionSubclassA>()?;
72+
m.add_class::<ExceptionSubclassAChild>()?;
73+
m.add_class::<ExceptionSubclassB>()?;
74+
m.add_function(wrap_pyfunction!(do_something, m)?)?;
75+
Ok(())
76+
}

pytests/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pub mod buf_and_str;
77
pub mod comparisons;
88
pub mod datetime;
99
pub mod dict_iter;
10+
pub mod exceptions;
1011
pub mod misc;
1112
pub mod objstore;
1213
pub mod othermod;
@@ -25,6 +26,7 @@ fn pyo3_pytests(py: Python<'_>, m: &PyModule) -> PyResult<()> {
2526
#[cfg(not(Py_LIMITED_API))]
2627
m.add_wrapped(wrap_pymodule!(datetime::datetime))?;
2728
m.add_wrapped(wrap_pymodule!(dict_iter::dict_iter))?;
29+
m.add_wrapped(wrap_pymodule!(exceptions::exceptions))?;
2830
m.add_wrapped(wrap_pymodule!(misc::misc))?;
2931
m.add_wrapped(wrap_pymodule!(objstore::objstore))?;
3032
m.add_wrapped(wrap_pymodule!(othermod::othermod))?;
@@ -44,6 +46,7 @@ fn pyo3_pytests(py: Python<'_>, m: &PyModule) -> PyResult<()> {
4446
sys_modules.set_item("pyo3_pytests.comparisons", m.getattr("comparisons")?)?;
4547
sys_modules.set_item("pyo3_pytests.datetime", m.getattr("datetime")?)?;
4648
sys_modules.set_item("pyo3_pytests.dict_iter", m.getattr("dict_iter")?)?;
49+
sys_modules.set_item("pyo3_pytests.exceptions", m.getattr("exceptions")?)?;
4750
sys_modules.set_item("pyo3_pytests.misc", m.getattr("misc")?)?;
4851
sys_modules.set_item("pyo3_pytests.objstore", m.getattr("objstore")?)?;
4952
sys_modules.set_item("pyo3_pytests.othermod", m.getattr("othermod")?)?;

pytests/tests/test_exceptions.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from pyo3_pytests import exceptions
2+
import pytest
3+
4+
5+
def test_exceptions():
6+
assert exceptions.do_something("success") is None
7+
8+
with pytest.raises(exceptions.CustomException) as exc_info:
9+
exceptions.do_something("fail")
10+
11+
assert exc_info.value.args == ("unknown op `fail`",)
12+
13+
with pytest.raises(exceptions.ExceptionSubclassA) as exc_info:
14+
exceptions.do_something("subclass_a")
15+
16+
assert exc_info.value.args == ("subclass_a",)
17+
18+
with pytest.raises(exceptions.ExceptionSubclassAChild) as exc_info:
19+
exceptions.do_something("subclass_a_child")
20+
21+
assert exc_info.value.args == ("subclass_a_child",)
22+
23+
with pytest.raises(exceptions.ExceptionSubclassB) as exc_info:
24+
exceptions.do_something("subclass_b")
25+
26+
assert exc_info.value.args == ("subclass_b",)

0 commit comments

Comments
 (0)