Py2Native Python to native compiler

← All posts

2026-08-29

How to Compile Python to a Native Executable with Zero Config

How to Compile Python to a Native Executable with Zero Config
python compiler cython code protection open source

How to Compile Python to a Native Executable with Zero Config

Shipping proprietary Python code usually means shipping readable .py files—or spending hours on the hard way: hand-writing C extensions or managing Cython .pyx files. Py2Native takes a different path. You write plain Python, run one command with uv, and get a native executable or library without touching Cython syntax, .pyx files, or manual build configuration.

By the end of this walkthrough, you will have compiled a simple Python script into a native binary, verified that it runs without the original source file, and seen the main build variations available in the command line.

Prerequisites

Before you start, make sure your machine has:

  • CPython 3.11–3.15, including free-threaded 3.14t and 3.15t builds.
  • A platform C compiler and linker:
    • Windows: MSVC
    • Linux: GCC
    • macOS: Clang
  • Internet access, because Py2Native and uv may download Python distributions and libraries.
  • uv installed and available on your PATH.
  • Basic familiarity with the command line.

You can check the main prerequisites with:

python --version
uv --version
cc --version   # or clang --version, or cl on Windows

The supported CPU targets are x86-64 and ARM64. Windows 8+, manylinux2014/musl Linux, and macOS are supported.

Step-by-Step: Compile Your Python Script

Step 1: Create a simple Python script

Start with a small script you want to protect. In a new project directory, create hello.py:

def main():
    print("hello from a native Python build")

if __name__ == "__main__":
    main()

This is plain Python. There is nothing special about the file, no annotations, no Cython imports, and no build metadata.

Step 2: Add Py2Native with uv

Py2Native is used through uv run py2native, not through global pip installs. Initialize a uv project and add Py2Native as a development dependency:

uv init py2native-demo
cd py2native-demo
uv add --dev py2native

Confirm the CLI is available:

uv run py2native --help

Keeping Py2Native as a dev dependency keeps your runtime dependency tree clean while still making the compiler available to every uv run command in the project.

Step 3: Run the build command

Compile hello.py into a native executable with one command:

uv run py2native build hello.py

When the command finishes, Py2Native prints the path to the generated executable. The exact output filename depends on your platform, for example an executable on Linux or macOS and an .exe on Windows.

If you have several source files, you can pass multiple positional arguments or glob patterns:

uv run py2native build main.py "src/**/*.py"

Just make sure no two source files use the same module name. Py2Native skips __init__.py during compilation because it is a namespace marker, not compilable code.

Step 4: Understand what happens under the hood

With uv run py2native build hello.py, the pipeline runs automatically:

  1. Glob sources — hello.py is expanded under the configured base directory.
  2. Plugin dispatch — platform plugins and the optional Pro plugin extend the build.
  3. Cython to C — Py2Native generates a bootstrap source from its templates, invokes Cython with --embed --shared --embed-modules, and compiles the generated .pyx files to C. You do not write or manage those files.
  4. C to objects — the platform compiler compiles the generated C files with the right flags.
  5. Link — Py2Native links the objects into a native executable.
  6. Optional wheel — if requested, Py2Native builds a PEP 427 wheel.
  7. Optional embed — if requested, Py2Native creates a uv-managed deployment directory.

The important point: Cython is used under the hood, but it is hidden. You do not run cythonize manually, you do not maintain .pyx or .pxd files for the core build, and you do not write C extensions.

Step 5: Build a library instead of an executable

If your product is an importable Python package rather than a standalone CLI, use library mode:

uv run py2native build hello.py --library --wheel wheels

This creates a shared library (.pyd on Windows, .so on Linux, .dylib on macOS) and packages it into a wheel. The generated wheel contains:

  • One compiled shared library that includes all your modules.
  • __init__.py, which acts as a meta-path finder and redirects imports to the compiled library.
  • __main__.py, which enables python -m <pkg>.

Both generated Python files are required. __init__.py bridges Python’s import system to the native library, and __main__.py provides the runnable entry point. Py2Native creates them for you.

Step 6: Create a self-contained deployment directory

An executable still needs a Python runtime unless you bundle one. For a self-contained deployment directory, use --embed:

uv run py2native build hello.py --embed deploy/hello

This creates a uv-managed deployment directory containing the compiled executable and the Python environment it needs. The result is not a single static file, but a directory you can ship together.

Step 7: Add license key verification with Pro

The community edition is MIT-licensed and handles full compilation. The Pro plugin, py2nativepro, adds JWT license verification and string compression.

For license verification, start by adding the Pro plugin:

uv add --dev py2nativepro

Generate 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 '{"sub":"customer","exp":1830000000}' license.dat

Inspect the license, optionally verifying it against the public key:

uv run py2native show --public public.pem license.dat

Then build with the license and public key:

uv run py2native build hello.py --license license.dat --public public.pem

In your Python source, include the Pro plugin’s verification .pxd declaration and call the verification helper with the license key. The plugin bakes the signature verification code and public key into the executable, and only the public key is stored in the binary. The elliptic key verification is handled by compiled code in the Pro plugin, with no third-party crypto libraries.

For a full walkthrough of this workflow, see Add License Key Verification to Your Compiled Python App.

How to Confirm It Worked

Run the generated executable from the path printed by your build:

./<build-output>/hello

On Windows, use .\<build-output>\hello.exe. You should see:

hello from a native Python build

The real test is independence from the source file. Rename the original Python source and run the binary again:

mv hello.py hello.py.bak
./<build-output>/hello
mv hello.py.bak hello.py

If the executable still runs, it is not importing your readable .py file at runtime.

If you used --embed, inspect the deployment directory:

ls deploy/hello

You should see the compiled executable plus the uv-managed Python runtime files.

If you used --library, create a clean uv environment, install the generated wheel there, and import the package:

uv run python -c "import hello; hello.main()"

The import should succeed through the compiled shared library and the generated __init__.py wrapper.

Troubleshooting Common Issues

Missing C compiler

If the build fails while compiling C code, check that your compiler is installed and on PATH.

  • Windows: install Visual Studio Build Tools with the MSVC toolchain.
  • Linux: install GCC and core build utilities.
  • macOS: install Xcode Command Line Tools with xcode-select --install.

Module name conflicts

Py2Native does not support two source files with identical module names. If you see ambiguous import or symbol errors, rename one of the modules so every source module has a unique name.

Platform-specific linking errors

Py2Native loads platform plugins such as winplatform, linuxplatform, and macplatform automatically. If linking fails, check the build log for platform flag issues and make sure the platform plugin was loaded during the build.

License verification failures with Pro

If a Pro build rejects the license:

  • Confirm the public key file passed with --public is the correct public.pem.
  • Verify the license JWT with uv run py2native show --public public.pem license.dat.
  • Make sure the Pro plugin is available as py2nativepro in your project.
  • Check that the .pxd verification declaration is included in your source tree.

Monkey-patching limitations

Py2Native compiles your Python code to native machine code. Third-party libraries remain as Python source and are not compiled, so they can still be monkey-patched. This is expected behavior, not a build failure.

FAQ

Can I compile any Python code to a native executable?

Py2Native compiles your custom Python code into native machine code. It supports standard Python syntax and most libraries, but there are constraints: no modules with identical names across source files, and __init__.py is skipped during compilation. Third-party libraries remain as Python source and are not compiled.

Does Py2Native support third-party libraries?

Yes. Third-party libraries are left unmodified and work as-is with minimal or no configuration. Py2Native only compiles your own Python files; dependencies are bundled or referenced as needed.

How does Py2Native compare to using Cython directly?

Py2Native uses Cython under the hood but hides all the complexity. You write plain Python—no .pyx files, no manual cythonize steps, and no C extensions. Py2Native automates the full pipeline from Python source to native executable or library. For a deeper comparison, see Cython vs Py2Native: Which Is Better for Protecting Python Code?.

Is the compiled binary truly standalone?

The native executable may still require a Python runtime unless you use --embed, which creates a self-contained deployment directory with a uv-managed Python environment. In library mode, the compiled shared library is used alongside a small Python wrapper.

Conclusion

Py2Native gives you a zero-config path from plain Python source to a native executable or library. One uv run py2native build command replaces manual Cython setup, .pyx maintenance, and hand-written C extension work. Under the hood it automates Cython transpilation, C compilation, linking, and optional wheel or embed output.

If you need stronger protection and commercial features, the Pro edition adds JWT license verification and string compression on top of the same build workflow.

Ready to try it yourself? Start with the MIT-licensed community edition by adding py2native to a small project and running your first build. Then explore --library, --embed, and Pro license verification once the basic pipeline works.

Try Py2Native today.

Related posts

EU label: AI-generated content