Obscuring Python source from end users

Chris Angelico rosuav at gmail.com
Mon Sep 29 12:14:08 CEST 2014

On Mon, Sep 29, 2014 at 6:55 PM, Chris Angelico <rosuav at gmail.com> wrote:
> It ought to be possible to do an AST reconstitution for at least part
> of this. I can hunt down some of my PEP 463 test code to help out with
> that. It should be possible to figure out what names are local, and
> then just use those.
> If this is of interest, I'll see what I can knock together.

Actually, why reconstitute? The AST can be compiled.

Here's a Python cruncher/obscurer:

import ast, sys, os

# Note: If the file can't be parsed, this will raise SyntaxError
# (from the ast.parse() line).
def crunch(fn, outfn):
    with open(fn, "rb") as f:
        data = f.read()
    tree = ast.parse(data)
    os.makedirs(os.path.split(outfn)[0], exist_ok=True)
    with open(outfn, "w") as f:
        print("from ast import *", file=f)
        print("_code = "+ast.dump(tree), file=f)
        print("fix_missing_locations(_code)", file=f)
        print("[globals().__delitem__(_) for _ in dir() if _[0]!='_']", file=f)
        print("exec(compile(_code,__name__,'exec'))", file=f)

if __name__ == "__main__":
    for fn in sys.argv[1:]:
        outfn = "crunched/" + fn
        crunch(fn, outfn)
        print("Crunched", fn, "to", outfn)

The resulting files should work just the same as the original ones,
but will be virtually unreadable. This is still only obscurity,
though; the only information actually removed is formatting and
comments, not even names. But you could easily add an ast.walk() in
there, just after the ast.parse(), and do whatever transformations you

Note that unlike with JavaScript crunching, this won't shrink the
files - in fact, it's usually going to expand them. They'll probably
also take longer to load. There is no benefit other than the

Run this over some of your files, then see if your auditors are happy.


More information about the Python-list mailing list