#!/usr/bin/env python # encoding: utf-8 """ Python Code Protector """ import os import sys import imp import marshal import traceback import _pyprotect MAGIC = imp.get_magic() # recurse dir m_recurse = True # delete after protect m_delete = False # follow symlink m_symlink = True def wr_long(f, x): """Internal; write a 32-bit int to a file in little-endian order.""" f.write(chr(x & 0xff)) f.write(chr((x >> 8) & 0xff)) f.write(chr((x >> 16) & 0xff)) f.write(chr((x >> 24) & 0xff)) def protect_file(file, cfile=None): """ Protect a pyfile """ if os.path.islink(file) and not m_symlink: print 'Protect: ignore symlink file:', file return True if not os.path.isfile(file): return False if file.endswith(".pyc") or file.endswith(".pyo"): return False if not file.endswith(".py"): print 'Protect: ignore non-py file:', file return True f = open(file, 'U') try: timestamp = long(os.fstat(f.fileno()).st_mtime) except AttributeError: timestamp = long(os.stat(file).st_mtime) codestring = f.read() f.close() if codestring and codestring[-1] != '\n': codestring = codestring + '\n' try: os.putenv('PYTHONOPTIMIZE', '2') codeobject = compile(codestring, file, 'exec') except: traceback.print_exc() return False _pyprotect.protect(codeobject) if cfile is None: cfile = file + (__debug__ and 'c' or 'o') fc = open(cfile, 'wb') fc.write('\0\0\0\0') wr_long(fc, timestamp) marshal.dump(codeobject, fc) fc.flush() fc.seek(0, 0) fc.write(MAGIC) fc.close() if m_delete: try: os.unlink(file) except Exception, e: print 'Protect: failed to delete file: %s, reason: %s' % (file, str(e)) return True def protect_dir(dirname): """ Protect py files in a dir """ for dirname, dirnames, filenames in os.walk(dirname): for filename in filenames: fullname = os.path.join(dirname, filename) protect_file(fullname) if not m_recurse: break def protect(filenames): """ Protect file or dirs """ for filename in filenames: if os.path.isfile(filename): protect_file(filename) elif os.path.isdir(filename): protect_dir(filename) else: print 'Unknown file type:', filename def parse_cmdline(): """ Parse command line """ from optparse import OptionParser global m_recurse, m_delete, m_symlink parser = OptionParser(usage="usage: %prog [options]") parser.add_option('-R', dest='recurse', default=None, action='store_true', help='Protect all .py in the dir and subdirs') parser.add_option('--delete', default=None, action='store_true', help='Delete origin .py file after protect') parser.add_option('--symlink', default=None, action='store_true', help='Follow symlink file') opts, args = parser.parse_args() m_recurse = opts.recurse m_delete = opts.delete m_symlink = opts.symlink return args if __name__ == '__main__': filenames = parse_cmdline() if not filenames: print 'Usage: pyprotect [-R] [--delete] file|dir' sys.exit(0) protect(filenames)