PyO3 将 TryFrom 错误转换为 PyErr

PyO3 turn TryFrom errors into PyErr

提问人:Nils Werner 提问时间:10/14/2023 更新时间:11/4/2023 访问量:44

问:

我正在尝试为一些 Rust 代码编写一个 Python 包装器,它在内部进行一些转换:TryFrom

pub enum MicrophoneType {
    Bidirectional,
    Omnidirectional,
}

impl TryFrom<char> for MicrophoneType {
    type Error = &'static str;

    fn try_from(x: char) -> Result<Self, Self::Error> {
        match x {
            'b' => Ok(Self::Bidirectional),
            'o' => Ok(Self::Omnidirectional),
            _ => Err("MicrophoneType: Invalid character given"),
        }
    }
}

#[pyfunction]
fn compute(
    _py: Python,
    microphone: char,
) -> PyResult<()> {
    let _microphone = MicrophoneType::try_from(microphone)?;

    Ok(())
}

但是,我看到错误消息

error[E0277]: `?` couldn't convert the error to `PyErr`
  --> rir_generator_py/src/lib.rs:59:59
   |
58 | ) -> PyResult<()> {
   |      ------------ expected `PyErr` because of this
59 |     let _microphone = MicrophoneType::try_from(microphone)?;
   |                                                           ^ the trait `From<&str>` is not implemented for `PyErr`

我对为什么感到困惑。

看来PyO3不知道如何处理我的类型,对吧?但这难道不是给出错误消息的默认方式吗?如果 PyO3 是一种常见模式,难道不应该期待这些吗?我在这里错过了什么?type Error = &'static str;impl

PYO3型

评论


答:

1赞 Nils Werner 10/15/2023 #1

找到这些文档后,我意识到了一些事情:

首先,这不是一个好的模式,相反,如果需要,您应该使用可以 ed 和 /ed 的自定义错误type Error = &'static str;matchFromInto

#[derive(Debug)]
pub struct InvalidMicrophoneCharError {}

impl Error for InvalidMicrophoneCharError {}

impl fmt::Display for InvalidMicrophoneCharError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Incorrect microphone character")
    }
}

impl TryFrom<char> for MicrophoneType {
    type Error = InvalidMicrophoneCharError;

    fn try_from(x: char) -> Result<Self, Self::Error> {
        match x {
            'b' => Ok(Self::Bidirectional),
            'o' => Ok(Self::Omnidirectional),
            _ => Err(InvalidMicrophoneCharError {}),
        }
    }
}

其次,你必须使用 newtype 模式创建一个 “-chain”,它以 your type 开头,以 a 结尾,然后可以与 PyO3 对 Rust 原生 s 的支持相结合:FromPyErrResult<T, E>

struct MyInvalidMicrophoneCharError(InvalidMicrophoneCharError);

impl From<MyInvalidMicrophoneCharError> for PyErr {
    fn from(err: MyInvalidMicrophoneCharError) -> Self {
        PyValueError::new_err(err.0.to_string())
    }
}

impl From<InvalidMicrophoneCharError> for MyInvalidMicrophoneCharError {
    fn from(other: InvalidMicrophoneCharError) -> Self {
        Self(other)
    }
}

#[pyfunction]
fn compute(
    _py: Python,
    microphone: char,
) -> Result<(), MyInvalidMicrophoneCharError> {
    let _microphone = MicrophoneType::try_from(microphone)?;

    Ok(())
}

或者跳过链条,直接使用map_err()

#[pyfunction]
fn compute(
    _py: Python,
    microphone: char,
) -> Result<(), MyInvalidMicrophoneCharError> {
    let _microphone = MicrophoneType::try_from(microphone)
        .map_err(|error| PyValueError::new_err(error.to_string()))?;

    Ok(())
}