Files
micropython/tests/basics/string_tstring_parser1.py
Koudai Aono bdef10a92b tests: Add full feature and coverage tests for PEP 750 template strings.
Includes corresponding .exp files because this feature is only available in
Python 3.14+.

Tests for `!a` conversion specifier and space after `!` are not included
because they are not supported by MicroPython.

Signed-off-by: Koudai Aono <koxudaxi@gmail.com>
Signed-off-by: Damien George <damien@micropython.org>
2026-03-09 23:47:33 +11:00

289 lines
8.3 KiB
Python

from string.templatelib import Template
print("\n=== Empty expression tests ===")
try:
exec("t'{}'")
print("ERROR: Empty expression should have raised SyntaxError")
except SyntaxError as e:
print("Empty expr SyntaxError:", e)
try:
exec("t'{ }'")
print("ERROR: Whitespace-only expression should have raised SyntaxError")
except SyntaxError as e:
print("Whitespace expr SyntaxError:", e)
try:
exec("t'{\\n\\t \\n}'")
print("ERROR: Whitespace-only expr should fail")
except SyntaxError as e:
print(f"Whitespace expr: SyntaxError")
try:
exec("t'{value'")
print("ERROR: Unterminated expr should fail")
except SyntaxError as e:
print(f"Unterminated expr: {type(e).__name__}")
try:
exec("t'{ !r}'")
print("ERROR: Whitespace-conversion expr should fail")
except SyntaxError as e:
print(f"Whitespace + conversion: {type(e).__name__}")
try:
long_expr = 'x' * 104
code = f"t'{{{long_expr}}}'"
exec(f"{long_expr} = 'test'; result = {code}")
print("Long expression: OK")
except Exception as e:
print(f"Long expr error: {type(e).__name__}: {e}")
try:
large_str = 'x' * 1099
tmpl = Template(large_str)
result = str(tmpl)
print("Template.__str__ with large string: OK")
except Exception as e:
print(f"Template.__str__ error: {type(e).__name__}: {e}")
try:
x = 'a'
result = t'{x}'
print(f"Basic t-string: OK, result={result}")
except Exception as e:
print(f"Basic t-string error: {type(e).__name__}: {e}")
try:
code = 't"' + 'text{nested{x}}more' * 50 + '"'
x = 'test'
exec(f"x = 'test'; result = {code}")
print("Nested interpolations: OK")
except Exception as e:
print(f"Nested interpolations error: {type(e).__name__}: {e}")
width = 5
x = 42
try:
tmpl = t'{x:{width}{{literal}}}'
result = str(tmpl)
print(f'Escaped braces result: {result}')
except ValueError as e:
print(f'Escaped braces ValueError: {e}')
print("\n=== Expression parser tests ===")
try:
exec("t'{[{(x,y):[1,2,3]} for x,y in [(1,2),(3,4)]]}'")
print("Complex expr: OK")
except Exception as e:
print(f"Complex expr error: {type(e).__name__}")
print("\n=== Lexer edge cases ===")
print("Lexer NULL case: Tested via heapalloc_fail_tstring.py")
print("\n=== Parser allocation edge cases ===")
try:
deep_expr = "(" * 50 + "x" + ")" * 50
exec(f"x = 1; result = t'{{{deep_expr}}}'")
print("Deep nesting: OK")
except Exception as e:
print(f"Deep nesting error: {type(e).__name__} - {e}")
try:
long_expr = "(" * 100 + "x" + ")" * 100
exec(f"x = 1; result = t'{{{long_expr}}}'")
print("Very long expression: OK")
except Exception as e:
print(f"Long expression error: {e}")
print("\n=== Additional parser regression tests ===")
try:
fmt = '"QUOTED"'
value = 'raw'
tmpl = rt"{value:{fmt}}"
print(f"Raw nested spec: {tmpl.interpolations[0].format_spec}")
except Exception as e:
print(f"Raw nested spec error: {type(e).__name__}: {e}")
try:
value = 'fmt'
tmpl = t'{value:"QUOTED"}'
print(f"Escaped quote spec: {tmpl.interpolations[0].format_spec}")
except Exception as e:
print(f"Escaped quote spec error: {type(e).__name__}: {e}")
try:
value = 'fmt'
tmpl = t"{value:\\n}"
print(f"Escaped backslash spec: {tmpl.interpolations[0].format_spec}")
except Exception as e:
print(f"Escaped backslash spec error: {type(e).__name__}: {e}")
try:
ns = {}
exec("value = 'fmt'\nresult = t\"{value:{'\\\\'}}\"", globals(), ns)
tmpl = ns['result']
print(f"Nested quoted spec: {tmpl.interpolations[0].format_spec}")
except Exception as e:
print(f"Nested quoted spec error: {type(e).__name__}: {e}")
try:
value = 'brace'
exec('tmpl = t"{value}}}}tail"')
print(f"Literal brace strings: {tmpl.strings}")
except Exception as e:
print(f"Literal brace error: {type(e).__name__}: {e}")
try:
exec('t"{value"')
print("ERROR: Unterminated field should have raised")
except SyntaxError as e:
print(f"Unterminated field: {e}")
print("\n=== Complex expression stress test ===")
try:
vars_dict = {f"x{i}": i for i in range(60)}
exec_globals = globals().copy()
exec_globals.update(vars_dict)
expr_parts = [f"x{i}" for i in range(60)]
expr_str = " + ".join(expr_parts)
code = f't"{{{expr_str}}}"'
result = eval(code, exec_globals)
expected = sum(range(60))
actual = result.interpolations[0].value
if actual == expected:
print(f"60 terms: PASS")
else:
print(f"60 terms: FAIL (expected={expected}, got={actual})")
except Exception as e:
print(f"60 terms: ERROR - {type(e).__name__}: {e}")
try:
vars_dict = {f"x{i}": i for i in range(100)}
exec_globals = globals().copy()
exec_globals.update(vars_dict)
expr_parts = [f"x{i}" for i in range(100)]
expr_str = " + ".join(expr_parts)
code = f't"{{{expr_str}}}"'
result = eval(code, exec_globals)
expected = sum(range(100))
actual = result.interpolations[0].value
if actual == expected:
print(f"100 terms: PASS")
else:
print(f"100 terms: FAIL (expected={expected}, got={actual})")
except Exception as e:
print(f"100 terms: ERROR - {type(e).__name__}: {e}")
print("\n=== Stack expansion test (copy_parse_node) ===")
try:
depth = 20
nested_expr = "[" * depth + "1" + "]" * depth
code = f't"{{{nested_expr}}}"'
result = eval(code)
if len(str(result.interpolations[0].value)) > 30:
print("Stack expansion: OK")
else:
print(f"Stack expansion: FAIL")
except Exception as e:
print(f"Stack expansion error: {type(e).__name__}")
print("\n=== Format spec with triple-quoted strings ===")
try:
x = 42
code = '''t'{x:{"""test"""}}' '''
result = eval(code)
print(f"Triple-quote in format spec: OK")
except Exception as e:
print(f"Triple-quote error: {type(e).__name__}: {e}")
try:
x = 42
code = """t"{x:{'''test'''}}" """
result = eval(code)
print(f"Triple single-quote in format spec: OK")
except Exception as e:
print(f"Triple single-quote error: {type(e).__name__}: {e}")
print("\n=== Format spec parse error (exception handler) ===")
try:
x = 42
code = "t\"{x:{1 + (}}\" " # Unclosed parenthesis
result = eval(code)
print("ERROR: Should have raised SyntaxError")
except SyntaxError as e:
print(f"Parse error handler: OK")
except Exception as e:
print(f"Unexpected error: {type(e).__name__}: {e}")
try:
x = 42
code = "t\"{x:{1 2 3}}\" " # Invalid syntax
result = eval(code)
print("ERROR: Should have raised SyntaxError")
except SyntaxError as e:
print(f"Parse error handler 2: OK")
except Exception as e:
print(f"Unexpected error 2: {type(e).__name__}: {e}")
print("\n=== Large integer in t-string ===")
try:
# Use eval() to avoid compile-time overflow in longlong variant
large_int = eval("10**30")
tmpl = t"{large_int}"
if tmpl.interpolations[0].value == large_int:
print("Large integer: OK")
else:
print(f"Large integer: FAIL")
except OverflowError:
# longlong variant doesn't support arbitrary precision integers
print("Large integer: OK")
except Exception as e:
print(f"Large integer error: {type(e).__name__}: {e}")
print("\n=== Bytes in expression ===")
try:
data = b"test"
tmpl = t"{data}"
if tmpl.interpolations[0].value == b"test":
print("Bytes in expression: OK")
else:
print(f"Bytes: FAIL")
except Exception as e:
print(f"Bytes error: {type(e).__name__}: {e}")
print("\n=== Boolean constants ===")
try:
tmpl = t"{True} {False}"
if tmpl.values == (True, False):
print("Boolean constants: OK")
else:
print(f"Booleans: FAIL")
except Exception as e:
print(f"Boolean error: {type(e).__name__}: {e}")
print("\n=== None constant ===")
try:
tmpl = t"{None}"
if tmpl.values[0] is None:
print("None constant: OK")
else:
print(f"None: FAIL")
except Exception as e:
print(f"None error: {type(e).__name__}: {e}")
print("\n=== Deep nesting for rule stack expansion ===")
try:
depth = 80
code = '(' * depth + '1' + ')' * depth
result = eval(code)
if result == 1:
print(f"Deep nesting (depth={depth}): OK")
else:
print(f"Deep nesting: FAIL (result={result})")
except Exception as e:
print(f"Deep nesting error: {type(e).__name__}: {e}")