Py2Native Python to native compiler

← All posts

2026-09-05

How to Distribute Python Apps Without Exposing Source Code

How to Distribute Python Apps Without Exposing Source Code
python compiler cython code protection open source

How to Distribute Python Apps Without Exposing Source Code

If you distribute a Python application today, the default artifact is source code: .py files that any customer, partner, or competitor can open, copy, or modify. This article walks through a repeatable way to distribute a Python application without exposing that source. You will compile your own Python code into native machine code, then ship a binary, a wheel, or an embedded deployment directory.

We’ll use Py2Native, a zero-config Python-to-native compiler. Py2Native compiles your custom Python into native machine code while leaving third-party Python libraries as regular Python source. You write plain Python. You do not hand-maintain Cython files, .pyx/.pxd files, or C-extension build scripts. For license-protected distribution, Py2Native Pro adds built-in JWT license verification.

Prerequisites for Compiling Python to Native Code

Before you build, make sure your environment matches the requirements:

  • CPython 3.11–3.15, including free-threaded 3.14t and 3.15t
  • A platform C compiler and linker:
    • MSVC on Windows
    • GCC on Linux
    • Clang on macOS
  • Windows 8+, manylinux2014/musl Linux, or macOS
  • x86-64 or ARM64 CPU
  • Internet access for downloading Python distributions and dependencies
  • uv 0.11.8 or newer as the package manager

Py2Native is used through uv run py2native, not pip. Create or open a uv-managed project and add the Community package:

uv --version
uv init myapp
cd myapp
uv add py2native

The Community edition is MIT-licensed and includes full compilation, embedding, and cross-platform support. If you plan to verify signed license keys, also add the Pro plugin when you have access:

uv add py2nativepro

The Pro plugin is proprietary and adds JWT license verification, string compression, and the keygen, sign, and show commands.

Step-by-Step: Compile a Python App Without Exposing Source

Step 1: Organize your Python project

Py2Native expects a main module and one or more source globs. A typical layout might look like this:

myapp/
├── main.py
├── config.py
└── services/
    ├── __init__.py
    ├── auth.py
    └── reporting.py

__init__.py files are treated as namespace markers and are skipped during compilation. Your custom code in main.py, config.py, and services/*.py is what gets compiled.

Step 2: Run the build command

From the project root, run:

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

If your sources live under a subdirectory, use --base:

uv run py2native build --base myapp main.py "*.py" "services/*.py"

By default, Py2Native produces a native executable. The result is a compiled binary, not a folder of readable .py files.

Step 3: Understand what happens inside the compilation pipeline

Py2Native hides the entire Cython workflow. You never edit generated C or hand-write .pyx/.pxd files. The build still performs all of these steps:

  1. Glob expansion under --base to collect source files
  2. Plugin dispatch for Pro license checks or platform-specific behavior
  3. Cython transpilation from Python to C
  4. C compilation into native objects with the platform compiler
  5. Linking into an executable or shared library
  6. Optional wheel creation or uv-managed embedding

The important point is that your source files are consumed by the compiler, not shipped to end users.

Step 4: Build a shared library and wheel for library-style distribution

If you want users to install and import your package rather than run a single executable, use --library and --wheel:

uv run py2native build main.py "*.py" "services/*.py" --library --wheel dist

This produces a PEP 427 wheel in dist/. The wheel contains:

  • A single compiled shared library that includes all of your modules
  • __init__.py, a meta-path finder that redirects imports such as import mypkg.submodule to the native shared library
  • __main__.py, so users can run the package with python -m mypkg

Both generated files are required for library mode. The __init__.py bridges Python’s import system to the native library, while __main__.py provides the runnable entry point.

Step 5: Create a self-contained deployment directory with --embed

For a standalone directory that includes a uv-managed Python environment, use --embed:

uv run py2native build main.py "*.py" "services/*.py" --embed deploy/myapp

The output in deploy/myapp is a uv-managed deployment directory. This is useful when you want to ship a complete runtime together with your compiled code without requiring users to set up Python dependencies themselves.

Step 6: Add Pro license verification before distribution

For commercial distribution, Py2Native Pro lets you build license enforcement directly into the executable. First, generate an EC P-256 keypair:

uv run py2native keygen private.pem public.pem

Keep private.pem private. It is used only for signing licenses.

Next, sign a JWT payload to create a license file:

uv run py2native sign --private private.pem '{"customer":"acme","exp":"2027-01-01T00:00:00Z"}' license.dat

Inspect the token whenever needed:

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

Then build with license verification enabled:

uv run py2native build main.py "*.py" "services/*.py" --license license.dat --public public.pem

Only the public key is stored in the executable. The elliptic key verification is compiled directly into your binary with no third-party libraries. Your private key never ships to customers. For a deeper look at this workflow, see Secure Your Python App with Built-in License Verification.

Step 7: Distribute the resulting artifacts

Depending on the flags you used, you now have:

  • A native executable from a default build
  • A wheel in dist/ from --wheel --library
  • A deployment directory from --embed

Distribute the binary, publish the wheel to your private package index for installation with uv, or copy the embed directory to the target machine. Your proprietary Python logic is no longer present as readable .py source.

Verifying Your Compiled Application

Run the generated executable from a clean shell:

./main

On Windows, run main.exe.

For library mode, confirm the package imports and runs:

python -m mypkg

Check the distribution for source files. In a wheel, the generated __init__.py and __main__.py bridge files may be present, but your application code should be compiled into the shared library:

find dist -type f -name "*.py"

Windows users can use the equivalent directory listing or search in File Explorer.

If you built with Pro, verify a license token:

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

Finally, test on a clean machine or container. Copy the binary, wheel, or embed directory somewhere without your development environment and run it there. If the artifact works on a clean system, it has no hidden dependency on your build machine.

Troubleshooting Common Compilation Issues

Missing C compiler or build tools

Py2Native needs a working platform toolchain. Install the right tools for your OS:

# Ubuntu/Debian
sudo apt-get install build-essential

# macOS
xcode-select --install

On Windows, install the “Desktop development with C++” workload from Visual Studio Build Tools.

Duplicate module names

Py2Native does not support modules with identical names across source files. Keep module names unique across your project, then rerun the build.

Third-party library compatibility

Py2Native intentionally leaves third-party libraries as Python source. They are bundled as-is and remain replaceable. If a dependency behaves unexpectedly, check whether it relies on assumptions about source files that your compiled code no longer exposes.

License verification failures

If a Pro build or show command reports a signature problem, confirm that:

  • The license file was signed with the correct private key
  • The --public flag points to the matching public key
  • The token has not expired
  • The build is using the Pro plugin and a valid Pro license

Cross-platform builds

Run the build on the target OS and architecture. Py2Native’s architecture includes platform plugs for Windows, Linux, and macOS, but a Windows binary is built on Windows, a Linux binary on Linux, and so on.

FAQ: Distributing Python Apps Without Source Code

Can I distribute my Python app as a single executable?

Yes. Py2Native can compile your Python code into a native executable. Use the build command without --library to produce an executable that includes your compiled code and the required runtime support.

Does Py2Native protect my code from reverse engineering?

Py2Native compiles your Python code to native machine code, making it much harder to reverse engineer than plain .py files. However, no protection is absolute. Determined attackers may still analyze the binary. The Pro edition adds license verification to control distribution.

How do I handle third-party libraries?

Py2Native leaves third-party libraries as Python source. They are not compiled, so they remain replaceable and compliant with their licenses. Your own custom code is compiled, while dependencies are bundled as-is.

What is the difference between the Community and Pro editions?

The Community edition, py2native, is open source under MIT and provides full compilation, uv embedding, and cross-platform support. The Pro edition, py2nativepro, adds JWT license verification, string compression, and key management commands such as keygen, sign, and show.

Can I use Py2Native for commercial closed-source software?

Yes. The Community edition is MIT-licensed and can compile commercial Python code. The Pro plugin is a proprietary option for teams that also want built-in signed-license enforcement on top of native compilation.

Conclusion: Ship Protected Python Apps with Confidence

Py2Native gives you a practical, zero-config route to distribute a Python application without source. You write plain Python, run uv run py2native build, and ship a native binary, library wheel, or uv-managed directory. The underlying Cython work stays hidden, so you do not need to maintain .pyx/.pxd files or write C extensions.

For commercial builds, add the Pro plugin and compile license verification directly into the executable. Start with:

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

Then expand to --library, --wheel, and --embed as your distribution model requires. For a closer look at the Pro license workflow, see Secure Your Python App with Built-in License Verification. If you are still evaluating native Python compilation options, see The Best Python Native Compiler for Proprietary Software.

Related posts

EU label: AI-generated content