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
uv0.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:
- Glob expansion under
--baseto collect source files - Plugin dispatch for Pro license checks or platform-specific behavior
- Cython transpilation from Python to C
- C compilation into native objects with the platform compiler
- Linking into an executable or shared library
- 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 asimport mypkg.submoduleto the native shared library__main__.py, so users can run the package withpython -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
--publicflag 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
- Secure Your Python App with Built-in License Verification
- The Best Python Native Compiler for Proprietary Software
- How to Protect Python Source Code: A Beginner’s Guide