Skip to main content

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:

  1. Transpile:

    pyturbo transpile hello.py -o hello.c
    
  2. 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); }

  3. Build:

    gcc -shared -fPIC hello.c -lpython3.11 -o hello.so
    

    On Windows with MinGW:

    gcc -shared hello.c -L. -lpython311 -o hello.pyd
    
  4. 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.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pyturbo-v3 3.1.1
File Size Uploaded
pyturbo_v3-3.1.1.tar.gz 50.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pyturbo-v3 3.1.1
File Interpreter ABI Platform
pyturbo_v3-3.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 103.5 kB

Release files / pyturbo_v3-3.1.1.tar.gz

Download URL pyturbo_v3-3.1.1.tar.gz
Size 50.3 kB
Tags Source
SHA-256 checksum
How to use checksums
f36f6f5d061885670a5023227e0400ca3ee5b95270908f82c4bca6ec66c73ece
BLAKE2b-256 checksum
How to use checksums
33b83502a17aa8e5d511d468e034b2ec10371ebad86c2b3f0696556db948e596
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.1-py3-none-any.whl

Download URL pyturbo_v3-3.1.1-py3-none-any.whl
Size 53.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2efd283f2fb92c6124ec68c8bfe252d9dcbe014e217ebd5151c94e562630dc84
BLAKE2b-256 checksum
How to use checksums
fd4d132e805db37748d6886f23e004b04c8ac03bdf5d32e61a68209f8bd92138
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.13

Release history Release notifications | RSS feed

This release

3.1.1 This release

2 release files

3.1.0

2 release files

3.0.9

2 release files

3.0.8

2 release files

3.0.7

2 release files

3.0.6

2 release files

3.0.5

2 release files

3.0.4

2 release files

3.0.3

2 release files

3.0.2

2 release files

3.0.1

2 release files

3.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page