π¦ High-performance Rust functions + π Python productivity = Perfect integration
This template demonstrates how to integrate Rust with Python using PyO3, providing a solid foundation for projects that need the performance of Rust with the convenience of Python. It includes 6 mathematical functions showcasing different aspects of Rust-Python integration: from simple calculations to complex matrix operations with comprehensive error handling.
Perfect for: Performance-critical applications, numerical computing, learning Rust-Python integration, and building high-performance Python extensions.
What's included: 95 tests (32 Rust + 63 Python), enterprise-grade code quality with type stubs, complete development workflow, and comprehensive documentation.
rust-with-python/
βββ digits-calculator/ # Rust crate (PyO3 extension)
β βββ src/
β β βββ lib.rs # PyO3 wrapper functions
β β βββ math.rs # Pure Rust mathematical functions
β βββ Cargo.toml # Rust dependencies & metadata
β βββ pyproject.toml # Python build configuration
βββ tests/
β βββ test_digits_calculator.py # 63 comprehensive Python tests
βββ digits_calculator.pyi # Type stubs for PyO3 module
βββ main.py # Example usage demonstration
βββ Makefile # 15 development commands
βββ pyproject.toml # Project configuration & dependencies
βββ README.md # This documentation
βββ CONTRIBUTING.md # Development guidelines
βββ TEMPLATE_COMPLETION.md # Template transformation report
βββ MACOS_TROUBLESHOOTING.md # Comprehensive macOS guide
βββ uv.lock # Dependency lock file
Two-module design for maximum testability:
src/math.rs: Pure Rust mathematical functions (32 unit tests, no PyO3 dependencies)src/lib.rs: PyO3 wrapper layer that exposes functions to Python (handles type conversion and error mapping)digits_calculator.pyi: Type stubs for perfect IDE support and static analysis
This separation allows:
- Fast Rust unit testing without Python FFI overhead
- Clean separation of business logic and binding code
- Complete type safety with modern Python type checking
- Zero warnings from basedpyright/pylsp/mypy
- Professional code organization
- Python 3.8+ (tested with 3.13)
- Rust 1.70+ (latest stable recommended)
- uv for Python dependency management (install guide)
# Clone the repository
git clone https://github.com/macurandb/rust-with-python.git
cd rust-with-python
# Install and build everything
make install
# Verify installation
make testThis will:
- Create a Python virtual environment with
uv - Install all Python dependencies (including mypy for type checking)
- Build the Rust extension module
- Run all 95 tests to verify everything works
# Run the demonstration
make runExample output:
======================================================================
Welcome to rust-with-python!
Demonstrating Python and Rust Integration with PyO3
======================================================================
π Testing calculate_pi function (Rust Implementation):
----------------------------------------------------------------------
Ο approximation (1,000,000 iterations using Leibniz formula):
Result: 3.1415916535897743
Expected: 3.141592653589793
Error: 0.0000010000
π’ Testing sum_as_string function (Rust Implementation):
----------------------------------------------------------------------
Sum of 10 + 20 = 30
======================================================================
β
All demonstrations completed successfully!
======================================================================
Shows all available commands with descriptions.
Complete setup: creates virtual environment, installs dependencies, builds Rust module.
Builds the Rust extension module only.
Quick verification without full rebuild.
Runs the main demonstration script.
Runs all tests (32 Rust unit tests + 63 Python integration tests = 95 total tests).
Runs only the Rust unit tests (32 tests, fast, no Python FFI).
Runs only the Python integration tests with pytest (63 tests with full type checking).
Checks code quality for both Rust (clippy) and Python (ruff) with zero-tolerance policy.
Example output:
π Checking Rust code with clippy...
β
Rust code quality checks passed!
π Checking Python code with ruff...
β
Python code quality checks passed!Quality standards: Zero warnings policy, modern type annotations, enterprise-grade linting.
Rust-only linting with clippy.
Python-only linting with ruff.
Formats all code (Rust with cargo fmt, Python with ruff format).
Rust-only formatting.
Python-only formatting.
Removes all build artifacts and virtual environments.
Complete workflow: install, build, test, lint, and run.
import digits_calculator
# High-precision Ο calculation
pi = digits_calculator.calculate_pi(1_000_000)
print(f"Ο β {pi}")
# Matrix multiplication
a = [[1.0, 2.0], [3.0, 4.0]]
b = [[5.0, 6.0], [7.0, 8.0]]
result = digits_calculator.matrix_multiply(a, b)
print(f"Result: {result}") # [[19.0, 22.0], [43.0, 50.0]]
# Safe mathematical operations
sqrt_result = digits_calculator.safe_sqrt(25.0) # 5.0
division_result = digits_calculator.divide(10.0, 3.0) # 3.333...
factorial_result = digits_calculator.factorial(5) # 120All functions are implemented in pure Rust for maximum performance:
Calculates Ο using the Leibniz formula: Ο/4 = 1 - 1/3 + 1/5 - 1/7 + ...
# More iterations = higher precision
pi_low = digits_calculator.calculate_pi(100) # ~3.131
pi_medium = digits_calculator.calculate_pi(10000) # ~3.1414
pi_high = digits_calculator.calculate_pi(1000000) # ~3.141592
import math
error = abs(pi_high - math.pi) # Very small errorPerformance: 1,000,000 iterations in ~0.004s
High-performance matrix multiplication with comprehensive validation.
# 2D transformation example
rotation = [[0.866, -0.5], [0.5, 0.866]] # 30Β° rotation
point = [[1.0], [0.0]]
rotated = digits_calculator.matrix_multiply(rotation, point)
# Result: [[0.866], [0.5]]
# Supports rectangular matrices
a = [[1, 2], [3, 4], [5, 6]] # 3x2
b = [[7, 8, 9], [10, 11, 12]] # 2x3
result = digits_calculator.matrix_multiply(a, b) # 3x3 resultFeatures: Dimension validation, empty matrix detection, floating-point precision handling.
Simple integer addition returning string result (useful for very large numbers).
result = digits_calculator.sum_as_string(999999999, 1)
print(result) # "1000000000" (as string)All functions include proper error handling with specific Python exception types:
Safe division with zero-division protection.
try:
result = digits_calculator.divide(10.0, 2.0) # 5.0
print(f"Result: {result}")
except ZeroDivisionError as e:
print(f"Division error: {e}")
# This will raise ZeroDivisionError
try:
digits_calculator.divide(10.0, 0.0)
except ZeroDivisionError as e:
print("Cannot divide by zero!") # Exception caughtSquare root calculation with negative number protection.
try:
result = digits_calculator.safe_sqrt(16.0) # 4.0
print(f"β16 = {result}")
except ValueError as e:
print(f"Square root error: {e}")
# This will raise ValueError
try:
digits_calculator.safe_sqrt(-9.0)
except ValueError as e:
print("Cannot calculate square root of negative number!")Factorial calculation with input validation and overflow detection.
try:
result = digits_calculator.factorial(5) # 120
print(f"5! = {result}")
except ValueError as e:
print(f"Input error: {e}")
except OverflowError as e:
print(f"Result too large: {e}")
# This will raise ValueError
try:
digits_calculator.factorial(-5)
except ValueError as e:
print("Factorial not defined for negative numbers!")make testExpected output:
π¦ Running Rust unit tests (pure math module)...
running 30 tests
test result: ok. 30 passed; 0 failed
π Running Python integration tests...
collected 63 items
================================= 63 passed in 0.03s =================================
β
All tests passed!
make test-rust # Fast Rust unit tests only
make test-python # Python integration tests onlyThe Python tests use pytest with professional patterns:
- Parametrized tests for comprehensive coverage
- Fixtures for test data management
- Exception testing with
pytest.raises() - Type verification and consistency checks
- Performance benchmarks included
Example test:
@pytest.mark.parametrize("iterations,max_error", [
(1_000, 0.01),
(10_000, 0.001),
(1_000_000, 0.001),
])
def test_calculate_pi_accuracy(self, iterations, max_error):
result = digits_calculator.calculate_pi(iterations)
error = abs(result - math.pi)
assert error < max_errorTotal: 95 tests (100% passing)
Pure Rust function testing without PyO3 dependencies:
- calculate_pi tests (6 tests): Accuracy, edge cases, convergence, high-precision algorithms
- matrix_multiply tests (10 tests): Basic operations, dimensions, error cases, floating-point precision
- divide tests (4 tests): Valid operations, error handling, negative numbers
- safe_sqrt tests (3 tests): Valid inputs, negative handling, edge cases
- factorial tests (5 tests): Valid calculations, error cases, large numbers
- sum_as_string tests (4 tests): Basic operations, edge cases, large numbers
# Run with details
cd digits-calculator && cargo test -- --nocapture
test math::tests::test_calculate_pi_zero_iterations ... ok
test math::tests::test_calculate_pi_high_precision ... ok
test math::tests::test_matrix_multiply_basic_2x2 ... ok
test math::tests::test_divide_basic ... ok
test math::tests::test_safe_sqrt_basic ... ok
test math::tests::test_factorial_basic ... ok
test math::tests::test_sum_as_string_basic ... ok
# ... and 25 more testsComprehensive PyO3 integration testing:
- TestCalculatePi: 10 parametrized tests (ranges, accuracy, consistency)
- TestMatrixMultiply: 11 tests (valid operations, error cases, edge cases)
- TestSumAsString: 8 parametrized tests (operations, types, consistency)
- TestModuleIntegration: 10 parametrized tests (exports, callability)
- TestDivide: 7 tests (operations, exception handling)
- TestSafeSqrt: 8 tests (valid inputs, exception handling)
- TestFactorial: 9 tests (calculations, exception handling)
# Example parametrized test
@pytest.mark.parametrize("a,b,expected", [
(10.0, 2.0, 5.0),
(7.0, 2.0, 3.5),
(-10.0, 2.0, -5.0),
])
def test_divide_valid_operations(self, a, b, expected):
result = digits_calculator.divide(a, b)
assert abs(result - expected) < 1e-10================================= test session starts =================================
collected 63 items
tests/test_digits_calculator.py::TestCalculatePi::test_calculate_pi_ranges[0-expected_range0] PASSED
tests/test_digits_calculator.py::TestMatrixMultiply::test_matrix_multiply_valid[a0-b0-expected0] PASSED
tests/test_digits_calculator.py::TestDivide::test_divide_valid_operations[10.0-2.0-5.0] PASSED
tests/test_digits_calculator.py::TestSafeSqrt::test_safe_sqrt_valid_inputs[16.0-4.0] PASSED
tests/test_digits_calculator.py::TestFactorial::test_factorial_valid_inputs[5-120] PASSED
# ... 58 more tests all PASSED
================================= 63 passed in 0.03s =================================
Type Safety: All tests include comprehensive type hints and pass strict mypy checking.
# Test specific function
cd digits-calculator && cargo test calculate_pi
uv run pytest tests/ -k "calculate_pi" -v
# Test specific class
uv run pytest tests/test_digits_calculator.py::TestMatrixMultiply -v
# Test with coverage
uv run pytest tests/ --tb=shortThe project maintains zero-warning policy with modern tooling:
- Type stubs:
digits_calculator.pyiprovides complete PyO3 module types - Modern annotations: Python 3.13+ style (
listinstead ofList) - Strict mypy: Full type checking with enterprise-grade configuration
- Zero basedpyright errors: Perfect IDE support and static analysis
make lint-rust
# Runs: cargo clippy -- -D warningsClippy checks for:
- Performance improvements
- Memory safety issues
- Idiomatic Rust patterns
- Potential bugs
make lint-python
# Runs: ruff check .Ruff checks for:
- PEP 8 style violations
- Import organization
- Modern type annotations (UP006, UP035)
- Code complexity and best practices
make lint
# Runs both Rust and Python linting
# Zero tolerance policy: all warnings treated as errorsLocated in pyproject.toml:
[tool.ruff.lint]
line-length = 100
target-version = "py313"
extend-select = ["E", "F", "W", "I", "UP", "C4"][tool.mypy]
python_version = "3.13"
strict = true
warn_return_any = true
disallow_untyped_defs = true
check_untyped_defs = truedigits_calculator.pyi provides complete type information for the PyO3 module:
- Resolves basedpyright/pylsp attribute errors
- Enables perfect IDE autocompletion
- Supports static type analysis
Standard clippy with deny-warnings policy in Makefile.
Add your function to digits-calculator/src/math.rs:
pub fn factorial(n: i32) -> Result<u64, String> {
if n < 0 {
return Err("Factorial is not defined for negative numbers".to_string());
}
let mut result: u64 = 1;
for i in 2..=(n as u64) {
result *= i;
}
Ok(result)
}Add tests in the same file:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_factorial_basic() {
assert_eq!(factorial(5).unwrap(), 120);
assert_eq!(factorial(0).unwrap(), 1);
}
}Add PyO3 wrapper in digits-calculator/src/lib.rs:
#[pyfunction]
fn factorial(n: i32) -> PyResult<u64> {
match math::factorial(n) {
Ok(result) => Ok(result),
Err(msg) => Err(PyErr::new::<pyo3::exceptions::PyValueError, _>(msg)),
}
}
#[pymodule]
fn digits_calculator(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_wrapped(wrap_pyfunction!(factorial))?;
Ok(())
}Create Python tests in tests/test_digits_calculator.py:
def test_factorial_basic():
result = digits_calculator.factorial(5)
assert result == 120make clean && make install
make testgit clone <your-fork>
cd rust-with-python
make install- Edit Rust code in
digits-calculator/src/ - Add tests as you develop
- Update documentation
make fmt # Format all code
make lint # Check code qualityFix any issues before continuing.
make clean # Clean previous builds
make install # Rebuild everything
make test # Run all 93 testsAll tests must pass:
β
Rust unit tests passed! (30/30)
β
Python integration tests passed! (63/63)
make runVerify your changes work in the demo.
# Complete workflow
make all
# This runs:
# 1. make clean
# 2. make install
# 3. make test
# 4. make lint
# 5. make runExpected output:
π§Ή Cleaning up build artifacts...
π¦ Installing dependencies and building...
π§ͺ Running all tests...
β
All tests passed! (95/95)
π Checking code quality...
β
All quality checks passed! (Zero warnings)
π Running demonstration...
β
Application completed successfully!
make clean
make installThis rebuilds everything from scratch.
Ensure you're in the virtual environment:
source .venv/bin/activate
python -c "import digits_calculator; print('β
Import successful')"Run tests individually to isolate issues:
make test-rust # Check Rust unit tests
make test-python # Check Python integration testsCheck the specific error messages for guidance.
This template includes specific solutions for common PyO3 issues on macOS:
If maturin fails with linking errors when Conda is active:
unset CONDA_PREFIX
make buildOur Makefile automatically handles this for you.
If you encounter linking errors, ensure Xcode command line tools are installed:
xcode-select --installFor M1/M2/M3 Macs, if you see architecture warnings:
export ARCHFLAGS="-arch arm64"
make buildUse uv for consistent Python management (already configured):
make clean && make installFor comprehensive macOS troubleshooting, see MACOS_TROUBLESHOOTING.md
- PyO3 Documentation
- maturin User Guide
- Rust Book
- uv Documentation
- macOS Troubleshooting Guide - Comprehensive macOS-specific solutions
- Rust 1.70+: Core mathematical functions
- Python 3.13+: Interface and testing with modern type annotations
- PyO3 0.27.0: Rust-Python bindings
- maturin: Build tool for PyO3 projects
- pytest: Python testing framework
- uv: Fast Python package manager
- ruff: Python linting and formatting (zero warnings)
- mypy: Static type checking (strict mode)
- cargo clippy: Rust linting (deny warnings)
This project is licensed under the MIT License. See LICENSE for details.
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
Quick contribution checklist:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass (
make test) - Check code quality (
make lint) - Submit a pull request
- Rename the module: Change
digits-calculatorto your project name - Replace functions: Modify
src/math.rswith your own functions - Update tests: Add comprehensive tests for your functions
- Documentation: Update this README with your project details
- Dependencies: Add any additional Rust or Python dependencies you need
New to Rust-Python integration? Follow this path:
- Start here: Run
make testand explore the existing functions - Understand the architecture: Read
src/math.rs(pure Rust) andsrc/lib.rs(PyO3 bindings) - Explore type safety: Check
digits_calculator.pyifor complete type definitions - Modify existing functions: Try changing the
calculate_piimplementation - Add a simple function: Start with a basic math function
- Advanced features: Explore error handling and complex data types like matrices
- Type annotations: Learn modern Python type hints and mypy integration
- Performance: Benchmark your Rust vs pure Python implementations
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Template Questions: Open an issue with the
templatelabel
π Happy coding! Enjoy the power of Rust with the convenience of Python.