Skip to content

aleo.codegen: build-time ABI→Python emitter - #47

Merged
iamalwaysuncomfortable merged 7 commits into
masterfrom
feat/aleo-codegen
Jul 13, 2026
Merged

aleo.codegen: build-time ABI→Python emitter#47
iamalwaysuncomfortable merged 7 commits into
masterfrom
feat/aleo-codegen

Conversation

@iamalwaysuncomfortable

Copy link
Copy Markdown
Member

What

A contract-agnostic, build-time code generator inside the main SDK: aleo.codegen turns any aleo-abi JSON description of a program into a Python module of frozen dataclasses with to_plaintext() encoders and from_plaintext() decoders — the Python analog of aleo-viem's veil-codegen, minus the typed contract factory (the facade's PreparedCall + hand-written verbs fill that role in Python).

python -m aleo.codegen --abi shield_swap.abi.json --out _generated.py
python -m aleo.codegen --config codegen.config.json   # multi-program mode

Emitted per program: struct dataclasses (encode + decode), record dataclasses (decode-only — scanners produce them, callers never construct them; _nonce rides along), a MAPPING_VALUE_DECODERS table, PROGRAM_ID, and the full ABI dict (so mapping key types stay recoverable at runtime, like the TS bindings' PROGRAM_ABI).

Why

Two things an ABI can't provide at runtime: static types (slot.sqrt_price completing in an IDE, pyright catching typos) and struct input encoding — the facade's _coerce_one deliberately rejects dicts, so struct-typed function arguments must arrive as preformatted plaintext strings. Generated to_plaintext() is how a typed client builds them. First consumer: the upcoming aleo-shield-swap package (follow-up PR).

Layout

  • aleo/codegen/runtime.py — stdlib-only plaintext parser + literal formatters that generated modules import (strict grammar: malformed plaintext raises instead of silently gluing tokens; None/null mapping values get clear errors)
  • aleo/codegen/_emit.py — ABI ty-tree → source emitter; validates every ABI identifier before interpolation (keywords / injection / reserved _nonce → generation-time ValueError), rejects cross-program and duplicate struct references that would emit NameError-at-import modules, topo-sorts nested structs
  • aleo/codegen/__main__.py — CLI
  • 19 tests, including a smoke test that generates from the real shield_swap_v3.aleo ABI (12 structs / 4 records / 19 mappings, vendored with _source provenance) and round-trips live-shaped plaintexts

Verification

  • python -m pytest python/tests -m "not slow" — 839 passed (19 new)
  • npx pyright on python/aleo/codegen — 0 errors
  • Adversarial review pass applied (emitter hardening: identifier validation, unresolvable-reference guards, strict parsing, signed field literals)

Build-time only: nothing in the aleo runtime imports this package, and no new dependencies.

🤖 Generated with Claude Code

iamalwaysuncomfortable and others added 7 commits July 13, 2026 14:43
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e test

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- validate ABI identifiers before interpolating into emitted source
  (keywords, non-identifiers, reserved _nonce -> generation-time ValueError)
- reject cross-program/undefined struct references and duplicate struct
  names at emit time instead of emitting NameError-at-import modules
- strict plaintext parsing: missing separators and malformed member names
  now raise instead of silently gluing tokens into strings
- clear TypeError/ValueError for None/'null'/empty mapping values
- accept signed field/group/scalar literals in fmt_fieldlike (-1field)
- emit the full ABI dict constant (mapping key types stay recoverable)
- fixture provenance (_source) per AGENTS.md; drop git-ignored spec path
  from the package docstring

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@iamalwaysuncomfortable
iamalwaysuncomfortable merged commit e050984 into master Jul 13, 2026
16 of 18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants