// PYTHONRS — PYTHON IN RUST

pythonrs v0.1.10 · Python on fusevm · lex/parse → AST → bytecode → Cranelift JIT · transparent rkyv cache on every run · AOT native-exe via --build · a fusevm language host (with zshrs, stryke, awkrs, elisp, rubylang) · MIT · in active development

Report GitHub Issues
// Color scheme

>_PYTHONRS REFERENCE

A compiled Python runtime written in Rust. Source is lexed and parsed to an AST, lowered to fusevm bytecode, and executed on the same language-agnostic VM + three-tier Cranelift JIT that hosts zshrs, stryke, awkrs, elisp, and rubylang. Every run transparently caches its bytecode via rkyv, and python --build bakes a script into a native executable. In active development.

What it is

pythonrs runs Python programs as ordinary compiled bytecode: it lexes and parses the source, lowers it to fusevm instructions, and lets the shared engine execute and JIT-compile them. There is no bespoke VM or tree-walker — arithmetic and comparisons lower to native fusevm ops so the Cranelift JIT can trace hot loops, while Python-specific behaviour (truthiness, attribute and method dispatch, str/list concatenation, bignum promotion, object construction) is served by the PyHost object heap.

It is another language hosted on fusevm, the shared bytecode VM and Cranelift JIT behind zshrs (the shell), stryke (the language), awkrs (AWK), elisp (Emacs Lisp), and rubylang (Ruby). pythonrs carries no VM or JIT of its own.

To our knowledge it is the first compiled standalone Python runtime that both transparently caches bytecode via rkyv on every run and AOT-compiles a script to a native executable.

Architecture

The pipeline mirrors how zshrs hosts zsh and rubylang hosts Ruby:

Python source  →  lexer  →  parser (AST)  →  lower to fusevm bytecode  →  fusevm VM + Cranelift JIT
                                                    │
                                          PyHost object heap (str/list/dict/tuple/set/instances)

fusevm-hosted

No local vm.rs / jit.rs. Python is lowered to fusevm bytecode and executed on the shared three-tier Cranelift JIT; jit-disk-cache persists native code across runs.

Transparent rkyv cache

python foo.py hashes the source, consults ~/.pythonrs/scripts.rkyv, and on a hit runs the compiled chunks directly — lex/parse/lower skipped. No flags, no separate build step, no __pycache__ ritual.

AOT native executable

python --build foo.py emits a standalone native binary via fusevm::aot, linked against the pythonrs runtime staticlib, that runs the script with no interpreter present.

Reference-typed objects

str, list, dict, tuple, set, and instances live on the PyHost heap behind Value::Obj handles, so a.append(x) mutates in place — real Python reference semantics, not value copies.

Example

def fib(n):
    return n if n < 2 else fib(n - 1) + fib(n - 2)

print(", ".join(str(fib(i)) for i in range(11)))
# => 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55

total = 0
for x in [1, 2, 3, 4]:
    total += x
print(total)          # => 10

squares = [n * n for n in range(5)]
print(squares)        # => [0, 1, 4, 9, 16]

print(len({**{"a": 1, "b": 2}}.keys()))   # => 2
print(f"{fib(10):,}")                      # => 55

Status & roadmap

The table below reflects the current state of the tree. See BUGS.md for the honest ledger of what is not yet carried.

ComponentStateNotes
Lexer / parser → ASTImplementedIndentation-significant tokenizer (INDENT/DEDENT/NEWLINE), f-strings (src/lexer.rs, src/parser.rs).
AST → fusevm bytecode loweringImplementedNo local VM; native arithmetic + CallBuiltin dispatch (src/compiler.rs).
Object heap & method dispatchImplementedstr / list / dict / tuple / set / instances (src/host.rs, src/builtins.rs).
Kernel builtins & per-type methodsImplementedprint / len / range / … plus str, list, dict, set methods. Growing.
Functions, closures, *args / **kwargsImplementedDef-site splat and keyword args, defaults, real closures, and full call-site */** unpacking plus literal spreads.
Classes, inheritance, operator dundersImplemented__init__, instance attrs, arithmetic/comparison dunders with reflected fallbacks, __getitem__/__len__/__iter__/__repr__ (src/host.rs).
Comprehensions (list/dict/set), f-stringsImplementedFormat mini-language (fill/align/sign/width/,/.prec/type); comprehensions get their own scope, so the loop variable does not leak.
Exceptions (try/except/finally/raise)ImplementedTyped classes; with desugars to try/finally over __enter__/__exit__.
Arbitrary-precision intImplementedBignum promotion across + - * ** // % and the bitwise ops & | ^ << >> — the earlier i64 cap is gone.
Standalone python binary + REPLImplementedRun .py files, -c one-liners, interactive REPL (src/main.rs, src/repl.rs).
Transparent rkyv bytecode cacheImplementedVersioned on-disk shard at ~/.pythonrs/scripts.rkyv, consulted on every run (src/cache.rs).
AOT native executable (--build)ImplementedStandalone binary via fusevm::aot linked to the runtime staticlib (src/aot_native.rs). Requires the libpython-free build — a stdlib-ffi binary refuses up front.
Parity harness vs reference python3ImplementedDifferential corpus diffed byte-for-byte against CPython (src/bin/parity.rs).
Generators, async/await, match/caseImplementedStackful corosensei generators with full yield from delegation, a native fusevm asyncio event loop, and PEP 634 structural pattern matching.
LSP server (--lsp)PartialBuiltin/keyword/method completion, position-aware hover, and diagnostics from the real parser; go-to-def and signature help not yet (src/lsp.rs).
DAP adapter (--dap)PartialBreakpoints, step in/out/over/continue, stack trace, locals, expression evaluate, and program-stdout capture; watch expressions not yet (src/dap.rs).
Standard libraryPartialThe stdlib-ffi bridge (on by default) serves the real CPython stdlib over an embedded libpython; sys stays wholly native and math/collections/functools/contextlib resolve native arms first. A --no-default-features build drops the bridge and serves import from the vendored pylib/ tree. See BUGS.md.
Diagnostics (--doctor, --cacheview, --tiers)ImplementedRuntime/CPython/cache/env report, an rkyv-shard listing, and a per-chunk fusevm execution-tier report (src/tiers.rs).

Why pythonrs

Compiled, not bytecode-walked

CPython compiles to its own bytecode and walks it in C; pythonrs lowers Python to fusevm bytecode and JITs hot paths through Cranelift — the same architecture that makes zshrs and stryke fast.

Cache on every run

No __pycache__ dance and no separate build step: the rkyv shard is consulted transparently on every python foo.py, so warm starts skip lex/parse/lower entirely.

One shared engine

Bug fixes and JIT improvements in fusevm benefit zshrs, stryke, awkrs, elisp, rubylang, and pythonrs at once.

AOP intercepts

A glob-matched before/after/around method-intercept registry (src/intercepts.rs) — aspect weaving over Python method calls, the same design as zshrs's function intercepts.

Cache benchmark

The rkyv shard skips lex/parse/lower on a warm run — including every imported module, which is where the win lands. The gain scales with how much source a run compiles: a tiny import-free script is already sub-parse-time, so warm ≈ cold; a run that pulls in stdlib modules amortizes their compilation once and reads them back from the shard on every later run.

WorkloadCold (first run: compile + store)Warm (cache hit: load only)Speedup
6-line script, no imports7.3 ± 0.6 ms6.4 ± 0.4 ms≈1× (neutral — nothing to skip)
3 stdlib imports (~a dozen modules transitively)69.1 ± 3.4 ms19.7 ± 0.8 ms3.5× (−49 ms)
10 stdlib imports (21 cached modules)102.2 ± 4.5 ms28.6 ± 1.0 ms3.6× (−74 ms)

cargo build --release binary, hyperfine -N (80 runs each), isolated cache dir. Cold clears ~/.pythonrs/scripts.rkyv before each run so it pays the full compile plus the shard write; warm is a straight cache hit. The shard is a single content-keyed store shared across concurrent processes (an exclusive flock serializes writers so no entry is lost), deduped by source hash — the 10-import run leaves 21 module entries in a 3.1 MiB shard. Import-free scripts see no benefit by design; the cache pays off precisely when a run compiles real amounts of source.

Building from source

pythonrs builds as a standalone Rust crate (it is not a workspace member of the meta repo):

# clone
git clone https://github.com/MenkeTechnologies/pythonrs
cd pythonrs

# build (release; produces target/release/python + libpythonrs.a)
# the default build links an embedded libpython (the stdlib-ffi bridge),
# so CPython must be present at build time
cargo build --release

# run a script, a one-liner, or the REPL
./target/release/python script.py
./target/release/python -c 'print(sum(range(1, 101)))'
./target/release/python --repl

# diagnostics
./target/release/python --doctor
./target/release/python --cacheview
./target/release/python --tiers script.py

# AOT-compile a script to a native ./script executable
# --build needs the libpython-free runtime; a stdlib-ffi binary refuses up front
cargo build --release --no-default-features
./target/release/python --build script.py

# run tests
cargo test

fusevm is pulled from crates.io with the jit, jit-disk-cache, aot, and ffi features. Set PYTHONRS_TRACE=1 to log cache hit/miss to stderr, or PYTHONRS_CACHE=0 to disable the bytecode cache entirely.

License

pythonrs is MIT licensed — free and open source. See LICENSE.

Repository & links