S-JIS[2025-12-21]

pyo3 例外

Rustpyo3で例外クラスを作成する方法のメモ。


概要

PyO3では、Pythonの例外クラス(Rustの構造体)はcreate_exception!マクロで作成できる。

例外の基底クラス(継承元クラス)としてPyException等の構造体(Pythonのクラス)が用意されている。

use pyo3::{create_exception, exceptions::PyException};

create_exception!(モジュール名, 例外名, 継承元クラス名);
create_exception!(モジュール名, 例外名, 継承元クラス名, 説明文);

モジュール名.例外名」の例外クラス(Rustの構造体)が定義される。

説明文を付けておくと、「例外クラス.__doc__」でその説明文が取得できる。(説明文を指定しなかった場合はNoneが返る)
help(例外クラス)」でも表示される。


例外のインスタンスは「例外クラス::new_err(メッセージ)」で生成する。
これはPyErr型であり、戻り型がPyResultである関数からErrで返すと、Python側では例外が送出される。


例外を送出するだけなら例外クラス(構造体)を定義しておくだけでいいが、それをPython側で使いたい場合(try〜exceptに記述する場合等)は、例外クラス(構造体)をエクスポート (pymodule_export)する必要がある。


example-pyo3/src/error.rs

use pyo3::{create_exception, exceptions::PyException};

create_exception!(example_pyo3, MyError, PyException);
create_exception!(example_pyo3, MyError2, PyException);
create_exception!(example_pyo3, MyError3, MyError);

example-pyo3/src/lib.rs:

use pyo3::prelude::*;

mod error;

#[pymodule]
mod example_pyo3 {
    use pyo3::prelude::*;

    #[pymodule_export]
    use crate::error::{MyError, MyError2, MyError3};

    #[pyfunction]
    fn throw_exception(n: i32) -> PyResult<()> {
        match n {
            2 => Err(MyError2::new_err("error message2")),
            3 => Err(MyError3::new_err("error message3")),
            _ => Err(MyError::new_err("error message1")),
        }
    }
}

pymodule_exportで例外クラスをエクスポートする(Python側から使えるようにする)。
(pymodule_exportでは*を使えない。すなわち「use create::error::*;」と書くことは出来ない)

呼び出すPython側

call-pyo3/main.py:

import example_pyo3

def main():
    try:
        example_pyo3.throw_exception(3)
    except example_pyo3.MyError as e:
        print(f"1: {e}")
    except example_pyo3.MyError2 as e:
        print(f"2: {e}")

if __name__ == "__main__":
    main()

> uv run main.py
1: error message3

この例では、throw_exception(3)はMyError3が発生するが、MyError3はMyErrorを継承しているので、MyErrorとして捕捉される。


pyo3へ戻る / Rustへ戻る / 技術メモへ戻る
メールの送信先:ひしだま