diff --git a/test/Makefile b/test/Makefile index 6857e0cf..c32bf96c 100644 --- a/test/Makefile +++ b/test/Makefile @@ -72,6 +72,9 @@ check-3.7: check-bytecode $(PYTHON) test_pythonlib.py --bytecode-3.7-run --verify-run $(PYTHON) test_pythonlib.py --bytecode-3.7 --syntax-verify $(COMPILE) +check-pypy37: check-bytecode + $(PYTHON) test_pythonlib.py --bytecode-pypy37 --verify-run + #: Run working tests from Python 3.8 check-3.8: check-bytecode $(PYTHON) test_pythonlib.py --bytecode-3.8-run --verify-run @@ -353,10 +356,14 @@ check-bytecode-pypy3.6: 7.1 #: PyPy 5.0.x with Python 3.6.9 check-bytecode-pypy3.6: 7.2 7.3 -7.3 7.2: + +7.2: $(PYTHON) test_pythonlib.py --bytecode-pypy3.6-run --verify-run $(PYTHON) test_pythonlib.py --bytecode-pypy3.6 --verify +7.3: + $(PYTHON) test_pythonlib.py --bytecode-pypy3.7 --verify-run + clean: clean-py-dis clean-dis clean-unverified diff --git a/test/bytecode_pypy3.7/01_callback.pyc b/test/bytecode_pypy3.7/01_callback.pyc new file mode 100644 index 00000000..a5d724ad Binary files /dev/null and b/test/bytecode_pypy3.7/01_callback.pyc differ diff --git a/test/simple_source/bug_pypy37/01_callback.py b/test/simple_source/bug_pypy37/01_callback.py new file mode 100644 index 00000000..7325b4f5 --- /dev/null +++ b/test/simple_source/bug_pypy37/01_callback.py @@ -0,0 +1,10 @@ +"""This program is self-checking!""" + + +def lcase(s): + return s.lower() + + +l = ["xyz", "ABC"] +l.sort(key=lcase) +assert l == ["ABC", "xyz"] diff --git a/test/test_pyenvlib.py b/test/test_pyenvlib.py index 6db1cf11..fbf061e0 100755 --- a/test/test_pyenvlib.py +++ b/test/test_pyenvlib.py @@ -48,6 +48,7 @@ TEST_VERSIONS = ( "pypy3.6-7.1.0", "pypy3.6-7.1.1", "pypy3.6-7.2.0", + "pypy3.8-7.3.7", "native", ) + tuple(python_versions) diff --git a/test/test_pythonlib.py b/test/test_pythonlib.py index a45e392b..d9e23fdc 100755 --- a/test/test_pythonlib.py +++ b/test/test_pythonlib.py @@ -116,6 +116,12 @@ for vers in ( pythonlib = os.path.join(pythonlib, "__pycache__") test_options[key] = (os.path.join(lib_prefix, pythonlib), PYOC, pythonlib, vers) +vers = 3.7 +bytecode = "bytecode_pypy%s_run" % vers +key = "bytecode-pypy37" +test_options[key] = (bytecode, PYC, bytecode, vers) + + # ----- diff --git a/uncompyle6/parsers/parse37.py b/uncompyle6/parsers/parse37.py index 7b166c5f..e1e7c300 100644 --- a/uncompyle6/parsers/parse37.py +++ b/uncompyle6/parsers/parse37.py @@ -17,6 +17,7 @@ Python 3.7 grammar for the spark Earley-algorithm parser. """ from __future__ import print_function +from uncompyle6.scanners.tok import Token from uncompyle6.parser import PythonParserSingle, nop_func from spark_parser import DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG from uncompyle6.parsers.parse37base import Python37BaseParser diff --git a/uncompyle6/parsers/parse37base.py b/uncompyle6/parsers/parse37base.py index a4a1650b..5d45b33e 100644 --- a/uncompyle6/parsers/parse37base.py +++ b/uncompyle6/parsers/parse37base.py @@ -523,15 +523,30 @@ class Python37BaseParser(PythonParser): args_pos, args_kw = self.get_pos_kw(token) - # number of apply equiv arguments: - nak = (len(opname_base) - len("CALL_METHOD")) // 3 - rule = ( - "call ::= expr " - + ("pos_arg " * args_pos) - + ("kwarg " * args_kw) - + "expr " * nak - + opname - ) + if opname == "CALL_METHOD_KW": + args_kw = token.attr + rules_str = """ + expr ::= call_kw_pypy37 + pypy_kw_keys ::= LOAD_CONST + """ + self.add_unique_doc_rules(rules_str, customize) + rule = ( + "call_kw_pypy37 ::= expr " + + ("expr " * args_kw) + + " pypy_kw_keys " + + opname + ) + else: + args_pos, args_kw = self.get_pos_kw(token) + # number of apply equiv arguments: + nak = (len(opname_base) - len("CALL_METHOD")) // 3 + rule = ( + "call ::= expr " + + ("expr " * args_pos) + + ("kwarg " * args_kw) + + "expr " * nak + + opname + ) self.add_unique_rule(rule, opname, token.attr, customize) elif opname == "CONTINUE": diff --git a/uncompyle6/semantics/customize.py b/uncompyle6/semantics/customize.py index 0d850ef8..249402f3 100644 --- a/uncompyle6/semantics/customize.py +++ b/uncompyle6/semantics/customize.py @@ -16,9 +16,9 @@ """Isolate Python version-specific semantic actions here. """ -from uncompyle6.semantics.consts import PRECEDENCE, TABLE_R, TABLE_DIRECT - from uncompyle6.parsers.treenode import SyntaxTree +from uncompyle6.semantics.consts import INDENT_PER_LEVEL, PRECEDENCE, TABLE_R, TABLE_DIRECT +from uncompyle6.semantics.helper import flatten_list from uncompyle6.scanners.tok import Token @@ -28,9 +28,10 @@ def customize_for_version(self, is_pypy, version): # PyPy changes ####################### TABLE_DIRECT.update({ - 'assert_pypy': ( '%|assert %c\n' , (1, 'assert_expr') ), - # This is as a result of an if transoration - 'assert0_pypy': ( '%|assert %c\n' , (0, 'assert_expr') ), + "assert": ("%|assert %c\n", 0), + "assert_pypy": ( '%|assert %c\n' , (1, 'assert_expr') ), + # This is as a result of an if transformation + 'assert0_pypy': ( '%|assert %c\n' , 0), 'assert_not_pypy': ( '%|assert not %c\n' , (1, 'assert_exp') ), 'assert2_not_pypy': ( '%|assert not %c, %c\n' , (1, 'assert_exp'), @@ -42,6 +43,42 @@ def customize_for_version(self, is_pypy, version): 'assign3_pypy': ( '%|%c, %c, %c = %c, %c, %c\n', 5, 4, 3, 0, 1, 2 ), 'assign2_pypy': ( '%|%c, %c = %c, %c\n', 3, 2, 0, 1), }) + + if version[:2] == (3, 7): + + def n_call_kw_pypy37(node): + self.template_engine(("%p(", (0, 100)), node) + assert node[-1] == "CALL_METHOD_KW" + pypy_kw_keys = node[-2] + assert pypy_kw_keys == "pypy_kw_keys" + + flat_elems = flatten_list(node[1:-2]) + # FIXME zip pypy_kw_keys and elems + + self.indent_more(INDENT_PER_LEVEL) + sep = "" + + n = len(flat_elems) + kw_keys_tuple = pypy_kw_keys[0].attr + assert n == len(kw_keys_tuple) + for i in range(n): + elem = flat_elems[i] + assert elem == "expr" + line_number = self.line_number + value = self.traverse(elem) + if line_number != self.line_number: + sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1] + pass + self.write(sep) + self.write(f"{kw_keys_tuple[i]}={value}") + sep = ", " + pass + + self.write(")") + self.prune() + + self.n_call_kw_pypy37 = n_call_kw_pypy37 + else: ######################## # Without PyPy diff --git a/uncompyle6/version.py b/uncompyle6/version.py index 138213a1..fed5a211 100644 --- a/uncompyle6/version.py +++ b/uncompyle6/version.py @@ -14,4 +14,4 @@ # This file is suitable for sourcing inside POSIX shell as # well as importing into Python # fmt: off -__version__="3.8.0" # noqa +__version__="3.8.1.dev0" # noqa