PyTurbo V3
A small .c generator and C extension builder for Python.
PyTurbo transpiles pure Python (.py) to C99 (~5 KB) without #include <Python.h>, using DCE for the CPython API, and can produce small C extensions (~50 KB .so) with no dependencies.
Optimization method: all you can make to C99; the rest through CPython API.
Not a Cython replacement. A minimal alternative for small numeric extensions.
Author: Suleiman License: Apache 2.0 Copyright 2026 Suleiman
REQUIREMENTS
Windows 10 or 11, 64-bit
Python 3.10 - 3.14 (64-bit)
A C compiler: GCC (MinGW), clang, MSVC, or tcc
PyTurbo V3 is Windows-first by design. The transpiler core (analyzer, type inference, codegen, DCE) is platform-independent and produces standard C99, but the entry point (pyturbo.py) currently requires Windows.
The --min-dll feature is Windows-only and 64-bit only: - it reads PE files (pe_parser.py) - it copies pythonXY.dll into pyturbo_dist/ - it generates .def and .dll.a for MinGW - it applies UPX compression to the DLL
PLATFORM SUPPORT
Windows 10 / 11 (64-bit) full support (recommended)
Linux / macOS / Android .c output only (requires patch)
Windows 32-bit not tested
On Linux / macOS / Android: - .c and .h output works - --min-dll is a no-op with a warning - link against system libpythonXY.so - example: gcc -shared -fPIC hello.c -lpython3.11 -o hello.so
To enable transpilation on non-Windows platforms, remove or relax the platform guard in pyturbo.py:
# was:
if sys.platform != "win32":
_fail_os()
# now:
if sys.platform != "win32":
import warnings
warnings.warn(
"PyTurbo: --min-dll is Windows-only; "
".c output works everywhere"
)
This is the only change required for .c output on Linux / macOS / Android.
QUICK START
pyturbo transpile hello.py -o hello.c -v
Output:
hello.c C99 + inlined CPython prototypes (~5 KB)
hello.h function prototypes
hello.manifest.json full analysis report
Note: pyturbo_python_min.h is NOT written to disk. DCE always inlines the ~15 needed prototypes directly into hello.c. The .c is self-contained.
Compile with any C compiler:
gcc -std=c99 hello.c -lpython3.11 -o hello
./hello
Or MinGW, clang, tcc, MSVC. Any of them works.
FEATURES
C99 where possible:
numbers -> double
arithmetic -> native + - * /
for i in range(n) -> for (double i = 0; i < n; i += 1)
+=, -=, *=, ... -> native
print(x, y) -> printf("%g %g\n", x, y)
numeric function parameters -> unboxed to double
numeric return values -> boxed via PyFloat_FromDouble
CPython API fallback:
strings, lists, tuples, dicts
imports, module calls
classes (via type())
exceptions
lambdas
DCE for Python.h:
reads Python.h from the system
extracts only the used prototypes (~15 of ~1800)
inlines them into the .c file
no #include <Python.h>
no -I flag needed
no pyturbo_python_min.h on disk
self-contained .c
DLL isolation on Windows with --min-dll:
detects the running Python version (310, 311, 312, 313, ...)
finds pythonXY.dll
produces an isolated copy next to the .c file
optionally compresses it with UPX (~2.5x smaller)
no Visual Studio required
uses a pure-Python PE parser
Then:
cd pyturbo_dist
gcc hello.c -L. -lpython311 -o hello.exe
Or with MSVC, clang, tcc. Any of them.
COMMANDS
pyturbo transpile <file.py> [-o out.c] [-v] [--min-dll]
[--no-upx] [--upx-brute]
pyturbo install upx
pyturbo analyze <file.py>
pyturbo infer <file.py>
pyturbo tokens <file.py>
pyturbo version
pyturbo help
EXAMPLE
import math
def sum_squares(n):
total = 0
for i in range(n):
total += i * i
return total
def main():
r = sum_squares(100)
print("Result:", r)
print("Sqrt:", math.sqrt(r))
main()
Generated C (excerpt):
/* ---- PyTurbo V3 DCE: inlined CPython prototypes ---- */
extern void Py_Initialize(void);
extern PyObject* PyFloat_AsDouble(PyObject*);
extern PyObject* PyFloat_FromDouble(double);
/* ... ~15 prototypes ... */
/* ---- end PyTurbo V3 DCE ---- */
PyObject* py_user_sum_squares(PyObject* py_n) {
double py_n_v = PyFloat_AsDouble(py_n);
double py_total = (double)((0L));
for (double py_i = (double)((0));
py_i < (double)(py_n_v);
py_i += (double)((1))) {
py_total += py_i * py_i;
}
return PyFloat_FromDouble(py_total);
}
PyObject* py_user_main(void) {
PyObject* py_r = py_user_sum_squares(PyLong_FromLong(100L));
double py_r_v = PyFloat_AsDouble(py_r);
printf("Result: %g\n", py_r_v);
PyObject* _m = PyImport_ImportModule("math");
PyObject* _f = PyObject_GetAttrString(_m, "sqrt");
PyObject* _t = PyObject_Vectorcall(_f, &py_r, 1, NULL);
double _t_v = PyFloat_AsDouble(_t);
printf("Sqrt: %g\n", _t_v);
Py_INCREF(Py_None);
return Py_None;
}
int main(int argc, char** argv) {
(void)argc; (void)argv;
Py_Initialize();
py_main();
if (PyErr_Occurred()) PyErr_Print();
Py_Finalize();
return 0;
}
C EXTENSIONS
PyTurbo output is a STANDALONE C PROGRAM, not a C extension. It contains main() and calls Py_Initialize / Py_Finalize.
To use the generated .c as a C extension, you MUST add the module boilerplate manually and remove main(). This is not automated yet (--as-extension is not implemented).
Manual steps:
-
Transpile:
pyturbo transpile hello.py -o hello.c -
Edit hello.c:
-
remove main()
-
rename py_main() to py_user_main() if needed
-
add:
static PyMethodDef module_methods[] = { {"sum_squares", py_user_sum_squares, METH_O, NULL}, {"main", py_user_main, METH_NOARGS, NULL}, {NULL, NULL, 0, NULL} };
static struct PyModuleDef module_def = { PyModuleDef_HEAD_INIT, "hello", NULL, -1, module_methods };
PyMODINIT_FUNC PyInit_hello(void) { return PyModule_Create(&module_def); }
-
-
Build:
gcc -shared -fPIC hello.c -lpython3.11 -o hello.soOn Windows with MinGW:
gcc -shared hello.c -L. -lpython311 -o hello.pyd -
Import:
python -c "import hello; hello.sum_squares(100)"
Why this is not automatic yet:
--as-extension is a planned flag, not implemented.
The generated functions (py_user_*) already have the
correct signature PyObject*(PyObject*), so the boilerplate
is mechanical, but the flag, the automatic METH_* selection,
and the self handling are not done.
What a PyTurbo extension gives you (once boilerplate is added):
.c size ~5 KB (Cython: ~500 KB)
.so size ~50 KB (Cython: ~1 MB)
dependencies 0 (Cython: 3)
Python.h not needed (Cython: #include <Python.h>)
-I flag not needed (Cython: needed)
build time seconds (Cython: minutes)
input .py (Cython: .pyx)
Limitations compared to Cython:
NumPy not supported
C++ not supported
cdef class not supported (classes are built at runtime)
attribute specialization not supported
object-heavy speed ~1x (Cython: 2-10x)
ecosystem none (Cython: 18 years)
SPEED
Realistic speedup range over CPython: 6x to 50x.
50x upper bound: fully numeric hot path
6x lower bound: mixed numeric + object code
~1x object-heavy code (same as CPython)
<1x worst case: print(object) in loop, m.f(x) in loop
The 50x figure applies only to numeric code that fully passes try_pure_c99. On object-heavy code PyTurbo emits generic CPython API calls and may be slower than CPython due to repeated ImportModule / GetAttrString.
This is not a bug; it is the boundary of the model: "all you can to C99, the rest through CPython API".
SIZE COMPARISON
feature PyTurbo V3 Cython Nuitka
--------------------- ----------- ----------- -----------
input .py .pyx .py
annotations not needed needed not needed
output .c .c .c / .exe
.c size ~5 KB ~500 KB ~1 MB
binary ~50 KB ~1 MB ~5 MB
dependencies 0 (CPython) 3 5
Python.h inlined #include #include
DLL DCE (Windows) copy + UPX no no
works on Android .c only no no
speed (numeric) ~50x ~50x ~1.3x
speed (objects) ~1x 2-10x 1.3x
PYTURBO VS CYTHON
Cython requires .pyx syntax with cdef, cpdef. PyTurbo accepts pure Python and infers types automatically.
Cython: full plate armor: .pyx, cdef, cpdef broadsword: #include <Python.h> (~1800 prototypes) castle: ~3 MB package, 150,000 lines 500 contributors 18 years old
PyTurbo: t-shirt: pure .py scalpel: inline Python.h (~15 prototypes) laptop: ~49 KB package, ~3200 lines 1 author brand new
Both produce fast C. Both are peers in the same category. Different eras, different styles.
Where PyTurbo wins:
input is pure .py (no .pyx)
.c is ~100x smaller
no #include <Python.h>
no -I flag
0 dependencies
build time: seconds
Windows distribution with isolated UPX-compressed DLL
.c output works on Linux / macOS / Android
Where Cython wins:
NumPy integration
C++ integration
cdef class (real C types)
attribute / method specialization
object-heavy speed (2-10x)
production ecosystem (18 years, 500 contributors)
PYTURBO VS NUITKA
Nuitka compiles for packaging (.exe). PyTurbo focuses on generating minimal C with DCE.
Nuitka pulls in the full Python runtime. PyTurbo removes unused declarations and inlines only the needed ~15 prototypes directly into the .c file.
PYTURBO VS PYTHRAN
Pythran compiles scientific Python to C++ with NumPy support and automatic vectorization.
Pythran: NumPy arrays, SIMD, auto-parallelism
PyTurbo: scalar numbers, small .c, no NumPy
If your hot path is NumPy arrays -> Pythran. If your hot path is scalar loops -> PyTurbo.
PYTURBO VS PYPY
PyPy is a JIT. PyTurbo is an AOT transpiler.
PyPy: no code changes, 2-10x on objects, warmup,
100 MB runtime, weak with C extensions
PyTurbo: no code changes, ~1x on objects, no warmup,
~50 KB .c, strong on scalar numbers
If your program is long-running and object-heavy -> PyPy. If your program is a short script with scalar loops -> PyTurbo.
DCE
Cython and Nuitka both require #include <Python.h> and pull in all ~1800 prototypes.
PyTurbo removes unused declarations and inlines only the needed ~15 prototypes directly into the .c file.
No pyturbo_python_min.h is written to disk. The generated .c is self-contained: grep for "#include <Python.h>" -> not found. grep for "PyTurbo V3 DCE" -> inlined prototypes.
On Windows with --min-dll, PyTurbo also isolates pythonXY.dll next to the .c file, so the .exe loads our copy first (Windows side-by-side rule).
No Visual Studio required. Uses a pure-Python PE parser.
Note: the PE parser (pe_parser.py) is Windows-only by design. It reads PE files, not ELF. It is not needed for .c output; it is only used by --min-dll.
WHY PYTURBO
Not a new category. A minimal implementation in an existing category.
Category: Python-to-C transpilers. Peers: Cython, Nuitka, Pythran, PyPy.
PyTurbo is the best when you want:
pure Python input, no annotations
tiny .c (5 KB)
no -I flag
no #include <Python.h>
0 dependencies
small C extensions (~50 KB .so, with manual boilerplate)
minimal Windows distribution (isolated DLL + UPX)
.c output on Linux / macOS / Android
fast build cycle (seconds)
Cython is the best when you want:
NumPy integration
C++ integration
maximum speed with annotations
cdef class
production ecosystem
Pythran is the best when you want:
NumPy arrays, vectorization
scientific computing
automatic SIMD
PyPy is the best when you want:
object-heavy code without changes
long-running processes
JIT adaptation
HONEST LIMITATIONS
Windows is the primary platform.
--min-dll, pe_parser.py, dll_decreaser.py, embed.py
are Windows-only. The transpiler core is portable,
but pyturbo.py currently blocks non-Windows at startup.
Remove the platform guard to enable .c output elsewhere.
No NumPy.
PyTurbo does not understand ndarray, memoryview, or
buffer protocols. numpy calls go through CPython API.
No C++.
PyTurbo emits C99 only.
Classes are dynamic.
class Foo: is built at runtime via PyType_Type.
It is not a real C type.
Object-heavy code is not faster.
print(obj), m.f(x), obj.attr in loops may be slower
than CPython due to repeated ImportModule / GetAttrString.
Half of Python syntax is not implemented.
Missing: with, try, raise, lambda, yield, await,
decorators, f-strings, match. These are not C99
limitations - they are unimplemented in codegen.py.
DCE reads Python.h from the system.
It is needed at transpile time, not at compile time.
Without it, PyTurbo falls back to a hardcoded set
of ~80 prototypes.
--as-extension is NOT implemented.
Output is a standalone .exe (with main()).
To build a C extension, add the module boilerplate
manually (see C EXTENSIONS section).
PE parser is Windows-only.
pe_parser.py reads PE, not ELF.
It is only used by --min-dll.
LICENSE
Apache License 2.0. See LICENSE.txt.
Metadata
Release files for pyturbo-v3 3.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pyturbo_v3-3.1.0.tar.gz | 50.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pyturbo_v3-3.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 103.0 kB
Release files / pyturbo_v3-3.1.0.tar.gz
| Download URL | pyturbo_v3-3.1.0.tar.gz |
|---|---|
| Size | 50.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
74b913605cbb7bf8a8bd70ba6ac443c18ac95204defafd211900523981fb7cc5
|
|
BLAKE2b-256 checksum How to use checksums |
591a1ca90efcfc657b1ad36005a55c5a14e7602aea0149d940f99fc55f6ba892
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.13
|
Release files / pyturbo_v3-3.1.0-py3-none-any.whl
| Download URL | pyturbo_v3-3.1.0-py3-none-any.whl |
|---|---|
| Size | 53.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fd547582a10a3b609fc9928db4fb46adce53d4a53184ba96010c3a83fe97ea61
|
|
BLAKE2b-256 checksum How to use checksums |
c7bc95bd15d772c616047dfde43f3abcd23815e79aa17816f0a37c5e82337a9a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.13
|