How to Compile Python to a Native Binary Without Cython Syntax
You need to ship a Python application without handing over readable source code. You could learn Cython, write .pyx and .pxd files, configure build steps, and debug C extension issues. That is the hard way.
Py2Native gives you the shortcut: write plain Python, run one uv run py2native build command, and get a native executable or shared library. Cython still runs under the hood, but Py2Native hides all of it. You never write Cython syntax, never create .pyx files, and never touch a manual build pipeline.
By the end of this guide, you will take an existing Python project and compile it into a distributable native binary—without changing your Python code into Cython.
Prerequisites for Compiling Python with Py2Native
Before you start, make sure you have:
- CPython 3.11–3.15, including free-threaded 3.14t and 3.15t
- A platform C compiler and linker:
- Windows: MSVC
- Linux: GCC
- macOS: Clang
- Internet access, because Py2Native downloads Python distributions and libraries when needed
uvinstalled, since all Py2Native commands start withuv run py2native
Supported targets are Windows 8+, manylinux2014/musl Linux, and macOS on x86-64 or ARM64 CPUs.
Check your environment quickly:
uv --version
python --version
If uv is not installed, install it first from the uv project. Py2Native then runs as a normal uv-managed package.
Step-by-Step: Compile Your Python Code to a Native Binary
Step 1: Install Py2Native with uv
You can install Py2Native as a standalone tool:
uv tool install py2native
For project-local builds, add it to the project that uv manages:
uv add py2native
From this point on, all examples use uv run py2native so the command runs in the exact project environment.
Step 2: Prepare a plain Python project
Py2Native compiles the Python you already have. There is no project restructure, no type annotation pass, and no Cython boilerplate.
Example project:
myapp/
├── main.py
└── helpers.py
helpers.py:
def format_amount(value: float) -> str:
return f"${value:.2f}"
main.py:
from helpers import format_amount
def main():
print(format_amount(19.99))
if __name__ == "__main__":
main()
Both files are ordinary Python.
Step 3: Build a native executable
From the project directory, run:
uv run py2native build main.py *.py
Py2Native expands the *.py glob, transpiles every matched Python file to C, compiles the C to object files, and links them into a native executable. The first argument, main.py, is the entry point.
The generated executable is a native binary. On Windows, expect an .exe; on Linux and macOS, expect an ELF or Mach-O binary. The command prints the exact output path after linking.
Step 4: Build a shared library instead
If you want to distribute a package that other Python code can import, use --library:
uv run py2native build main.py *.py --library
This creates a shared library with a platform-specific extension:
.pydon Windows.soon Linux.dylibon macOS
To package that library as a wheel:
uv run py2native build main.py *.py --library --wheel dist/
The wheel contains the compiled shared library plus an __init__.py meta-path finder and a __main__.py entry point. Those two files are required: __init__.py bridges Python’s import system to the native library, and __main__.py enables python -m <package>.
Step 5: Create a self-contained deployment
For a deployment directory that includes your binary and its dependencies, use --embed:
uv run py2native build main.py *.py --embed dist/myapp
Py2Native creates a uv-managed deployment directory. Your own code is compiled; third-party libraries remain as Python packages and are handled by the embedded environment. This is useful when you want to hand off a directory instead of a single executable.
Step 6: Embed Pro license verification
If you have Py2Native Pro, you can embed license verification directly into the binary. Start by generating an EC P-256 keypair:
uv run py2native keygen private.pem public.pem
Sign a JWT payload with the private key:
uv run py2native sign --private private.pem payload.json license.dat
Then build with the license and public key:
uv run py2native build main.py *.py --license license.dat --public public.pem
The Pro plugin bakes the public key and the signature verification code into the executable. Only the public key is stored in the binary; the private key stays offline.
With Pro, you do not write the verifier by hand. The build generates the bootstrap module and its interface. The generated _p2n_bootstrap.pxd contract is:
from _p2n_bootstrap cimport _runtime_verify_es256_jwt
cdef _runtime_verify_es256_jwt(token, expected_iss=*, expected_aud=*)
The runtime check uses an ES256 JWT verifier with expected claims:
_runtime_verify_es256_jwt(token, expected_iss="RSJ Software GmbH", expected_aud="TimestampGIT")
In the compiled code, the call assigns the result to a variable:
license = _runtime_verify_es256_jwt(licenseString, expected_iss="RSJ Software GmbH", expected_aud="TimestampGIT")
You pass the license token at runtime. The verification logic is compiled into the binary, not shipped as readable Python.
Verifying the Compilation Worked
First, run the executable. If your main module is main.py, the generated binary is typically named main or main.exe:
./main
For a library build, import the package from Python. Replace <package_name> with the package name you normally import:
python -m <package_name>
The compiled library should behave like the original Python code.
You can also confirm that the readable Python definition is gone:
strings ./main | grep "def format_amount"
If that command returns nothing, the original function definition is not present as readable Python source. This is a sanity check, not a security proof, but it confirms that Py2Native moved your logic into native code.
Third-party imports should continue to work normally. For example, if your project imports requests, the embedded environment or the host Python environment supplies it at runtime. Py2Native does not compile third-party libraries; it compiles your own source files.
Troubleshooting Common Issues
Missing C compiler
If the build fails with a compiler error, install the required toolchain:
# Windows: install Visual Studio Build Tools with "Desktop development with C++"
# Linux:
sudo apt install build-essential
# macOS:
xcode-select --install
Py2Native needs a working C compiler because it transpiles Python to C and then compiles that C.
Duplicate module names
Py2Native requires unique module names across your source files. If two source files define modules with identical names, the build can fail or produce conflicting symbols. Rename one of the modules before rebuilding.
Monkey-patching is still possible
Compiling your code does not prevent all runtime modification. If your application or a third-party library monkey-patches your classes or functions, those dynamic changes can still happen at runtime because Python’s object model remains active.
LGPL libraries remain replaceable
Py2Native leaves third-party libraries as Python source. This means LGPL-licensed dependencies remain replaceable by the end user, which keeps you compliant with LGPL obligations.
Platform-specific output issues
On Windows, compiled extensions use .pyd naming. On Linux, --wheel builds can trigger auditwheel repair for manylinux compliance. If you see wheel repair warnings on Linux, review the auditwheel output and ensure your build environment matches your target manylinux profile.
FAQ: Compiling Python to Native Binary with Py2Native
Q: Does Py2Native require me to write Cython code?
A: No. Py2Native uses Cython internally, but you write plain Python. There are no .pyx files to maintain, no type annotations required, and no manual cythonize step.
Q: Can I compile Python to a native binary without a C compiler?
A: No. A C compiler is required because Py2Native transpiles Python to C and then compiles it. On Windows you need MSVC, on Linux GCC, and on macOS Clang.
Q: Does Py2Native compile third-party libraries?
A: No. Py2Native only compiles your own Python source files. Third-party libraries remain as Python packages and are imported normally at runtime.
Q: How does Py2Native Pro license verification work?
A: Py2Native Pro embeds a public key and signature verification code into the executable. You call _runtime_verify_es256_jwt with your license token and expected issuer and audience to validate a JWT license.
Conclusion: Protect Your Python Code with Zero-Config Compilation
Py2Native turns plain Python into native machine code without asking you to learn Cython, maintain .pyx files, or manage a manual build process. You write Python, run uv run py2native build, and get a binary or shared library ready to distribute.
Start with the open-source Community edition from Py2Native. If you need signed license verification baked into your executable, upgrade to Pro and add the license flags shown above.
Related posts
- Secure License Verification for Python Native Binaries
- Looking for a Cython Alternative? Try Py2Native
- Python EXE Packaging: A Beginner’s Guide