Examples
Each folder is one program that shows one thing. examples/run_all.py runs
all of them on the three paths (plain CPython, the Python backend, and LLVM
native) and diffs the output. A difference is a compiler bug.
Where a folder holds both <name>.py and <name>.ppy, the .ppy is what
ppy convert wrote, and examples/verify_conversions.py regenerates it to
check that it still does.
Basics
| Example |
What it shows |
| Basics |
Three small functions show fixed-width integer markers, a purity contract, and a per-function optimization level. |
| Arbitrary precision |
Integers stay Python integers: when a native multiply overflows a 64-bit word, the call goes back to CPython and still returns the exact answer. |
| Effects and contracts |
This example shows what @ppy.pure refuses, and how a value from a dynamic boundary gets back into typed code. |
| Classes |
Ordinary classes work when their fields are known statically, and a misspelled method is a compile-time error (E1202). |
| Native data |
This example shows which Python values cross the boundary as machine values, and which stay boxed. |
| Narrowing |
This example shows each form the checker narrows on, one function each. |
| Numerics |
PPy gives the same answers as CPython for overflow, floor division, and the sign of the remainder, cases where several other native compilers differ. |
| Value classes |
An all-scalar @dataclass has no boxed form in native code. |
| Tuples |
A tuple of known length and scalar elements is passed and returned unboxed. |
| Dynamic boundaries |
ppy.dynamic is the escape hatch for code strict PPy rejects, and this example shows what it costs. |
| Containers |
Element types inferred from first use, and the difference between mutating a container the function made and one it was given. |
| Exceptions |
Exception behavior is part of the contract: divide(1, 0) raises ZeroDivisionError and at([10, 20, 30], 9) raises IndexError at the same point, with the same type, whichever path runs them. |
| Strings |
String code checks in strict mode and is proven pure, but it stays on CPython, and the compiler says so. |
Native code
| Example |
What it shows |
| Buffers and JIT |
Borrowed memory, reassociation, and specialization each set how fast a numeric function runs, and each is chosen once in a signature or a decorator. |
| Algorithms |
Eight compute kernels measured against the same eight in C, and six judge problems timed the way a judge times them. |
| Threads |
A native function releases the GIL, so compute in it scales across threads. |
| Native memory |
Typed pointers, memory a function owns, a C function bound from libm, and a function exported as a C symbol. |
| Lanes and the machine |
This example shows ppy.simd, a few scalars operated on at once, and ppy.cpu, the machine as a facade with no instruction named. |
| Atomics and threads |
ppy.atomic and ppy.concurrent give you shared memory one operation at a time, and threads with what keeps them apart, over memory the program owns. |
| Parallel ranges |
for i in parallel.range(n) says the iterations may run at once. |
| Derivatives |
This example takes derivatives with ppy.grad and ppy.value_and_grad, which follow one rule table on every path, so the nine digits this program prints are the same nine everywhere. |
| Coroutines |
An echo server and its client in one program, written with ppy.aio. |
| Generics |
Generic functions use the Python 3.12 type-parameter syntax, and PPy infers the type arguments at each call and checks them against their bounds. |
| The toolbox |
One small program, run through each tool that looks inside its build: the IR at each stage, the source backends, the optimization report, the sanitizers, and profile-guided optimization. |
| Regular expressions |
A pattern compiled from a bytes literal is matched natively over a byte buffer. |
| Collections |
Five problems written with ppy.Vec, ppy.Deque, ppy.Heap, ppy.LinkedList, ppy.HashMap, and ppy.TreeSet, with no pointers in sight. |
Accelerators
| Example |
What it shows |
| GPU kernels |
A saxpy and a block reduction with shared memory and a warp shuffle, written once in ppy.cuda. |
| Tile kernels |
These GPU kernels work on tiles rather than threads: you write what a program does to a tile, and the compiler writes the threads, the shared memory, and the shuffles. |
| XLA |
@xla.jit compiles a function of floats, ints, and bools to StableHLO and runs each call on an XLA device. |
| Multi-GPU JAX training |
This trainer runs a data-parallel MLP over a mesh of every accelerator in the machine, and holds the result to a single-device run. |
Libraries
| Example |
What it shows |
| NumPy fusion |
An elementwise NumPy expression becomes one loop with no temporaries. |
| Pydantic |
Pydantic models are typed by the plugin and still validated by pydantic at run time. |
| Parallel fused kernels |
@ppy.parallel splits a fused NumPy loop across the worker pool, and the output is bit-identical to the serial kernel and to NumPy. |
| PyTorch ATen regions |
A function whose body is entirely curated tensor operations compiles into one C++ region that calls ATen directly: one Python round trip per call instead of one per operator. |
| GPT-2 XL: one region per block |
This example compiles each GPT-2 XL transformer block into one ATen region and measures it against PyTorch eager and torch.compile. |
| Training a torch MLP |
An ordinary PyTorch training script, converted by ppy convert with no hand editing. |
| Training a JAX MLP |
The same trainer as 21_training_torch with JAX, converted by ppy convert with no hand editing. |
| JAX export |
Build-time export of a @jax.jit function to StableHLO. |
| Serving over Uvicorn |
A raw ASGI callable and a converted FastAPI application, both served on Uvicorn through the same plugin. |
| Flax |
An MLP regression trained with Flax (linen) and optax, converted from ordinary Python and checked under strict = true with nothing extra installed or configured. |
A trainer under torchrun and accelerate launch |
A plain PyTorch trainer whose kernels are .ppy modules, started by python, torchrun, or accelerate launch. |
| Columnar expressions |
Expressions over pandas Series converge onto the columnar dialect of the IR and fuse into one kernel over the columns' memory, nulls included. |
Conversion and projects
| Example |
What it shows |
| Inventory |
Untyped Python that converts cleanly, with no hand editing afterwards. |
| Inference |
Three modules with no type annotations, and the three .ppy files ppy convert wrote from them. |
| Interop |
A plain .py file importing a .ppy module, with no build step. |
| A multi-module project |
Two modules analyzed as one call graph and built as one program. |
| Migration |
A small legacy telemetry script, run through ppy migrate instead of ppy convert. |