You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-03 00:45:53 +08:00
Fix up linemap option
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
# Copyright (C) 2018-2023 Rocky Bernstein <rocky@gnu.org>
|
# Copyright (C) 2018-2024 Rocky Bernstein <rocky@gnu.org>
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# 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:
|
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"
|
failed_file = outfile + "_failed"
|
||||||
if os.path.exists(failed_file):
|
if os.path.exists(failed_file):
|
||||||
os.remove(failed_file)
|
os.remove(failed_file)
|
||||||
try:
|
try:
|
||||||
os.makedirs(dir)
|
os.makedirs(dir_name)
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
return open(outfile, mode="w", encoding="utf-8")
|
return open(outfile, mode="w", encoding="utf-8")
|
||||||
@@ -50,7 +53,7 @@ def decompile(
|
|||||||
co,
|
co,
|
||||||
bytecode_version: Tuple[int] = PYTHON_VERSION_TRIPLE,
|
bytecode_version: Tuple[int] = PYTHON_VERSION_TRIPLE,
|
||||||
out=sys.stdout,
|
out=sys.stdout,
|
||||||
showasm: Optional[str]=None,
|
showasm: Optional[str] = None,
|
||||||
showast={},
|
showast={},
|
||||||
timestamp=None,
|
timestamp=None,
|
||||||
showgrammar=False,
|
showgrammar=False,
|
||||||
@@ -118,13 +121,12 @@ def decompile(
|
|||||||
if isinstance(mapstream, str):
|
if isinstance(mapstream, str):
|
||||||
mapstream = _get_outstream(mapstream)
|
mapstream = _get_outstream(mapstream)
|
||||||
|
|
||||||
|
debug_opts = {"asm": showasm, "tree": showast, "grammar": showgrammar}
|
||||||
|
|
||||||
deparsed = deparse_code_with_map(
|
deparsed = deparse_code_with_map(
|
||||||
bytecode_version,
|
co=co,
|
||||||
co,
|
out=out,
|
||||||
out,
|
version=bytecode_version,
|
||||||
showasm,
|
|
||||||
showast,
|
|
||||||
showgrammar,
|
|
||||||
code_objects=code_objects,
|
code_objects=code_objects,
|
||||||
is_pypy=is_pypy,
|
is_pypy=is_pypy,
|
||||||
)
|
)
|
||||||
|
@@ -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
|
# 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
|
# 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
|
from xdis.version_info import IS_PYPY, PYTHON_VERSION_TRIPLE
|
||||||
|
|
||||||
import uncompyle6.parser as python_parser
|
import uncompyle6.parser as python_parser
|
||||||
from uncompyle6 import parser
|
|
||||||
from uncompyle6.parsers.treenode import SyntaxTree
|
from uncompyle6.parsers.treenode import SyntaxTree
|
||||||
from uncompyle6.scanner import Code, Token, get_scanner
|
from uncompyle6.scanner import Code, Token, get_scanner
|
||||||
from uncompyle6.semantics import pysource
|
from uncompyle6.semantics import pysource
|
||||||
@@ -89,7 +88,7 @@ from uncompyle6.semantics.consts import (
|
|||||||
TABLE_DIRECT,
|
TABLE_DIRECT,
|
||||||
escape,
|
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
|
from uncompyle6.show import maybe_show_asm, maybe_show_tree
|
||||||
|
|
||||||
NodeInfo = namedtuple("NodeInfo", "node start finish")
|
NodeInfo = namedtuple("NodeInfo", "node start finish")
|
||||||
@@ -1118,7 +1117,13 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
|||||||
n_classdefdeco2 = n_classdef
|
n_classdefdeco2 = n_classdef
|
||||||
|
|
||||||
def gen_source(
|
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"""
|
"""convert parse tree to Python source code"""
|
||||||
|
|
||||||
@@ -1204,7 +1209,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
|||||||
self.p.insts = self.scanner.insts
|
self.p.insts = self.scanner.insts
|
||||||
self.p.offset2inst_index = self.scanner.offset2inst_index
|
self.p.offset2inst_index = self.scanner.offset2inst_index
|
||||||
self.p.opc = self.scanner.opc
|
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
|
self.p.insts = p_insts
|
||||||
except (python_parser.ParserError, AssertionError) as e:
|
except (python_parser.ParserError, AssertionError) as e:
|
||||||
raise ParserError(e, tokens, {})
|
raise ParserError(e, tokens, {})
|
||||||
@@ -2065,11 +2070,11 @@ def code_deparse(
|
|||||||
|
|
||||||
# Build Syntax Tree from tokenized and massaged disassembly.
|
# Build Syntax Tree from tokenized and massaged disassembly.
|
||||||
# deparsed = pysource.FragmentsWalker(out, scanner, showast=showast)
|
# deparsed = pysource.FragmentsWalker(out, scanner, showast=showast)
|
||||||
show_ast = debug_opts.get("ast", None)
|
show_tree = debug_opts.get("tree", False)
|
||||||
deparsed = walker(
|
deparsed = walker(
|
||||||
version,
|
version,
|
||||||
scanner,
|
scanner,
|
||||||
showast=show_ast,
|
showast=show_tree,
|
||||||
debug_parser=debug_parser,
|
debug_parser=debug_parser,
|
||||||
compile_mode=compile_mode,
|
compile_mode=compile_mode,
|
||||||
is_pypy=is_pypy,
|
is_pypy=is_pypy,
|
||||||
|
@@ -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
|
# 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
|
# 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
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from uncompyle6.semantics.fragments import FragmentsWalker, code_deparse as fragments_code_deparse
|
||||||
from uncompyle6.semantics.pysource import SourceWalker, 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
|
# FIXME: does this handle nested code, and lambda properly
|
||||||
class LineMapWalker(SourceWalker):
|
class LineMapWalker(SourceWalker):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(LineMapWalker, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.source_linemap = {}
|
self.source_linemap = {}
|
||||||
self.current_line_number = 1
|
self.current_line_number = 1
|
||||||
|
|
||||||
def write(self, *data):
|
def write(self, *data):
|
||||||
"""Augment write routine to keep track of current line"""
|
"""Augment write routine to keep track of current line"""
|
||||||
for l in data:
|
for line in data:
|
||||||
## print("XXX write: '%s'" % l)
|
## print("XXX write: '%s'" % l)
|
||||||
for i in str(l):
|
for i in str(line):
|
||||||
if i == '\n':
|
if i == "\n":
|
||||||
self.current_line_number += 1
|
self.current_line_number += 1
|
||||||
pass
|
pass
|
||||||
pass
|
pass
|
||||||
pass
|
pass
|
||||||
return super(LineMapWalker, self).write(*data)
|
return super().write(*data)
|
||||||
|
|
||||||
# Note n_expr needs treatment too
|
# Note n_expr needs treatment too
|
||||||
|
|
||||||
def default(self, node):
|
def default(self, node):
|
||||||
"""Augment write default routine to record line number changes"""
|
"""Augment write default routine to record line number changes"""
|
||||||
if hasattr(node, 'linestart'):
|
if hasattr(node, "linestart"):
|
||||||
if node.linestart:
|
if node.linestart:
|
||||||
self.source_linemap[self.current_line_number] = 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):
|
def n_LOAD_CONST(self, node):
|
||||||
if hasattr(node, 'linestart'):
|
if hasattr(node, "linestart"):
|
||||||
if node.linestart:
|
if node.linestart:
|
||||||
self.source_linemap[self.current_line_number] = 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):
|
def __init__(self, *args, **kwargs):
|
||||||
super(LineMapFragmentWalker, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.source_linemap = {}
|
|
||||||
self.current_line_number = 0
|
|
||||||
|
|
||||||
def deparse_code_with_map(*args, **kwargs):
|
def deparse_code_with_map(*args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Like deparse_code but saves line number correspondences.
|
Like deparse_code but saves line number correspondences.
|
||||||
Deprecated. Use code_deparse_with_map
|
Deprecated. Use code_deparse_with_map
|
||||||
"""
|
"""
|
||||||
kwargs['walker'] = LineMapWalker
|
kwargs["walker"] = LineMapWalker
|
||||||
return code_deparse(*args, **kwargs)
|
return code_deparse(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def code_deparse_with_map(*args, **kwargs):
|
def code_deparse_with_map(*args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Like code_deparse but saves line number correspondences.
|
Like code_deparse but saves line number correspondences.
|
||||||
"""
|
"""
|
||||||
kwargs['walker'] = LineMapWalker
|
kwargs["walker"] = LineMapWalker
|
||||||
return code_deparse(*args, **kwargs)
|
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):
|
def code_deparse_with_fragments_and_map(*args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Like code_deparse_with_map but saves fragments.
|
Like code_deparse_with_map but saves fragments.
|
||||||
"""
|
"""
|
||||||
kwargs['walker'] = LineMapFragmentWalker
|
kwargs["walker"] = LineMapFragmentWalker
|
||||||
return fragments.code_deparse(*args, **kwargs)
|
return fragments_code_deparse(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
def deparse_test(co):
|
def deparse_test(co):
|
||||||
"This is a docstring"
|
"This is a docstring"
|
||||||
deparsed = code_deparse_with_map(co)
|
deparsed = code_deparse_with_map(co)
|
||||||
a = 1; b = 2
|
a = 1
|
||||||
|
b = 2
|
||||||
print("\n")
|
print("\n")
|
||||||
linemap = [(line_no, deparsed.source_linemap[line_no])
|
linemap = [
|
||||||
for line_no in
|
(line_no, deparsed.source_linemap[line_no])
|
||||||
sorted(deparsed.source_linemap.keys())]
|
for line_no in sorted(deparsed.source_linemap.keys())
|
||||||
|
]
|
||||||
print(linemap)
|
print(linemap)
|
||||||
deparsed = code_deparse_with_fragments_and_map(co)
|
deparsed = code_deparse_with_fragments_and_map(co)
|
||||||
print("\n")
|
print("\n")
|
||||||
linemap2 = [(line_no, deparsed.source_linemap[line_no])
|
linemap2 = [
|
||||||
for line_no in
|
(line_no, deparsed.source_linemap[line_no])
|
||||||
sorted(deparsed.source_linemap.keys())]
|
for line_no in sorted(deparsed.source_linemap.keys())
|
||||||
|
]
|
||||||
print(linemap2)
|
print(linemap2)
|
||||||
# assert linemap == linemap2
|
# assert linemap == linemap2
|
||||||
return
|
return
|
||||||
|
|
||||||
deparse_test(deparse_test.__code__)
|
deparse_test(deparse_test.__code__)
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2015-2023 by Rocky Bernstein
|
# Copyright (c) 2015-2024 by Rocky Bernstein
|
||||||
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
|
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
|
||||||
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
||||||
# Copyright (c) 1999 John Aycock
|
# Copyright (c) 1999 John Aycock
|
||||||
@@ -196,6 +196,10 @@ class SourceWalkerError(Exception):
|
|||||||
|
|
||||||
|
|
||||||
class SourceWalker(GenericASTTraversal, NonterminalActions, ComprehensionMixin):
|
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")
|
stacked_params = ("f", "indent", "is_lambda", "_globals")
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -245,24 +249,24 @@ class SourceWalker(GenericASTTraversal, NonterminalActions, ComprehensionMixin):
|
|||||||
is_pypy=is_pypy,
|
is_pypy=is_pypy,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Initialize p_lambda on demand
|
self.ast_errors = []
|
||||||
self.p_lambda = None
|
self.currentclass = None
|
||||||
|
self.classes = []
|
||||||
self.treeTransform = TreeTransform(version=self.version, show_ast=showast)
|
|
||||||
self.debug_parser = dict(debug_parser)
|
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.params = params
|
||||||
self.param_stack = []
|
self.param_stack = []
|
||||||
self.ERROR = None
|
self.ERROR = None
|
||||||
self.prec = 100
|
self.prec = 100
|
||||||
self.return_none = False
|
self.return_none = False
|
||||||
self.mod_globs = set()
|
self.mod_globs = set()
|
||||||
self.currentclass = None
|
self.showast = showast
|
||||||
self.classes = []
|
|
||||||
self.pending_newlines = 0
|
self.pending_newlines = 0
|
||||||
self.linestarts = linestarts
|
self.linestarts = linestarts
|
||||||
self.line_number = 1
|
self.treeTransform = TreeTransform(version=self.version, show_ast=showast)
|
||||||
self.ast_errors = []
|
|
||||||
# FIXME: have p.insts update in a better way
|
# FIXME: have p.insts update in a better way
|
||||||
# modularity is broken here
|
# modularity is broken here
|
||||||
self.insts = scanner.insts
|
self.insts = scanner.insts
|
||||||
@@ -1257,7 +1261,6 @@ def code_deparse(
|
|||||||
|
|
||||||
assert iscode(co)
|
assert iscode(co)
|
||||||
|
|
||||||
|
|
||||||
if version is None:
|
if version is None:
|
||||||
version = PYTHON_VERSION_TRIPLE
|
version = PYTHON_VERSION_TRIPLE
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user