mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 15:41:43 +00:00 
			
		
		
		
	Co-authored-by: Lysandros Nikolaou <lisandrosnik@gmail.com> Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Co-authored-by: Wingy <git@wingysam.xyz> Co-authored-by: Koudai Aono <koxudaxi@gmail.com> Co-authored-by: Dave Peck <davepeck@gmail.com> Co-authored-by: Terry Jan Reedy <tjreedy@udel.edu> Co-authored-by: Paul Everitt <pauleveritt@me.com> Co-authored-by: sobolevn <mail@sobolevn.me>
		
			
				
	
	
		
			313 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			313 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
import unittest
 | 
						|
 | 
						|
from test.test_string._support import TStringBaseCase, fstring
 | 
						|
 | 
						|
 | 
						|
class TestTString(unittest.TestCase, TStringBaseCase):
 | 
						|
    def test_string_representation(self):
 | 
						|
        # Test __repr__
 | 
						|
        t = t"Hello"
 | 
						|
        self.assertEqual(repr(t), "Template(strings=('Hello',), interpolations=())")
 | 
						|
 | 
						|
        name = "Python"
 | 
						|
        t = t"Hello, {name}"
 | 
						|
        self.assertEqual(repr(t),
 | 
						|
            "Template(strings=('Hello, ', ''), "
 | 
						|
            "interpolations=(Interpolation('Python', 'name', None, ''),))"
 | 
						|
        )
 | 
						|
 | 
						|
    def test_interpolation_basics(self):
 | 
						|
        # Test basic interpolation
 | 
						|
        name = "Python"
 | 
						|
        t = t"Hello, {name}"
 | 
						|
        self.assertTStringEqual(t, ("Hello, ", ""), [(name, "name")])
 | 
						|
        self.assertEqual(fstring(t), "Hello, Python")
 | 
						|
 | 
						|
        # Multiple interpolations
 | 
						|
        first = "Python"
 | 
						|
        last = "Developer"
 | 
						|
        t = t"{first} {last}"
 | 
						|
        self.assertTStringEqual(
 | 
						|
            t, ("", " ", ""), [(first, 'first'), (last, 'last')]
 | 
						|
        )
 | 
						|
        self.assertEqual(fstring(t), "Python Developer")
 | 
						|
 | 
						|
        # Interpolation with expressions
 | 
						|
        a = 10
 | 
						|
        b = 20
 | 
						|
        t = t"Sum: {a + b}"
 | 
						|
        self.assertTStringEqual(t, ("Sum: ", ""), [(a + b, "a + b")])
 | 
						|
        self.assertEqual(fstring(t), "Sum: 30")
 | 
						|
 | 
						|
        # Interpolation with function
 | 
						|
        def square(x):
 | 
						|
            return x * x
 | 
						|
        t = t"Square: {square(5)}"
 | 
						|
        self.assertTStringEqual(
 | 
						|
            t, ("Square: ", ""), [(square(5), "square(5)")]
 | 
						|
        )
 | 
						|
        self.assertEqual(fstring(t), "Square: 25")
 | 
						|
 | 
						|
        # Test attribute access in expressions
 | 
						|
        class Person:
 | 
						|
            def __init__(self, name):
 | 
						|
                self.name = name
 | 
						|
 | 
						|
            def upper(self):
 | 
						|
                return self.name.upper()
 | 
						|
 | 
						|
        person = Person("Alice")
 | 
						|
        t = t"Name: {person.name}"
 | 
						|
        self.assertTStringEqual(
 | 
						|
            t, ("Name: ", ""), [(person.name, "person.name")]
 | 
						|
        )
 | 
						|
        self.assertEqual(fstring(t), "Name: Alice")
 | 
						|
 | 
						|
        # Test method calls
 | 
						|
        t = t"Name: {person.upper()}"
 | 
						|
        self.assertTStringEqual(
 | 
						|
            t, ("Name: ", ""), [(person.upper(), "person.upper()")]
 | 
						|
        )
 | 
						|
        self.assertEqual(fstring(t), "Name: ALICE")
 | 
						|
 | 
						|
        # Test dictionary access
 | 
						|
        data = {"name": "Bob", "age": 30}
 | 
						|
        t = t"Name: {data['name']}, Age: {data['age']}"
 | 
						|
        self.assertTStringEqual(
 | 
						|
            t, ("Name: ", ", Age: ", ""),
 | 
						|
            [(data["name"], "data['name']"), (data["age"], "data['age']")],
 | 
						|
        )
 | 
						|
        self.assertEqual(fstring(t), "Name: Bob, Age: 30")
 | 
						|
 | 
						|
    def test_format_specifiers(self):
 | 
						|
        # Test basic format specifiers
 | 
						|
        value = 3.14159
 | 
						|
        t = t"Pi: {value:.2f}"
 | 
						|
        self.assertTStringEqual(
 | 
						|
            t, ("Pi: ", ""), [(value, "value", None, ".2f")]
 | 
						|
        )
 | 
						|
        self.assertEqual(fstring(t), "Pi: 3.14")
 | 
						|
 | 
						|
    def test_conversions(self):
 | 
						|
        # Test !s conversion (str)
 | 
						|
        obj = object()
 | 
						|
        t = t"Object: {obj!s}"
 | 
						|
        self.assertTStringEqual(t, ("Object: ", ""), [(obj, "obj", "s")])
 | 
						|
        self.assertEqual(fstring(t), f"Object: {str(obj)}")
 | 
						|
 | 
						|
        # Test !r conversion (repr)
 | 
						|
        t = t"Data: {obj!r}"
 | 
						|
        self.assertTStringEqual(t, ("Data: ", ""), [(obj, "obj", "r")])
 | 
						|
        self.assertEqual(fstring(t), f"Data: {repr(obj)}")
 | 
						|
 | 
						|
        # Test !a conversion (ascii)
 | 
						|
        text = "Café"
 | 
						|
        t = t"ASCII: {text!a}"
 | 
						|
        self.assertTStringEqual(t, ("ASCII: ", ""), [(text, "text", "a")])
 | 
						|
        self.assertEqual(fstring(t), f"ASCII: {ascii(text)}")
 | 
						|
 | 
						|
        # Test !z conversion (error)
 | 
						|
        num = 1
 | 
						|
        with self.assertRaises(SyntaxError):
 | 
						|
            eval("t'{num!z}'")
 | 
						|
 | 
						|
    def test_debug_specifier(self):
 | 
						|
        # Test debug specifier
 | 
						|
        value = 42
 | 
						|
        t = t"Value: {value=}"
 | 
						|
        self.assertTStringEqual(
 | 
						|
            t, ("Value: value=", ""), [(value, "value", "r")]
 | 
						|
        )
 | 
						|
        self.assertEqual(fstring(t), "Value: value=42")
 | 
						|
 | 
						|
        # Test debug specifier with format (conversion default to !r)
 | 
						|
        t = t"Value: {value=:.2f}"
 | 
						|
        self.assertTStringEqual(
 | 
						|
            t, ("Value: value=", ""), [(value, "value", None, ".2f")]
 | 
						|
        )
 | 
						|
        self.assertEqual(fstring(t), "Value: value=42.00")
 | 
						|
 | 
						|
        # Test debug specifier with conversion
 | 
						|
        t = t"Value: {value=!s}"
 | 
						|
        self.assertTStringEqual(
 | 
						|
            t, ("Value: value=", ""), [(value, "value", "s")]
 | 
						|
        )
 | 
						|
 | 
						|
        # Test white space in debug specifier
 | 
						|
        t = t"Value: {value = }"
 | 
						|
        self.assertTStringEqual(
 | 
						|
            t, ("Value: value = ", ""), [(value, "value", "r")]
 | 
						|
        )
 | 
						|
        self.assertEqual(fstring(t), "Value: value = 42")
 | 
						|
 | 
						|
    def test_raw_tstrings(self):
 | 
						|
        path = r"C:\Users"
 | 
						|
        t = rt"{path}\Documents"
 | 
						|
        self.assertTStringEqual(t, ("", r"\Documents"), [(path, "path")])
 | 
						|
        self.assertEqual(fstring(t), r"C:\Users\Documents")
 | 
						|
 | 
						|
        # Test alternative prefix
 | 
						|
        t = tr"{path}\Documents"
 | 
						|
        self.assertTStringEqual(t, ("", r"\Documents"), [(path, "path")])
 | 
						|
 | 
						|
 | 
						|
    def test_template_concatenation(self):
 | 
						|
        # Test template + template
 | 
						|
        t1 = t"Hello, "
 | 
						|
        t2 = t"world"
 | 
						|
        combined = t1 + t2
 | 
						|
        self.assertTStringEqual(combined, ("Hello, world",), ())
 | 
						|
        self.assertEqual(fstring(combined), "Hello, world")
 | 
						|
 | 
						|
        # Test template + string
 | 
						|
        t1 = t"Hello"
 | 
						|
        combined = t1 + ", world"
 | 
						|
        self.assertTStringEqual(combined, ("Hello, world",), ())
 | 
						|
        self.assertEqual(fstring(combined), "Hello, world")
 | 
						|
 | 
						|
        # Test template + template with interpolation
 | 
						|
        name = "Python"
 | 
						|
        t1 = t"Hello, "
 | 
						|
        t2 = t"{name}"
 | 
						|
        combined = t1 + t2
 | 
						|
        self.assertTStringEqual(combined, ("Hello, ", ""), [(name, "name")])
 | 
						|
        self.assertEqual(fstring(combined), "Hello, Python")
 | 
						|
 | 
						|
        # Test string + template
 | 
						|
        t = "Hello, " + t"{name}"
 | 
						|
        self.assertTStringEqual(t, ("Hello, ", ""), [(name, "name")])
 | 
						|
        self.assertEqual(fstring(t), "Hello, Python")
 | 
						|
 | 
						|
    def test_nested_templates(self):
 | 
						|
        # Test a template inside another template expression
 | 
						|
        name = "Python"
 | 
						|
        inner = t"{name}"
 | 
						|
        t = t"Language: {inner}"
 | 
						|
 | 
						|
        t_interp = t.interpolations[0]
 | 
						|
        self.assertEqual(t.strings, ("Language: ", ""))
 | 
						|
        self.assertEqual(t_interp.value.strings, ("", ""))
 | 
						|
        self.assertEqual(t_interp.value.interpolations[0].value, name)
 | 
						|
        self.assertEqual(t_interp.value.interpolations[0].expression, "name")
 | 
						|
        self.assertEqual(t_interp.value.interpolations[0].conversion, None)
 | 
						|
        self.assertEqual(t_interp.value.interpolations[0].format_spec, "")
 | 
						|
        self.assertEqual(t_interp.expression, "inner")
 | 
						|
        self.assertEqual(t_interp.conversion, None)
 | 
						|
        self.assertEqual(t_interp.format_spec, "")
 | 
						|
 | 
						|
    def test_syntax_errors(self):
 | 
						|
        for case, err in (
 | 
						|
            ("t'", "unterminated t-string literal"),
 | 
						|
            ("t'''", "unterminated triple-quoted t-string literal"),
 | 
						|
            ("t''''", "unterminated triple-quoted t-string literal"),
 | 
						|
            ("t'{", "'{' was never closed"),
 | 
						|
            ("t'{'", "t-string: expecting '}'"),
 | 
						|
            ("t'{a'", "t-string: expecting '}'"),
 | 
						|
            ("t'}'", "t-string: single '}' is not allowed"),
 | 
						|
            ("t'{}'", "t-string: valid expression required before '}'"),
 | 
						|
            ("t'{=x}'", "t-string: valid expression required before '='"),
 | 
						|
            ("t'{!x}'", "t-string: valid expression required before '!'"),
 | 
						|
            ("t'{:x}'", "t-string: valid expression required before ':'"),
 | 
						|
            ("t'{x;y}'", "t-string: expecting '=', or '!', or ':', or '}'"),
 | 
						|
            ("t'{x=y}'", "t-string: expecting '!', or ':', or '}'"),
 | 
						|
            ("t'{x!s!}'", "t-string: expecting ':' or '}'"),
 | 
						|
            ("t'{x!s:'", "t-string: expecting '}', or format specs"),
 | 
						|
            ("t'{x!}'", "t-string: missing conversion character"),
 | 
						|
            ("t'{x=!}'", "t-string: missing conversion character"),
 | 
						|
            ("t'{x!z}'", "t-string: invalid conversion character 'z': "
 | 
						|
                         "expected 's', 'r', or 'a'"),
 | 
						|
            ("t'{lambda:1}'", "t-string: lambda expressions are not allowed "
 | 
						|
                              "without parentheses"),
 | 
						|
            ("t'{x:{;}}'", "t-string: expecting a valid expression after '{'"),
 | 
						|
        ):
 | 
						|
            with self.subTest(case), self.assertRaisesRegex(SyntaxError, err):
 | 
						|
                eval(case)
 | 
						|
 | 
						|
    def test_runtime_errors(self):
 | 
						|
        # Test missing variables
 | 
						|
        with self.assertRaises(NameError):
 | 
						|
            eval("t'Hello, {name}'")
 | 
						|
 | 
						|
    def test_literal_concatenation(self):
 | 
						|
        # Test concatenation of t-string literals
 | 
						|
        t = t"Hello, " t"world"
 | 
						|
        self.assertTStringEqual(t, ("Hello, world",), ())
 | 
						|
        self.assertEqual(fstring(t), "Hello, world")
 | 
						|
 | 
						|
        # Test concatenation with interpolation
 | 
						|
        name = "Python"
 | 
						|
        t = t"Hello, " t"{name}"
 | 
						|
        self.assertTStringEqual(t, ("Hello, ", ""), [(name, "name")])
 | 
						|
        self.assertEqual(fstring(t), "Hello, Python")
 | 
						|
 | 
						|
        # Test concatenation with string literal
 | 
						|
        name = "Python"
 | 
						|
        t = t"Hello, {name}" "and welcome!"
 | 
						|
        self.assertTStringEqual(
 | 
						|
            t, ("Hello, ", "and welcome!"), [(name, "name")]
 | 
						|
        )
 | 
						|
        self.assertEqual(fstring(t), "Hello, Pythonand welcome!")
 | 
						|
 | 
						|
        # Test concatenation with Unicode literal
 | 
						|
        name = "Python"
 | 
						|
        t = t"Hello, {name}" u"and welcome!"
 | 
						|
        self.assertTStringEqual(
 | 
						|
            t, ("Hello, ", "and welcome!"), [(name, "name")]
 | 
						|
        )
 | 
						|
        self.assertEqual(fstring(t), "Hello, Pythonand welcome!")
 | 
						|
 | 
						|
        # Test concatenation with f-string literal
 | 
						|
        tab = '\t'
 | 
						|
        t = t"Tab: {tab}. " f"f-tab: {tab}."
 | 
						|
        self.assertTStringEqual(t, ("Tab: ", ". f-tab: \t."), [(tab, "tab")])
 | 
						|
        self.assertEqual(fstring(t), "Tab: \t. f-tab: \t.")
 | 
						|
 | 
						|
        # Test concatenation with raw string literal
 | 
						|
        tab = '\t'
 | 
						|
        t = t"Tab: {tab}. " r"Raw tab: \t."
 | 
						|
        self.assertTStringEqual(
 | 
						|
            t, ("Tab: ", r". Raw tab: \t."), [(tab, "tab")]
 | 
						|
        )
 | 
						|
        self.assertEqual(fstring(t), "Tab: \t. Raw tab: \\t.")
 | 
						|
 | 
						|
        # Test concatenation with raw f-string literal
 | 
						|
        tab = '\t'
 | 
						|
        t = t"Tab: {tab}. " rf"f-tab: {tab}. Raw tab: \t."
 | 
						|
        self.assertTStringEqual(
 | 
						|
            t, ("Tab: ", ". f-tab: \t. Raw tab: \\t."), [(tab, "tab")]
 | 
						|
        )
 | 
						|
        self.assertEqual(fstring(t), "Tab: \t. f-tab: \t. Raw tab: \\t.")
 | 
						|
 | 
						|
        what = 't'
 | 
						|
        expected_msg = 'cannot mix bytes and nonbytes literals'
 | 
						|
        for case in (
 | 
						|
            "t'{what}-string literal' b'bytes literal'",
 | 
						|
            "t'{what}-string literal' br'raw bytes literal'",
 | 
						|
        ):
 | 
						|
            with self.assertRaisesRegex(SyntaxError, expected_msg):
 | 
						|
                eval(case)
 | 
						|
 | 
						|
    def test_triple_quoted(self):
 | 
						|
        # Test triple-quoted t-strings
 | 
						|
        t = t"""
 | 
						|
        Hello,
 | 
						|
        world
 | 
						|
        """
 | 
						|
        self.assertTStringEqual(
 | 
						|
            t, ("\n        Hello,\n        world\n        ",), ()
 | 
						|
        )
 | 
						|
        self.assertEqual(fstring(t), "\n        Hello,\n        world\n        ")
 | 
						|
 | 
						|
        # Test triple-quoted with interpolation
 | 
						|
        name = "Python"
 | 
						|
        t = t"""
 | 
						|
        Hello,
 | 
						|
        {name}
 | 
						|
        """
 | 
						|
        self.assertTStringEqual(
 | 
						|
            t, ("\n        Hello,\n        ", "\n        "), [(name, "name")]
 | 
						|
        )
 | 
						|
        self.assertEqual(fstring(t), "\n        Hello,\n        Python\n        ")
 | 
						|
 | 
						|
if __name__ == '__main__':
 | 
						|
    unittest.main()
 |