diff --git a/setup.py b/setup.py index 2cf75af1..889456d9 100755 --- a/setup.py +++ b/setup.py @@ -24,6 +24,6 @@ setup( py_modules = py_modules, test_suite = 'nose.collector', url = web, - test_requires = ['nose>=1.0'], + tests_require = ['nose>=1.0'], version = VERSION, zip_safe = zip_safe) diff --git a/uncompyle6/bin/uncompile.py b/uncompyle6/bin/uncompile.py index 7aa68910..5315b2a8 100755 --- a/uncompyle6/bin/uncompile.py +++ b/uncompyle6/bin/uncompile.py @@ -5,7 +5,7 @@ # Copyright (c) 2000-2002 by hartmut Goebel # from __future__ import print_function -import sys, os, getopt, tempfile, time +import sys, os, getopt, time program, ext = os.path.splitext(os.path.basename(__file__)) @@ -35,7 +35,8 @@ Options: -p use number of processes -r recurse directories looking for .pyc and .pyo files --verify compare generated source with input byte-code - (requires -o) + --linemaps generated line number correspondencies between byte-code + and generated source output --help show this message Debugging Options: @@ -81,8 +82,8 @@ def main_bin(): try: opts, files = getopt.getopt(sys.argv[1:], 'hagtdrVo:c:p:', - 'help asm grammar recurse timestamp tree verify version ' - 'showgrammar'.split(' ')) + 'help asm grammar linemaps 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) @@ -97,6 +98,8 @@ def main_bin(): sys.exit(0) elif opt == '--verify': options['do_verify'] = True + elif opt == '--linemaps': + options['do_linemaps'] = True elif opt in ('--asm', '-a'): options['showasm'] = 'after' options['do_verify'] = False @@ -146,11 +149,7 @@ def main_bin(): usage() if outfile == '-': - if 'do_verify' in options and options['do_verify'] and len(files) == 1: - junk, outfile = tempfile.mkstemp(suffix=".pyc", - prefix=files[0][0:-4]+'-') - else: - outfile = None # use stdout + outfile = None # use stdout elif outfile and os.path.isdir(outfile): out_base = outfile; outfile = None elif outfile and len(files) > 1: diff --git a/uncompyle6/linenumbers.py b/uncompyle6/linenumbers.py new file mode 100644 index 00000000..933ca5af --- /dev/null +++ b/uncompyle6/linenumbers.py @@ -0,0 +1,14 @@ +from xdis.load import load_file, load_module +from xdis.bytecode import findlinestarts, offset2line + +def line_number_mapping(pyc_filename, src_filename): + (version, timestamp, magic_int, code_obj1, is_pypy, + source_size) = load_module(pyc_filename) + try: + code_obj2 = load_file(src_filename) + except SyntaxError as e: + return str(e) + + linestarts_orig = findlinestarts(code_obj1) + linestarts_uncompiled = list(findlinestarts(code_obj2)) + return [[line, offset2line(offset, linestarts_uncompiled)] for offset, line in linestarts_orig] diff --git a/uncompyle6/main.py b/uncompyle6/main.py index 1955fae8..b0132503 100644 --- a/uncompyle6/main.py +++ b/uncompyle6/main.py @@ -1,5 +1,5 @@ from __future__ import print_function -import datetime, os, sys +import datetime, os, subprocess, sys, tempfile from uncompyle6 import verify, IS_PYPY from xdis.code import iscode @@ -7,6 +7,7 @@ from uncompyle6.disas import check_object_path from uncompyle6.semantics import pysource from uncompyle6.parser import ParserError from uncompyle6.version import VERSION +from uncompyle6.linenumbers import line_number_mapping from xdis.load import load_module @@ -76,7 +77,8 @@ def uncompyle_file(filename, outstream=None, showasm=None, showast=False, # FIXME: combine into an options parameter def main(in_base, out_base, files, codes, outfile=None, showasm=None, showast=False, do_verify=False, - showgrammar=False, raise_on_error=False): + showgrammar=False, raise_on_error=False, + do_linemaps=False): """ in_base base directory for input files out_base base directory for output files (ignored when @@ -99,7 +101,6 @@ def main(in_base, out_base, files, codes, outfile=None, pass return open(outfile, 'w') - of = outfile tot_files = okay_files = failed_files = verify_failed_files = 0 # for code in codes: @@ -117,10 +118,21 @@ def main(in_base, out_base, files, codes, outfile=None, # print (infile, file=sys.stderr) - if of: # outfile was given as parameter + if outfile: # outfile was given as parameter outstream = _get_outstream(outfile) elif out_base is None: outstream = sys.stdout + if do_linemaps or do_verify: + prefix = os.path.basename(filename) + if prefix.endswith('.py'): + prefix = prefix[:-len('.py')] + junk, outfile = tempfile.mkstemp(suffix=".pyc", + prefix=prefix) + # Unbuffer output + sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0) + tee = subprocess.Popen(["tee", outfile], stdin=subprocess.PIPE) + os.dup2(tee.stdin.fileno(), sys.stdout.fileno()) + os.dup2(tee.stdin.fileno(), sys.stderr.fileno()) else: if filename.endswith('.pyc'): outfile = os.path.join(out_base, filename[0:-1]) @@ -152,7 +164,13 @@ def main(in_base, out_base, files, codes, outfile=None, # sys.stderr.write("\n# Can't uncompile %s\n" % infile) else: # uncompile successful if outfile: + if do_linemaps: + mapping = line_number_mapping(infile, outfile) + print("\n\n## Line number correspondences", infile) + for m in mapping: + print("## %s" % m, infile) outstream.close() + if do_verify: weak_verify = do_verify == 'weak' try: