| name | tinypy-1-1-0 |
| description | Minimalist Python implementation in ~64k of code with a bootstrapped parser, bytecode compiler, and VM with incremental GC. Supports classes, dicts, lists, exceptions, and modules. Cross-platform with C API for embedding. Use when building embedded scripting engines, studying VM internals, or needing a tiny Python-compatible runtime for constrained environments. |
TinyPy 1.1
Overview
TinyPy is a minimalist implementation of Python in approximately 64k of code. Created by Phil Hassey and released under the MIT license, it features:
- Parser and bytecode compiler written in tinypy — fully bootstrapped
- Lua-esque virtual machine with garbage collection written in C (stackless sans Stackless features)
- Cross-platform — runs under Windows, Linux, and macOS
- A fairly decent subset of Python:
- Classes and single inheritance
- Functions with variable or keyword arguments
- Strings, lists, dicts, numbers
- Modules, list comprehensions
- Exceptions with full traceback
- Some builtins
- Easy C API for building custom modules
- MIT licensed — static compile it and its modules
The project was originally described as "Lua for people who like Python" and "batteries not (yet) included."
When to Use
- Embedding a tiny Python-compatible scripting engine in games or applications
- Studying virtual machine internals, bytecode compilation, and garbage collection
- Bootstrapping toolchains where the compiler is written in the language itself
- Constrained environments where minimal footprint matters
- Educational purposes — understanding tokenizer, parser, encoder, and VM architecture
- Building custom C modules with a simple C API
Core Concepts
Architecture
TinyPy has two main components:
- C runtime — The virtual machine, garbage collector, built-in operations, and object types written in C (~64k of code)
- Python compiler — The tokenizer, parser (TDOP), and bytecode encoder written in tinypy itself
Bootstrapping Process
The build process is fully bootstrapped:
- Python runs
setup.py to compile the initial C VM
- The VM loads Python source files (
tokenize.py, parse.py, encode.py, py2bc.py)
- These are compiled to bytecode (
.tpc files) and embedded into the binary
- The resulting binary can then run any tinypy script independently
Object Model
Every object in tinypy is a tp_obj union with a type field:
- TP_NONE — None value
- TP_NUMBER — Double-precision floating point (
double tp_num)
- TP_STRING — Character string with length
- TP_DICT — Hash map (used for dicts, classes, and objects)
- TP_LIST — Dynamic array
- TP_FNC — Function (C or tinypy)
- TP_DATA — Custom data pointer with magic number
In tinypy, dict == object — meaning a.x == a['x']. Classes and instantiated objects are special dictionaries with "meta" dictionaries for inheritance.
Meta System
Instead of Python-style __get__ descriptors, tinypy uses a Lua-like meta system:
class MetaX:
def __get__(self, k): return "OK"
class X:
def __init__(self): setmeta(self, MetaX)
When dictionary attributes are accessed but not present, they are looked up in the meta dictionary. This also enables dynamic class changes at runtime (like Python's x.__class__ = B).
Installation / Setup
Dependencies
- Python (only for bootstrapping)
- GCC (or Visual Studio 2005/2008 on Windows)
- SDL (optional, for the pygame module)
Building
python setup.py linux pygame
./build/tinypy examples/julia.py
./build/tinypy your-program-goes-here.py
The setup.py script handles the full bootstrapping process. On Windows, Visual Studio 2005/2008 support was added in version 1.1 by Krzysztof Kowalczyk.
Size Notes
The "64k" claim is approximate. Running python mk64k.py compresses the source by converting 4 spaces to tabs, removing blank lines and comments, and stripping namespacing prefixes (e.g., tp_print becomes print). The compiled binary can be compressed below 64k with UPX.
Usage Examples
Running a Julia Fractal
The included examples/julia.py demonstrates tinypy's capabilities:
import pygame
if '.' in str(1.0):
import pygame.locals
SW, SH = 120, 120
def julia(s, ca, cb):
pal = [((min(255,v)), (min(255,v*3/2)), (min(255,v*2))) for v in range(0,256)]
for y in range(0, SH):
for x in range(0, SW):
i = 0
a = ((float(x)/SW) * 4.0 - 2.0)
b = ((float(y)/SH) * 4.0 - 2.0)
while i < 15 and (a*a) + (b*b) < 4.0:
na = (a*a) - (b*b) + ca
nb = (2.0*a*b) + cb
a = na
b = nb
i = i + 1
s.set_at((x, y), pal[i*16])
def main():
pygame.init()
s = pygame.display.set_mode((SW, SH), 0, )
_quit =
_quit:
e pygame.event.get():
e. (pygame..QUIT, pygame..KEYDOWN):
_quit =
x, y = pygame.mouse.get_pos()
ca = (((x)/SW) * - )
cb = (((y)/SH) * - )
julia(s, ca, cb)
pygame.display.flip()
__name__ == :
main()
Embedding TinyPy in C
#include "tinypy/tp.h"
int main(int argc, char *argv[]) {
tp_vm *tp = tp_init(argc, argv);
tp_call(tp,
tp_get(tp, tp->builtins, tp_string("foo")),
tp_params_v(tp, 1, tp_string("hello")));
tp_deinit(tp);
return 0;
}
Writing a C Module
#include "tinypy/tp.h"
tp_obj my_add(TP) {
tp_obj a = TP_OBJ();
tp_obj b = TP_OBJ();
return tp_number(a.number.val + b.number.val);
}
void init_mymodule(TP) {
tp_set(tp, tp->builtins, tp_string("my_add"),
tp_fnc(tp, my_add));
}
Advanced Topics
C API Reference: Complete reference for all C functions, macros, and data structures → C API Reference
Bytecode and VM Internals: Instructions, frames, registers, and execution model → Bytecode and VM Internals
Garbage Collection: Incremental mark-and-sweep implementation details → Garbage Collection
Compiler Pipeline: Tokenizer, TDOP parser, and bytecode encoder → Compiler Pipeline
Built-in Functions: All tinypy builtins with descriptions → Built-in Functions
Python Subset Reference: What Python features are supported and what is not → Python Subset Reference