From d249c522a702f622cfd6678fda00ce3dc9fb615e Mon Sep 17 00:00:00 2001 From: rocky Date: Sat, 3 Feb 2024 12:37:48 -0500 Subject: [PATCH] Fix up linemap option --- uncompyle6/main.py | 22 +++++----- uncompyle6/semantics/fragments.py | 19 +++++---- uncompyle6/semantics/linemap.py | 69 ++++++++++++++++--------------- uncompyle6/semantics/pysource.py | 25 ++++++----- 4 files changed, 73 insertions(+), 62 deletions(-) diff --git a/uncompyle6/main.py b/uncompyle6/main.py index f7da7430..4285736c 100644 --- a/uncompyle6/main.py +++ b/uncompyle6/main.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2023 Rocky Bernstein +# Copyright (C) 2018-2024 Rocky Bernstein # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -35,12 +35,15 @@ from uncompyle6.version import __version__ def _get_outstream(outfile: str) -> Any: - dir = os.path.dirname(outfile) + """ + Return an opened output file descriptor for ``outfile``. + """ + dir_name = os.path.dirname(outfile) failed_file = outfile + "_failed" if os.path.exists(failed_file): os.remove(failed_file) try: - os.makedirs(dir) + os.makedirs(dir_name) except OSError: pass return open(outfile, mode="w", encoding="utf-8") @@ -50,7 +53,7 @@ def decompile( co, bytecode_version: Tuple[int] = PYTHON_VERSION_TRIPLE, out=sys.stdout, - showasm: Optional[str]=None, + showasm: Optional[str] = None, showast={}, timestamp=None, showgrammar=False, @@ -118,13 +121,12 @@ def decompile( if isinstance(mapstream, str): mapstream = _get_outstream(mapstream) + debug_opts = {"asm": showasm, "tree": showast, "grammar": showgrammar} + deparsed = deparse_code_with_map( - bytecode_version, - co, - out, - showasm, - showast, - showgrammar, + co=co, + out=out, + version=bytecode_version, code_objects=code_objects, is_pypy=is_pypy, ) diff --git a/uncompyle6/semantics/fragments.py b/uncompyle6/semantics/fragments.py index a2783911..40dfbcc0 100644 --- a/uncompyle6/semantics/fragments.py +++ b/uncompyle6/semantics/fragments.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2019, 2021-2023 by Rocky Bernstein +# Copyright (c) 2015-2019, 2021-2024 by Rocky Bernstein # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -75,7 +75,6 @@ from xdis import iscode from xdis.version_info import IS_PYPY, PYTHON_VERSION_TRIPLE import uncompyle6.parser as python_parser -from uncompyle6 import parser from uncompyle6.parsers.treenode import SyntaxTree from uncompyle6.scanner import Code, Token, get_scanner from uncompyle6.semantics import pysource @@ -89,7 +88,7 @@ from uncompyle6.semantics.consts import ( TABLE_DIRECT, escape, ) -from uncompyle6.semantics.pysource import ParserError, StringIO +from uncompyle6.semantics.pysource import DEFAULT_DEBUG_OPTS, ParserError, StringIO from uncompyle6.show import maybe_show_asm, maybe_show_tree NodeInfo = namedtuple("NodeInfo", "node start finish") @@ -1118,7 +1117,13 @@ class FragmentsWalker(pysource.SourceWalker, object): n_classdefdeco2 = n_classdef def gen_source( - self, ast, name, customize, is_lambda=False, returnNone=False, debug_opts=None + self, + ast, + name, + customize, + is_lambda=False, + returnNone=False, + debug_opts=DEFAULT_DEBUG_OPTS, ): """convert parse tree to Python source code""" @@ -1204,7 +1209,7 @@ class FragmentsWalker(pysource.SourceWalker, object): self.p.insts = self.scanner.insts self.p.offset2inst_index = self.scanner.offset2inst_index self.p.opc = self.scanner.opc - ast = parser.parse(self.p, tokens, customize, code) + ast = python_parser.parse(self.p, tokens, customize, code) self.p.insts = p_insts except (python_parser.ParserError, AssertionError) as e: raise ParserError(e, tokens, {}) @@ -2065,11 +2070,11 @@ def code_deparse( # Build Syntax Tree from tokenized and massaged disassembly. # deparsed = pysource.FragmentsWalker(out, scanner, showast=showast) - show_ast = debug_opts.get("ast", None) + show_tree = debug_opts.get("tree", False) deparsed = walker( version, scanner, - showast=show_ast, + showast=show_tree, debug_parser=debug_parser, compile_mode=compile_mode, is_pypy=is_pypy, diff --git a/uncompyle6/semantics/linemap.py b/uncompyle6/semantics/linemap.py index 23eafdae..3a447afb 100644 --- a/uncompyle6/semantics/linemap.py +++ b/uncompyle6/semantics/linemap.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018 by Rocky Bernstein +# Copyright (c) 2018, 2024 by Rocky Bernstein # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -12,96 +12,97 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . + +from uncompyle6.semantics.fragments import FragmentsWalker, code_deparse as fragments_code_deparse from uncompyle6.semantics.pysource import SourceWalker, code_deparse -import uncompyle6.semantics.fragments as fragments + # FIXME: does this handle nested code, and lambda properly class LineMapWalker(SourceWalker): def __init__(self, *args, **kwargs): - super(LineMapWalker, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.source_linemap = {} self.current_line_number = 1 def write(self, *data): """Augment write routine to keep track of current line""" - for l in data: + for line in data: ## print("XXX write: '%s'" % l) - for i in str(l): - if i == '\n': + for i in str(line): + if i == "\n": self.current_line_number += 1 pass pass pass - return super(LineMapWalker, self).write(*data) + return super().write(*data) # Note n_expr needs treatment too def default(self, node): """Augment write default routine to record line number changes""" - if hasattr(node, 'linestart'): + if hasattr(node, "linestart"): if node.linestart: self.source_linemap[self.current_line_number] = node.linestart - return super(LineMapWalker, self).default(node) + return super().default(node) def n_LOAD_CONST(self, node): - if hasattr(node, 'linestart'): + if hasattr(node, "linestart"): if node.linestart: self.source_linemap[self.current_line_number] = node.linestart - return super(LineMapWalker, self).n_LOAD_CONST(node) + return super().n_LOAD_CONST(node) -class LineMapFragmentWalker(fragments.FragmentsWalker, LineMapWalker): +class LineMapFragmentWalker(LineMapWalker, FragmentsWalker): def __init__(self, *args, **kwargs): - super(LineMapFragmentWalker, self).__init__(*args, **kwargs) - self.source_linemap = {} - self.current_line_number = 0 + super().__init__(*args, **kwargs) + def deparse_code_with_map(*args, **kwargs): """ Like deparse_code but saves line number correspondences. Deprecated. Use code_deparse_with_map """ - kwargs['walker'] = LineMapWalker + kwargs["walker"] = LineMapWalker return code_deparse(*args, **kwargs) + def code_deparse_with_map(*args, **kwargs): """ Like code_deparse but saves line number correspondences. """ - kwargs['walker'] = LineMapWalker + kwargs["walker"] = LineMapWalker return code_deparse(*args, **kwargs) -def deparse_code_with_fragments_and_map(*args, **kwargs): - """ - Like deparse_code_with_map but saves fragments. - Deprecated. Use code_deparse_with_fragments_and_map - """ - kwargs['walker'] = LineMapFragmentWalker - return fragments.deparse_code(*args, **kwargs) def code_deparse_with_fragments_and_map(*args, **kwargs): """ Like code_deparse_with_map but saves fragments. """ - kwargs['walker'] = LineMapFragmentWalker - return fragments.code_deparse(*args, **kwargs) + kwargs["walker"] = LineMapFragmentWalker + return fragments_code_deparse(*args, **kwargs) + + +if __name__ == "__main__": -if __name__ == '__main__': def deparse_test(co): "This is a docstring" deparsed = code_deparse_with_map(co) - a = 1; b = 2 + a = 1 + b = 2 print("\n") - linemap = [(line_no, deparsed.source_linemap[line_no]) - for line_no in - sorted(deparsed.source_linemap.keys())] + linemap = [ + (line_no, deparsed.source_linemap[line_no]) + for line_no in sorted(deparsed.source_linemap.keys()) + ] print(linemap) deparsed = code_deparse_with_fragments_and_map(co) print("\n") - linemap2 = [(line_no, deparsed.source_linemap[line_no]) - for line_no in - sorted(deparsed.source_linemap.keys())] + linemap2 = [ + (line_no, deparsed.source_linemap[line_no]) + for line_no in sorted(deparsed.source_linemap.keys()) + ] print(linemap2) # assert linemap == linemap2 return + deparse_test(deparse_test.__code__) diff --git a/uncompyle6/semantics/pysource.py b/uncompyle6/semantics/pysource.py index b52f90f9..a43075c9 100644 --- a/uncompyle6/semantics/pysource.py +++ b/uncompyle6/semantics/pysource.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2023 by Rocky Bernstein +# Copyright (c) 2015-2024 by Rocky Bernstein # Copyright (c) 2005 by Dan Pascu # Copyright (c) 2000-2002 by hartmut Goebel # Copyright (c) 1999 John Aycock @@ -196,6 +196,10 @@ class SourceWalkerError(Exception): class SourceWalker(GenericASTTraversal, NonterminalActions, ComprehensionMixin): + """ + Class to traverses a Parse Tree of the bytecode instruction built from parsing to produce some sort of source text. + The Parse tree may be turned an Abstract Syntax tree as an intermediate step. + """ stacked_params = ("f", "indent", "is_lambda", "_globals") def __init__( @@ -245,24 +249,24 @@ class SourceWalker(GenericASTTraversal, NonterminalActions, ComprehensionMixin): is_pypy=is_pypy, ) - # Initialize p_lambda on demand - self.p_lambda = None - - self.treeTransform = TreeTransform(version=self.version, show_ast=showast) + self.ast_errors = [] + self.currentclass = None + self.classes = [] self.debug_parser = dict(debug_parser) - self.showast = showast + # Initialize p_lambda on demand + self.line_number = 1 + self.linemap = {} + self.p_lambda = None self.params = params self.param_stack = [] self.ERROR = None self.prec = 100 self.return_none = False self.mod_globs = set() - self.currentclass = None - self.classes = [] + self.showast = showast self.pending_newlines = 0 self.linestarts = linestarts - self.line_number = 1 - self.ast_errors = [] + self.treeTransform = TreeTransform(version=self.version, show_ast=showast) # FIXME: have p.insts update in a better way # modularity is broken here self.insts = scanner.insts @@ -1257,7 +1261,6 @@ def code_deparse( assert iscode(co) - if version is None: version = PYTHON_VERSION_TRIPLE