From ebebda3ca6f9d485c896897ceef7decda7527ea0 Mon Sep 17 00:00:00 2001 From: TheAnyKey Date: Wed, 6 May 2020 17:15:19 -0400 Subject: [PATCH] implementation of generalized decorators and test update from cpython --- Lib/test/test_decorators.py | 78 ++++++++++++++++++++++++++++++------- Lib/test/test_types.py | 4 +- parser/src/python.lalrpop | 16 +------- 3 files changed, 68 insertions(+), 30 deletions(-) diff --git a/Lib/test/test_decorators.py b/Lib/test/test_decorators.py index d0a2ec9fd..7809f44e7 100644 --- a/Lib/test/test_decorators.py +++ b/Lib/test/test_decorators.py @@ -151,21 +151,18 @@ class TestDecorators(unittest.TestCase): self.assertEqual(counts['double'], 4) def test_errors(self): - # Test syntax restrictions - these are all compile-time errors: - # - for expr in [ "1+2", "x[3]", "(1, 2)" ]: - # Sanity check: is expr is a valid expression by itself? - compile(expr, "testexpr", "exec") - codestr = "@%s\ndef f(): pass" % expr - self.assertRaises(SyntaxError, compile, codestr, "test", "exec") + # Test SyntaxErrors: + for stmt in ("x,", "x, y", "x = y", "pass", "import sys"): + compile(stmt, "test", "exec") # Sanity check. + with self.assertRaises(SyntaxError): + compile(f"@{stmt}\ndef f(): pass", "test", "exec") - # You can't put multiple decorators on a single line: - # - self.assertRaises(SyntaxError, compile, - "@f1 @f2\ndef f(): pass", "test", "exec") - - # Test runtime errors + # Test TypeErrors that used to be SyntaxErrors: + for expr in ("1.+2j", "[1, 2][-1]", "(1, 2)", "True", "...", "None"): + compile(expr, "test", "eval") # Sanity check. + with self.assertRaises(TypeError): + exec(f"@{expr}\ndef f(): pass") def unimp(func): raise NotImplementedError @@ -179,6 +176,18 @@ class TestDecorators(unittest.TestCase): code = compile(codestr, "test", "exec") self.assertRaises(exc, eval, code, context) + def test_expressions(self): + for expr in ( + ## original tests + # "(x,)", "(x, y)", "x := y", "(x := y)", "x @y", "(x @ y)", "x[0]", + # "w[x].y.z", "w + x - (y + z)", "x(y)()(z)", "[w, x, y][z]", "x.y", + + ##same without := + "(x,)", "(x, y)", "x @y", "(x @ y)", "x[0]", + "w[x].y.z", "w + x - (y + z)", "x(y)()(z)", "[w, x, y][z]", "x.y", + ): + compile(f"@{expr}\ndef f(): pass", "test", "exec") + def test_double(self): class C(object): @funcattrs(abc=1, xyz="haha") @@ -265,6 +274,47 @@ class TestDecorators(unittest.TestCase): self.assertEqual(bar(), 42) self.assertEqual(actions, expected_actions) + # this test was already not working before adding the Py39 decorator extension + @unittest.expectedFailure('TODO RustPython') + def test_wrapped_descriptor_inside_classmethod(self): + class BoundWrapper: + def __init__(self, wrapped): + self.__wrapped__ = wrapped + + def __call__(self, *args, **kwargs): + return self.__wrapped__(*args, **kwargs) + + class Wrapper: + def __init__(self, wrapped): + self.__wrapped__ = wrapped + + def __get__(self, instance, owner): + bound_function = self.__wrapped__.__get__(instance, owner) + return BoundWrapper(bound_function) + + def decorator(wrapped): + return Wrapper(wrapped) + + class Class: + @decorator + @classmethod + def inner(cls): + # This should already work. + return 'spam' + + @classmethod + @decorator + def outer(cls): + # Raised TypeError with a message saying that the 'Wrapper' + # object is not callable. + return 'eggs' + + self.assertEqual(Class.inner(), 'spam') + #self.assertEqual(Class.outer(), 'eggs') # TODO RustPython + self.assertEqual(Class().inner(), 'spam') + #self.assertEqual(Class().outer(), 'eggs') # TODO RustPython + + class TestClassDecorators(unittest.TestCase): def test_simple(self): @@ -301,4 +351,4 @@ class TestClassDecorators(unittest.TestCase): self.assertEqual(C.extra, 'second') if __name__ == "__main__": - unittest.main() + unittest.main() \ No newline at end of file diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index cd509933c..66bfcc52d 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -390,8 +390,8 @@ class TypesTests(unittest.TestCase): self.assertEqual(locale.format_string('%g', x, grouping=True), format(x, 'n')) self.assertEqual(locale.format_string('%.10g', x, grouping=True), format(x, '.10n')) - # TODO: RUSTPYTHON - @unittest.expectedFailure + # DONE but why? + #@unittest.expectedFailure @run_with_locale('LC_NUMERIC', 'en_US.UTF8') def test_int__format__locale(self): # test locale support for __format__ code 'n' for integers diff --git a/parser/src/python.lalrpop b/parser/src/python.lalrpop index 548f256e8..5a7dae32d 100644 --- a/parser/src/python.lalrpop +++ b/parser/src/python.lalrpop @@ -606,20 +606,8 @@ Path: ast::Expression = { // Decorators: Decorator: ast::Expression = { - "@" "\n" => { - match a { - Some((location, _, arg, _)) => { - ast::Expression { - location, - node: ast::ExpressionType::Call { - function: Box::new(p), - args: arg.args, - keywords: arg.keywords, - } - } - }, - None => p, - } + "@" "\n" => { + p }, };