mirror of
https://github.com/micropython/micropython.git
synced 2026-03-10 19:00:30 +01:00
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>
247 lines
6.5 KiB
Python
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")
|