A Gofannon agent that takes a Rust crate and produces a Python wrapper project using PyO3 and maturin.
Rust crate goes in, Python package comes out.
Two Gofannon agents work together:
rustopyian (Constructinator) — fetches crate metadata, audits licenses, generates wrapper code via LLM, stores files in data_store, optionally pushes to GitHubrustopyian_export (Exporter) — reads generated files from data_store and pushes them to a GitHub repo via the Git Trees APIThe Constructinator calls the Exporter automatically when publish=true is set along with output_repo and output_token. You can also run the Exporter separately.
For fine-grained PATs (recommended), the token needs these repository permissions:
.github/workflows/ filesFor classic PATs, the repo scope covers both.
The token must be scoped to the target repository.
Create an agent in Gofannon using rustopyian.py and configure these inputs:
| Input | Required | Default | Description |
|---|---|---|---|
crate_name | yes | — | Crate name on crates.io (e.g. swhid) |
crate_repo | yes | — | Source GitHub repo URL (e.g. https://github.com/swhid/swhid-rs) |
package_name | no | {crate_name}-py | Python package name |
github_pat | no | — | GitHub PAT for reading source (raises API rate limits) |
output_repo | no | — | GitHub repo to push to, as owner/repo (e.g. apache/swhid-py) |
output_token | no | — | GitHub PAT with write access to output_repo |
output_branch | no | main | Branch to push to |
license | no | Apache-2.0 | License for the generated wrapper |
license_header | no | ASF header | Per-file license header text (added to all source files) |
copyleft_ok | no | false | Set true to allow copyleft dependencies |
publish | no | false | Set true to push generated files to output_repo |
crate_name: swhid crate_repo: https://github.com/swhid/swhid-rs
Files are stored in data_store. You retrieve them manually or run the Exporter.
crate_name: swhid crate_repo: https://github.com/swhid/swhid-rs package_name: swhid-py github_pat: ghp_readtoken123 output_repo: apache/swhid-py output_token: ghp_writetoken456 output_branch: main publish: true
The ASF license header is added to all generated source files by default. Override with license_header or set it to empty to omit.
Create a second agent in Gofannon using rustopyian_export.py. The Constructinator calls it automatically, but you can also run it standalone:
| Input | Required | Default | Description |
|---|---|---|---|
package_name | yes | — | Must match the Constructinator run (e.g. swhid-py) |
github_repo | yes | — | Target repo as owner/repo |
github_pat | yes | — | GitHub PAT with write access |
branch | no | main | Branch to push to |
commit_msg | no | Initial wrapper from Rustopyian | Commit message |
dry_run | no | false | Set true to list files without pushing |
package_name: swhid-py github_repo: apache/swhid-py dry_run: true
package_name: swhid-py github_repo: apache/swhid-py github_pat: ghp_writetoken456
| File | Description |
|---|---|
src/lib.rs | PyO3 bindings wrapping the crate's public API |
Cargo.toml | Rust dependencies (target crate + PyO3) |
pyproject.toml | Python package metadata for maturin |
python/{module}/__init__.py | Re-exports from the native module |
python/{module}/__init__.pyi | Type stubs for IDE support |
tests/test_{module}.py | Pytest test suite |
.github/workflows/ci.yml | CI for Linux, macOS, Windows × Python 3.9–3.13 |
.gitignore | Rust + Python ignores |
The generated code is a starting point. The LLM does its best to map the Rust API to Python, but it may get import paths, type sizes, or builder patterns wrong.
# 1. Clone the repo git clone https://github.com/apache/swhid-py.git cd swhid-py # 2. Install prerequisites curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh uv venv && source .venv/bin/activate uv pip install maturin pytest # 3. Build — the compiler tells you what's wrong maturin develop # 4. Fix src/lib.rs based on compiler errors, repeat until clean # 5. Format and test cargo fmt pytest tests/ -v # 6. Commit git add -A && git commit -m "fix: compile errors from initial generation" git push
Based on building swhid-py:
| Error | Fix |
|---|---|
cannot find type Error in crate | Type is in a submodule: crate::error::Error not crate::Error |
mismatched types: expected u64, found u32 | Struct fields are u64, not u32 — update the PyO3 function signature |
this method takes 1 argument but 2 were supplied | Method takes a struct (LineRange), not separate args |
expected Vec<Entry>, found &PathBuf | Use DiskDirectoryBuilder::new(path) not Directory::new(path) |
cannot import name ObjectType ... did you mean PyObjectType? | Missing #[pyclass(name = "ObjectType")] on the enum |
The agent checks every dependency's license:
| License family | Action |
|---|---|
| GPL-2.0/3.0, AGPL-3.0 | Block (unless copyleft_ok=true) |
| LGPL-2.0/2.1/3.0 | Block (unless copyleft_ok=true) |
| MPL-2.0 | Flag (weak copyleft) |
| MIT, Apache-2.0, BSD, ISC | Pass |
Optional dependencies behind feature flags are flagged but don't block. The agent records which features to avoid.
The generated CI encodes lessons from building swhid-py across all three platforms:
shell: bash on build steps (PowerShell doesn't expand *.whl globs)--interpreter python3.9 ... python3.13 on Linux (manylinux container), plain --release --out dist on macOS/Windows (avoids Python 3.14 which PyO3 0.23 doesn't support)actions/checkout@v5, actions/upload-artifact@v7 (Node 24)maturin[patchelf] (patchelf can't build on Windows)All data lives under the namespace rustopyian:{package_name}:
rustopyian:swhid-py/ files/src/lib.rs files/Cargo.toml files/pyproject.toml files/python/swhid_py/__init__.py files/python/swhid_py/__init__.pyi files/tests/test_swhid_py.py files/.gitignore files/.github/workflows/ci.yml metadata # crate info, API surface, flagged deps llm_raw_response # only if JSON parsing failed
Retrieve manually:
ns = data_store.use_namespace("rustopyian:swhid-py") ns.list_keys() # see everything ns.get("files/src/lib.rs") # get a specific file ns.get("metadata")["api_surface"] # wrapped API items
Built as the “Rustopyian Constructinator” — a conveyor belt where a Rust crate goes in and a Python package comes out. The first project through the pipeline was swhid-py, Python bindings for the SWHID v1.2 reference implementation. The lessons from that build (license auditing, API mismatch patterns, CI platform quirks) are encoded directly into the agent's LLM prompt and static templates.
See apache/tooling-trusted-releases#1154 for the original discussion.