Py2Native Compile custom Python into native machine code to protect proprietary code

← All posts

2026-09-02

Challenges Faced by Python Developers Shipping Proprietary Software

Challenges Faced by Python Developers Shipping Proprietary Software
python compiler cython code protection open source

Challenges Faced by Python Developers Shipping Proprietary Software

Maya is a founder who built a profitable SaaS product in Python. The cloud version is stable, the customers are happy, and now an enterprise client wants the same software installed on their own infrastructure. The sales call goes well — right up until the client’s security team asks how the on-premise version will be delivered.

That is when Maya realizes the problem: if she ships the product as .py files, her entire source code becomes readable and editable by the client. Support contracts, license tiers, proprietary algorithms, and future product leverage suddenly feel fragile. Python’s readability is why she shipped fast, but for proprietary distribution, that same openness is a liability.

This article walks through the specific challenges Python developers face when shipping proprietary software, then shows a practical path to protect Python code without turning into a build engineer.

The Founder’s Dilemma: Shipping Python Without Exposing Your Code

Maya’s situation is common in Python-centric product teams. The language is excellent for rapid development, data handling, and maintainability. But for a company that sells access to software rather than source code, shipping plain .py files creates a different kind of risk.

The core tension is straightforward:

  • Python packages are usually distributed as source files.
  • Source files are designed to be read.
  • Reading the code often means understanding the business logic well enough to copy, modify, or bypass it.

For a founder like Maya, that can weaken licensing enforcement, increase unpaid support requests from clients who modify the code, and make it harder to defend intellectual property when a competitor has access to the original source.

The solution is not to stop using Python. It is to change the artifact that leaves the building.

Why Python’s Open Nature Creates Unique Shipping Challenges

Python code normally ships as .py source. End users can open those files, read every conditional branch, rename a function, create a fork, or strip out a license check. Even compiled Python bytecode in .pyc files can be decompiled with widely available tools.

The business risks go beyond copying:

  • IP theft. Custom pricing models, scoring algorithms, or integration logic can be extracted and reused.
  • Unauthorized modifications. Clients may edit code to bypass limits, remove audit logging, or “fix” something in a way that breaks your support model.
  • Support burden. Once customers modify source, they often report bugs that do not exist in your release.
  • Loss of competitive advantage. A services company or reseller could use readable source to build a competing implementation.

Traditional obfuscation techniques — base64 encoding, variable renaming, minification — can slow down a casual reader, but they are usually reversible. If the Python interpreter can still execute the code, a determined person can recover enough of it to understand the original behavior. For a deeper look at why those approaches fall short, see Python Obfuscation Techniques: Do They Really Protect Your Code?.

A stronger path exists: compile your Python code into native machine code.

Cython can translate Python into C, then compile that C into a shared object or executable. But the traditional Cython workflow is not friendly for a typical Python team. It often requires manually writing .pyx and .pxd files, configuring build scripts, and understanding Cython-specific syntax. That is the hard way.

The practical alternative is a compiler that handles those details for you: Py2Native.

A Practical Playbook: Protecting Your Python Code with Py2Native

Py2Native is a zero-config Python-to-native compiler. You write plain Python, run one command, and get a native executable or shared library instead of readable source. It uses Cython under the hood, but hides the Cython build process completely.

Here is the playbook Maya could follow.

Step 1: Add Py2Native to the project

Py2Native works with CPython 3.11–3.15, including free-threaded builds. You also need a platform C compiler and linker: MSVC on Windows, GCC on Linux, or Clang on macOS.

Add the compiler through uv, not pip:

uv add py2native

All Py2Native commands start with:

uv run py2native

Step 2: Build a native executable

For a simple project with a main.py entry point:

uv run py2native build main.py *.py

Py2Native expands the source globs, compiles your Python into native machine code, links it, and produces an executable for your platform. On Windows, you can also create a non-console binary when you do not need a visible terminal:

uv run py2native build main.py *.py --no-console

The key point is that you are still writing ordinary Python. You are not writing C extensions, maintaining .pyx files, or invoking Cython manually.

Step 3: Distribute as a shared library or wheel

If you are building a package that clients import, use library mode:

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

Library mode compiles your modules into a shared library — .pyd on Windows, .so on Linux, or .dylib on macOS. The generated wheel contains that compiled library plus an __init__.py meta-path finder and a __main__.py entry point. Both files are required: __init__.py bridges Python’s import system to the native library, and __main__.py makes python -m mypkg work.

This is the distribution shape you want when the client installs your product with a standard Python toolchain, but should not receive readable application code.

Step 4: Create a managed deployment directory

For enterprise clients who need a self-contained deployment, use embed mode:

uv run py2native build main.py mypkg/*.py --embed deploy/

This creates a uv-managed deployment directory containing the compiled binary and its dependencies. Third-party libraries remain unmodified Python source, so you do not need to compile every dependency by hand. They work as-is with minimal or no configuration.

Step 5: Add license verification with the Pro plugin

Compilation protects the code itself, but a compiled binary can still be copied. If you need to enforce commercial licenses, use the Py2Native Pro plugin.

The Pro plugin adds JWT-based license verification and string compression to the open-source compiler. The license flow starts with key generation:

uv run py2native keygen private.pem public.pem

This generates an EC P-256 keypair. You keep private.pem private and distribute public.pem with the build or bake its contents into the executable through your .pxd declaration.

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

uv run py2native sign --private private.pem '{"sub":"acme-corp","exp":"2027-01-01"}' license.dat

You can inspect claims, and optionally verify against the public key, with:

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

The Pro plugin includes a compiled verification routine. This is the one place where Py2Native asks for a .pxd declaration rather than plain Python, because the plugin needs to link the native verification code into your executable. The exact entry point is documented in the Pro plugin docs; in practice, a .pxd file can look like this:

# license_check.pxd
cdef extern from "py2nativepro/license.h":
    int py2nativepro_verify_license(const char *license_file)

Then your application calls the verification logic with the license key before allowing startup:

# app.py
cdef extern from "license_check.pxd":
    int py2nativepro_verify_license(const char *license_file)

def enforce_license():
    if py2nativepro_verify_license("license.dat") != 0:
        raise SystemExit("License verification failed")

enforce_license()

Py2Native compiles the declaration, links the verification code, and bakes the public key into the executable. The private key is never shipped. The elliptic-key verification happens in compiled code without third-party libraries.

Build a licensed executable with:

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

This gives you a single command for protected distribution and enforceable licensing.

What to Avoid When Shipping Proprietary Python Software

Maya’s safest path is also the one that avoids several common mistakes.

Do not rely on obfuscation alone. Renaming variables or encoding strings may stop a casual reader, but it is a speed bump rather than a wall. Native compilation raises the cost of reverse engineering substantially.

Do not build a manual Cython workflow. Writing .pyx and .pxd files by hand, maintaining a setup.py, and debugging Cython-specific build errors is a separate engineering project. The zero-config approach keeps the team focused on product code.

Do not ship raw .py files to customers when IP protection matters. Even a clean, well-organized Python package is still readable source. Compile your application code before it leaves your build pipeline.

Do not ignore license verification. A compiled binary can still be copied from one client to another. The Pro plugin’s signed JWT verification gives you a real enforcement point through the --license flag on build.

Do not overcomplicate the build process. Most Python projects do not need a custom build system. With Py2Native, the compiler invokes Cython, compiles C to objects, links the result, and optionally creates a wheel or managed deployment directory for you.

FAQ: Common Questions About Shipping Proprietary Python Software

Q: Will Py2Native protect my code from reverse engineering?

A: Py2Native compiles your Python code into native machine code, which is significantly harder to reverse engineer than Python bytecode or source. While no protection is absolute, it raises the bar substantially compared to shipping .py files or using simple obfuscation.

Q: Can I still use third-party libraries?

A: Yes. Py2Native leaves third-party libraries unmodified and they work as-is with minimal or no configuration. Your own code is compiled, but dependencies remain as Python source, so you don’t need to compile or configure them.

Q: How does Py2Native compare to Cython?

A: Py2Native uses Cython under the hood but hides it completely. You write plain Python — no manual .pyx files, no manual cythonize step, and no hand-written C extensions. Cython requires you to learn its syntax and build process; Py2Native automates everything with a single command. For a side-by-side look, see Cython vs Py2Native: Which Is Better for Protecting Python Code?.

Q: Is Py2Native suitable for commercial products?

A: Yes. The core compiler is open source under the MIT license. A commercial Pro plugin adds license verification with signed JWT keys and string compression. That makes Py2Native useful for both open-source projects and proprietary commercial software.

Conclusion: Ship with Confidence

Python’s readability is a development superpower, but it becomes a distribution risk when you sell software rather than source code. Readable .py files invite copying and modification. Obfuscation is easy to reverse, and a manual Cython workflow is often too complex for a busy product team.

Py2Native changes the equation: write plain Python, run uv run py2native build, and ship a native executable or shared library. Add --library for importable packages, --embed for managed deployments, or the Pro plugin for signed JWT license verification.

If you are in Maya’s position, start with the open-source compiler on one real module:

uv run py2native build main.py *.py

Then consider the Py2Native Pro plugin when you need license enforcement baked into the binary. For more on the compiler itself, see Python to Native Compiler: Protect Your Code Effortlessly, or learn how to fit the build into your release pipeline in Automating Python to Native Compilation in CI/CD.

Related posts

EU label: AI-generated content