diff --git a/pytest/test_deparse.py b/pytest/test_deparse.py index 0c883970..8b936f66 100644 --- a/pytest/test_deparse.py +++ b/pytest/test_deparse.py @@ -95,10 +95,10 @@ x = [] ------ 6 y = {} - -- + - Contained in... y = {} ------- + -- 9 y = {} - diff --git a/uncompyle6/bin/uncompile.py b/uncompyle6/bin/uncompile.py index 596e925a..001de8da 100755 --- a/uncompyle6/bin/uncompile.py +++ b/uncompyle6/bin/uncompile.py @@ -34,6 +34,7 @@ Options: -d print timestamps -p use number of processes -r recurse directories looking for .pyc and .pyo files + --fragments use fragments deparser --verify compare generated source with input byte-code --verify-run compile generated source, run it and check exit code --weak-verify compile generated source @@ -85,7 +86,7 @@ def main_bin(): try: opts, files = getopt.getopt(sys.argv[1:], 'hagtdrVo:c:p:', 'help asm grammar linemaps recurse timestamp tree ' - 'verify verify-run version weak-verify ' + 'fragments verify verify-run version weak-verify ' 'showgrammar'.split(' ')) except getopt.GetoptError as e: print('%s: %s' % (os.path.basename(sys.argv[0]), e), file=sys.stderr) @@ -103,6 +104,8 @@ def main_bin(): options['do_verify'] = 'strong' elif opt == '--weak-verify': options['do_verify'] = 'weak' + elif opt == '--fragments': + options['do_fragments'] = True elif opt == '--verify-run': options['do_verify'] = 'verify-run' elif opt == '--linemaps': diff --git a/uncompyle6/main.py b/uncompyle6/main.py index cce7b8f3..b09d66a1 100644 --- a/uncompyle6/main.py +++ b/uncompyle6/main.py @@ -7,9 +7,10 @@ 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 uncompyle6.linenumbers import line_number_mapping from uncompyle6.semantics.pysource import deparse_code +from uncompyle6.semantics.fragments import deparse_code as deparse_code_fragments from uncompyle6.semantics.linemap import deparse_code_with_map from xdis.load import load_module @@ -29,7 +30,7 @@ def decompile( bytecode_version, co, out=None, showasm=None, showast=False, timestamp=None, showgrammar=False, code_objects={}, source_size=None, is_pypy=False, magic_int=None, - mapstream=None): + mapstream=None, do_fragments=False): """ ingests and deparses a given code block 'co' @@ -75,9 +76,13 @@ def decompile( sorted(deparsed.source_linemap.keys())] mapstream.write("\n\n# %s\n" % linemap) else: - deparsed = deparse_code(bytecode_version, co, out, showasm, showast, - showgrammar, code_objects=code_objects, - is_pypy=is_pypy) + if do_fragments: + deparse_fn = deparse_code_fragments + else: + deparse_fn = deparse_code + deparsed = deparse_fn(bytecode_version, co, out, showasm, showast, + showgrammar, code_objects=code_objects, + is_pypy=is_pypy) pass return deparsed except pysource.SourceWalkerError as e: @@ -85,7 +90,7 @@ def decompile( raise pysource.SourceWalkerError(str(e)) def decompile_file(filename, outstream=None, showasm=None, showast=False, - showgrammar=False, mapstream=None): + showgrammar=False, mapstream=None, do_fragments=False): """ decompile Python byte-code file (.pyc). Return objects to all of the deparsed objects found in `filename`. @@ -109,7 +114,7 @@ def decompile_file(filename, outstream=None, showasm=None, showast=False, timestamp, showgrammar, code_objects=code_objects, source_size=source_size, is_pypy=is_pypy, magic_int=magic_int, - mapstream=mapstream)] + mapstream=mapstream, do_fragments=do_fragments)] co = None return deparsed @@ -118,7 +123,7 @@ def decompile_file(filename, outstream=None, showasm=None, showast=False, def main(in_base, out_base, files, codes, outfile=None, showasm=None, showast=False, do_verify=False, showgrammar=False, raise_on_error=False, - do_linemaps=False): + do_linemaps=False, do_fragments=False): """ in_base base directory for input files out_base base directory for output files (ignored when @@ -189,7 +194,24 @@ def main(in_base, out_base, files, codes, outfile=None, # Try to uncompile the input file try: - decompile_file(infile, outstream, showasm, showast, showgrammar, linemap_stream) + deparsed = decompile_file(infile, outstream, showasm, showast, showgrammar, + linemap_stream, do_fragments) + if do_fragments: + for d in deparsed: + last_mod = None + offsets = d.offsets + for e in sorted(offsets.keys()): + if e[0] != last_mod: + line = '=' * len(e[0]) + outstream.write("%s\n%s\n%s\n" % (line, e[0], line)) + last_mod = e[0] + info = offsets[e] + extractInfo = d.extract_node_info(info) + outstream.write("%s" % info.node.format().strip() + "\n") + outstream.write(extractInfo.selectedLine + "\n") + outstream.write(extractInfo.markerLine + "\n\n") + pass + pass tot_files += 1 except (ValueError, SyntaxError, ParserError, pysource.SourceWalkerError) as e: sys.stdout.write("\n") diff --git a/uncompyle6/semantics/fragments.py b/uncompyle6/semantics/fragments.py index a091d44a..66328c62 100644 --- a/uncompyle6/semantics/fragments.py +++ b/uncompyle6/semantics/fragments.py @@ -1287,6 +1287,7 @@ class FragmentsWalker(pysource.SourceWalker, object): sep = INDENT_PER_LEVEL[:-1] start = len(self.f.getvalue()) self.write('{') + self.set_pos_info(node[0], start, start+1) if self.version > 3.0: if node[0].kind.startswith('kvlist'): @@ -1355,9 +1356,6 @@ class FragmentsWalker(pysource.SourceWalker, object): sep = line_seperator self.write('}') finish = len(self.f.getvalue()) - for n in node: - n.parent = node - self.set_pos_info(n, start, finish) self.set_pos_info(node, start, finish) self.indent_less(INDENT_PER_LEVEL) self.prec = p @@ -1610,7 +1608,8 @@ class FragmentsWalker(pysource.SourceWalker, object): pass def deparse_code(version, co, out=StringIO(), showasm=False, showast=False, - showgrammar=False, is_pypy=False, walker=FragmentsWalker): + showgrammar=False, code_objects={}, compile_mode='exec', + is_pypy=False, walker=FragmentsWalker): """ Convert the code object co into a python source fragment. @@ -1638,7 +1637,8 @@ def deparse_code(version, co, out=StringIO(), showasm=False, showast=False, # store final output stream for case of error scanner = get_scanner(version, is_pypy=is_pypy) - tokens, customize = scanner.ingest(co) + tokens, customize = scanner.ingest(co, code_objects=code_objects, + show_asm=showasm) tokens, customize = scanner.ingest(co) maybe_show_asm(showasm, tokens) @@ -1651,7 +1651,8 @@ def deparse_code(version, co, out=StringIO(), showasm=False, showast=False, # Build AST from disassembly. # deparsed = pysource.FragmentsWalker(out, scanner, showast=showast) deparsed = walker(version, scanner, showast=showast, - debug_parser=debug_parser) + debug_parser=debug_parser, compile_mode=compile_mode, + is_pypy=is_pypy) deparsed.ast = deparsed.build_ast(tokens, customize)