Files
micropython/tests/basics/string_tstring_errors1.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

247 lines
6.5 KiB
Python

from string.templatelib import Template, Interpolation
print("\n=== Edge cases ===")
t_empty = Template()
print(f"Empty template: '{t_empty}'")
t_empty_strs = Template("", Interpolation(1, "a"), "", Interpolation(2, "b"), "")
print(f"Empty strings: {list(t_empty_strs)}")
t_adj = t"{1}{2}{3}"
print(f"Adjacent: '{t_adj}'")
t_single = Template("only")
print(f"Single iter: {list(t_single)}")
t_self = t"test"
print(f"Self+self: '{(t_self + t_self)}'")
print("\n=== Additional coverage tests ===")
t_str_literal = t"{'hello'}"
print(f"String literal: {t_str_literal}")
path = "/usr/local/bin"
count = 42
raw_path = rt"Path: {path}\n"
print(f"Raw path strings: {raw_path.strings}, value={raw_path.interpolations[0].value}")
raw_regex = rt"Regex: \\d+{count}"
print(f"Raw regex strings: {raw_regex.strings}, value={raw_regex.interpolations[0].value}")
try:
t_str_expr = t'{"test"}'
print(f"String expr: '{t_str_expr}'")
except Exception as e:
print(f"String expr error: {e}")
def raise_error():
raise ValueError("Special error")
try:
t_exc = t"{raise_error()}"
print(t_exc)
except ValueError as e:
print(f"Re-raised exception: {e}")
try:
large_str = "x" * 100000
exec(f'very_long_name_{large_str} = t"test"')
except (ValueError, MemoryError, SyntaxError, RuntimeError) as e:
print("Large template: SyntaxError")
try:
exec('''t_triple = t"""Triple "quoted" string"""''')
print(f"Triple quoted: '{t_triple}'")
except Exception as e:
print(f"Triple quoted error: {e}")
try:
exec(r'''t_raw_triple = rt"""Raw triple\n{42}"""''')
print(f"Raw triple: '{t_raw_triple}'")
except Exception as e:
print(f"Raw triple error: {e}")
t_concat1 = t"a{1}b"
t_concat2 = t"c{2}d{3}e"
t_concat3 = t_concat1 + t_concat2
print(f"Complex concat: strings={t_concat3.strings}, values={t_concat3.values}")
t_empty = Template()
t_nonempty = t"test{42}"
t_concat_empty = t_empty + t_nonempty
print(f"Empty concat: '{t_concat_empty}'")
t_self_interp = t"x{1}y"
t_self_concat = t_self_interp + t_self_interp
print(f"Self interp concat: values={t_self_concat.values}")
try:
exec('t"unterminated')
except SyntaxError as e:
print(f"Lonely string: {type(e).__name__}")
try:
t_edge = t""
print(f"Empty t-string: '{t_edge}'")
except Exception as e:
print(f"Empty t-string error: {e}")
print("\n=== High byte handling ===")
try:
result = t'\x7F\x80\x81\xFE\xFF'
first_str = result.strings[0]
print(f"High bytes: len={len(first_str)}, first=0x{ord(first_str[0]):02x}, last=0x{ord(first_str[-1]):02x}")
except Exception as e:
print(f"High bytes error: {e}")
try:
result = t'\200\201\377'
print(f"Octal high bytes: OK, len={len(result.strings[0])}")
except Exception as e:
print(f"Octal high bytes error: {e}")
print("\n=== Deep nesting test ===")
try:
nested = "1" + " + 1" * 150
code = f't"{{{nested}}}"'
exec(code)
print("Deep nesting: OK")
except Exception as e:
print(f"Deep nesting error: {type(e).__name__}")
print("\n=== Trailing whitespace in expression ===")
try:
x = 42
code = 't"{x }"'
result = eval(code)
print(f"Trailing whitespace: OK")
except Exception as e:
print(f"Trailing whitespace error: {e}")
print("\n=== Single closing brace ===")
try:
exec('t"test }"')
print("ERROR: Single } should have raised SyntaxError")
except SyntaxError as e:
print(f"Single }}: SyntaxError - {e}")
print("\n=== Escape in string within interpolation ===")
try:
compile('x = 1; t"{x=:"', '<test>', 'exec')
except SyntaxError:
print('Debug unclosed colon: SyntaxError')
try:
compile('x = 1; t"{x=!"', '<test>', 'exec')
except SyntaxError:
print('Debug unclosed exclaim: SyntaxError')
try:
compile('t"prefix{incomplete"', '<test>', 'exec')
except SyntaxError as e:
print('Unclosed brace literal: SyntaxError')
try:
import sys
if hasattr(sys.implementation, '_mpy'):
compile('f"{x!invalid}"', '<test>', 'exec')
except SyntaxError:
print('Malformed f-string: SyntaxError')
# NOTE: Error messages are shortened in MicroPython to avoid stack overflow
# during ROM compression on constrained platforms (Windows x86, ASan).
# CPython 3.14 message: "Template.__new__ *args need to be of type 'str' or 'Interpolation', got int"
# MicroPython message: "Template.__new__ args must be str or Interpolation, got 'int'"
try:
t = Template("string", 123)
except TypeError as e:
print('Type error int:', e)
try:
t = Template("a", "b", 3.14)
except TypeError:
print('Type error float: TypeError')
try:
compile('t"text { more text"', '<test>', 'exec')
except SyntaxError as e:
print('Unmatched brace:', e)
try:
compile('x=1; t"{x= {nested}}"', '<test>', 'exec')
except SyntaxError:
print('Nested debug braces: SyntaxError')
try:
compile('t"{x!}"', '<test>', 'exec')
except SyntaxError:
print('Missing conversion: SyntaxError')
try:
compile('t"{x!z}"', '<test>', 'exec')
except SyntaxError as e:
print('Invalid conversion:', e)
try:
compile('t"{x!r' + chr(0x0b) + ':10}"', '<test>', 'exec')
except SyntaxError as e:
print('Vertical tab:', e)
try:
compile('t"{x!r@:10}"', '<test>', 'exec')
except SyntaxError as e:
print('Invalid char after conversion:', e)
# MicroPython doesn't allow space after conversion.
# try:
# x = 'test'
# exec('result = t"{x!r :10}"')
# print('Space after conversion: OK')
# except Exception as e:
# print(f'Unexpected error: {e}')
try:
x = 'test'
exec('result = t"{x!s\\t:10}"')
print('Tab after conversion: OK')
except Exception as e:
print(f'Unexpected error: {e}')
try:
x = 'test'
exec('result = t"{x!r\\n:10}"')
print('Newline after conversion: OK')
except Exception as e:
print(f'Unexpected error: {e}')
try:
x = 'test'
exec('result = t"{x!r\\r:10}"')
print('CR after conversion: OK')
except Exception as e:
print(f'Unexpected error: {e}')
try:
x = 'test'
exec('result = t"{x!s\\f:10}"')
print('Form-feed after conversion: OK')
except Exception as e:
print(f'Unexpected error: {e}')
print("\n=== Mixed prefixes ===")
for src in ('ft"{x}"', 'tf"{x}"', 'frt"{x}"', 'rtf"{x}"', 'trf"{x}"'):
try:
exec(src)
print(src.split('"')[0] + ": OK (BUG!)")
except SyntaxError as e:
prefix = src.split('"')[0]
# Check if we get the specific error message or generic one
if "'f' and 't' prefixes are incompatible" in str(e):
print(f"{prefix}: incompatible prefixes")
else:
print(f"{prefix}: invalid syntax")