Merge branch 'master' into python-3.3-to-3.5

This commit is contained in:
rocky 2024-11-19 15:29:40 -05:00
commit 8c15ce92f9
12 changed files with 105 additions and 80 deletions

View File

@ -282,6 +282,7 @@ to spend.
See Also See Also
-------- --------
* https://rocky.github.io/blackhat-asia-2024-additional/all-notes-print.html : How to Read and Write a High-Level Bytecode Decompiler: ``uncompyle6`` ``decompyle3`` -- BlackHat 2024 Asia (`video <https://www.youtube.com/watch?v=NA77SFncppE>`_. A big thanks to the Organizers and Reviewers for letting me speak. This kind of thing encourages me to work on projects like this.
* https://github.com/rocky/python-decompile3 : Much smaller and more modern code, focusing on 3.7 and 3.8. Changes in that will get migrated back here. * https://github.com/rocky/python-decompile3 : Much smaller and more modern code, focusing on 3.7 and 3.8. Changes in that will get migrated back here.
* https://code.google.com/archive/p/unpyc3/ : supports Python 3.2 only. The above projects use a different decompiling technique than what is used here. Currently unmaintained. * https://code.google.com/archive/p/unpyc3/ : supports Python 3.2 only. The above projects use a different decompiling technique than what is used here. Currently unmaintained.
* https://github.com/figment/unpyc3/ : fork of above, but supports Python 3.3 only. Includes some fixes like supporting function annotations. Currently unmaintained. * https://github.com/figment/unpyc3/ : fork of above, but supports Python 3.3 only. Includes some fixes like supporting function annotations. Currently unmaintained.
@ -306,8 +307,8 @@ See Also
.. _uncompyle2: https://github.com/wibiti/uncompyle2 .. _uncompyle2: https://github.com/wibiti/uncompyle2
.. _unpyc37: https://github.com/andrew-tavera/unpyc37 .. _unpyc37: https://github.com/andrew-tavera/unpyc37
.. _this: https://github.com/rocky/python-uncompyle6/wiki/Deparsing-technology-and-its-use-in-exact-location-reporting .. _this: https://github.com/rocky/python-uncompyle6/wiki/Deparsing-technology-and-its-use-in-exact-location-reporting
.. |buildstatus| image:: https://travis-ci.org/rocky/python-uncompyle6.svg .. |buildstatus| image:: https://circleci.com/gh/rocky/python-uncompyle6.svg?style=svg
:target: https://travis-ci.org/rocky/python-uncompyle6 :target: https://app.circleci.com/pipelines/github/rocky/python-uncompyle6
.. |packagestatus| image:: https://repology.org/badge/vertical-allrepos/python:uncompyle6.svg .. |packagestatus| image:: https://repology.org/badge/vertical-allrepos/python:uncompyle6.svg
:target: https://repology.org/project/python:uncompyle6/versions :target: https://repology.org/project/python:uncompyle6/versions
.. _PJOrion: http://www.koreanrandom.com/forum/topic/15280-pjorion-%D1%80%D0%B5%D0%B4%D0%B0%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D0%BA%D0%BE%D0%BC%D0%BF%D0%B8%D0%BB%D1%8F%D1%86%D0%B8%D1%8F-%D0%B4%D0%B5%D0%BA%D0%BE%D0%BC%D0%BF%D0%B8%D0%BB%D1%8F%D1%86%D0%B8%D1%8F-%D0%BE%D0%B1%D1%84 .. _PJOrion: http://www.koreanrandom.com/forum/topic/15280-pjorion-%D1%80%D0%B5%D0%B4%D0%B0%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D0%BA%D0%BE%D0%BC%D0%BF%D0%B8%D0%BB%D1%8F%D1%86%D0%B8%D1%8F-%D0%B4%D0%B5%D0%BA%D0%BE%D0%BC%D0%BF%D0%B8%D0%BB%D1%8F%D1%86%D0%B8%D1%8F-%D0%BE%D0%B1%D1%84

View File

@ -1,4 +1,4 @@
# Copyright (c) 2018-2019, 2021-2022 by Rocky Bernstein # Copyright (c) 2018-2019, 2021-2022 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
@ -34,7 +34,7 @@ def customize_for_version(self, is_pypy, version):
# PyPy changes # PyPy changes
####################### #######################
# fmt: off # fmt: off
TABLE_DIRECT.update({ self.TABLE_DIRECT.update({
"assert": ("%|assert %c\n", 0), "assert": ("%|assert %c\n", 0),
# This can happen as a result of an if transformation # This can happen as a result of an if transformation
@ -115,7 +115,7 @@ def customize_for_version(self, is_pypy, version):
######################## ########################
# Without PyPy # Without PyPy
####################### #######################
TABLE_DIRECT.update( self.TABLE_DIRECT.update(
{ {
# "assert" and "assert_expr" are added via transform rules. # "assert" and "assert_expr" are added via transform rules.
"assert": ("%|assert %c\n", 0), "assert": ("%|assert %c\n", 0),
@ -134,23 +134,23 @@ def customize_for_version(self, is_pypy, version):
) )
if version >= (3, 0): if version >= (3, 0):
if version >= (3, 2): if version >= (3, 2):
TABLE_DIRECT.update( self.TABLE_DIRECT.update(
{"del_deref_stmt": ("%|del %c\n", 0), "DELETE_DEREF": ("%{pattr}", 0)} {"del_deref_stmt": ("%|del %c\n", 0), "DELETE_DEREF": ("%{pattr}", 0)}
) )
from uncompyle6.semantics.customize3 import customize_for_version3 from uncompyle6.semantics.customize3 import customize_for_version3
customize_for_version3(self, version) customize_for_version3(self, version)
else: # < 3.0 else: # < 3.0
TABLE_DIRECT.update( self.TABLE_DIRECT.update(
{"except_cond3": ("%|except %c, %c:\n", (1, "expr"), (-2, "store"))} {"except_cond3": ("%|except %c, %c:\n", (1, "expr"), (-2, "store"))}
) )
if version <= (2, 6): if version <= (2, 6):
TABLE_DIRECT["testtrue_then"] = TABLE_DIRECT["testtrue"] self.TABLE_DIRECT["testtrue_then"] = self.TABLE_DIRECT["testtrue"]
if (2, 4) <= version <= (2, 6): if (2, 4) <= version <= (2, 6):
TABLE_DIRECT.update({"comp_for": (" for %c in %c", 3, 1)}) self.TABLE_DIRECT.update({"comp_for": (" for %c in %c", 3, 1)})
else: else:
TABLE_DIRECT.update({"comp_for": (" for %c in %c%c", 2, 0, 3)}) self.TABLE_DIRECT.update({"comp_for": (" for %c in %c%c", 2, 0, 3)})
if version >= (2, 5): if version >= (2, 5):
from uncompyle6.semantics.customize25 import customize_for_version25 from uncompyle6.semantics.customize25 import customize_for_version25
@ -198,7 +198,7 @@ def customize_for_version(self, is_pypy, version):
) )
], ],
) )
TABLE_DIRECT.update( self.TABLE_DIRECT.update(
{ {
"importmultiple": ("%|import %c%c\n", 2, 3), "importmultiple": ("%|import %c%c\n", 2, 3),
"import_cont": (", %c", 2), "import_cont": (", %c", 2),
@ -248,9 +248,9 @@ def customize_for_version(self, is_pypy, version):
self.n_call = n_call self.n_call = n_call
else: # 1.0 <= version <= 2.3: else: # 1.0 <= version <= 2.3:
TABLE_DIRECT.update({"if1_stmt": ("%|if 1\n%+%c%-", 5)}) self.TABLE_DIRECT.update({"if1_stmt": ("%|if 1\n%+%c%-", 5)})
if version <= (2, 1): if version <= (2, 1):
TABLE_DIRECT.update( self.TABLE_DIRECT.update(
{ {
"importmultiple": ("%c", 2), "importmultiple": ("%c", 2),
# FIXME: not quite right. We have indiividual imports # FIXME: not quite right. We have indiividual imports
@ -264,7 +264,7 @@ def customize_for_version(self, is_pypy, version):
# < 3.0 continues # < 3.0 continues
TABLE_R.update( self.TABLE_R.update(
{ {
"STORE_SLICE+0": ("%c[:]", 0), "STORE_SLICE+0": ("%c[:]", 0),
"STORE_SLICE+1": ("%c[%p:]", 0, (1, -1)), "STORE_SLICE+1": ("%c[%p:]", 0, (1, -1)),
@ -276,7 +276,7 @@ def customize_for_version(self, is_pypy, version):
"DELETE_SLICE+3": ("%|del %c[%c:%c]\n", 0, 1, 2), "DELETE_SLICE+3": ("%|del %c[%c:%c]\n", 0, 1, 2),
} }
) )
TABLE_DIRECT.update({"raise_stmt2": ("%|raise %c, %c\n", 0, 1)}) self.TABLE_DIRECT.update({"raise_stmt2": ("%|raise %c, %c\n", 0, 1)})
# exec as a built-in statement is only in Python 2.x # exec as a built-in statement is only in Python 2.x
def n_exec_stmt(node): def n_exec_stmt(node):

View File

@ -1,4 +1,4 @@
# Copyright (c) 2022 by Rocky Bernstein # Copyright (c) 2022, 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
@ -15,16 +15,13 @@
"""Isolate Python 1.4- version-specific semantic actions here. """Isolate Python 1.4- version-specific semantic actions here.
""" """
from uncompyle6.semantics.consts import TABLE_DIRECT
####################### #######################
# Python 1.4- Changes # # Python 1.4- Changes #
####################### #######################
def customize_for_version14(self, version): def customize_for_version14(self, version: tuple):
TABLE_DIRECT.update( self.TABLE_DIRECT.update(
{ {
"print_expr_stmt": ( "print_expr_stmt": (("%|print %c\n", 0)),
("%|print %c\n", 0)
),
} }
) )

View File

@ -25,7 +25,7 @@ def customize_for_version25(self, version):
######################## ########################
# Import style for 2.5+ # Import style for 2.5+
######################## ########################
TABLE_DIRECT.update( self.TABLE_DIRECT.update(
{ {
"importmultiple": ("%|import %c%c\n", 2, 3), "importmultiple": ("%|import %c%c\n", 2, 3),
"import_cont": (", %c", 2), "import_cont": (", %c", 2),

View File

@ -1,4 +1,4 @@
# Copyright (c) 2019 2021 by Rocky Bernstein # Copyright (c) 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
@ -17,8 +17,8 @@
from uncompyle6.semantics.consts import TABLE_DIRECT from uncompyle6.semantics.consts import TABLE_DIRECT
def customize_for_version26_27(self, version):
def customize_for_version26_27(self, version: tuple):
######################################## ########################################
# Python 2.6+ # Python 2.6+
# except <condition> as <var> # except <condition> as <var>
@ -29,16 +29,20 @@ def customize_for_version26_27(self, version):
# matches how we parse this in bytecode # matches how we parse this in bytecode
######################################## ########################################
if version > (2, 6): if version > (2, 6):
TABLE_DIRECT.update({ self.TABLE_DIRECT.update(
"except_cond2": ( "%|except %c as %c:\n", 1, 5 ), {
# When a generator is a single parameter of a function, "except_cond2": ("%|except %c as %c:\n", 1, 5),
# it doesn't need the surrounding parenethesis. # When a generator is a single parameter of a function,
"call_generator": ('%c%P', 0, (1, -1, ', ', 100)), # it doesn't need the surrounding parenethesis.
}) "call_generator": ("%c%P", 0, (1, -1, ", ", 100)),
}
)
else: else:
TABLE_DIRECT.update({ self.TABLE_DIRECT.update(
'testtrue_then': ( 'not %p', (0, 22) ), {
}) "testtrue_then": ("not %p", (0, 22)),
}
)
# FIXME: this should be a transformation # FIXME: this should be a transformation
def n_call(node): def n_call(node):
@ -47,22 +51,24 @@ def customize_for_version26_27(self, version):
for i in mapping[1:]: for i in mapping[1:]:
key = key[i] key = key[i]
pass pass
if key.kind == 'CALL_FUNCTION_1': if key.kind == "CALL_FUNCTION_1":
# A function with one argument. If this is a generator, # A function with one argument. If this is a generator,
# no parenthesis is needed. # no parenthesis is needed.
args_node = node[-2] args_node = node[-2]
if args_node == 'expr': if args_node == "expr":
n = args_node[0] n = args_node[0]
if n == 'generator_exp': if n == "generator_exp":
node.kind = 'call_generator' node.kind = "call_generator"
pass pass
pass pass
self.default(node) self.default(node)
self.n_call = n_call self.n_call = n_call
def n_import_from(node): def n_import_from(node):
if node[0].pattr > 0: if node[0].pattr > 0:
node[2].pattr = ("." * node[0].pattr) + node[2].pattr node[2].pattr = ("." * node[0].pattr) + node[2].pattr
self.default(node) self.default(node)
self.n_import_from = n_import_from self.n_import_from = n_import_from

View File

@ -29,8 +29,8 @@ from uncompyle6.semantics.make_function3 import make_function3_annotate
from uncompyle6.util import get_code_name from uncompyle6.util import get_code_name
def customize_for_version3(self, version): def customize_for_version3(self, version: tuple):
TABLE_DIRECT.update( self.TABLE_DIRECT.update(
{ {
"comp_for": (" for %c in %c", (2, "store"), (0, "expr")), "comp_for": (" for %c in %c", (2, "store"), (0, "expr")),
"if_exp_not": ( "if_exp_not": (
@ -183,7 +183,7 @@ def customize_for_version3(self, version):
# the iteration variable. These rules we can ignore # the iteration variable. These rules we can ignore
# since we pick up the iteration variable some other way and # since we pick up the iteration variable some other way and
# we definitely don't include in the source _[dd]. # we definitely don't include in the source _[dd].
TABLE_DIRECT.update( self.TABLE_DIRECT.update(
{ {
"ifstmt30": ( "ifstmt30": (
"%|if %c:\n%+%c%-", "%|if %c:\n%+%c%-",
@ -335,7 +335,7 @@ def customize_for_version3(self, version):
self.n_mkfunc_annotate = n_mkfunc_annotate self.n_mkfunc_annotate = n_mkfunc_annotate
TABLE_DIRECT.update( self.TABLE_DIRECT.update(
{ {
"tryelsestmtl3": ( "tryelsestmtl3": (
"%|try:\n%+%c%-%c%|else:\n%+%c%-", "%|try:\n%+%c%-%c%|else:\n%+%c%-",
@ -350,7 +350,7 @@ def customize_for_version3(self, version):
####################### #######################
# Python 3.4+ Changes # # Python 3.4+ Changes #
####################### #######################
TABLE_DIRECT.update( self.TABLE_DIRECT.update(
{ {
"LOAD_CLASSDEREF": ("%{pattr}",), "LOAD_CLASSDEREF": ("%{pattr}",),
"yield_from": ("yield from %c", (0, "expr")), "yield_from": ("yield from %c", (0, "expr")),

View File

@ -1,4 +1,4 @@
# Copyright (c) 2019-2020, 2022 by Rocky Bernstein # Copyright (c) 2019-2020, 2022, 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
@ -16,21 +16,17 @@
""" """
from xdis import co_flags_is_async, iscode from xdis import co_flags_is_async, iscode
from uncompyle6.semantics.consts import (
INDENT_PER_LEVEL,
PRECEDENCE,
TABLE_DIRECT,
)
from uncompyle6.semantics.consts import INDENT_PER_LEVEL, PRECEDENCE, TABLE_DIRECT
from uncompyle6.semantics.helper import flatten_list, gen_function_parens_adjust from uncompyle6.semantics.helper import flatten_list, gen_function_parens_adjust
####################### #######################
# Python 3.5+ Changes # # Python 3.5+ Changes #
####################### #######################
def customize_for_version35(self, version): def customize_for_version35(self, version: tuple):
# fmt: off # fmt: off
TABLE_DIRECT.update( self.TABLE_DIRECT.update(
{ {
# nested await expressions like: # nested await expressions like:
# return await (await bar()) # return await (await bar())
@ -197,7 +193,11 @@ def customize_for_version35(self, version):
self.template_engine(template, args_node) self.template_engine(template, args_node)
else: else:
if len(node) - nargs > 3: if len(node) - nargs > 3:
template = ("*%c, %P)", nargs + 1, (nargs + kwargs + 1, -1, ", ", 100)) template = (
"*%c, %P)",
nargs + 1,
(nargs + kwargs + 1, -1, ", ", 100),
)
else: else:
template = ("*%c)", nargs + 1) template = ("*%c)", nargs + 1)
self.template_engine(template, node) self.template_engine(template, node)

View File

@ -38,7 +38,7 @@ def escape_format(s):
####################### #######################
def customize_for_version36(self, version): def customize_for_version36(self, version: tuple):
# fmt: off # fmt: off
PRECEDENCE["call_kw"] = 0 PRECEDENCE["call_kw"] = 0
PRECEDENCE["call_kw36"] = 1 PRECEDENCE["call_kw36"] = 1
@ -50,7 +50,7 @@ def customize_for_version36(self, version):
PRECEDENCE["dict_pack"] = 0 # **{ ... } PRECEDENCE["dict_pack"] = 0 # **{ ... }
PRECEDENCE["formatted_value1"] = 100 PRECEDENCE["formatted_value1"] = 100
TABLE_DIRECT.update( self.TABLE_DIRECT.update(
{ {
"ann_assign_init_value": ( "ann_assign_init_value": (
"%|%c = %p\n", "%|%c = %p\n",
@ -96,7 +96,7 @@ def customize_for_version36(self, version):
} }
) )
TABLE_R.update( self.TABLE_R.update(
{ {
"CALL_FUNCTION_EX": ("%c(*%P)", 0, (1, 2, ", ", 100)), "CALL_FUNCTION_EX": ("%c(*%P)", 0, (1, 2, ", ", 100)),
# Not quite right # Not quite right

View File

@ -1,4 +1,4 @@
# Copyright (c) 2019-2023 by Rocky Bernstein # Copyright (c) 2019-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
@ -25,7 +25,7 @@ FSTRING_CONVERSION_MAP = {1: "!s", 2: "!r", 3: "!a", "X": ":X"}
####################### #######################
def customize_for_version37(self, version): def customize_for_version37(self, version: tuple):
######################## ########################
# Python 3.7+ changes # Python 3.7+ changes
####################### #######################
@ -47,7 +47,7 @@ def customize_for_version37(self, version):
PRECEDENCE["dict_unpack"] = 0 # **{...} PRECEDENCE["dict_unpack"] = 0 # **{...}
# fmt: on # fmt: on
TABLE_DIRECT.update( self.TABLE_DIRECT.update(
{ {
"and_not": ("%c and not %c", (0, "expr"), (2, "expr")), "and_not": ("%c and not %c", (0, "expr"), (2, "expr")),
"ann_assign": ( "ann_assign": (

View File

@ -1,4 +1,4 @@
# Copyright (c) 2019-2020, 2022 by Rocky Bernstein # Copyright (c) 2019-2020, 2022, 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
@ -24,13 +24,12 @@ from uncompyle6.semantics.customize37 import FSTRING_CONVERSION_MAP
from uncompyle6.semantics.helper import escape_string, strip_quotes from uncompyle6.semantics.helper import escape_string, strip_quotes
def customize_for_version38(self, version): def customize_for_version38(self, version: tuple):
# FIXME: pytest doesn't add proper keys in testing. Reinstate after we have fixed pytest. # FIXME: pytest doesn't add proper keys in testing. Reinstate after we have fixed pytest.
# for lhs in 'for forelsestmt forelselaststmt ' # for lhs in 'for forelsestmt forelselaststmt '
# 'forelselaststmtc tryfinally38'.split(): # 'forelselaststmtc tryfinally38'.split():
# del TABLE_DIRECT[lhs] # del TABLE_DIRECT[lhs]
self.TABLE_DIRECT.update(
TABLE_DIRECT.update(
{ {
"async_for_stmt38": ( "async_for_stmt38": (
"%|async for %c in %c:\n%+%c%-%-\n\n", "%|async for %c in %c:\n%+%c%-%-\n\n",

View File

@ -84,6 +84,7 @@ from uncompyle6.semantics.consts import (
PASS, PASS,
PRECEDENCE, PRECEDENCE,
TABLE_DIRECT, TABLE_DIRECT,
TABLE_R,
escape, escape,
) )
from uncompyle6.semantics.pysource import ( from uncompyle6.semantics.pysource import (
@ -188,8 +189,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
self.is_pypy = is_pypy self.is_pypy = is_pypy
# FIXME: is there a better way? # FIXME: is there a better way?
global MAP_DIRECT_FRAGMENT self.MAP_DIRECT_FRAGMENT = (dict(TABLE_DIRECT, **TABLE_DIRECT_FRAGMENT),)
MAP_DIRECT_FRAGMENT = (dict(TABLE_DIRECT, **TABLE_DIRECT_FRAGMENT),)
return return
f = property( f = property(
@ -654,6 +654,17 @@ class FragmentsWalker(pysource.SourceWalker, object):
code = Code(cn.attr, self.scanner, self.currentclass) code = Code(cn.attr, self.scanner, self.currentclass)
ast = self.build_ast(code._tokens, code._customize, code) ast = self.build_ast(code._tokens, code._customize, code)
self.MAP_DIRECT = (self.TABLE_DIRECT,)
self.MAP_R = (self.TABLE_R, -1)
self.MAP = {
"stmt": self.MAP_R,
"call": self.MAP_R,
"delete": self.MAP_R,
"store": self.MAP_R,
}
self.customize(code._customize) self.customize(code._customize)
# Remove single reductions as in ("stmts", "sstmt"): # Remove single reductions as in ("stmts", "sstmt"):
@ -2006,8 +2017,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
self.set_pos_info(last_node, startnode_start, self.last_finish) self.set_pos_info(last_node, startnode_start, self.last_finish)
return return
@classmethod def _get_mapping(self, node):
def _get_mapping(cls, node):
if ( if (
hasattr(node, "data") hasattr(node, "data")
and len(node) > 0 and len(node) > 0
@ -2015,7 +2025,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
and not hasattr(node[-1], "parent") and not hasattr(node[-1], "parent")
): ):
node[-1].parent = node node[-1].parent = node
return MAP.get(node, MAP_DIRECT_FRAGMENT) return self.MAP.get(node, self.MAP_DIRECT_FRAGMENT)
pass pass

View File

@ -145,8 +145,6 @@ from uncompyle6.semantics.consts import (
ASSIGN_TUPLE_PARAM, ASSIGN_TUPLE_PARAM,
INDENT_PER_LEVEL, INDENT_PER_LEVEL,
LINE_LENGTH, LINE_LENGTH,
MAP,
MAP_DIRECT,
NAME_MODULE, NAME_MODULE,
NO_PARENTHESIS_EVER, NO_PARENTHESIS_EVER,
NONE, NONE,
@ -155,6 +153,7 @@ from uncompyle6.semantics.consts import (
RETURN_LOCALS, RETURN_LOCALS,
RETURN_NONE, RETURN_NONE,
TAB, TAB,
TABLE_DIRECT,
TABLE_R, TABLE_R,
escape, escape,
) )
@ -315,7 +314,21 @@ class SourceWalker(GenericASTTraversal, NonterminalActions, ComprehensionMixin):
# An example is: # An example is:
# __module__ = __name__ # __module__ = __name__
self.hide_internal = True self.hide_internal = True
self.TABLE_DIRECT = TABLE_DIRECT.copy()
self.TABLE_R = TABLE_R.copy()
self.MAP_DIRECT = (self.TABLE_DIRECT,)
self.MAP_R = (self.TABLE_R, -1)
self.MAP = {
"stmt": self.MAP_R,
"call": self.MAP_R,
"delete": self.MAP_R,
"store": self.MAP_R,
}
customize_for_version(self, is_pypy, version) customize_for_version(self, is_pypy, version)
return return
def maybe_show_tree(self, tree, phase): def maybe_show_tree(self, tree, phase):
@ -906,17 +919,17 @@ class SourceWalker(GenericASTTraversal, NonterminalActions, ComprehensionMixin):
of arguments -- we add a new entry for each in TABLE_R. of arguments -- we add a new entry for each in TABLE_R.
""" """
for k, v in list(customize.items()): for k, v in list(customize.items()):
if k in TABLE_R: if k in self.TABLE_R:
continue continue
op = k[: k.rfind("_")] op = k[: k.rfind("_")]
if k.startswith("CALL_METHOD"): if k.startswith("CALL_METHOD"):
# This happens in PyPy and Python 3.7+ # This happens in PyPy and Python 3.7+
TABLE_R[k] = ("%c(%P)", (0, "expr"), (1, -1, ", ", 100)) self.TABLE_R[k] = ("%c(%P)", (0, "expr"), (1, -1, ", ", 100))
elif self.version >= (3, 6) and k.startswith("CALL_FUNCTION_KW"): elif self.version >= (3, 6) and k.startswith("CALL_FUNCTION_KW"):
TABLE_R[k] = ("%c(%P)", (0, "expr"), (1, -1, ", ", 100)) self.TABLE_R[k] = ("%c(%P)", (0, "expr"), (1, -1, ", ", 100))
elif op == "CALL_FUNCTION": elif op == "CALL_FUNCTION":
TABLE_R[k] = ( self.TABLE_R[k] = (
"%c(%P)", "%c(%P)",
(0, "expr"), (0, "expr"),
(1, -1, ", ", PRECEDENCE["yield"] - 1), (1, -1, ", ", PRECEDENCE["yield"] - 1),
@ -975,13 +988,13 @@ class SourceWalker(GenericASTTraversal, NonterminalActions, ComprehensionMixin):
else: else:
assert False, "Unhandled CALL_FUNCTION %s" % op assert False, "Unhandled CALL_FUNCTION %s" % op
TABLE_R[k] = entry self.TABLE_R[k] = entry
pass pass
# handled by n_dict: # handled by n_dict:
# if op == 'BUILD_SLICE': TABLE_R[k] = ('%C' , (0,-1,':')) # if op == 'BUILD_SLICE': self.TABLE_R[k] = ('%C' , (0,-1,':'))
# handled by n_list: # handled by n_list:
# if op == 'BUILD_LIST': TABLE_R[k] = ('[%C]' , (0,-1,', ')) # if op == 'BUILD_LIST': self.TABLE_R[k] = ('[%C]' , (0,-1,', '))
# elif op == 'BUILD_TUPLE': TABLE_R[k] = ('(%C%,)', (0,-1,', ')) # elif op == 'BUILD_TUPLE': self.TABLE_R[k] = ('(%C%,)', (0,-1,', '))
pass pass
return return
@ -1276,9 +1289,8 @@ class SourceWalker(GenericASTTraversal, NonterminalActions, ComprehensionMixin):
del ast # Save memory del ast # Save memory
return transform_tree return transform_tree
@classmethod def _get_mapping(self, node):
def _get_mapping(cls, node): return self.MAP.get(node, self.MAP_DIRECT)
return MAP.get(node, MAP_DIRECT)
def code_deparse( def code_deparse(