5.3 Maturin 開發流程
5.3 Maturin 開發流程
本章介紹 Maturin,Rust Python 套件的建構工具。
本章目標
學完本章後,你將能夠:
- 設定 Maturin 專案
- 使用 maturin develop 快速迭代
- 建構跨平台 wheel 並發布到 PyPI
【原理層】Maturin 是什麼?
建構工具的角色
1傳統 Python 擴展建構:
2├── setuptools + setup.py
3├── 複雜的編譯器設定
4├── 手動處理連結問題
5└── 跨平台建構困難
6
7Maturin 提供:
8├── 一鍵建構 Rust Python 套件
9├── 自動處理 PyO3 設定
10├── 跨平台 wheel 生成
11├── 與 pyproject.toml 整合
12└── 開發模式快速迭代支援的專案類型
1Maturin 支援:
2├── pyo3:純 Rust 擴展(最常用)
3├── cffi:生成 cffi 綁定
4├── uniffi:跨語言綁定
5└── bin:Rust 二進位程式打包【設計層】專案設定
安裝 Maturin
1# 使用 pip
2pip install maturin
3
4# 使用 pipx(推薦,隔離環境)
5pipx install maturin
6
7# 使用 cargo
8cargo install maturin
9
10# 驗證安裝
11maturin --version建立新專案
1# 互動式建立
2maturin new my_rust_module
3cd my_rust_module
4
5# 或者指定綁定類型
6maturin new my_rust_module --bindings pyo3
7
8# 專案結構
9my_rust_module/
10├── Cargo.toml
11├── pyproject.toml
12├── src/
13│ └── lib.rs
14└── python/
15 └── my_rust_module/
16 └── __init__.py # 選用pyproject.toml 設定
1[build-system]
2requires = ["maturin>=1.5,<2.0"]
3build-backend = "maturin"
4
5[project]
6name = "my-rust-module"
7version = "0.1.0"
8description = "A Python module written in Rust"
9requires-python = ">=3.8"
10classifiers = [
11 "Programming Language :: Rust",
12 "Programming Language :: Python :: Implementation :: CPython",
13 "Programming Language :: Python :: Implementation :: PyPy",
14]
15
16[project.optional-dependencies]
17dev = ["pytest", "hypothesis"]
18
19[tool.maturin]
20# 模組名稱(如果與 crate 名稱不同)
21module-name = "my_rust_module"
22
23# Python 原始碼目錄(如果有純 Python 程式碼)
24python-source = "python"
25
26# 功能標誌
27features = ["pyo3/extension-module"]
28
29# 啟用 abi3
30# features = ["pyo3/extension-module", "pyo3/abi3-py38"]
31
32# 排除不需要的檔案
33exclude = ["tests/*", "benches/*"]
34
35# 建構設定
36# strip = true # 移除除錯符號(減小檔案大小)Cargo.toml 設定
1[package]
2name = "my_rust_module"
3version = "0.1.0"
4edition = "2021"
5
6[lib]
7name = "my_rust_module"
8crate-type = ["cdylib"]
9
10[dependencies]
11pyo3 = { version = "0.23", features = ["extension-module"] }
12
13[profile.release]
14lto = true # Link-Time Optimization
15codegen-units = 1 # 更好的優化
16strip = true # 移除除錯符號
17
18[profile.dev]
19opt-level = 0 # 快速編譯【實作層】開發流程
maturin develop
1# 開發模式:編譯並安裝到當前虛擬環境
2maturin develop
3
4# 使用 release 模式(較慢但更快的執行時效能)
5maturin develop --release
6
7# 指定功能
8maturin develop --features "some-feature"
9
10# 在其他虛擬環境中安裝
11maturin develop --target-dir target -E /path/to/venv
12
13# 使用後立即測試
14maturin develop && python -c "import my_rust_module; print(my_rust_module.add(1, 2))"完整開發循環
1# 1. 建立虛擬環境
2python -m venv .venv
3source .venv/bin/activate # Linux/macOS
4# .venv\Scripts\activate # Windows
5
6# 2. 安裝開發依賴
7pip install maturin pytest
8
9# 3. 開發循環
10while editing:
11 # 編輯 Rust 程式碼
12 vim src/lib.rs
13
14 # 建構並安裝
15 maturin develop
16
17 # 執行測試
18 pytest tests/
19
20# 4. 準備發布
21maturin build --release混合 Python/Rust 專案
1專案結構:
2my_package/
3├── Cargo.toml
4├── pyproject.toml
5├── src/
6│ └── lib.rs # Rust 程式碼
7└── python/
8 └── my_package/
9 ├── __init__.py # 匯入 Rust 模組
10 ├── utils.py # 純 Python 程式碼
11 └── py.typed # 型別標記1# python/my_package/__init__.py
2from .my_package import * # 從 Rust 模組匯入
3from .utils import helper_function # 純 Python
4
5__version__ = "0.1.0"1# pyproject.toml
2[tool.maturin]
3python-source = "python"
4module-name = "my_package.my_package"【實作層】建構與發布
maturin build
1# 建構 wheel
2maturin build
3
4# Release 模式
5maturin build --release
6
7# 指定 Python 版本
8maturin build --interpreter python3.11
9
10# 多個 Python 版本
11maturin build --interpreter python3.10 python3.11 python3.12
12
13# 使用 abi3(穩定 ABI)
14maturin build --release --features pyo3/abi3-py38
15
16# 建構結果位置
17ls target/wheels/
18# my_rust_module-0.1.0-cp311-cp311-linux_x86_64.whl跨平台建構
1# 使用 Docker 建構 manylinux wheel
2maturin build --release --manylinux 2014
3
4# 常用的 manylinux 版本
5# manylinux1: CentOS 5 (非常老舊)
6# manylinux2010: CentOS 6
7# manylinux2014: CentOS 7 (推薦)
8# manylinux_2_28: Debian 9 / Ubuntu 18.04
9
10# 使用 zig 進行交叉編譯
11pip install ziglang
12maturin build --release --target x86_64-unknown-linux-gnu --zig
13
14# 常見目標平台
15# x86_64-unknown-linux-gnu
16# aarch64-unknown-linux-gnu
17# x86_64-apple-darwin
18# aarch64-apple-darwin
19# x86_64-pc-windows-msvc發布到 PyPI
1# 發布到 TestPyPI(測試)
2maturin publish --repository testpypi
3
4# 發布到 PyPI
5maturin publish
6
7# 使用 API token
8maturin publish --username __token__ --password <your-pypi-token>
9
10# 或者設定環境變數
11export MATURIN_PYPI_TOKEN=<your-pypi-token>
12maturin publish【實作層】CI/CD 整合
GitHub Actions
1# .github/workflows/build.yml
2name: Build and Publish
3
4on:
5 push:
6 tags:
7 - 'v*'
8 pull_request:
9 branches:
10 - main
11
12jobs:
13 # Linux
14 linux:
15 runs-on: ubuntu-latest
16 strategy:
17 matrix:
18 target: [x86_64, aarch64]
19 steps:
20 - uses: actions/checkout@v4
21
22 - uses: actions/setup-python@v5
23 with:
24 python-version: '3.11'
25
26 - name: Build wheels
27 uses: PyO3/maturin-action@v1
28 with:
29 target: ${{ matrix.target }}
30 args: --release --out dist
31 manylinux: auto
32
33 - name: Upload wheels
34 uses: actions/upload-artifact@v4
35 with:
36 name: wheels-linux-${{ matrix.target }}
37 path: dist
38
39 # macOS
40 macos:
41 runs-on: macos-latest
42 strategy:
43 matrix:
44 target: [x86_64, aarch64]
45 steps:
46 - uses: actions/checkout@v4
47
48 - uses: actions/setup-python@v5
49 with:
50 python-version: '3.11'
51
52 - name: Build wheels
53 uses: PyO3/maturin-action@v1
54 with:
55 target: ${{ matrix.target }}-apple-darwin
56 args: --release --out dist
57
58 - name: Upload wheels
59 uses: actions/upload-artifact@v4
60 with:
61 name: wheels-macos-${{ matrix.target }}
62 path: dist
63
64 # Windows
65 windows:
66 runs-on: windows-latest
67 steps:
68 - uses: actions/checkout@v4
69
70 - uses: actions/setup-python@v5
71 with:
72 python-version: '3.11'
73
74 - name: Build wheels
75 uses: PyO3/maturin-action@v1
76 with:
77 args: --release --out dist
78
79 - name: Upload wheels
80 uses: actions/upload-artifact@v4
81 with:
82 name: wheels-windows
83 path: dist
84
85 # 發布
86 publish:
87 needs: [linux, macos, windows]
88 runs-on: ubuntu-latest
89 if: startsWith(github.ref, 'refs/tags/')
90 steps:
91 - uses: actions/download-artifact@v4
92 with:
93 pattern: wheels-*
94 merge-multiple: true
95 path: dist
96
97 - name: Publish to PyPI
98 uses: PyO3/maturin-action@v1
99 env:
100 MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
101 with:
102 command: upload
103 args: --non-interactive --skip-existing dist/*測試工作流程
1# .github/workflows/test.yml
2name: Test
3
4on:
5 push:
6 branches: [main]
7 pull_request:
8
9jobs:
10 test:
11 runs-on: ${{ matrix.os }}
12 strategy:
13 matrix:
14 os: [ubuntu-latest, macos-latest, windows-latest]
15 python-version: ['3.9', '3.10', '3.11', '3.12']
16 steps:
17 - uses: actions/checkout@v4
18
19 - uses: actions/setup-python@v5
20 with:
21 python-version: ${{ matrix.python-version }}
22
23 - name: Install Rust
24 uses: dtolnay/rust-toolchain@stable
25
26 - name: Install maturin
27 run: pip install maturin pytest
28
29 - name: Build and install
30 run: maturin develop
31
32 - name: Run tests
33 run: pytest tests/【進階】效能優化
編譯優化
1# Cargo.toml
2[profile.release]
3lto = "fat" # 最佳化連結(較慢編譯)
4codegen-units = 1 # 單一編譯單元(更好優化)
5panic = "abort" # 不展開 panic(較小二進位)
6strip = true # 移除符號
7
8# 針對特定 CPU 優化
9# RUSTFLAGS="-C target-cpu=native" maturin build --release減小二進位大小
1# Cargo.toml
2[profile.release]
3opt-level = "z" # 優化大小而非速度
4lto = true
5strip = true
6panic = "abort"
7
8[dependencies]
9# 使用 features 減少不需要的程式碼
10pyo3 = { version = "0.23", default-features = false, features = ["extension-module"] }除錯技巧
1# 保留除錯符號
2maturin develop --profile dev
3
4# 啟用 Rust backtrace
5RUST_BACKTRACE=1 python -c "import my_module; my_module.buggy_function()"
6
7# 使用 lldb/gdb
8lldb python
9(lldb) run -c "import my_module; my_module.buggy_function()"【常見問題】疑難排解
編譯錯誤
1問題:找不到 Python
2
3解決:
41. 確認 Python 在 PATH 中
52. 使用 --interpreter 指定路徑
6 maturin develop --interpreter /usr/bin/python3.11
7
8問題:連結錯誤(Linux)
9
10解決:
111. 安裝 python3-dev
12 sudo apt install python3-dev
132. 安裝 build-essential
14 sudo apt install build-essential
15
16問題:找不到 PyO3
17
18解決:
191. 確認 Cargo.toml 中有正確的 pyo3 依賴
202. 執行 cargo update執行時錯誤
1問題:ImportError: undefined symbol
2
3原因:模組和 Python 版本不匹配
4
5解決:
61. 重新建構
7 maturin develop
82. 確認使用正確的 Python
9 which python
10
11問題:記憶體錯誤 / Segfault
12
13原因:通常是 unsafe 程式碼或 GIL 問題
14
15解決:
161. 檢查 unsafe 區塊
172. 確認正確使用 py.allow_threads()
183. 使用 RUST_BACKTRACE=1 獲取堆疊追蹤完整範例專案
專案結構
1fibonacci_rs/
2├── Cargo.toml
3├── pyproject.toml
4├── src/
5│ └── lib.rs
6├── python/
7│ └── fibonacci_rs/
8│ ├── __init__.py
9│ └── py.typed
10├── tests/
11│ └── test_fib.py
12└── README.md完整程式碼
1# Cargo.toml
2[package]
3name = "fibonacci_rs"
4version = "0.1.0"
5edition = "2021"
6
7[lib]
8name = "fibonacci_rs"
9crate-type = ["cdylib"]
10
11[dependencies]
12pyo3 = { version = "0.23", features = ["extension-module"] }
13
14[profile.release]
15lto = true
16codegen-units = 1 1# pyproject.toml
2[build-system]
3requires = ["maturin>=1.5,<2.0"]
4build-backend = "maturin"
5
6[project]
7name = "fibonacci-rs"
8version = "0.1.0"
9description = "Fast Fibonacci implementation in Rust"
10requires-python = ">=3.8"
11
12[tool.maturin]
13python-source = "python"
14module-name = "fibonacci_rs._core" 1// src/lib.rs
2use pyo3::prelude::*;
3
4/// 計算 Fibonacci 數列第 n 項
5#[pyfunction]
6fn fib(n: u64) -> u64 {
7 if n <= 1 {
8 return n;
9 }
10 let mut a = 0u64;
11 let mut b = 1u64;
12 for _ in 2..=n {
13 let tmp = a + b;
14 a = b;
15 b = tmp;
16 }
17 b
18}
19
20/// 計算 Fibonacci 數列前 n 項
21#[pyfunction]
22fn fib_sequence(py: Python<'_>, n: usize) -> Vec<u64> {
23 py.allow_threads(|| {
24 let mut seq = Vec::with_capacity(n);
25 let (mut a, mut b) = (0u64, 1u64);
26 for _ in 0..n {
27 seq.push(a);
28 let tmp = a + b;
29 a = b;
30 b = tmp;
31 }
32 seq
33 })
34}
35
36#[pymodule]
37fn _core(m: &Bound<'_, PyModule>) -> PyResult<()> {
38 m.add_function(wrap_pyfunction!(fib, m)?)?;
39 m.add_function(wrap_pyfunction!(fib_sequence, m)?)?;
40 Ok(())
41}1# python/fibonacci_rs/__init__.py
2from ._core import fib, fib_sequence
3
4__all__ = ["fib", "fib_sequence"]
5__version__ = "0.1.0" 1# tests/test_fib.py
2import fibonacci_rs
3
4def test_fib_zero():
5 assert fibonacci_rs.fib(0) == 0
6
7def test_fib_one():
8 assert fibonacci_rs.fib(1) == 1
9
10def test_fib_ten():
11 assert fibonacci_rs.fib(10) == 55
12
13def test_fib_sequence():
14 seq = fibonacci_rs.fib_sequence(10)
15 assert seq == [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]思考題
- 為什麼 Maturin 使用
cdylibcrate type?與rlib有什麼差異? - abi3 功能如何減少需要建構的 wheel 數量?有什麼限制?
- 在 CI 中建構跨平台 wheel 時,最大的挑戰是什麼?
實作練習
- 建立一個新的 Maturin 專案,實現一個簡單的字串處理函式
- 設定 GitHub Actions 自動建構並發布到 TestPyPI
- 比較
maturin develop和maturin develop --release的編譯時間和執行效能