scramblenames
Obfuscate Python source code by scrambling variable, function, and class names into meaningless eight-character identifiers, while keeping the program working exactly the same. Comments and docstrings are removed by default too. The point is to strip every human-readable clue from a program without changing what it does.
It does not un-scramble — this is a one-way transform.
Install
pip install scramblenames
scramblenames uses libcst to preserve
formatting (and comments/docstrings when you choose to keep them).
Library usage
from scramblenames import scramble
source = '''
def add(a, b):
total = a + b
return total
'''
print(scramble(source, seed=1))
def gFb51yBM(iK2ZWeqh, FWCEPyYn):
WXaSCrUZ = iK2ZWeqh + FWCEPyYn
return WXaSCrUZ
scramble() accepts the code as a string, a pathlib.Path, or a string
filename of an existing file:
from pathlib import Path
scramble(Path("mymodule.py")) # read from a Path
scramble("mymodule.py") # read from a filename
scramble("x = 1\n") # scramble a code string directly
Scrambling a whole directory
scramble_tree() scrambles every matching file under a directory, mirroring the
tree into a destination directory:
from scramblenames import scramble_tree
scramble_tree("myproject/", "obfuscated/", import_safe=True)
Each file is scrambled independently, so use import_safe=True for multi-file
packages — otherwise a public name renamed in one module wouldn't be matched at
its import site in another. Use include/exclude globs and recursive=False
to control which files are processed.
Errors
Source that can't be parsed raises scramblenames.ScrambleError, carrying the
filename, line, and column of the problem when available.
Options
scramble(
source,
import_safe=False, # keep top-level function/class/global names
strip_comments=True, # remove all comments
strip_docstrings=True, # remove module/function/class docstrings
style="random", # "random" | "hex" | "reorder"
seed=None, # int for reproducible output
)
| Option | Effect |
|---|---|
import_safe=True |
Assume the file may be imported by other code. Module-level function, class, and global names are kept; only function-local names are scrambled. |
import_safe=False (default) |
Also scramble module-level function, class, and global names (except those in __all__). Best for standalone scripts. |
strip_comments=True (default) |
Remove every comment. A first-line shebang (#!...) and a PEP 263 encoding cookie are always kept. Set False to keep all comments. |
strip_docstrings=True (default) |
Remove the leading docstring of the module and of every function and class (an empty body becomes pass). Set False to keep them. |
style="random" |
Eight random alphanumeric characters, first is always a letter. |
style="hex" |
Eight hexadecimal characters, first is always a letter (a–f). |
style="reorder" |
A permutation of the original name's own characters. |
Command line
scramblenames mymodule.py # scramble to stdout (strips prose)
scramblenames mymodule.py -o out.py # write to a file
scramblenames mymodule.py --import-safe # keep the public API
scramblenames mymodule.py --keep-prose # keep comments + docstrings
scramblenames mymodule.py --style hex --seed 7 # reproducible hex names
cat mymodule.py | scramblenames # read from stdin
# scramble a whole project into a mirrored output directory
scramblenames myproject/ -d obfuscated/ --import-safe
scramblenames src/ -d out/ --exclude '*_test.py' --no-recursive
Scrambling a directory (or more than one file) requires -d/--output-dir, which
mirrors the input tree. The paths written are listed on stderr. A parse error
prints a clean message and exits non-zero.
What is and isn't scrambled
Every generated name is a valid Python identifier and never a keyword, so the output is always syntactically valid and behaves identically to the input.
Scrambled
- Local variables inside functions.
for,with ... as,except ... as, and walrus targets.- Comprehension and generator variables.
- Module-level functions, classes, and globals — only when
import_safe=False. - Nested function and class names (they are local to their enclosing function).
- Parameters that can't be reached by keyword — positional-only params,
*args, and**kwargs— always. - Other parameters, when the function is safe to analyze: it's local (its
own name is scrambled too), undecorated, never passed around or stored, and
never called with
**unpacking. Every keyword argument at its call sites is rewritten to match (f(name=1)→f(x7Kd2p9q=1)).
Never scrambled (renaming these could break the program)
- Attributes —
x.foo,self.foo, and the string entries in__slots__, since the object's type generally can't be proven. - Method names — reached by attribute access, like other attributes.
- Method and public-API parameters — outside callers may pass them by keyword, so keyword-eligible parameters of methods, of the public API, and of functions that escape the module are kept.
- Imported names and builtins.
- Names listed in
__all__, and dunder names like__init__.
Because attributes are preserved and parameters are only renamed when all call
sites are visible, code that relies on dynamic access — getattr/setattr
with computed strings, locals(), **kwargs forwarding by string key — keeps
working.
Development
python -m venv .venv && source .venv/bin/activate
pip install -e ".[test]"
pytest
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file scramblenames-0.1.0.tar.gz.
File metadata
- Download URL: scramblenames-0.1.0.tar.gz
- Upload date:
- Size: 24.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.1.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6b3e66dc98ecfd9f13af846b4e6908c6f813c0a6474d05c0f93315cbcb435ded
|
|
| MD5 |
f166d3494bc20f37d112f5c03555f1d0
|
|
| BLAKE2b-256 |
cecd8f7fa06204762fa3e5747a3346d6ede6a4b564ed69e7b94e19c4fae62c5d
|
File details
Details for the file scramblenames-0.1.0-py3-none-any.whl.
File metadata
- Download URL: scramblenames-0.1.0-py3-none-any.whl
- Upload date:
- Size: 17.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.1.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
42645dbc3f618bfd808b9545a760b1c8ead9200f3e15d7f44867e725e5d9b3ee
|
|
| MD5 |
16ccf4daa87d206be2928576bf02bd6a
|
|
| BLAKE2b-256 |
2deaaf6b87face20ee9340b67a0d42a326f4d6c85fd653bad9f452b046dedd4f
|