Uodate test_grammar.py from 3.13.7

This commit is contained in:
ShaharNaveh committed 2025-08-31 11:47:32 +03:00
1 parent d9ffc47c43
commit 6a4d4b727c
1 file changed
+310 -140
+310 -140
View File
@@ -2,7 +2,7 @@
# This just tests whether the parser accepts them all.
from test.support import check_syntax_error
from test.support.warnings_helper import check_syntax_warning
from test.support import import_helper
import inspect
import unittest
import sys
@@ -12,97 +12,19 @@ from sys import *
# different import patterns to check that __annotations__ does not interfere
# with import machinery
# import test.ann_module as ann_module
import test.typinganndata.ann_module as ann_module
import typing
from collections import ChainMap
# from test import ann_module2
from test.typinganndata import ann_module2
import test
# These are shared with test_tokenize and other test modules.
#
# Note: since several test cases filter out floats by looking for "e" and ".",
# don't add hexadecimal literals that contain "e" or "E".
VALID_UNDERSCORE_LITERALS = [
'0_0_0',
'4_2',
'1_0000_0000',
'0b1001_0100',
'0xffff_ffff',
'0o5_7_7',
'1_00_00.5',
'1_00_00.5e5',
'1_00_00e5_1',
'1e1_0',
'.1_4',
'.1_4e1',
'0b_0',
'0x_f',
'0o_5',
'1_00_00j',
'1_00_00.5j',
'1_00_00e5_1j',
'.1_4j',
'(1_2.5+3_3j)',
'(.5_6j)',
]
INVALID_UNDERSCORE_LITERALS = [
# Trailing underscores:
'0_',
'42_',
'1.4j_',
'0x_',
'0b1_',
'0xf_',
'0o5_',
'0 if 1_Else 1',
# Underscores in the base selector:
'0_b0',
'0_xf',
'0_o5',
# Old-style octal, still disallowed:
'0_7',
'09_99',
# Multiple consecutive underscores:
'4_______2',
'0.1__4',
'0.1__4j',
'0b1001__0100',
'0xffff__ffff',
'0x___',
'0o5__77',
'1e1__0',
'1e1__0j',
# Underscore right before a dot:
'1_.4',
'1_.4j',
# Underscore right after a dot:
'1._4',
'1._4j',
'._5',
'._5j',
# Underscore right after a sign:
'1.0e+_1',
'1.0e+_1j',
# Underscore right before j:
'1.4_j',
'1.4e5_j',
# Underscore right before e:
'1_e1',
'1.4_e1',
'1.4_e1j',
# Underscore right after e:
'1e_1',
'1.4e_1',
'1.4e_1j',
# Complex cases with parens:
'(1+1.5_j_)',
'(1+1.5_j)',
]
from test.support.numbers import (
VALID_UNDERSCORE_LITERALS,
INVALID_UNDERSCORE_LITERALS,
)
class TokenTests(unittest.TestCase):
from test.support import check_syntax_error
from test.support.warnings_helper import check_syntax_warning
def test_backslash(self):
# Backslash means line continuation:
@@ -164,7 +86,7 @@ class TokenTests(unittest.TestCase):
x = 3.14
x = 314.
x = 0.314
# XXX x = 000.314
x = 000.314
x = .314
x = 3e14
x = 3E14
@@ -176,8 +98,10 @@ class TokenTests(unittest.TestCase):
def test_float_exponent_tokenization(self):
# See issue 21642.
self.assertEqual(1 if 1else 0, 1)
self.assertEqual(1 if 0else 0, 0)
with warnings.catch_warnings():
warnings.simplefilter('ignore', SyntaxWarning)
self.assertEqual(eval("1 if 1else 0"), 1)
self.assertEqual(eval("1 if 0else 0"), 0)
self.assertRaises(SyntaxError, eval, "0 if 1Else 0")
def test_underscore_literals(self):
@@ -188,8 +112,7 @@ class TokenTests(unittest.TestCase):
# Sanity check: no literal begins with an underscore
self.assertRaises(NameError, eval, "_0")
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_bad_numerical_literals(self):
check = self.check_syntax_error
check("0b12", "invalid digit '2' in binary literal")
@@ -212,6 +135,47 @@ class TokenTests(unittest.TestCase):
check("1e2_", "invalid decimal literal")
check("1e+", "invalid decimal literal")
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_end_of_numerical_literals(self):
def check(test, error=False):
with self.subTest(expr=test):
if error:
with warnings.catch_warnings(record=True) as w:
with self.assertRaisesRegex(SyntaxError,
r'invalid \w+ literal'):
compile(test, "<testcase>", "eval")
self.assertEqual(w, [])
else:
self.check_syntax_warning(test,
errtext=r'invalid \w+ literal')
for num in "0xf", "0o7", "0b1", "9", "0", "1.", "1e3", "1j":
compile(num, "<testcase>", "eval")
check(f"{num}and x", error=(num == "0xf"))
check(f"{num}or x", error=(num == "0"))
check(f"{num}in x")
check(f"{num}not in x")
check(f"{num}if x else y")
check(f"x if {num}else y", error=(num == "0xf"))
check(f"[{num}for x in ()]")
check(f"{num}spam", error=True)
# gh-88943: Invalid non-ASCII character following a numerical literal.
with self.assertRaisesRegex(SyntaxError, r"invalid character '⁄' \(U\+2044\)"):
compile(f"{num}⁄7", "<testcase>", "eval")
with self.assertWarnsRegex(SyntaxWarning, r'invalid \w+ literal'):
compile(f"{num}is x", "<testcase>", "eval")
with warnings.catch_warnings():
warnings.simplefilter('error', SyntaxWarning)
with self.assertRaisesRegex(SyntaxError,
r'invalid \w+ literal'):
compile(f"{num}is x", "<testcase>", "eval")
check("[0x1ffor x in ()]")
check("[0x1for x in ()]")
check("[0xfor x in ()]")
def test_string_literals(self):
x = ''; y = ""; self.assertTrue(len(x) == 0 and x == y)
x = '\''; y = "'"; self.assertTrue(len(x) == 1 and x == y and ord(x) == 39)
@@ -257,12 +221,13 @@ the \'lazy\' dog.\n\
self.assertTrue(x is Ellipsis)
self.assertRaises(SyntaxError, eval, ".. .")
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_eof_error(self):
samples = ("def foo(", "\ndef foo(", "def foo(\n")
for s in samples:
with self.assertRaises(SyntaxError) as cm:
compile(s, "<test>", "exec")
self.assertIn("unexpected EOF", str(cm.exception))
self.assertIn("was never closed", str(cm.exception))
var_annot_global: int # a global annotated is necessary for test_var_annot
@@ -281,6 +246,7 @@ class GrammarTests(unittest.TestCase):
from test.support import check_syntax_error
from test.support.warnings_helper import check_syntax_warning
from test.support.warnings_helper import check_no_warnings
# single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE
# XXX can't test in a script -- this rule is only used when interactive
@@ -307,14 +273,18 @@ class GrammarTests(unittest.TestCase):
my_lst[one()-1]: int = 5
self.assertEqual(my_lst, [5])
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_var_annot_syntax_errors(self):
# parser pass
check_syntax_error(self, "def f: int")
check_syntax_error(self, "x: int: str")
check_syntax_error(self, "def f():\n"
" nonlocal x: int\n")
check_syntax_error(self, "def f():\n"
" global x: int\n")
check_syntax_error(self, "x: int = y = 1")
check_syntax_error(self, "z = w: int = 1")
check_syntax_error(self, "x: int = y: int = 1")
# AST pass
check_syntax_error(self, "[x, 0]: int\n")
check_syntax_error(self, "f(): int\n")
@@ -328,9 +298,14 @@ class GrammarTests(unittest.TestCase):
check_syntax_error(self, "def f():\n"
" global x\n"
" x: int\n")
check_syntax_error(self, "def f():\n"
" x: int\n"
" nonlocal x\n")
check_syntax_error(self, "def f():\n"
" nonlocal x\n"
" x: int\n")
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_var_annot_basic_semantics(self):
# execution order
with self.assertRaises(ZeroDivisionError):
@@ -377,6 +352,28 @@ class GrammarTests(unittest.TestCase):
x: int
x.y: list = []
def test_annotations_inheritance(self):
# Check that annotations are not inherited by derived classes
class A:
attr: int
class B(A):
pass
class C(A):
attr: str
class D:
attr2: int
class E(A, D):
pass
class F(C, A):
pass
self.assertEqual(A.__annotations__, {"attr": int})
self.assertEqual(B.__annotations__, {})
self.assertEqual(C.__annotations__, {"attr" : str})
self.assertEqual(D.__annotations__, {"attr2" : int})
self.assertEqual(E.__annotations__, {})
self.assertEqual(F.__annotations__, {})
def test_var_annot_metaclass_semantics(self):
class CMeta(type):
@classmethod
@@ -386,13 +383,11 @@ class GrammarTests(unittest.TestCase):
XX: 'ANNOT'
self.assertEqual(CC.__annotations__['xx'], 'ANNOT')
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_var_annot_module_semantics(self):
with self.assertRaises(AttributeError):
print(test.__annotations__)
self.assertEqual(test.__annotations__, {})
self.assertEqual(ann_module.__annotations__,
{1: 2, 'x': int, 'y': str, 'f': typing.Tuple[int, int]})
{1: 2, 'x': int, 'y': str, 'f': typing.Tuple[int, int], 'u': int | float})
self.assertEqual(ann_module.M.__annotations__,
{'123': 123, 'o': type})
self.assertEqual(ann_module2.__annotations__, {})
@@ -400,13 +395,13 @@ class GrammarTests(unittest.TestCase):
def test_var_annot_in_module(self):
# check that functions fail the same way when executed
# outside of module where they were defined
from test.typinganndata.ann_module3 import f_bad_ann, g_bad_ann, D_bad_ann
ann_module3 = import_helper.import_fresh_module("test.typinganndata.ann_module3")
with self.assertRaises(NameError):
f_bad_ann()
ann_module3.f_bad_ann()
with self.assertRaises(NameError):
g_bad_ann()
ann_module3.g_bad_ann()
with self.assertRaises(NameError):
D_bad_ann(5)
ann_module3.D_bad_ann(5)
def test_var_annot_simple_exec(self):
gns = {}; lns= {}
@@ -417,8 +412,7 @@ class GrammarTests(unittest.TestCase):
with self.assertRaises(KeyError):
gns['__annotations__']
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_var_annot_custom_maps(self):
# tests with custom locals() and __annotations__
ns = {'__annotations__': CNS()}
@@ -440,8 +434,7 @@ class GrammarTests(unittest.TestCase):
exec('x: int = 1', {}, CNS2())
self.assertEqual(nonloc_ns['__annotations__']['x'], int)
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_var_annot_refleak(self):
# complex case: custom locals plus custom __annotations__
# this was causing refleak
@@ -459,7 +452,6 @@ class GrammarTests(unittest.TestCase):
exec('X: str', {}, CNS2())
self.assertEqual(nonloc_ns['__annotations__']['x'], str)
def test_var_annot_rhs(self):
ns = {}
exec('x: tuple = 1, 2', ns)
@@ -475,7 +467,7 @@ class GrammarTests(unittest.TestCase):
def test_funcdef(self):
### [decorators] 'def' NAME parameters ['->' test] ':' suite
### decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
### decorator: '@' namedexpr_test NEWLINE
### decorators: decorator+
### parameters: '(' [typedargslist] ')'
### typedargslist: ((tfpdef ['=' test] ',')*
@@ -597,12 +589,14 @@ class GrammarTests(unittest.TestCase):
d22v(1, *(2, 3), **{'d': 4})
# keyword argument type tests
try:
str('x', **{b'foo':1 })
except TypeError:
pass
else:
self.fail('Bytes should not work as keyword argument names')
with warnings.catch_warnings():
warnings.simplefilter('ignore', BytesWarning)
try:
str('x', **{b'foo':1 })
except TypeError:
pass
else:
self.fail('Bytes should not work as keyword argument names')
# keyword only argument tests
def pos0key1(*, key): return key
pos0key1(key=100)
@@ -681,6 +675,20 @@ class GrammarTests(unittest.TestCase):
def f(x) -> list: pass
self.assertEqual(f.__annotations__, {'return': list})
# Test expressions as decorators (PEP 614):
@False or null
def f(x): pass
@d := null
def f(x): pass
@lambda f: null(f)
def f(x): pass
@[..., null, ...][1]
def f(x): pass
@null(null)(null)
def f(x): pass
@[null][0].__call__.__call__
def f(x): pass
# test closures with a variety of opargs
closure = 1
def f(): return closure
@@ -768,10 +776,9 @@ class GrammarTests(unittest.TestCase):
check_syntax_error(self, "x + 1 = 1")
check_syntax_error(self, "a + 1 = b + 2")
# TODO: RUSTPYTHON
@unittest.expectedFailure
# Check the heuristic for print & exec covers significant cases
# As well as placing some limits on false positives
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_former_statements_refer_to_builtins(self):
keywords = "print", "exec"
# Cases where we want the custom error
@@ -804,6 +811,23 @@ class GrammarTests(unittest.TestCase):
del abc
del x, y, (z, xyz)
x, y, z = "xyz"
del x
del y,
del (z)
del ()
a, b, c, d, e, f, g = "abcdefg"
del a, (b, c), (d, (e, f))
a, b, c, d, e, f, g = "abcdefg"
del a, [b, c], (d, [e, f])
abcd = list("abcd")
del abcd[1:2]
compile("del a, (b[0].c, (d.e, f.g[1:2])), [h.i.j], ()", "<testcase>", "exec")
def test_pass_stmt(self):
# 'pass'
pass
@@ -1062,8 +1086,7 @@ class GrammarTests(unittest.TestCase):
self.assertEqual(g2(False), 0)
self.assertEqual(g2(True), ('end', 1))
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_yield(self):
# Allowed as standalone statement
def g(): yield 1
@@ -1103,8 +1126,7 @@ class GrammarTests(unittest.TestCase):
# Check annotation refleak on SyntaxError
check_syntax_error(self, "def g(a:(yield)): pass")
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_yield_in_comprehensions(self):
# Check yield in comprehensions
def g(): [x for x in [(yield 1)]]
@@ -1184,11 +1206,9 @@ class GrammarTests(unittest.TestCase):
self.fail("'assert True, msg' should not have "
"raised an AssertionError")
# TODO: RUSTPYTHON
@unittest.expectedFailure
# these tests fail if python is run with -O, so check __debug__
@unittest.skipUnless(__debug__, "Won't work if __debug__ is False")
def testAssert2(self):
def test_assert_failures(self):
try:
assert 0, "msg"
except AssertionError as e:
@@ -1203,11 +1223,38 @@ class GrammarTests(unittest.TestCase):
else:
self.fail("AssertionError not raised by 'assert False'")
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_assert_syntax_warnings(self):
# Ensure that we warn users if they provide a non-zero length tuple as
# the assertion test.
self.check_syntax_warning('assert(x, "msg")',
'assertion is always true')
self.check_syntax_warning('assert(False, "msg")',
'assertion is always true')
self.check_syntax_warning('assert(False,)',
'assertion is always true')
with self.check_no_warnings(category=SyntaxWarning):
compile('assert x, "msg"', '<testcase>', 'exec')
compile('assert False, "msg"', '<testcase>', 'exec')
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_assert_warning_promotes_to_syntax_error(self):
# If SyntaxWarning is configured to be an error, it actually raises a
# SyntaxError.
# https://bugs.python.org/issue35029
with warnings.catch_warnings():
warnings.simplefilter('error', SyntaxWarning)
compile('assert x, "msg"', '<testcase>', 'exec')
try:
compile('assert x, "msg" ', '<testcase>', 'exec')
except SyntaxError:
self.fail('SyntaxError incorrectly raised for \'assert x, "msg"\'')
with self.assertRaises(SyntaxError):
compile('assert(x, "msg")', '<testcase>', 'exec')
with self.assertRaises(SyntaxError):
compile('assert(False, "msg")', '<testcase>', 'exec')
with self.assertRaises(SyntaxError):
compile('assert(False,)', '<testcase>', 'exec')
### compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | funcdef | classdef
@@ -1268,10 +1315,16 @@ class GrammarTests(unittest.TestCase):
result.append(x)
self.assertEqual(result, [1, 2, 3])
result = []
a = b = c = [1, 2, 3]
for x in *a, *b, *c:
result.append(x)
self.assertEqual(result, 3 * a)
def test_try(self):
### try_stmt: 'try' ':' suite (except_clause ':' suite)+ ['else' ':' suite]
### | 'try' ':' suite 'finally' ':' suite
### except_clause: 'except' [expr ['as' expr]]
### except_clause: 'except' [expr ['as' NAME]]
try:
1/0
except ZeroDivisionError:
@@ -1289,6 +1342,36 @@ class GrammarTests(unittest.TestCase):
except (EOFError, TypeError, ZeroDivisionError) as msg: pass
try: pass
finally: pass
with self.assertRaises(SyntaxError):
compile("try:\n pass\nexcept Exception as a.b:\n pass", "?", "exec")
compile("try:\n pass\nexcept Exception as a[b]:\n pass", "?", "exec")
# TODO: RUSTPYTHON
'''
def test_try_star(self):
### try_stmt: 'try': suite (except_star_clause : suite) + ['else' ':' suite]
### except_star_clause: 'except*' expr ['as' NAME]
try:
1/0
except* ZeroDivisionError:
pass
else:
pass
try: 1/0
except* EOFError: pass
except* ZeroDivisionError as msg: pass
else: pass
try: 1/0
except* (EOFError, TypeError, ZeroDivisionError): pass
try: 1/0
except* (EOFError, TypeError, ZeroDivisionError) as msg: pass
try: pass
finally: pass
with self.assertRaises(SyntaxError):
compile("try:\n pass\nexcept* Exception as a.b:\n pass", "?", "exec")
compile("try:\n pass\nexcept* Exception as a[b]:\n pass", "?", "exec")
compile("try:\n pass\nexcept*:\n pass", "?", "exec")
'''
def test_suite(self):
# simple_stmt | NEWLINE INDENT NEWLINE* (stmt NEWLINE*)+ DEDENT
@@ -1333,17 +1416,24 @@ class GrammarTests(unittest.TestCase):
if 1 not in (): pass
if 1 < 1 > 1 == 1 >= 1 <= 1 != 1 in 1 not in x is x is not x: pass
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_comparison_is_literal(self):
def check(test, msg='"is" with a literal'):
def check(test, msg):
self.check_syntax_warning(test, msg)
check('x is 1')
check('x is "thing"')
check('1 is x')
check('x is y is 1')
check('x is not 1', '"is not" with a literal')
check('x is 1', '"is" with \'int\' literal')
check('x is "thing"', '"is" with \'str\' literal')
check('1 is x', '"is" with \'int\' literal')
check('x is y is 1', '"is" with \'int\' literal')
check('x is not 1', '"is not" with \'int\' literal')
check('x is not (1, 2)', '"is not" with \'tuple\' literal')
check('(1, 2) is not x', '"is not" with \'tuple\' literal')
check('None is 1', '"is" with \'int\' literal')
check('1 is None', '"is" with \'int\' literal')
check('x == 3 is y', '"is" with \'int\' literal')
check('x == "thing" is y', '"is" with \'str\' literal')
with warnings.catch_warnings():
warnings.simplefilter('error', SyntaxWarning)
@@ -1351,9 +1441,12 @@ class GrammarTests(unittest.TestCase):
compile('x is False', '<testcase>', 'exec')
compile('x is True', '<testcase>', 'exec')
compile('x is ...', '<testcase>', 'exec')
compile('None is x', '<testcase>', 'exec')
compile('False is x', '<testcase>', 'exec')
compile('True is x', '<testcase>', 'exec')
compile('... is x', '<testcase>', 'exec')
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_warn_missed_comma(self):
def check(test):
self.check_syntax_warning(test, msg)
@@ -1483,7 +1576,7 @@ class GrammarTests(unittest.TestCase):
s = a[-5:]
s = a[:-1]
s = a[-4:-3]
# A rough test of SF bug 1333982. http://python.org/sf/1333982
# A rough test of SF bug 1333982. https://bugs.python.org/issue1333982
# The testing here is fairly incomplete.
# Test cases should include: commas with 1 and 2 colons
d = {}
@@ -1530,6 +1623,8 @@ class GrammarTests(unittest.TestCase):
### testlist: test (',' test)* [',']
# These have been exercised enough above
# TODO: RUSTPYTHON
'''
def test_classdef(self):
# 'class' NAME ['(' [testlist] ')'] ':' suite
class B: pass
@@ -1542,13 +1637,28 @@ class GrammarTests(unittest.TestCase):
def meth2(self, arg): pass
def meth3(self, a1, a2): pass
# decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
# decorator: '@' namedexpr_test NEWLINE
# decorators: decorator+
# decorated: decorators (classdef | funcdef)
def class_decorator(x): return x
@class_decorator
class G: pass
# Test expressions as decorators (PEP 614):
@False or class_decorator
class H: pass
@d := class_decorator
class I: pass
@lambda c: class_decorator(c)
class J: pass
@[..., class_decorator, ...][1]
class K: pass
@class_decorator(class_decorator)(class_decorator)
class L: pass
@[class_decorator][0].__call__.__call__
class M: pass
'''
def test_dictcomps(self):
# dictorsetmaker: ( (test ':' test (comp_for |
# (',' test ':' test)* [','])) |
@@ -1693,6 +1803,54 @@ class GrammarTests(unittest.TestCase):
with manager() as x, manager():
pass
with (
manager()
):
pass
with (
manager() as x
):
pass
with (
manager() as (x, y),
manager() as z,
):
pass
with (
manager(),
manager()
):
pass
with (
manager() as x,
manager() as y
):
pass
with (
manager() as x,
manager()
):
pass
with (
manager() as x,
manager() as y,
manager() as z,
):
pass
with (
manager() as x,
manager() as y,
manager(),
):
pass
def test_if_else_expr(self):
# Test ifelse expressions in various cases
def _checkeval(msg, ret):
@@ -1815,6 +1973,18 @@ class GrammarTests(unittest.TestCase):
with self.assertRaises(Done):
foo().send(None)
def test_complex_lambda(self):
def test1(foo, bar):
return ""
def test2():
return f"{test1(
foo=lambda: '、、、、、、、、、、、、、、、、、',
bar=lambda: 'abcdefghijklmnopqrstuvwxyz 123456789 123456789',
)}"
self.assertEqual(test2(), "")
if __name__ == '__main__':
unittest.main()