#!/usr/bin/env python # Mode: -*- python -*- # # Copyright (c) 2015-2016 by Rocky Bernstein # Copyright (c) 2000-2002 by hartmut Goebel # from __future__ import print_function import sys, os, getopt, time program = os.path.basename(__file__) __doc__ = """ Usage: %s [OPTIONS]... [ FILE | DIR]... %s [--help | -h | --V | --version] Examples: %s foo.pyc bar.pyc # decompile foo.pyc, bar.pyc to stdout %s -o . foo.pyc bar.pyc # decompile to ./foo.pyc_dis and ./bar.pyc_dis %s -o /tmp /usr/lib/python1.5 # decompile whole library Options: -o output decompiled files to this path: if multiple input files are decompiled, the common prefix is stripped from these names and the remainder appended to uncompyle6 -o /tmp bla/fasel.pyc bla/foo.pyc -> /tmp/fasel.pyc_dis, /tmp/foo.pyc_dis uncompyle6 -o /tmp bla/fasel.pyc bar/foo.pyc -> /tmp/bla/fasel.pyc_dis, /tmp/bar/foo.pyc_dis uncompyle6 -o /tmp /usr/lib/python1.5 -> /tmp/smtplib.pyc_dis ... /tmp/lib-tk/FixTk.pyc_dis -c attempts a disassembly after compiling -d print timestamps -p use number of processes -r recurse directories looking for .pyc and .pyo files --verify compare generated source with input byte-code (requires -o) --help show this message Debugging Options: --asm -a include byte-code (disables --verify) --grammar -g show matching grammar --tree -t include syntax tree (disables --verify) Extensions of generated files: '.pyc_dis' '.pyo_dis' successfully decompiled (and verified if --verify) + '_unverified' successfully decompile but --verify failed + '_failed' decompile failed (contact author for enhancement) """ % ((program,) * 5) program = os.path.basename(__file__) from uncompyle6 import verify, check_python_version from uncompyle6.main import main, status_msg from uncompyle6.version import VERSION def usage(): print("""usage: %s [--verify] [--asm] [--tree] [--grammar] [-o ] FILE|DIR... %s [--help | -h | --version | -V] """ % (program, program)) sys.exit(1) def main_bin(): check_python_version(program) showasm = showast = do_verify = recurse_dirs = False numproc = 0 outfile = '-' out_base = None codes = [] timestamp = False timestampfmt = "# %Y.%m.%d %H:%M:%S %Z" try: opts, files = getopt.getopt(sys.argv[1:], 'hagtdrVo:c:p:', 'help asm grammar recurse timestamp tree verify version ' 'showgrammar'.split(' ')) except getopt.GetoptError as e: print('%s: %s' % (os.path.basename(sys.argv[0]), e), file=sys.stderr) sys.exit(-1) options = {} for opt, val in opts: if opt in ('-h', '--help'): print(__doc__) sys.exit(0) elif opt in ('-V', '--version'): print("%s %s" % (program, VERSION)) sys.exit(0) elif opt == '--verify': options['do_verify'] = True elif opt in ('--asm', '-a'): options['showasm'] = True options['do_verify'] = False elif opt in ('--tree', '-t'): options['showast'] = True options['do_verify'] = False elif opt in ('--grammar', '-g'): options['showgrammar'] = True elif opt == '-o': outfile = val elif opt in ('--timestamp', '-d'): timestamp = True elif opt == '-c': codes.append(val) elif opt == '-p': numproc = int(val) elif opt in ('--recurse', '-r'): recurse_dirs = True else: print(opt, file=sys.stderr) usage() # expand directory if specified if recurse_dirs: expanded_files = [] for f in files: if os.path.isdir(f): for root, _, dir_files in os.walk(f): for df in dir_files: if df.endswith('.pyc') or df.endswith('.pyo'): expanded_files.append(os.path.join(root, df)) files = expanded_files # argl, commonprefix works on strings, not on path parts, # thus we must handle the case with files in 'some/classes' # and 'some/cmds' src_base = os.path.commonprefix(files) if src_base[-1:] != os.sep: src_base = os.path.dirname(src_base) if src_base: sb_len = len( os.path.join(src_base, '') ) files = [f[sb_len:] for f in files] del sb_len if not files: print("No files given", file=sys.stderr) usage() if outfile == '-': outfile = None # use stdout elif outfile and os.path.isdir(outfile): out_base = outfile; outfile = None elif outfile and len(files) > 1: out_base = outfile; outfile = None if timestamp: print(time.strftime(timestampfmt)) if numproc <= 1: try: result = main(src_base, out_base, files, codes, outfile, **options) if len(files) > 1: mess = status_msg(do_verify, *result) print('# ' + mess) pass except (KeyboardInterrupt): pass except verify.VerifyCmpError: raise else: from multiprocessing import Process, Queue try: from Queue import Empty except ImportError: from Queue import Empty fqueue = Queue(len(files)+numproc) for f in files: fqueue.put(f) for i in range(numproc): fqueue.put(None) rqueue = Queue(numproc) def process_func(): try: (tot_files, okay_files, failed_files, verify_failed_files) = (0, 0, 0, 0) while 1: f = fqueue.get() if f is None: break (t, o, f, v) = \ main(src_base, out_base, [f], codes, outfile, **options) tot_files += t okay_files += o failed_files += f verify_failed_files += v except (Empty, KeyboardInterrupt): pass rqueue.put((tot_files, okay_files, failed_files, verify_failed_files)) rqueue.close() try: procs = [Process(target=process_func) for i in range(numproc)] for p in procs: p.start() for p in procs: p.join() try: (tot_files, okay_files, failed_files, verify_failed_files) = (0, 0, 0, 0) while True: (t, o, f, v) = rqueue.get(False) tot_files += t okay_files += o failed_files += f verify_failed_files += v except Empty: pass print('# decompiled %i files: %i okay, %i failed, %i verify failed' % (tot_files, okay_files, failed_files, verify_failed_files)) except (KeyboardInterrupt, OSError): pass if timestamp: print(time.strftime(timestampfmt)) return if __name__ == '__main__': main_bin()