PyArmor vs Nuitka vs Cython vs Pyobfuscate: Python Code Protection Compared (Tested)
They protect Python differently: Pyobfuscate rewrites source to be unreadable (free, no compiler, secret hidden); PyArmor obfuscates + adds a licensing runtime; Cython and Nuitka compile to native .pyd/.so (no bytecode to decompile, but string literals still leak). I ran the same script through all four — real sizes: Pyobfuscate ~25 KB, PyArmor 4.8 KB + 631 KB runtime, Cython 35 KB, Nuitka 342 KB. Pyobfuscate and PyArmor hid the secret; Cython and Nuitka left it in plaintext.
Every "protect my Python" thread names the same tools — PyArmor, Nuitka, Cython, PyInstaller, obfuscators — usually with marketing claims and little testing. So I ran the same 12-line script (a hard-coded secret + a license check + a compute loop) through each one and measured what actually comes out.
Everything below is real: PyArmor 8.5.1, Nuitka 2.6.7, Cython 3.0.12, our own Pyobfuscate engine, all on Python 3.11 with MSVC. Real sizes, real secret-leak checks, real behaviour.
The three categories (this is the key)
Before the numbers, the distinction that resolves 90% of the confusion: these tools fall into three categories doing different jobs.
| Category | Tools | What it does |
|---|---|---|
| Obfuscator | Pyobfuscate, PyArmor | Rewrites source unreadable (stays Python) |
| Compiler | Cython, Nuitka | Turns Python into native machine code |
| Packager | PyInstaller | Bundles bytecode + interpreter (no protection) |
PyArmor also adds a licensing runtime, and PyInstaller is packaging only — I cover it in does PyInstaller protect source code?. The four below are the real protection contenders.
The tested results
Same input script for all four. The two things that matter most: output size and does the secret leak (I searched each artifact for the hard-coded PYOB-2026-PRO-9f83kd).
| Tool | Output | Secret in output? | Needs a compiler? |
|---|---|---|---|
| Pyobfuscate | ~25 KB obfuscated .py | Hidden ✓ | No (pure Python) |
| PyArmor | 4.8 KB script + 631 KB runtime .pyd | Hidden ✓ | No (ships a runtime) |
| Cython | 35 KB native .pyd | Leaks ✗ | Yes (C compiler) |
| Nuitka | 342 KB native .pyd | Leaks ✗ | Yes (C compiler) |
The surprise for most people: Cython and Nuitka did NOT hide the secret. A plain byte search found PYOB-2026-PRO-9f83kd in both native .pyd files. Compilation hides your *logic* (there's no bytecode to decompile) but not your *string literals*. Only the two obfuscators encrypted the string.
Pyobfuscate — free, no compiler, source stays Python
Our Python Obfuscator rewrites the source at the AST level — renaming, string encryption, control-flow flattening, junk — and returns runnable Python. My script came out ~25 KB with the secret encrypted and no external dependency. Best for: fast, free source hiding with zero toolchain, or as a first layer before compiling. Trade-off: it's still bytecode, so it decompiles — to the obfuscated version, not your original.
PyArmor — obfuscation plus a licensing runtime
PyArmor (8.5.1 trial) produced a tiny 4.8 KB stub that calls into a 631 KB `pyarmor_runtime.pyd` — the obfuscated script is a b'PY00...' blob decoded by that runtime. The secret was hidden and it ran correctly. Its real differentiator is licensing/DRM — expiration, trials, hardware binding — which the others don't do. Best for: commercial products needing license enforcement. Trade-off: the output depends on PyArmor's runtime, and full features are paid. See PyArmor vs Pyobfuscate and PyArmor free vs pro.
Cython — compile to native, strong logic protection
Cython compiled my module to a 35 KB native `.pyd` — the smallest compiled output — and in a separate benchmark ran a numeric loop 56× faster than pure Python. There's no bytecode, so .pyc decompilers can't touch it. Best for: compiling the sensitive *modules* of an otherwise-normal Python project. Trade-off: needs a C compiler, is platform/version-specific (cp311-win_amd64), and — as the test showed — leaves string literals in plaintext.
Nuitka — compile the whole app to a binary
Nuitka (2.6.7) also compiled to native code, but the .pyd was 342 KB — ~10× Cython's, because Nuitka bundles more of its own runtime and does whole-program optimization. It ran correctly. Nuitka shines at compiling an entire application to a standalone binary (see Nuitka vs PyArmor and Cython vs Nuitka). Best for: shipping a compiled whole app. Trade-off: same as Cython — C toolchain, per-platform builds, and strings still leak.
Which should you use?
- Free, fast, no toolchain → Pyobfuscate. Paste, click, ship runnable Python.
- Commercial licensing / expiry / hardware-lock → PyArmor.
- Protect an algorithm in a few modules → Cython (small native output).
- Compile a whole app to a binary → Nuitka.
- Just need users without Python to run it → PyInstaller — but it's not protection, so obfuscate or compile first.
The strongest real-world stack combines categories: obfuscate the source (hides strings + logic), then compile the sensitive modules (removes bytecode). No compiler encrypts strings and no obfuscator removes bytecode — together they cover both gaps.
And the honest ceiling on all of it: anything that runs on the user's machine can be analysed given enough effort — see can Python code be decompiled?. These tools raise the cost; server-side logic is the only absolute.
Try the free one from this test
Pyobfuscate hid the secret with no compiler and no cost. Paste your code and obfuscate it in your browser — rename, encrypt strings, flatten control flow — then compile the sensitive modules if you need native protection.
Open the Python ObfuscatorFree tools mentioned here
Related guides
Frequently asked questions
What is the best Python obfuscator or protection tool?
It depends on the goal. For free, fast source hiding with no compiler, Pyobfuscate; for commercial licensing/DRM, PyArmor; for native compilation of sensitive modules, Cython; for compiling a whole app to a binary, Nuitka. In my tests, Pyobfuscate and PyArmor hid a hard-coded secret while Cython and Nuitka left it in plaintext.
Do Cython and Nuitka hide string literals?
No. In my test, a plain byte search found the hard-coded secret in both the Cython and Nuitka native .pyd files. Compilation removes the bytecode (so decompilers can't recover source) but does not encrypt string literals. Obfuscate the source first, or keep secrets off the client.
Is PyArmor better than Cython for protecting Python?
They do different jobs. PyArmor obfuscates and adds a licensing runtime (and hid the secret in my test), keeping the code as Python that depends on its runtime. Cython compiles to native machine code with no bytecode to decompile but leaves strings in plaintext. For licensing use PyArmor; for compiled logic protection use Cython — or combine obfuscation with compilation.
Which produced the smallest output?
In my test on the same 12-line script: PyArmor's script stub was 4.8 KB (but needs a 631 KB runtime), Pyobfuscate was ~25 KB, Cython was a 35 KB native .pyd, and Nuitka was 342 KB. PyInstaller (a packager, tested separately) was a 9.17 MB exe.
Can I combine these tools?
Yes, and it's the strongest approach. Obfuscate the source first (encrypts strings and hides logic), then compile the sensitive modules with Cython or Nuitka (removes the bytecode). No compiler encrypts strings and no obfuscator removes bytecode, so combining them covers both weaknesses.