Rust製パッケージをインストールしてコマンドラインから実行できるのは、なぜ?

Rust製パッケージをインストールしてコマンドラインから実行できるのは、なぜ?

Event:

Python Meetup Fukuoka #4

Presented:

2025/09/12 nikkie

このLTは、以下ができる理由を自分の言葉で説明するものです

$ python -m venv .venv --upgrade-deps
$ source .venv/bin/activate
(.venv) $ python -m pip install ruff
(.venv) $ ruff check --fix --extend-select I

本LTのスコープ(パッケージの場合分け)

  • Python製パッケージの場合(例:sampleproject

  • Rust製パッケージの場合(例:Ruff

Python 製パッケージをインストールしてコマンドラインから実行できるのは、なぜ?

$ python -m venv .venv --upgrade-deps  # Python 3.13.0
$ source .venv/bin/activate
(.venv) $ python -m pip install sampleproject
(.venv) $ .venv/bin/sample  # /usr/bin/sample と区別
Call your main application code here

metadata の project.scripts

[project.scripts]
sample = "sample:main"

仮想環境下にスクリプト

$ ls -l .venv/bin/sample
-rwxr-xr-x  1 user  group  211 Sep 12 11:23 .venv/bin/sample
$ file .venv/bin/sample
.venv/bin/sample: a /.../.venv/bin/python script text executable, ASCII text
$ # .venv/bin は仮想環境の有効化で PATH に入っている

「エントリポイントスクリプト」(『ハイパーモダンPython』2章)

エントリポイントスクリプトの中身

#!/.../.venv/bin/python
import sys
from sample import main
if __name__ == '__main__':
    if sys.argv[0].endswith('.exe'):
        sys.argv[0] = sys.argv[0][:-4]
    sys.exit(main())

distlib によるらしい (pip 24.3.1 実装)

シバンにPythonのパス

#!/.../.venv/bin/python

仮想環境のPython処理系が実行

import sys
from sample import main
if __name__ == '__main__':
    if sys.argv[0].endswith('.exe'):
        sys.argv[0] = sys.argv[0][:-4]
    sys.exit(main())

🥟Python製パッケージをインストールしてコマンドラインから実行できるのは、なぜ?

  • インストールでテキストファイル(Pythonスクリプト)が置かれる

  • シバンにより、Python処理系で実行される

Rust 製パッケージをインストールしてコマンドラインから実行できるのは、なぜ?

$ python -m venv .venv --upgrade-deps
$ source .venv/bin/activate
(.venv) $ python -m pip install ruff
(.venv) $ ruff check --fix --extend-select I

仮想環境下に バイナリ

$ ls -lh .venv/bin/ruff
-rwxr-xr-x  1 user  group    30M Sep 12 11:30 .venv/bin/ruff
$ file .venv/bin/ruff
.venv/bin/ruff: Mach-O 64-bit executable arm64

🔑maturin

  • https://www.maturin.rs/#maturin

  • Rustバイナリ はもちろん、pyo3などバインディングを使ったクレートをPythonパッケージとしてビルド・公開できるツール

Rust版 sampleproject を今回自作

src/main.rs
fn main() {
    println!("Call your main application code here");
}
$ cargo run --quiet
Call your main application code here

pyproject.toml 追加

完全版 pyproject.toml
[build-system]
requires = ["maturin>=1.8,<2.0"]
build-backend = "maturin"

[tool.maturin]
bindings = "bin"
strip = true

ローカルでインストールして実行

(.venv) $ uvx maturin build
(.venv) $ python -m pip install target/wheels/sampleproject_rs-0.0.2-py3-none-macosx_11_0_arm64.whl
(.venv) $ .venv/bin/sample
Call your main application code here

別案:maturin develop なら .venv/bin/sample のインストールまで

PyPIにも公開

https://pypi.org/project/sampleproject-rs/

GitHub Codespaces にて
$ pipx run --spec sampleproject-rs sample
Call your main application code here

macOS向けなどのバイナリは今後対応予定

bindings = "bin" (pyproject.toml)

Maturin also supports distributing binary applications written in Rust as Python packages using the bin bindings.

https://www.maturin.rs/bindings#bin

Ruffの pyproject.toml

[tool.maturin]
bindings = "bin"
manifest-path = "crates/ruff/Cargo.toml"
module-name = "ruff"
python-source = "python"
strip = true
exclude = [
    "crates/ruff_linter/resources/test/fixtures/**/*",
    "crates/ruff_linter/src/rules/*/snapshots/**/*"
]

https://github.com/astral-sh/ruff/blob/0.13.0/pyproject.toml#L46-L55

🌯Rust製パッケージをインストールしてコマンドラインから実行できるのは、なぜ?

  • テキストファイルではなく、バイナリが置かれる

  • ビルドバックエンドに maturin を使うと、Rustで作ったバイナリがPythonパッケージになる

お前、誰だったのよ?

../_images/uzabase-white-logo.png

ご清聴ありがとうございました