S-JIS[2026-02-21/2026-02-24]

Rust PyO3 Rustモジュールを隠すメモ

Rustpyo3のRustモジュールを隠すメモ。


概要

デフォルトのPyO3アプリケーションでは、pdoc3によってAPIドキュメントを生成すると、「モジュール名.モジュール名」というサブモジュールが出てくる。
どうやら、ピリオドの左側のモジュール名はPythonとしてのモジュール名(パッケージ名)で、右側のモジュール名はRustで定義したモジュールの名前のようだ。

なので、Rustのモジュール名をアンダースコアから始めてやれば、Pythonではprivate扱いになり、APIドキュメントに出て来なくなる。


なお、env_loggerを使ってログ出力を制御している場合、環境変数RUST_LOGに「モジュール名=info」のようなフィルター条件が指定できるのだが、このモジュール名もアンダースコア付きのものを指定することになる。[2026-02-24]


Rustモジュールをアンダースコアから始まる名前に変更する例。


まず、普通のPyO3プロジェクトを作成する。

uv run maturin new hidden-example

次に、Rustのモジュール名を変更する。

hidden-example/src/lib.rs:

use pyo3::prelude::*;

/// A Python module implemented in Rust.
#[pymodule]
mod _hidden_example { // モジュール名の先頭にアンダースコアを追加
    use pyo3::prelude::*;

    /// Formats the sum of two numbers as string.
    #[pyfunction]
    fn sum_as_string(a: usize, b: usize) -> PyResult<String> {
        Ok((a + b).to_string())
    }
}

Cargo.tomlのライブラリー名も変更する。

hidden-example/Cargo.toml:

〜

[lib]
name = "_hidden_example" // モジュール名の先頭にアンダースコアを追加
crate-type = ["cdylib"]

〜

pyproject.tomlに[tool.maturin]を追加し、モジュール名を指定する。
同時に、プロジェクトの構成をPython/Rust混合構成に変更する。

hidden-example/pyproject.toml:

〜
[tool.uv]
cache-keys = [{ file = "pyproject.toml" }, { file = "src/**/*.rs" }, { file = "Cargo.toml" }, { file = "Cargo.lock" }, { file = "python/**/*.py" }]

[tool.maturin]
module-name = "hidden_example._hidden_example" // モジュール名の指定
python-source = "python" // Python/Rust混合構成の指定

Python/Rust混合構成用に、pythonディレクトリーを作成し、Pythonモジュール名/__init__.pyを用意する。

hidden-example/python/hidden_example/__init__.py:

"""
Rustモジュールを隠す実験
"""

from ._hidden_example import *

__all__ = [name for name in globals() if not name.startswith("_")]

呼び出すPython側の例

> cd hidden-example
> uv run python
>>> import hidden_example
>>> hidden_example.sum_as_string(1, 2)
'3'

普通のPyO3プロジェクトと同様に、Pythonモジュール名で関数が呼べる。


pdoc3でAPIドキュメントを作成する方法も同じ。

> cd hidden-example
> uv run pdoc hidden_example -o docs --html --force

サブモジュールの例

サブモジュールを追加してみる。


Rustのコードは、通常と変わらない。

hidden-example/src/sub.rs

use pyo3::prelude::*;
/// 別ファイルのサブモジュール
#[pymodule]
pub(crate) mod sub {
    use pyo3::prelude::*;

    /// 別ファイルのサブモジュールのクラス1
    #[pyclass]
    pub struct SubClass1 {}

    #[pymethods]
    impl SubClass1 {
        /// コンストラクター
        #[new]
        fn new() -> Self {
            SubClass1 {}
        }

        fn show(&self) {
            println!("SubClass1.show");
        }
    }
}

hidden-example/src/error.rs

use pyo3::{exceptions::PyException, *};
/// 例外モジュール
#[pymodule]
pub(crate) mod error {

    #[pymodule_export]
    use super::{MyError1, MyError2, MyError3};
}

create_exception!(hidden_example.error, MyError1, PyException, "エラー1");
create_exception!(hidden_example.error, MyError2, PyException, "エラー2");
create_exception!(hidden_example.error, MyError3, MyError1, "エラー3");

hidden-example/src/lib.rs:

use pyo3::prelude::*;

mod error;
mod sub;
/// A Python module implemented in Rust.
#[pymodule]
mod _hidden_example {
    use pyo3::prelude::*;

    #[pymodule_export]
    use super::sub::sub;

    #[pymodule_export]
    use super::error::error;

    #[pyfunction]
    pub fn raise1() -> PyResult<()> {
        use crate::error::MyError1;

        Err(MyError1::new_err("test1"))
    }
}

Pythonのコードは、Rustモジュールを使用する記述が必要。

hidden-example/python/hidden_example/sub/__init__.py:

from hidden_example._hidden_example import sub as _rust

for _name in _rust.__all__:
    globals()[_name] = getattr(_rust, _name)

__all__ = _rust.__all__

hidden-example/python/hidden_example/error/__init__.py:

from hidden_example._hidden_example import error as _rust

for _name in _rust.__all__:
    globals()[_name] = getattr(_rust, _name)

__all__ = _rust.__all__

呼び出すPython側の例

呼び出す側(Python)は、通常と変わらない。

> cd hidden-example
> uv run python
>>> import hidden_example

>>> s1 = hidden_example.sub.SubClass1()
>>> s1.show()
SubClass1.show

>>> try:
...     hidden_example.raise1()
... except hidden_example.error.MyError1 as e:
...     print(f"1: {e}")
...
1: test1

pdoc3でAPIドキュメントも作成できる。

> cd hidden-example
> uv run pdoc hidden_example -o docs --html --force

pyo3-stub-genの例

pyo3-stub-genによる型スタブファイルの作成も、通常と同様に行える。


クラスや関数に付ける属性は通常と同じ。(Pythonとしてのモジュール名を付ける)

    #[gen_stub_pyclass]
    #[pyclass(module = "hidden_example.sub")]
    pub struct SubClass1 {}
    #[gen_stub_pyfunction(module = "hidden_example")]
    #[pyfunction]
    pub fn raise1() -> PyResult<()> {
        use crate::error::MyError1;

        Err(MyError1::new_err("test1"))
    }
use pyo3_stub_gen::create_exception;

create_exception!(hidden_example.error, MyError1, PyException, "エラー1");
create_exception!(hidden_example.error, MyError2, PyException, "エラー2");
create_exception!(hidden_example.error, MyError3, MyError1, "エラー3");

example-pyo3/src/lib.rs:

use pyo3::prelude::*;
use pyo3_stub_gen::define_stub_info_gatherer;
#[pymodule]
mod _hidden_example {
    〜
}

define_stub_info_gatherer!(stub_info);

stub_gen.rsも通常と同じ。(ただ、アンダースコア付きのモジュール名になる)

example-pyo3/src/bin/stub_gen.rs:

use pyo3_stub_gen::Result;

fn main() -> Result<()> {
    let stub = _hidden_example::stub_info()?;
    stub.generate()?;
    Ok(())
}

型スタブファイルの生成方法も同じ。

> cd hidden-example
> cargo run --bin stub_gen

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