はじめに
七尾百合子さん、お誕生日 186日目 おめでとうございます! nikkieです。
Rust プログラムをバイナリにして Python パッケージとして配布してきましたが、Python で import もできるようにする方法を調べました。
目次
- はじめに
- 目次
- 前回の sampleproject-rs!
- sampleproject はsampleコマンドも import もできる
- ドキュメント「Both binary and library?」
- PyO3 で sample ライブラリ爆誕
- 宿題事項
- 終わりに
前回の sampleproject-rs!
https://pypi.org/project/sampleproject-rs/0.1.0/
Rust プログラムから作ったバイナリは、maturin で Python パッケージにできます!
pyproject.toml のこの指定がポイントです:
[tool.maturin] bindings = "bin"
Linux, macOS, Windows 各環境向けにビルドし、PyPI で配布しています1。
% uvx --python 3.13 --from sampleproject-rs sample Call your main application code here
sampleproject はsampleコマンドも import もできる
sampleproject-rs は Python の sampleproject を Rust で実現しようと始めました。
本家 sampleproject はsampleコマンドだけでなく、import もできます。
% uvx --python 3.13 --from sampleproject sample Call your main application code here
% uv run --python 3.13 --with sampleproject python >>> from sample.simple import add_one >> add_one(20) 21
sampleproject-rs を sample コマンドだけでなく、Python で import もできるようにするにはどうしたらよいのでしょう?
ドキュメント「Both binary and library?」
Maturin User Guide に見つけました。
Consider instead exposing a CLI function in the library and using a Python entrypoint:
- PyO3 を使って、Rust で書いたライブラリを Python からモジュールとして import できるようにする
- Python パッケージの metadata の
project.scriptsでエントリポイントスクリプトを実現する
PyO3 で sample ライブラリ爆誕
Declarative modulesを使いました2。
lib.rs
use pyo3::prelude::*; #[pyfunction] fn main() { println!("Call your main application code here"); } #[pyfunction] fn add_one(number: i32) -> i32 { number + 1 } #[pymodule] mod sample { #[pymodule_export] use super::main; #[pymodule_export] use super::add_one; }
Python 処理系から見て sample モジュールは main と add_one という2つの関数を持ちます。
main 関数をエントリポイントスクリプトに指定します。
[project.scripts] sample = "sample:main"
全容はこちらから:
ローカルで動作確認する例
(.venv) % python -V Python 3.13.0 (.venv) % uvx maturin develop (.venv) % python -c 'from sample import add_one; print(add_one(20))' 21 (.venv) % sample Call your main application code here
sampleコマンドの実体は、バイナリではなくテキストファイル(スクリプト)です。
これは純正 Python パッケージの場合と同じですね。
% cat .venv/bin/sample #!/.../sampleproject-rs/.venv/bin/python # -*- coding: utf-8 -*- import re import sys from sample import main if __name__ == '__main__': sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) sys.exit(main())
宿題事項
sample.simple.add_oneのようにネストさせられる?- 試しに
mainもadd_oneも simple の下に置いてみたらコンパイルエラー
- 試しに
main.rsを残しているが、lib.rsにあるmain()関数を呼ぶようにできる?(DRYにする意図)
終わりに
Rust プログラムから作ったバイナリを maturin で Python パッケージにするだけでなく、PyO3 からモジュールにまとめ、Python で import して使う方法を知りました。
後者のアプローチでは、エントリポイントスクリプトを使うことで、Rust プログラムを Python 環境で import もコマンド実行もできます!
直近のLTより
https://ftnext.github.io/2025-slides/python-fukuoka/why-we-can-run-rust-package-cli#/4

- 2025年9月17日(水)のリリース - nikkie-ftnextの日記↩
- 積ん読していた記事は PyO3 の変更で古くなってしまっていました ↩