SPy 🥸: Static Python




A modern language for the next decades

For Python users, communities and ecosystem



Pierre Augier

Compute! 2026

Python in 2026

Why Python dominates

  • Science, data science, machine/deep learning, AI
  • Unmatched ecosystem (NumPy, SciPy, PyTorch, …)
  • Massive community

Where Python falls short

  • ⚡ Low-level performance
  • 🚀 Fast startup
  • 📦 Small self-contained binaries
  • 🌐 Browser deployment
  • 🔬 Microcontrollers / edge computing
  • 🏷️ Typing as good as in static languages

Without structural improvements, Python risks losing ground: new projects in critical domains may start elsewhere.

Python problems

  • Too dynamic to be compiled well

    • the type of an object can change at any time
    • builtins can be modified at run time
    • modules can be modified after their definition

    Note

    A high price for a dynamism that real code almost never uses.

  • High level only: no access to the machine level

  • Not self-hosting: the interpreter cannot be written in Python itself

    RPython / PyPy come close, but diverge from CPython.

Existing solutions

  • C extensions: they tie the whole ecosystem to CPython internals
  • Partial solutions: Cython, Numba, PyPy, JAX, …

We love Python because

  • Readability
  • A gentle learning curve
  • Great APIs in packages
  • Usability: interpreter, debugger
  • Interoperability with C and other native languages

SPy keeps all that, and adds low-level control and a real compiler.

SPy 🥸: A New Companion Language for Python

A great project that the Python community should invest in

SPy 🥸: a new language, a companion for Python

Created by Antonio Cuni (PyPy, PyScript, Anaconda).

  • S for Static, and also the letter after R: successor of RPython
  • Think of a “Python 4”: deliberate incompatibilities with Python 3, but good interop
  • Built from first principles, learning from modern languages (Zig, Rust, …)

Target

  • Python-like high level, with an interpreter and a debugger

  • and low level, with a real compiler

  • Static typing (annotations) ⇒ the type checker guarantees no type error at run time

    Less of a burden with type inference and AI assistance.

  • Predictable performance

SPy is not

A fast Python interpreter (SPy code lives in .spy files), nor yet another tool to accelerate Python code

How? Language design

  • Easy to use and to implement

    • Garbage collector (or manual memory), no lifetimes or ownership
  • Few differences with Python: scopes, imports, less dynamism, two new keywords (var and const)

  • Low level through unsafe (pointers and co)

  • Clear split between compile time (fully dynamic) and run time (static)

    Blue functions control what runs at compile time; types are built there, by ordinary SPy code

Today vs target: a tower under construction

SPy will be a large, high tower. The foundations are built, and the tower is going up.

  • Today we can only show the foundations and the lower walls
  • We can already describe the plans

At the high level, SPy will be very close to Python: most real Python modules (starting with the stdlib) should work with few changes.

Low level in SPy, today

Low level stays in SPy (in Python, low level means C)

  • Structs, like in C and Rust:

    @struct
    class Point:
        x: f64
        y: f64
    
        def length(self) -> f64:
            return (self.x**2 + self.y**2)**0.5

    Special methods, @classmethod, @staticmethod, properties, …

  • unsafe pointers, gc_alloc, raw_alloc, …

  • Memory: (Boehm) GC or manual management

  • Memory manipulation: ptr_copy, ptr_copy_slice, ptr_move, …

  • Alignment in the pointer types

SPy Today: Between Alpha and Beta

What already works ✅

  • Interpreter + compiler + debugger (spdb)
  • Low level and GC
  • Object oriented programming (with structs)
  • Functions, for/while, range(), slice(), f-strings, decorators
  • list, dict, tuple
  • Generics (functions and structs)
  • Simple I/O, file operations, CLI arguments
  • WASM / Emscripten target, playground

Not yet ⏳

  • try/except, with
  • Python-like (heap allocated) classes, inheritance
  • Protocols, Variants
  • …

More in the roadmap part.

Already usable by Python developers for real programs. A good time to get interested.

Core Innovation: Blue/Red and Redshifting

  • 🔵 Blue expressions & functions

    • Values known at compile time
    • @blue functions run (interpreted) at compile time
  • 🔴 Red: everything else

redshift phase (compile time)

  • Full dynamism available
  • Partial evaluation: 🔵 + 🔵 → 🔵, 🔵(🔵, 🔵) → 🔵
  • Base of the type system, concretizes generic types
  • Static dispatch & type checking

Redshifted typed AST (🔴 + 🔵): fully static. It is what runs at run time and what goes into the compiled binary.

Similar to Zig’s comptime: compile-time execution of the same language, enabling zero-cost generics.

Example: Blue/Red and Redshifting

A generic function: types computed at compile time (blue), typed code at run time (red)

adder.spy

def add[T](x: T, y: T) -> T:
    return x + y

def main() -> None:
    add_i32 = add[i32]         # evaluated at compile time
    result_i = add_i32(3, 4)   # fast red execution at runtime
    result_f = add[f64](3.0, 4.0)

Result of spy redshift adder.spy

def main() -> None:
    result_i$0: i32 = `add[i32]`(3, 4)
    result_f$0: f64 = `add[f64]`(3.0, 4.0)

def `add[i32]`(x: i32, y: i32) -> i32:
    return `operator::i32_add`(x$0, y$0)

def `add[f64]`(x: f64, y: f64) -> f64:
    return `operator::f64_add`(x$0, y$0)

Generics: syntactic sugar for @blue.generic

What you write:

def dot[T](a: T, b: T) -> T:
    return a.x*b.x + a.y*b.y


@struct
class NdArray[DTYPE, NDIM]:
    ...

No magic: a blue function runs at compile time and returns the red function

@blue.generic
def dot(T):
    def impl(a: T, b: T) -> T:
        return a.x*b.x + a.y*b.y
    return impl



High level is built from low level and rich compile-time metaprogramming, without macros. This is how the stdlib implements list, tuple, dict, array, print, … See Antonio’s talk at PyConIt 2026.

The Compilation Pipeline

%%{init: {'theme': 'base', 'themeVariables': {'fontSize': '20px', 'edgeLabelBackground': 'transparent'}, 'flowchart': {'rankSpacing': 20, 'nodeSpacing': 20, 'padding': 0}}}%%
graph LR
    SRC["*.spy source"]
    MOD["Untyped AST<br/>+ scopes<br/>+ modules"]
    RED["Typed AST<br/>(Redshifted)"]
    C["C source (.c)"]
    OUT1(["output"])
    OUT2(["output"])
    NAT["native exe"]
    WASI["WASI exe"]
    EM["Emscripten"]
    OUT3(["output"])

    SRC -->|parse<br/>+ scope analysis<br/>+ import <br/>+ astcompile| MOD
    MOD -->|redshift<br/>+ type checking| RED
    MOD -->|interp.| OUT1
    RED -->|interp.| OUT2
    RED -->|cwrite| C
    C -->|cc| NAT
    C -->|cc| WASI
    C -->|cc| EM
    NAT --> OUT3
    WASI --> OUT3
    EM --> OUT3

    classDef output fill:#1D9E75,stroke:#0F6E56,color:#E1F5EE
    classDef exe    fill:#BA7517,stroke:#854F0B,color:#FAEEDA
    classDef src    fill:#534AB7,stroke:#3C3489,color:#EEEDFE
    classDef ast    fill:#185FA5,stroke:#0C447C,color:#E6F1FB
    classDef c      fill:#993556,stroke:#72243E,color:#FBEAF0

    class OUT1,OUT2,OUT3 output
    class NAT,WASI,EM exe
    class SRC src
    class MOD,RED ast
    class C c

Run SPy code:

spy hello.spy              # interpret
spy hello.spy --spdb       # debugger
spy redshift -x hello.spy  # redshift and interpret
spy build -x hello.spy     # native build and execute
spy build --target emscripten hello.spy

Inspect the pipeline:

spy parse hello.spy       # SPy AST
spy scopes hello.spy
spy imports hello.spy
spy colorize hello.spy    # blue/red expressions
spy redshift hello.spy    # typed AST

SPy in the Landscape of Compiled Languages

SPy 🥸 is a language in its own right, so let’s compare it with other languages.

C++ Rust Mojo Go SPy
Memory model Manual Ownership Ownership GC GC + Manual
Interpreter ❌ ❌ ✅ ❌ ✅
Pythonic syntax ❌ ❌ ✅ ❌ ✅
Python interop via bindings via bindings ✅ ❌ ✅ (goal)
Compile-time metaprogramming templates macros Parameters/comptime limited blue/red
WASM first-class ❌ partial ❌ partial ✅
Status mature mature stable mature alpha

SPy versus Mojo

  • Mojo: Python-like syntax with C++/Rust-level control (ownership, lifetimes, MLIR).

    A replacement: leave Python’s world for a more powerful but far more complex language.

  • SPy: stay close to Python, keep the implementation approachable, use a GC.

    A companion that the Python community can actually adopt.

SPy 🥸 in Action

Not shown today, see my JDev talk (SPy and Py-NI):

  • SPy on AWS Lambda (demo by Antonio Cuni): Python-like ergonomics, startup time and performance of Go or Rust
  • SPy in the browser (JSFFI, PR #484): no PyScript, no Pyodide; ~91 KB in total vs ~10 MB

Array Computing: Loop Fusion

Efficient array computing à la NumPy/Pythran, written in SPy.

  • Minimal array library with loop fusion: several operations merged into a single pass

    # User writes this:
    result = 2 * x + sin(x)
    
    # SPy generates a single fused loop:
    # for i in range(n): result[i] = 2 * x[i] + sin(x[i])
  • No intermediate allocations, and the C compiler can use SIMD instructions

  • Same technique as Pythran with C++ expression templates

The library uses advanced SPy (blue.metafunc, __convert_from__, @force_inline, …), but its users only write NumPy-like code.

Vectorization: explicit SIMD 🚧

SIMD: Single Instruction, Multiple Data

🚧 Soon supported 🚧

from unsafe import gc_ptr, cast, align, sizeof
from simd import SIMD, simd_width_of

W = simd_width_of(f64)
V = SIMD[f64, W]
ALIGN = align(W * sizeof(f64))

def dot(a: gc_ptr[f64, ALIGN], b: gc_ptr[f64, ALIGN], n: i32) -> f64:
    assert n % W == 0
    va = cast[gc_ptr[V]](a)
    vb = cast[gc_ptr[V]](b)
    acc = V.zeros()
    for i in range(n // W):
        acc += va[i] * vb[i]
    return acc.sum()
  • Alignment is encoded in the type

SPy’s Impact on the Python Ecosystem

What SPy changes for scientific Python

SPy as a better Cython and Pythran

Write in SPy

  • Python extensions
  • C wrappers
  • Python packages like NumPy
Cython SPy
Syntax Hybrid C/Python Python-like
Output Python extensions only Extensions, native, WASM
Standalone binary ❌ ✅
Interpreter ❌ ✅
Debugger gdb (C level) spdb (SPy level)

SPy + NumSPy (the Python array API standard, implemented in SPy): also a better Pythran!

  • Whole-program optimisation: the C compiler sees both the application and the array library
  • Not tied to CPython internals: with Py-NI’s universal ABI, it runs on CPython, PyPy, GraalPy, …

From Prototype to Production

One language, from prototypes to robust and efficient programs.

Prototype → production without rewrite

  1. Prototype in Python (or SPy)
  2. Add type annotations progressively
  3. Compile: type errors caught at compile time, native speed
  4. No rewrite in C, C++ or Rust

Fast startup, easy distribution

  • Native CLI tools: startup like Go or Rust, no Python runtime on the target machine
  • The same code targets native, WASI and the browser

Where SPy is Going

Serious foundations, built one careful step at a time

How SPy is Built

Principles

  • Simple implementation:

    • a simple C backend (no LLVM or MLIR) to get a usable SPy early
    • a GC language
  • Features added one at a time, with care: always look for the right abstraction at each level

  • Improve the compiler so that things are done in SPy, at app level, not built into the compiler

Three parts

  1. libspy: a tiny C library
  2. Interpreter / compiler (“interp level”): in SPy (today written in Python), with rich compile-time metaprogramming
  3. Stdlib (“app level”): in SPy
    • core types (tuple, list, dict, metaclasses, …)
    • a “best of” of the Python stdlib

Five Pillars of the Roadmap

%%{init: {'theme': 'base', 'themeVariables': {'fontSize': '20px', 'edgeLabelBackground': 'transparent'}, 'flowchart': {'rankSpacing': 40, 'nodeSpacing': 25, 'padding': 0}}}%%
graph LR
    P1["1. Core language<br/>(including metaprogramming)<br/>North Star: self-hosting"]
    P2["2. SPy/C integration"]
    P3["3. Stdlib<br/>(libraries in SPy)"]
    P4["4. Memory management<br/>(refcounting, other GCs)"]
    P5["5. SPy/Python integration"]

    P1 --> P3
    P2 --> P3
    P1 --> P5
    P2 --> P5
    P4 --> P5

    classDef key fill:#1D9E75,stroke:#0F6E56,color:#E1F5EE
    classDef other fill:#185FA5,stroke:#0C447C,color:#E6F1FB
    class P1,P2 key
    class P3,P4,P5 other

Current focus: core language and SPy/C integration. They unlock both the stdlib and SPy usable from Python (as Python extensions).

Two Languages, One Common Core

Python only dynamic features: - change an object's type - modify module globals Static Python most of the language: same behavior in both languages SPy only blue functions, redshifting structs, unsafe, SIMD var and const

SPy is neither a subset nor a superset of Python.

Self-Hosting as Dogfooding

The interpreter, compiler and CLI are meant to be valid Python and valid SPy, in the common part.

  • Run with CPython (today), or with SPy: interpreted or compiled
  • Dogfooding: if an application as large and complex as SPy can be written in static Python, SPy can serve many large Python applications

The stdlib is not concerned by this constraint.



SPy will be both:

  • a complete language, with all its own features
  • a compiler for a very large subset of Python (including heap classes with inheritance)

How to reach Self-Hosting

Goal: interpreter and compiler written in SPy (today: Python) ⇒ big performance boost.

What is needed, at least:

  • with and try / except
  • Heap classes with inheritance (like Python classes)


SPy’s answer: more powerful compile-time metaprogramming

⇒ metaclasses written as ordinary SPy code, not compiler magic.

class ClassMetaClass(StructType):
    ...

class Point(metaclass=ClassMetaClass):
    ...

Then heap classes (final, inheritance), protocols and variants can all live in the stdlib.

Stdlib: adapting CPython and PyPy code

Once the core language and SPy/C integration are ready:

The Python stdlib is already close to static Python:

  • C libraries, C code and

  • “static Python”, without crazy tricks;

    No changing an object’s type, no modifying a module’s globals (possible in Python, not in SPy)


⇒ many SPy stdlib packages can be adapted from CPython and PyPy code.

  • C wrapper in SPy for packages like posix, socket, sqlite
  • SPy for stdlib modules written in Python


Similar argument for NumSPy, with implementations in Numba and Codon.

SPy in a Couple of Years

A language for Python people

  • A compiler for a very large subset of Python
  • Gentle, progressive learning curve
  • Easy to learn and use for Python users, at every level
  • Nice workflow: interpreter and debugger, then debug and release builds

A powerful language

  • Low-level programming
  • Rich compile-time metaprogramming
  • Advanced modern types: Protocols and Variants (with match)
  • Explicit SIMD and parallelism

SPy: a project the Python community should invest in

Why invest in SPy?

  • 🐍 A companion for Python, not a replacement: Python’s readability, plus low level, native speed, WASM and Python interop
  • ✅ Already real: between alpha and beta, usable today
  • 🏗️ A serious, long-term design: abstractions written in SPy rather than built into the compiler, with a clear path to self-hosting
  • 🎯 Python + SPy in 2030 = “1.5 languages” on a solid, future-proof foundation

How to help

⭐ Star SPy on GitHub: 2 seconds, real impact.

Thank You

Pierre Augier CNRS researcher — LEGI, Grenoble



Slides produced with Quarto and the assistance of a language model.