Comparison with other tools
Several tools compile or accelerate Python.
They differ in what they take as input, what they produce, and how close they stay to CPython semantics.
This page states the differences without claiming one is faster than another: the projects measure themselves differently, and pyscriptc publishes its own numbers on the landing page instead of comparing here.
At a glance
| Input | Output | Types | CPython semantics | |
|---|---|---|---|---|
| pyscriptc | typed Python subset | native binary; untyped imports run on an embedded CPython island | annotations required, checked by pyrefly before lowering | subset; deviations are fenced with a PSxxxx diagnostic, numbered divergences documented |
| Nuitka | standard Python | extension module, standalone folder or onefile executable | not needed | aims for full compatibility, including Python 2.6/2.7 modes |
| Cython | Python or the Cython language (.pyx) |
CPython extension module (.so) |
optional; static types add speed and C interop | its own compiled language; documented limitations page |
| mypyc | typed Python | CPython extension module per compiled module | standard hints, enforced at runtime (TypeError at boundaries) |
mostly compatible; restricts some dynamic features |
| Codon | Python-like source | native executable or extension module | not required; its own Hindley-Milner inference, monomorphization | explicitly not a drop-in replacement (64-bit int, no monkey-patching, C-style numerics by default) |
| Numba | decorated Python functions | JIT-compiled machine code in-process | not required; argument types inferred at call time | nopython-mode subset with a documented supported-features list; object mode covers the rest |
| scriptc | TypeScript/JavaScript | native executable and WASI module | standard TypeScript via tsc |
not Python; listed here for the architectural parallel |
The tools
Nuitka
Nuitka translates a whole Python program into a C-level program that links libpython, so the compiled result executes the way CPython does.
It compiles standard Python as-is: no type annotations are needed, and its docs claim full compatibility with Python 3.4 through 3.14 (plus Python 2.6/2.7), with optimizations that never remove compatibility.
Its three modes are --module (an importable extension), --mode=standalone (a distributable folder with the runtime bundled) and --mode=onefile.
The typical use case in its docs is distributing applications.
pyscriptc differs in the input contract and the output: it compiles a typed subset, refuses the rest with a named diagnostic instead of lowering it slowly, and a native binary carries no interpreter except where you opt into the island.
Cython
Cython compiles .pyx (or plain .py) files to C, and a C compiler turns that into an extension module that you import back into a CPython session.
Static typing is optional; adding it speeds up hot paths and enables C/C++ interop.
The language is a superset of Python with its own limitations page, and the output is always an extension module, not a standalone program.
pyscriptc differs in output shape (a binary instead of a module for CPython to import) and in making annotations mandatory rather than an accelerator.
mypyc
mypyc compiles Python modules with standard type hints into C extensions, using mypy for checking and inference.
It is a strict, gradually typed Python variant: dynamic features are restricted to gain performance, and annotations are enforced at runtime with TypeError at static/dynamic boundaries.
Output is one extension module per compiled module (or a whole package via mypycify), which mypy itself uses to speed up its own codebase.
This is the closest input contract to pyscriptc’s: typed Python in, checked before lowering.
The difference is the artifact and the boundary. mypyc produces code that runs inside a CPython process; pyscriptc produces a binary, and unknown or untyped imports route to an embedded CPython island rather than to Any-erased compiled code.
Codon
Codon, from Exaloop, is an ahead-of-time compiler for Python-like source with its own type checker (a modified Hindley-Milner system) and LLVM backend.
Annotations are not required because the compiler infers types and monomorphizes per call site.
It builds native executables and extension modules, and its docs state plainly that it is not a drop-in replacement for CPython: int is 64-bit rather than arbitrary precision, static checking forbids monkey-patching and heterogeneous containers, numerics follow C semantics by default, and unimplemented stdlib modules run under a real CPython via from python import.
pyscriptc shares the “native binary, no interpreter inside” goal but keeps CPython semantics as the target within the subset: an int is a Python int, and anything the subset cannot compile is refused with a diagnostic or routed to an embedded CPython, never reinterpreted.
Numba
Numba is a JIT: you decorate functions with @numba.jit (or @njit), and LLVM-compiled machine code is produced at first call, with argument types inferred then.
It is aimed at array and numerical code, with deep NumPy integration and CPU/GPU targets, and its nopython mode supports a documented subset of Python while object mode catches the rest.
Compilation happens inside a running CPython process; there is no standalone binary.
pyscriptc is ahead-of-time and whole-program: the compile happens once, before distribution, and the output runs with no CPython in the loop outside the island.
scriptc
scriptc (Apache-2.0) is Vercel Labs’ experimental compiler for a related problem in a different language: it compiles TypeScript and JavaScript to native executables and WASI modules, with the real tsc checking types and unsupported operations fenced with an SC diagnostic plus a rewrite hint.
It also ships a scriptc coverage command that reports, per construct, what compiles.
pyscriptc is an independent project, but the shape of the problem is the same one scriptc describes for the Node ecosystem: a typed subset lowered through LLVM IR to native code, with everything outside the subset routed to a dynamic engine instead of guessed at.
The architectural difference comes from the languages: scriptc’s dynamic fallback runs on an embedded QuickJS, while pyscriptc embeds CPython itself, because Python’s runtime introspection is idiomatic in its ecosystem (frameworks read __annotations__ and build schemas at import time) and only real CPython provides it.
pyscriptc keeps that island as a permanent tier rather than a transition state, and credits scriptc as the closest prior art for the coverage-diagnostic developer experience.
Choosing
- You want to ship your existing, untyped script as an executable and need full-language behavior: Nuitka’s territory.
- You want to speed up a module inside a CPython process: Cython or mypyc.
- You want a native binary from numeric, statically-inferable code and accept its numeric and typing divergences: Codon.
- You want decorated functions to run faster inside a running interpreter, especially with NumPy: Numba.
- You have typed Python and want a native binary that refuses, names, and explains what it cannot compile, with untyped dependencies delegated to an embedded CPython: that is pyscriptc.