3

How do I correctly raise an Exception?

I've tried the following:

#[pymethods]
impl Foo {
    #[new]
    fn __new__(arg1: u8, ...) -> Self {
        if arg1 > LIMIT {
            let gil = Python::acquire_gil();
            let py = gil.python();
            PyErr::new::<exceptions::ValueError, _>("Parameter arg1 has invalid value").restore(py);
        }
        Foo {...}
    }

This is exactly the same implementation as how it is described here.

When I create an instance of Foo with an invalid parameter value then instead of a ValueError a SystemError is raised with error text <class 'Foo'> returned a result with an error set.

I'm using pyo3 0.11.1 and Rust 1.47.0 nightly on Linux.

Martin Bammer
  • 537
  • 7
  • 19

2 Answers2

4

You can use a PyResult that returns an exception (which will be raised in python):

#[pymethods]
impl Foo {
    #[new]
    fn __new__(arg1: u8, ...) -> PyResult<Self> {
        if arg1 > LIMIT {
            Err(exceptions::PyValueError::new_err("Parameter arg1 has invalid value"))
        } else {
            Ok(Foo {...})
        }
    }

Azat Ibrakov
  • 9,998
  • 9
  • 38
  • 50
Netwave
  • 40,134
  • 6
  • 50
  • 93
2

Note that the example you link to is about error management in main.

Here I believe that, as the SystemError indicates, you're simultanously setting an error and returning a value. Since these are obviously exclusive conditions (it makes no sense to raise and return), the Python interpreter faults.

As documented for ctors as well as noted after the first snippet of raising an exception, you should return a [Py]Result, with the Err case signaling an error which pyo3 will set for the interpreter.

Masklinn
  • 34,759
  • 3
  • 38
  • 57