diff --git a/decompyle3/semantics/consts.py b/decompyle3/semantics/consts.py index 301fb664..06b5d26f 100644 --- a/decompyle3/semantics/consts.py +++ b/decompyle3/semantics/consts.py @@ -592,9 +592,12 @@ "store": MAP_R, } -ASSIGN_TUPLE_PARAM = lambda param_name: SyntaxTree( # noqa - "expr", [Token("LOAD_FAST", pattr=param_name)] -) + +def assign_tuple_parameter_tree(param_name) -> SyntaxTree: + return SyntaxTree("expr", [Token("LOAD_FAST", pattr=param_name)]) + + +ASSIGN_TUPLE_PARAM = assign_tuple_parameter_tree escape = re.compile( r""" diff --git a/decompyle3/semantics/customize37.py b/decompyle3/semantics/customize37.py index 655382c1..86effce5 100644 --- a/decompyle3/semantics/customize37.py +++ b/decompyle3/semantics/customize37.py @@ -24,6 +24,7 @@ from decompyle3.scanners.tok import Token from decompyle3.semantics.consts import ( INDENT_PER_LEVEL, + NO_PARENTHESIS_EVER, PRECEDENCE, TABLE_DIRECT, TABLE_R, @@ -41,6 +42,7 @@ def escape_format(s): FSTRING_CONVERSION_MAP = {1: "!s", 2: "!r", 3: "!a", "X": ":X"} + ####################### def customize_for_version37(self, version): ######################## @@ -162,7 +164,7 @@ def customize_for_version37(self, version): "await_expr": ("await %p", (0, PRECEDENCE["await_expr"] - 1)), "await_stmt": ("%|%c\n", 0), "c_async_with_stmt": ("%|async with %c:\n%+%c%-", (0, "expr"), 3), - "call_ex": ("%c(%p)", (0, "expr"), (1, 100)), + "call_ex": ("%c(%p)", (0, "expr"), (1, NO_PARENTHESIS_EVER)), "compare_chained_middlea_37": ( "%p %p", (0, PRECEDENCE["compare"] - 1), @@ -419,7 +421,7 @@ def customize_for_version37(self, version): TABLE_R.update( { - "CALL_FUNCTION_EX": ("%c(*%P)", 0, (1, 2, ", ", 100)), + "CALL_FUNCTION_EX": ("%c(*%P)", 0, (1, 2, ", ", NO_PARENTHESIS_EVER)), # Not quite right "CALL_FUNCTION_EX_KW": ("%c(**%C)", 0, (2, 3, ",")), } @@ -465,7 +467,7 @@ def call36_dict(node): We will source-code use line breaks to guide us when to break. """ p = self.prec - self.prec = 100 + self.prec = NO_PARENTHESIS_EVER self.indent_more(INDENT_PER_LEVEL) sep = INDENT_PER_LEVEL[:-1] @@ -770,7 +772,7 @@ def n_call(node): if args_node in ("pos_arg", "expr"): args_node = args_node[0] if args_node == "build_list_unpack": - template = ("*%P)", (0, len(args_node) - 1, ", *", 100)) + template = ("*%P)", (0, len(args_node) - 1, ", *", NO_PARENTHESIS_EVER)) self.template_engine(template, args_node) else: if len(node) - nargs > 3: @@ -789,10 +791,19 @@ def n_call(node): and opname == "CALL_FUNCTION_1" or not re.match(r"\d", opname[-1]) ): - template = "(%c)(%p)" if node[0][0] == "lambda_body" else "%c(%p)" - self.template_engine( - (template, (0, "expr"), (1, PRECEDENCE["yield"] - 1)), node - ) + if node[0][0] == "lambda_body": + self.template_engine( + ( + "(\n%+%|%c%-\n)(%p)", + (0, ("expr", "arg")), + (1, PRECEDENCE["yield"] - 1), + ), + node, + ) + else: + self.template_engine( + ("%c(%p)", (0, ("expr", "arg")), (1, NO_PARENTHESIS_EVER)), node + ) self.prec = p self.prune() else: @@ -1190,7 +1201,7 @@ def n_call_ex_kw(node): if value == "": fmt = "%c(%p)" else: - fmt = "%c" + ("(%s, " % value).replace('%', '%%') + "%p)" + fmt = "%c" + ("(%s, " % value).replace("%", "%%") + "%p)" self.template_engine( (fmt, (0, "expr"), (2, "build_map_unpack_with_call", 100)), node @@ -1209,7 +1220,7 @@ def n_call_ex_kw2(node): if value == "": fmt = "%c(%p)" else: - fmt = "%c" + ("(%s, " % value).replace('%', '%%') + "%p)" + fmt = "%c" + ("(%s, " % value).replace("%", "%%") + "%p)" self.template_engine( (fmt, (0, "expr"), (2, "build_map_unpack_with_call", 100)), node diff --git a/decompyle3/semantics/customize38.py b/decompyle3/semantics/customize38.py index 2022832f..0893a643 100644 --- a/decompyle3/semantics/customize38.py +++ b/decompyle3/semantics/customize38.py @@ -19,7 +19,7 @@ # Python 3.8+ changes ####################### -from decompyle3.semantics.consts import PRECEDENCE, TABLE_DIRECT +from decompyle3.semantics.consts import NO_PARENTHESIS_EVER, PRECEDENCE, TABLE_DIRECT from decompyle3.semantics.customize37 import FSTRING_CONVERSION_MAP from decompyle3.semantics.helper import escape_string, strip_quotes @@ -380,7 +380,7 @@ def n_set_afor(node): def n_formatted_value_debug(node): p = self.prec - self.prec = 100 + self.prec = NO_PARENTHESIS_EVER formatted_value = node[1] value_equal = node[0].attr diff --git a/test/bytecode_3.7/code-fragment/generator/03_generator_compendium.pyc b/test/bytecode_3.7/code-fragment/generator/03_generator_compendium.pyc new file mode 100644 index 00000000..79fb29ec Binary files /dev/null and b/test/bytecode_3.7/code-fragment/generator/03_generator_compendium.pyc differ diff --git a/test/simple_source/generator_start/03_generator_compendium.py b/test/simple_source/generator_start/03_generator_compendium.py new file mode 100644 index 00000000..0b1f8e9f --- /dev/null +++ b/test/simple_source/generator_start/03_generator_compendium.py @@ -0,0 +1,84 @@ +# These are adapted from generators found when byte compiling the +# entire set of 3.8 installed packages on my disk. +# Many examples come from packages like sympy or numpy + +# fmt: off +# generator bugs +(i + for i + in + range(10) + ) + +tuple(j + for j + in t + if i != j) + +# # 3.8.12 weakrefset.py +# (e +# for s +# in ("a", "b") +# for e +# in s) + +# # Python 3.8.2 line 683 of git/refs/symbolic.py +# # Bug was in handling "or not" +# (r +# for r +# in (0, 1, 2, 3) +# if r +# or +# not +# 5 % 2) + +# # line 1512 of sympy/core/mul.py +# # Bug was handling "and" +# (x +# and +# (x + 1 % 2) +# is True for +# x +# in (0, 1, 2) +# ) + +# # line 330 of 3.8.12 setuptools/config.py +# # Problem was in handling or True +# ( +# path +# for path in __file__ +# if (path or True)) + +# # Line 602 of 3.8.2 nltk/corpus/reader/framenet.py +# # Bug was handling looping jump of chained comparison inside comp_if. +# (1 +# for x, +# y +# in __file__ +# if 2 <= y +# < 3 +# or 5 +# <= x +# < 6) + +# # The problem was handing IfExp as part of a tuple, i.e. +# # (None if .. and .. else) +# ( +# (k, +# (None if +# k +# and v +# else v) +# ) +# for k, v in +# {"foo": "bar"} +# ) + +# # line 1086 of 3.8.12 test/test_asyncgen.py +# # Bug was hooking genexpr_func_async to return_expr_lambda in grammar +# # and writing a semantic action for this when the code is decompiled +# # independant of the calling context. +# (i * 2 +# async +# for i +# in range(10))