Can Python Code Be Decompiled? What You Actually Need to Know
Yes. Python distributed to another machine can generally be analyzed or reverse-engineered to some degree. A .py is plain source; a .pyc decompiles back to near-original code and leaks its string literals; even a compiled .pyd/.so native module can be disassembled. Obfuscation raises the difficulty and cost of reverse engineering — it doesn't provide mathematically perfect secrecy. The only code that truly can't be decompiled is code that never leaves your server.
This is one of the most-asked questions in Python, and it deserves an honest answer instead of a sales pitch. The short version: yes, to varying degrees — how hard depends entirely on the *form* you ship. I compiled the same tiny program to each format and checked exactly what an analyst can pull back out.
Real output below, from .py all the way to a native .pyd.
The short answer, and the spectrum
"Decompiling" means recovering readable source (or at least the logic) from a distributed program. In Python it ranges from *trivial* to *very hard* depending on format:
| You ship… | What an analyst gets | Difficulty |
|---|---|---|
.py | Your exact source — just open it | None |
.pyc | Near-original source (decompilers) + all strings | Low |
Obfuscated .py | Runnable but unreadable code; strings maybe encrypted | Medium |
.pyd / .so (Cython) | Native machine code — must disassemble ASM | High |
| Server-side | Nothing — the code never leaves your machine | N/A |
Here's the program I used to test each row — a hard-coded key and a license check, the thing people most want to hide:
API_KEY = "sk-live-9f83kd02n"def check(key):return key == "PRO-2026-XYZ"print(check("PRO-2026-XYZ"))
`.pyc` bytecode decompiles — and leaks strings without any decompiler
Shipping .pyc instead of .py is the most common "protection" — and it's barely any. Compiling secret.py produced a 371-byte .pyc. Before decompiling anything, a plain byte search pulls the secrets straight out:
$ grep -a -o "sk-live-9f83kd02n" secret.pycsk-live-9f83kd02n
And the built-in dis module reads the logic right off the .pyc's code object — the key, the function, the comparison, all in plain sight:
1 LOAD_CONST 0 ('sk-live-9f83kd02n')STORE_NAME 0 (API_KEY)2 LOAD_CONST 1 (<code object check ...>)MAKE_FUNCTIONSTORE_NAME 1 (check)4 LOAD_NAME 1 (check)LOAD_CONST 2 ('PRO-2026-XYZ')
On top of that, decompilers rebuild actual .py source: uncompyle6/decompyle3 for older versions, pycdc for 3.10+. Our free .pyc Decompiler runs pycdc in your browser — paste a .pyc and watch source come back. So: .pyc is a performance cache, never protection. (Full breakdown: can you decompile a .pyc file?)
Obfuscation raises the cost — it doesn't make it impossible
Obfuscation is the honest middle ground. It keeps the code as runnable Python but strips the clues: renames every identifier irreversibly, encrypts string literals so a byte search finds nothing, flattens control flow into a state machine, and injects junk. A decompiler still "works" — but what it recovers is the obfuscated mess, not your original code. The goal is economic: make reverse engineering cost more time than your logic is worth.
This is the honest framing the whole Python community lands on: obfuscation increases the difficulty and cost of analysis, not mathematically perfect secrecy. Anyone selling "unbreakable" client-side Python protection is overselling it.
Native extensions (`.pyd`/`.so`) can't be *decompiled* — but can be disassembled
This is the strongest client-side option. Compiling to a native `.pyd`/`.so` with Cython turns your Python into machine code — there's no bytecode left, so .pyc decompilers have nothing to work with. When I tried to load my compiled module as a code object, it simply isn't one:
>>> import marshal>>> marshal.loads(open('liccore.cp311-win_amd64.pyd','rb').read()[16:])ValueError: bad marshal data # it's native machine code, not a Python code object
"Can't be decompiled" is not "can't be reverse-engineered." A native binary can still be disassembled (Ghidra, IDA) into assembly, and — as I found building it — Cython does not encrypt string literals, so a hard-coded key still leaks from the .pyd via a byte search. It raises the bar enormously; it isn't a vault.
The only code that truly can't be decompiled
…is code you never ship. If your sensitive logic — the pricing algorithm, the license validation, the model — runs on your server and the client only sends inputs and receives outputs, there's nothing on the user's machine to decompile. This is the one form of protection that's absolute, and it's why serious products keep their crown-jewel logic server-side and treat client obfuscation as a delay tactic for the rest.
Practical takeaway: layer it. Obfuscate what must ship (raises cost), compile the sensitive modules to native code (removes bytecode), and keep real secrets off the client entirely (removes the target). See the full playbook in how to protect Python source code.
Make your Python costly to reverse — free
You can't make Python impossible to decompile, but you can make it not worth the effort. Obfuscate your source — rename, encrypt strings, flatten flow — in your browser, then compile the sensitive parts.
Open the Python ObfuscatorFree tools mentioned here
Related guides
Frequently asked questions
Can Python code be decompiled back to source?
Yes, to varying degrees. A .py is already source. A .pyc decompiles back to near-original source with tools like uncompyle6 or pycdc, and even without a decompiler it leaks its string literals and reveals its logic via the dis module. Obfuscation and native compilation make it harder, but only server-side code truly can't be decompiled.
Is a .pyc file protected from decompiling?
No. A .pyc is cached bytecode: it runs without the .py, stores string literals (including keys) in plaintext, and decompiles back to near-original code with a matching decompiler. It only stops someone from casually opening the file in a text editor.
Can a compiled .pyd or .so be decompiled?
Not with a Python decompiler — there's no bytecode in a native extension, so tools like pycdc can't touch it. It can still be disassembled into machine assembly (Ghidra/IDA), which is far harder to read, and Cython doesn't encrypt string literals, so hard-coded secrets can still leak via a byte search.
How do I make my Python code hard to decompile?
Layer defenses: obfuscate the source (rename identifiers, encrypt strings, flatten control flow) to raise the cost, compile sensitive modules to a native .pyd/.so so there's no bytecode to decompile, and keep real secrets off the client by loading them from a server or environment. No client-side method is perfect; the goal is to make analysis cost more than your logic is worth.
Is it legal to decompile Python code?
It depends on jurisdiction, the software license, and purpose. Decompiling your own code, or for interoperability/security research where local law and the license permit, is common; reverse-engineering third-party software you don't have rights to can violate its license or the law. Check before you do it.