diff --git a/uncompyle6/semantics/customize.py b/uncompyle6/semantics/customize.py index 93855726..843c0fc7 100644 --- a/uncompyle6/semantics/customize.py +++ b/uncompyle6/semantics/customize.py @@ -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 # it under the terms of the GNU General Public License as published by @@ -17,24 +17,25 @@ """ from uncompyle6.parsers.treenode import SyntaxTree +from uncompyle6.scanners.tok import Token from uncompyle6.semantics.consts import ( INDENT_PER_LEVEL, NO_PARENTHESIS_EVER, PRECEDENCE, - TABLE_R, TABLE_DIRECT, + TABLE_R, ) from uncompyle6.semantics.helper import flatten_list -from uncompyle6.scanners.tok import Token def customize_for_version(self, is_pypy, version): + self.TABLE_DIRECT = TABLE_DIRECT.copy() if is_pypy: ######################## # PyPy changes ####################### # fmt: off - TABLE_DIRECT.update({ + self.TABLE_DIRECT.update({ "assert": ("%|assert %c\n", 0), # This can happen as a result of an if transformation @@ -114,7 +115,7 @@ def customize_for_version(self, is_pypy, version): ######################## # Without PyPy ####################### - TABLE_DIRECT.update( + self.TABLE_DIRECT.update( { # "assert" and "assert_expr" are added via transform rules. "assert": ("%|assert %c\n", 0), @@ -133,23 +134,23 @@ def customize_for_version(self, is_pypy, version): ) if version >= (3, 0): if version >= (3, 2): - TABLE_DIRECT.update( + self.TABLE_DIRECT.update( {"del_deref_stmt": ("%|del %c\n", 0), "DELETE_DEREF": ("%{pattr}", 0)} ) from uncompyle6.semantics.customize3 import customize_for_version3 customize_for_version3(self, version) else: # < 3.0 - TABLE_DIRECT.update( + self.TABLE_DIRECT.update( {"except_cond3": ("%|except %c, %c:\n", (1, "expr"), (-2, "store"))} ) 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): - TABLE_DIRECT.update({"comp_for": (" for %c in %c", 3, 1)}) + self.TABLE_DIRECT.update({"comp_for": (" for %c in %c", 3, 1)}) 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): from uncompyle6.semantics.customize25 import customize_for_version25 @@ -197,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), "import_cont": (", %c", 2), @@ -247,9 +248,9 @@ def customize_for_version(self, is_pypy, version): self.n_call = n_call 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): - TABLE_DIRECT.update( + self.TABLE_DIRECT.update( { "importmultiple": ("%c", 2), # FIXME: not quite right. We have indiividual imports @@ -263,7 +264,8 @@ def customize_for_version(self, is_pypy, version): # < 3.0 continues - TABLE_R.update( + self.TABLE_R = TABLE_R.copy() + self.TABLE_R.update( { "STORE_SLICE+0": ("%c[:]", 0), "STORE_SLICE+1": ("%c[%p:]", 0, (1, -1)), @@ -275,7 +277,7 @@ def customize_for_version(self, is_pypy, version): "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 def n_exec_stmt(node): diff --git a/uncompyle6/semantics/customize14.py b/uncompyle6/semantics/customize14.py index 3ba53f52..566a6ec1 100644 --- a/uncompyle6/semantics/customize14.py +++ b/uncompyle6/semantics/customize14.py @@ -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 # 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. """ -from uncompyle6.semantics.consts import TABLE_DIRECT ####################### # Python 1.4- Changes # ####################### -def customize_for_version14(self, version): - TABLE_DIRECT.update( +def customize_for_version14(self, version: tuple): + self.TABLE_DIRECT.update( { - "print_expr_stmt": ( - ("%|print %c\n", 0) - ), + "print_expr_stmt": (("%|print %c\n", 0)), } ) diff --git a/uncompyle6/semantics/customize26_27.py b/uncompyle6/semantics/customize26_27.py index 19196614..4f835239 100644 --- a/uncompyle6/semantics/customize26_27.py +++ b/uncompyle6/semantics/customize26_27.py @@ -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 # it under the terms of the GNU General Public License as published by @@ -17,8 +17,9 @@ from uncompyle6.semantics.consts import TABLE_DIRECT -def customize_for_version26_27(self, version): +def customize_for_version26_27(self, version: tuple): + self.TABLE_DIRECT = TABLE_DIRECT.copy() ######################################## # Python 2.6+ # except as @@ -29,16 +30,20 @@ def customize_for_version26_27(self, version): # matches how we parse this in bytecode ######################################## if version > (2, 6): - TABLE_DIRECT.update({ - "except_cond2": ( "%|except %c as %c:\n", 1, 5 ), - # When a generator is a single parameter of a function, - # it doesn't need the surrounding parenethesis. - "call_generator": ('%c%P', 0, (1, -1, ', ', 100)), - }) + self.TABLE_DIRECT.update( + { + "except_cond2": ("%|except %c as %c:\n", 1, 5), + # When a generator is a single parameter of a function, + # it doesn't need the surrounding parenethesis. + "call_generator": ("%c%P", 0, (1, -1, ", ", 100)), + } + ) else: - TABLE_DIRECT.update({ - 'testtrue_then': ( 'not %p', (0, 22) ), - }) + self.TABLE_DIRECT.update( + { + "testtrue_then": ("not %p", (0, 22)), + } + ) # FIXME: this should be a transformation def n_call(node): @@ -47,22 +52,24 @@ def customize_for_version26_27(self, version): for i in mapping[1:]: key = key[i] pass - if key.kind == 'CALL_FUNCTION_1': + if key.kind == "CALL_FUNCTION_1": # A function with one argument. If this is a generator, # no parenthesis is needed. args_node = node[-2] - if args_node == 'expr': + if args_node == "expr": n = args_node[0] - if n == 'generator_exp': - node.kind = 'call_generator' + if n == "generator_exp": + node.kind = "call_generator" pass pass self.default(node) + self.n_call = n_call def n_import_from(node): if node[0].pattr > 0: node[2].pattr = ("." * node[0].pattr) + node[2].pattr self.default(node) + self.n_import_from = n_import_from diff --git a/uncompyle6/semantics/customize3.py b/uncompyle6/semantics/customize3.py index 80309c37..194b8d49 100644 --- a/uncompyle6/semantics/customize3.py +++ b/uncompyle6/semantics/customize3.py @@ -29,8 +29,9 @@ from uncompyle6.semantics.make_function3 import make_function3_annotate from uncompyle6.util import get_code_name -def customize_for_version3(self, version): - TABLE_DIRECT.update( +def customize_for_version3(self, version: tuple): + self.TABLE_DIRECT = TABLE_DIRECT.copy() + self.TABLE_DIRECT.update( { "comp_for": (" for %c in %c", (2, "store"), (0, "expr")), "if_exp_not": ( @@ -183,7 +184,7 @@ def customize_for_version3(self, version): # the iteration variable. These rules we can ignore # since we pick up the iteration variable some other way and # we definitely don't include in the source _[dd]. - TABLE_DIRECT.update( + self.TABLE_DIRECT.update( { "ifstmt30": ( "%|if %c:\n%+%c%-", @@ -335,7 +336,7 @@ def customize_for_version3(self, version): self.n_mkfunc_annotate = n_mkfunc_annotate - TABLE_DIRECT.update( + self.TABLE_DIRECT.update( { "tryelsestmtl3": ( "%|try:\n%+%c%-%c%|else:\n%+%c%-", @@ -350,7 +351,7 @@ def customize_for_version3(self, version): ####################### # Python 3.4+ Changes # ####################### - TABLE_DIRECT.update( + self.TABLE_DIRECT.update( { "LOAD_CLASSDEREF": ("%{pattr}",), "yield_from": ("yield from %c", (0, "expr")), diff --git a/uncompyle6/semantics/customize35.py b/uncompyle6/semantics/customize35.py index 946c3e68..13bd5f10 100644 --- a/uncompyle6/semantics/customize35.py +++ b/uncompyle6/semantics/customize35.py @@ -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 # it under the terms of the GNU General Public License as published by @@ -16,21 +16,18 @@ """ 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 ####################### # Python 3.5+ Changes # ####################### -def customize_for_version35(self, version): +def customize_for_version35(self, version: tuple): # fmt: off - TABLE_DIRECT.update( + self.TABLE_DIRECT = TABLE_DIRECT.copy() + self.TABLE_DIRECT.update( { # nested await expressions like: # return await (await bar()) @@ -197,7 +194,11 @@ def customize_for_version35(self, version): self.template_engine(template, args_node) else: 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: template = ("*%c)", nargs + 1) self.template_engine(template, node) diff --git a/uncompyle6/semantics/customize36.py b/uncompyle6/semantics/customize36.py index 3bc2f574..99c4905d 100644 --- a/uncompyle6/semantics/customize36.py +++ b/uncompyle6/semantics/customize36.py @@ -38,7 +38,7 @@ def escape_format(s): ####################### -def customize_for_version36(self, version): +def customize_for_version36(self, version: tuple): # fmt: off PRECEDENCE["call_kw"] = 0 PRECEDENCE["call_kw36"] = 1 @@ -50,7 +50,7 @@ def customize_for_version36(self, version): PRECEDENCE["dict_pack"] = 0 # **{ ... } PRECEDENCE["formatted_value1"] = 100 - TABLE_DIRECT.update( + self.TABLE_DIRECT.update( { "ann_assign_init_value": ( "%|%c = %p\n", @@ -96,7 +96,8 @@ def customize_for_version36(self, version): } ) - TABLE_R.update( + self.TABLE_R = TABLE_R.copy() + self.TABLE_R.update( { "CALL_FUNCTION_EX": ("%c(*%P)", 0, (1, 2, ", ", 100)), # Not quite right diff --git a/uncompyle6/semantics/customize37.py b/uncompyle6/semantics/customize37.py index 6102d818..91b4de8d 100644 --- a/uncompyle6/semantics/customize37.py +++ b/uncompyle6/semantics/customize37.py @@ -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 # it under the terms of the GNU General Public License as published by @@ -25,11 +25,13 @@ 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 ####################### + self.TABLE_DIRECT = TABLE_DIRECT.copy() + # fmt: off PRECEDENCE["attribute37"] = 2 PRECEDENCE["call_ex"] = 1 @@ -47,7 +49,7 @@ def customize_for_version37(self, version): PRECEDENCE["dict_unpack"] = 0 # **{...} # fmt: on - TABLE_DIRECT.update( + self.TABLE_DIRECT.update( { "and_not": ("%c and not %c", (0, "expr"), (2, "expr")), "ann_assign": ( diff --git a/uncompyle6/semantics/customize38.py b/uncompyle6/semantics/customize38.py index df08c36f..c3e4cf78 100644 --- a/uncompyle6/semantics/customize38.py +++ b/uncompyle6/semantics/customize38.py @@ -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 # it under the terms of the GNU General Public License as published by @@ -24,13 +24,14 @@ from uncompyle6.semantics.customize37 import FSTRING_CONVERSION_MAP 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. # for lhs in 'for forelsestmt forelselaststmt ' # 'forelselaststmtc tryfinally38'.split(): # del TABLE_DIRECT[lhs] - TABLE_DIRECT.update( + self.TABLE_DIRECT = TABLE_DIRECT.copy() + self.TABLE_DIRECT.update( { "async_for_stmt38": ( "%|async for %c in %c:\n%+%c%-%-\n\n", diff --git a/uncompyle6/semantics/fragments.py b/uncompyle6/semantics/fragments.py index 6dfa4ac7..330b97bf 100644 --- a/uncompyle6/semantics/fragments.py +++ b/uncompyle6/semantics/fragments.py @@ -85,6 +85,7 @@ from uncompyle6.semantics.consts import ( PASS, PRECEDENCE, TABLE_DIRECT, + TABLE_R, escape, ) from uncompyle6.semantics.pysource import ( @@ -189,8 +190,7 @@ class FragmentsWalker(pysource.SourceWalker, object): self.is_pypy = is_pypy # FIXME: is there a better way? - global MAP_DIRECT_FRAGMENT - MAP_DIRECT_FRAGMENT = (dict(TABLE_DIRECT, **TABLE_DIRECT_FRAGMENT),) + self.MAP_DIRECT_FRAGMENT = (dict(TABLE_DIRECT, **TABLE_DIRECT_FRAGMENT),) return f = property( @@ -655,6 +655,19 @@ class FragmentsWalker(pysource.SourceWalker, object): code = Code(cn.attr, self.scanner, self.currentclass) ast = self.build_ast(code._tokens, code._customize, code) + + 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, + } + self.customize(code._customize) # Remove single reductions as in ("stmts", "sstmt"): @@ -2003,8 +2016,7 @@ class FragmentsWalker(pysource.SourceWalker, object): self.set_pos_info(last_node, startnode_start, self.last_finish) return - @classmethod - def _get_mapping(cls, node): + def _get_mapping(self, node): if ( hasattr(node, "data") and len(node) > 0 @@ -2012,7 +2024,7 @@ class FragmentsWalker(pysource.SourceWalker, object): and not hasattr(node[-1], "parent") ): node[-1].parent = node - return MAP.get(node, MAP_DIRECT_FRAGMENT) + return self.MAP.get(node, self.MAP_DIRECT_FRAGMENT) pass diff --git a/uncompyle6/semantics/pysource.py b/uncompyle6/semantics/pysource.py index c351337e..00138023 100644 --- a/uncompyle6/semantics/pysource.py +++ b/uncompyle6/semantics/pysource.py @@ -146,8 +146,6 @@ from uncompyle6.semantics.consts import ( ASSIGN_TUPLE_PARAM, INDENT_PER_LEVEL, LINE_LENGTH, - MAP, - MAP_DIRECT, NAME_MODULE, NO_PARENTHESIS_EVER, NONE, @@ -156,6 +154,7 @@ from uncompyle6.semantics.consts import ( RETURN_LOCALS, RETURN_NONE, TAB, + TABLE_DIRECT, TABLE_R, escape, ) @@ -316,7 +315,21 @@ class SourceWalker(GenericASTTraversal, NonterminalActions, ComprehensionMixin): # An example is: # __module__ = __name__ 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) + return def maybe_show_tree(self, tree, phase): @@ -902,17 +915,17 @@ class SourceWalker(GenericASTTraversal, NonterminalActions, ComprehensionMixin): of arguments -- we add a new entry for each in TABLE_R. """ for k, v in list(customize.items()): - if k in TABLE_R: + if k in self.TABLE_R: continue op = k[: k.rfind("_")] if k.startswith("CALL_METHOD"): # 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"): - 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": - TABLE_R[k] = ( + self.TABLE_R[k] = ( "%c(%P)", (0, "expr"), (1, -1, ", ", PRECEDENCE["yield"] - 1), @@ -971,13 +984,13 @@ class SourceWalker(GenericASTTraversal, NonterminalActions, ComprehensionMixin): else: assert False, "Unhandled CALL_FUNCTION %s" % op - TABLE_R[k] = entry + self.TABLE_R[k] = entry pass # 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: - # if op == 'BUILD_LIST': TABLE_R[k] = ('[%C]' , (0,-1,', ')) - # elif op == 'BUILD_TUPLE': TABLE_R[k] = ('(%C%,)', (0,-1,', ')) + # if op == 'BUILD_LIST': self.TABLE_R[k] = ('[%C]' , (0,-1,', ')) + # elif op == 'BUILD_TUPLE': self.TABLE_R[k] = ('(%C%,)', (0,-1,', ')) pass return @@ -1272,9 +1285,8 @@ class SourceWalker(GenericASTTraversal, NonterminalActions, ComprehensionMixin): del ast # Save memory return transform_tree - @classmethod - def _get_mapping(cls, node): - return MAP.get(node, MAP_DIRECT) + def _get_mapping(self, node): + return self.MAP.get(node, self.MAP_DIRECT) def code_deparse(