I built Tarvos, an open-source compiler that takes a defined, statically analyzable subset of Python and compiles it into standalone native executables using Rust.
The basic pipeline looks like this:
Python
↓
Ruff Parser
↓
Tarvos AST
↓
Semantic Analysis / Type Inference
↓
Tarvos IR
↓
Optimization
↓
Rust Code Generation
↓
rustc
↓
Native Executable
The interesting part isn't simply converting Python syntax into Rust syntax.
The real challenge is preserving Python semantics while moving execution into a native compilation pipeline.
Why Tarvos?
Python is incredibly productive, but executing Python normally means relying on the Python runtime and its dynamic semantics.
I wanted to explore a different question:
How much of Python can be compiled into a real native executable when the compiler knows enough about the program?
That led to Tarvos.
It is not a replacement for CPython, and it doesn't attempt to compile every Python program.
Instead, Tarvos deliberately targets a defined subset that can be analyzed and lowered safely.
The Supported Subset
Tarvos currently supports things such as:
int, float, bool, str, lists and tuples
Arithmetic, bitwise and logical operations
if, for, while, break, continue
Functions and type inference
List comprehensions
Native math operations
Native os.path and time operations
Local Python module imports
CPython-compatible behavior for supported cases
The compiler also handles cases where Python semantics differ from straightforward native operations.
For example, Python's / and // don't mean the same thing, especially when negative values are involved.
These details matter when the goal is compilation rather than simply producing code that looks similar.
From AST to IR
The compiler does not directly translate Python syntax into Rust.
Python source is parsed first and represented internally using Tarvos's own structures.
The program is then lowered into an intermediate representation.
That gives the compiler a place to perform transformations before code generation.
For example:
Python Source
↓
AST
↓
Semantic Analysis
↓
IR
↓
Optimization
↓
Rust
This separation is important because optimization and code generation shouldn't have to understand every detail of the original Python syntax.
Optimization
Tarvos already has compiler-level optimization passes including:
Constant propagation
Copy propagation
Dead-code elimination
Loop-related optimizations
Native lowering for supported operations
The goal is not to make Python "look like Rust."
The goal is to generate better native code from the information available to the compiler.
Native Rust Code Generation
Once the program has passed analysis and optimization, Tarvos generates Rust code.
That code is then compiled using rustc to produce the final native executable.
The resulting executable is intended to run without requiring the Python runtime on the target machine.
That's one of the main differences between simply packaging a Python application and compiling supported Python into native code.
Unsupported Python
Python is extremely dynamic.
Trying to pretend that every Python feature can be statically compiled would be misleading.
Tarvos therefore explicitly diagnoses unsupported features instead of silently generating incorrect behavior.
Where applicable, Tarvos can use a hybrid fallback path for functionality that cannot currently be lowered natively.
This is an important part of the compiler design:
unsupported does not mean silently wrong.
Testing Against CPython
A compiler can produce an executable successfully and still be wrong.
So Tarvos includes compatibility testing against CPython for supported programs.
The goal is to compare behavior rather than simply checking whether compilation succeeds.
That includes things such as:
Output
Errors
Exit codes
Supported semantic behavior
This makes compatibility testing part of the compiler workflow rather than an afterthought.
What's Next?
The next phase is focused on two things.
- More Python
Expanding support for more advanced Python features while keeping the compiler's semantics explicit and testable.
- Performance
I want to benchmark specific workloads across:
CPython
Nuitka
Numba
Cython
Tarvos
Native Rust
But I'm deliberately avoiding blanket claims like:
"Tarvos is X times faster than Python."
Different tools solve different problems, and performance depends heavily on the workload.
The goal is to produce reproducible benchmarks on identical workloads and understand where each approach performs well.
Why I Built It
Tarvos started as an engineering problem I wanted to understand.
It pushed me much deeper into:
Python internals
Compiler frontends
ASTs
Intermediate representations
Type inference
Optimization
Rust
Native code generation
Systems engineering
And it is still evolving.
The project is open source, and I'd genuinely like feedback from people working with Python, Rust, compilers and systems programming.
What would you improve, add, or change in Tarvos?
Top comments (0)