Implement or modify a Rust function exposed to Python via PyO3 in eo-processor. Use when adding new compute kernels, fixing numerical/shape bugs in the Rust core, or wiring Rust functions into the Python module safely and consistently.
Scanned 9/1/2026
Install to Claude Code
npx -y skills add phenobarbital/ai-parrot --skill rust-pyo3-function --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Rust Pyo3 Function?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/phenobarbital-rust-pyo3-function)More formats (shields.io, HTML) on the badges page.
---
name: rust-pyo3-function
description: Implement or modify a Rust function exposed to Python via PyO3 in eo-processor. Use when adding new compute kernels, fixing numerical/shape bugs in the Rust core, or wiring Rust functions into the Python module safely and consistently.
license: Proprietary. See repository LICENSE
compatibility: Requires Rust toolchain and Python packaging via maturin; assumes NumPy arrays passed through PyO3/numpy bindings.
metadata:
author: eo-processor maintainers
version: "1.0"
---
# Rust + PyO3 function skill (eo-processor)
Use this skill when you need to add or change a Rust implementation that is exposed to Python. The goal is: **correct math + stable API + high performance + predictable behavior**.
## Activation checklist (before you code)
1. Identify the *public Python name* of the function (e.g., `ndvi`) and confirm whether this is:
- a new function, or
- a behavior change, or
- a bug fix (no API change).
2. Find existing patterns in:
- `src/` for Rust/PyO3 style
- `python/eo_processor/` for exports/docstrings
- `python/eo_processor/__init__.pyi` for typing
- `tests/` for numerical expectations.
3. Define expected behavior for:
- shape mismatches
- division by zero / near-zero denominators
- NaN/Inf handling
- dtype support and output dtype.
If anything is ambiguous, decide based on existing functions in this repo (stay consistent).
---
## Design rules (repo conventions)
### 1) Keep the core pure
- Rust compute kernels should be **pure functions**: no file I/O, no network access, no global state.
- Deterministic results for deterministic inputs.
### 2) Validate shapes early, fail clearly
- If the function expects aligned shapes, check and raise a Python-friendly error.
- Do not silently broadcast unless the repo already does that everywhere.
### 3) Numerical stability is not optional
- For normalized differences and ratios, guard denominators with a small epsilon.
- Use a *consistent* epsilon strategy across similar functions.
- Make NaN behavior explicit (propagate vs sanitize) and match existing functions.
### 4) Avoid unnecessary allocations
- Prefer single-pass loops where possible.
- Avoid creating multiple temporaries for large rasters.
- If using ndarray operations, be mindful of intermediate allocations.
### 5) Don’t introduce `unsafe` without a compelling reason
- If you think you need `unsafe`, stop and provide:
- justification (benchmark evidence),
- a safety argument,
- tests that would catch UB-like symptoms.
---
## Implementation workflow (step-by-step)
### Step A: Specify the API contract
Write down:
- signature at Python level (args, defaults, return)
- expected input shapes and dtypes
- output dtype
- error behavior & messages
Example contract for a normalized difference:
- Inputs: `a`, `b` float arrays (1D or 2D depending on existing patterns)
- Output: float array same shape
- Math: `(a - b) / (a + b + EPS)`
- Errors: shape mismatch -> `ValueError` (or the project’s standard)
### Step B: Implement Rust function with PyO3 glue
Typical structure:
1. Accept `Python` token and `PyReadonlyArray*` inputs.
2. Convert to `ndarray::ArrayView*` using `.as_array()`.
3. Validate shape compatibility.
4. Allocate output (or create new array) and fill it.
5. Return `PyArray*` via `into_pyarray(py)` (or existing conventions).
Prefer returning a new array rather than mutating inputs.
### Step C: Register with the module
- Ensure the `#[pyfunction]` is added to the module in the `#[pymodule]` initializer.
- Keep ordering, naming, and grouping consistent with neighboring functions.
### Step D: Maintain Python surface coherence
Whenever you add/rename a Rust-exposed function, you almost always need to update:
- `python/eo_processor/__init__.py` (exports and `__all__`)
- `python/eo_processor/__init__.pyi` (typing stub)
- docs (`README.md` at minimum; `docs/` if used by the repo)
- tests (`tests/`)
If the change is internal-only, do not export it publicly.
---
## Error handling guidance (PyO3)
- Prefer returning `PyResult<T>`.
- Use Pythonic errors; typical choices:
- `ValueError` for invalid shapes/values
- `TypeError` for wrong types (rare if signature enforces arrays)
- Error messages should be:
- actionable
- short
- consistent across functions.
---
## Performance guidance
### What to optimize first
1. Avoid extra allocations / temporaries.
2. Ensure tight loops with minimal branching in the inner loop.
3. Use contiguous iteration patterns when possible.
### When to benchmark
Benchmark if:
- you’re adding a new kernel, or
- you changed the inner loop, or
- you changed dtype handling.
Don’t claim speedups without before/after numbers and array sizes.
---
## Testing expectations (what to add)
Add tests that cover:
1. **Correctness**: known inputs with known outputs (small arrays).
2. **Stability**: near-zero denominators; ensure outputs are finite if expected.
3. **Shape behavior**: mismatch raises the correct error.
4. **NaN behavior**: if inputs contain NaN, outcome matches contract.
5. **Dtype behavior**: float32/float64 if supported by project conventions.
For numerical comparisons:
- use tolerances appropriate for float64/float32
- avoid exact equality for floats unless intentionally exact.
---
## Documentation expectations
For a new EO index function, document:
- the formula
- what each band represents
- recommended input scaling (e.g., reflectance 0–1 vs scaled ints)
- typical output range and interpretation
Keep docs short in `README.md` and move deeper material to `docs/` if present.
---
## “Done” checklist (must be true before you stop)
- [ ] Rust function implemented with correct math and shape checks
- [ ] Function is registered in the PyO3 module
- [ ] Python exports updated (if public)
- [ ] Typing stubs updated (if public)
- [ ] Tests added/updated and pass
- [ ] Docs updated (if public)
- [ ] Lint/format gates pass for Rust (+ Python if touched)
- [ ] No unnecessary new dependencies
---
## Common pitfalls (avoid these)
- Returning wrong shape due to accidental broadcast or flattening
- Implicit dtype casts causing precision loss
- Division by zero producing noisy Inf/NaN without being documented
- Adding a Rust function but forgetting Python `__init__.py` / `__init__.pyi`
- Performance regressions from extra temporaries
- Changing behavior without updating tests and docs
---
## Local references (repo)
- Engineering rules & checklists: `AGENTS.md`
- User docs and API overview: `README.md`, `QUICKSTART.md`
- Existing workflows/examples: `WORKFLOWS.md`, `examples/`
- Rust crate entrypoints and module registration: `src/`
- Python exports/stubs: `python/eo_processor/`
- Tests: `tests/`
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!