Multi-stage Python source obfuscator. Encodes strings, scrambles numbers, renames identifiers with zalgo text, injects dead code, and wraps everything in a marshal/zlib/base85 bootstrap loader.
Give it a file and it obfuscates that file. Give it a directory and it obfuscates the whole project.
uv sync# Single file
uv run pyprotect input.py -o out.py
# Whole project
uv run pyprotect ./src -o ./dist| Flag | Description |
|---|---|
-o, --output |
Output file or directory (required) |
--no-strings |
Skip string encoding |
--no-rename |
Skip identifier renaming |
--no-numbers |
Skip number obfuscation |
--no-junk |
Skip dead code injection |
--no-wrap |
Skip marshal/zlib wrapping |
--no-zalgo |
Turn off zalgo on identifiers |
--zalgo-density N |
Combining marks per character (default: 300) |
--layers N |
Marshal wrap nesting depth (default: 1) |
--seed N |
RNG seed for reproducible output |
-v, --verbose |
Print progress info |
These are ignored when obfuscating a single file.
| Flag | Description |
|---|---|
--exclude NAME |
Directory name to skip (can be used multiple times) |
--no-preserve-public |
Also rename names listed in __all__ |
--no-copy-others |
Don't copy non-Python files to the output |
# Basic single file
uv run pyprotect input.py -o out.py
# 3 layers of marshal wrapping
uv run pyprotect input.py -o out.py --layers 3
# No zalgo, just the other passes
uv run pyprotect input.py -o out.py --no-zalgo
# Reproducible output
uv run pyprotect input.py -o out.py --seed 42
# Obfuscate a project, skip the tests folder
uv run pyprotect ./src -o ./dist --exclude tests
# Light mode, just marshal wrapping
uv run pyprotect input.py -o out.py --no-strings --no-rename --no-numbers --no-junk
# Max chaos
uv run pyprotect input.py -o out.py --layers 3 --zalgo-density 500