Learn
Six concepts. The workshop proves each one on a real app.
01
Python ships as source
Python is interpreted: the files you deploy are the files you wrote. Anyone with access to them can read every function, constant and comment.
Python compiles source to bytecode and caches it in __pycache__/*.pyc. Bytecode is not protection: it keeps every name and constant, and decompilers turn it back into readable Python. The workshop shows this with the standard dis module.
02
Obfuscation
Transforming code so it still runs but no longer reads like the original. It raises the effort needed to understand and reuse the logic.
Obfuscation is not encryption of your app: the means to run the code must ship with it. It is a cost for the reader, not a wall.
03
What PyArmor does
pyarmor gen rewrites each module into a three-line stub that hands an encrypted payload to a runtime extension.
Each function is also obfuscated on its own and restored only while it runs ("wrap mode"). Options add expiry dates (-e), device binding (-b), private mode (--private) and PyInstaller packing (--pack).
04
The runtime
pyarmor_runtime_000000 is a compiled extension that decodes protected code in memory. No runtime, no app.
The runtime is built for one platform and one CPython minor version. A build made on Python 3.12 does not load on 3.11, which is why the Docker image builds and runs on the same base image.
05
Packaging
A protected app is four parts: protected code, the runtime, third-party dependencies, and a matching Python interpreter.
Ship them as an archive, a PyInstaller bundle (PyInstaller alone does not protect code), or a container image. Dependencies such as FastAPI stay unobfuscated.
06
The limits
Once loaded, code runs in a process. Whoever controls that process can see what it holds: values, secrets, inputs and outputs.
That is why obfuscation is one layer. Secrets belong in a secrets manager, servers need access control, and releases need a trusted pipeline.
What a protected module looks like
This is the start of a real file from the workshop. The whole module is these three lines; the third one is several kilobytes long.
# Pyarmor 9.2.7 (trial), 000000, non-profits, 2026-10-08T09:17:16
from pyarmor_runtime_000000 import __pyarmor__
__pyarmor__(__name__, __file__, b'PY000000\x00\x03\x0c\x00\xcb\r\r\n\x80...')