|
| 1 | +"""Assignment expression (``NamedExpr``) tests.""" |
| 2 | + |
| 3 | + |
| 4 | +from ast import NodeTransformer |
| 5 | +from ast import parse |
| 6 | +from RestrictedPython import compile_restricted |
| 7 | +from RestrictedPython import safe_globals |
| 8 | +from RestrictedPython._compat import IS_PY38_OR_GREATER |
| 9 | +from unittest import skipUnless |
| 10 | +from unittest import TestCase |
| 11 | + |
| 12 | + |
| 13 | +@skipUnless(IS_PY38_OR_GREATER, "Feature available for Python 3.8+") |
| 14 | +class TestNamedExpr(TestCase): |
| 15 | + def test_works(self): |
| 16 | + code, gs = compile_str("if x:= x + 1: True\n") |
| 17 | + gs["x"] = 0 |
| 18 | + exec(code, gs) |
| 19 | + self.assertEqual(gs["x"], 1) |
| 20 | + |
| 21 | + def test_no_private_target(self): |
| 22 | + with self.assertRaises(SyntaxError): |
| 23 | + compile_str("if _x_:= 1: True\n") |
| 24 | + |
| 25 | + def test_simple_only(self): |
| 26 | + # we test here that only a simple variable is allowed |
| 27 | + # as assignemt expression target |
| 28 | + # Currently (Python 3.8, 3.9), this is enforced by the |
| 29 | + # Python concrete syntax; therefore, some (``ast``) trickery is |
| 30 | + # necessary to produce a test for it. |
| 31 | + class TransformNamedExprTarget(NodeTransformer): |
| 32 | + def visit_NamedExpr(self, node): |
| 33 | + # this is brutal but sufficient for the test |
| 34 | + node.target = None |
| 35 | + return node |
| 36 | + |
| 37 | + mod = parse("if x:= x + 1: True\n") |
| 38 | + mod = TransformNamedExprTarget().visit(mod) |
| 39 | + with self.assertRaisesRegex( |
| 40 | + SyntaxError, |
| 41 | + "Assignment expressions are only allowed for simple target"): |
| 42 | + code, gs = compile_str(mod) |
| 43 | + |
| 44 | + |
| 45 | +def compile_str(s, name="<unknown>"): |
| 46 | + """code and globals for *s*. |
| 47 | +
|
| 48 | + *s* must be acceptable for ``compile_restricted`` (this is (especially) the |
| 49 | + case for an ``str`` or ``ast.Module``). |
| 50 | +
|
| 51 | + *name* is a ``str`` used in error messages. |
| 52 | + """ |
| 53 | + code = compile_restricted(s, name, 'exec') |
| 54 | + gs = safe_globals.copy() |
| 55 | + gs["__debug__"] = True # assert active |
| 56 | + return code, gs |
0 commit comments