From 02bbbadf083340fb7fdc08551a4c63f6d8b7dd4b Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Sat, 19 Apr 2025 17:18:12 +0100 Subject: [PATCH 1/6] Add assertTStringEqual() helper --- Lib/test/test_tstring.py | 243 +++++++++++++++------------------------ 1 file changed, 94 insertions(+), 149 deletions(-) diff --git a/Lib/test/test_tstring.py b/Lib/test/test_tstring.py index 6245f417638903..e55d56e21e4087 100644 --- a/Lib/test/test_tstring.py +++ b/Lib/test/test_tstring.py @@ -11,6 +11,28 @@ def assertAllRaise(self, exception_type, regex, error_strings): with self.assertRaisesRegex(exception_type, regex): eval(s) + def assertTStringEqual(self, t, strings, interpolations): + """Test template string literal equality. + + The *strings* argument must be a tuple of strings equal to *t.strings*. + + The *interpolations* argument must be a sequence of tuples which are + compared against *t.interpolations*. Each tuple consists of + (value, expression, conversion, format_spec), though the final two + items may be omitted, and are assumed to be None and '' respectively. + """ + self.assertEqual(t.strings, strings) + self.assertEqual(len(t.interpolations), len(interpolations)) + + for actual, expected in zip(t.interpolations, interpolations, + strict=True): + if len(expected) == 2: + self.assertEqual(tuple(actual), expected + (None, '')) + elif len(expected) == 3: + self.assertEqual(tuple(actual), expected + ('',)) + else: + self.assertEqual(tuple(actual), expected) + def test_string_representation(self): # Test __repr__ t = t"Hello" @@ -27,48 +49,32 @@ def test_interpolation_basics(self): # Test basic interpolation name = "Python" t = t"Hello, {name}" - self.assertEqual(t.strings, ("Hello, ", "")) - self.assertEqual(t.interpolations[0].value, name) - self.assertEqual(t.interpolations[0].expression, "name") - self.assertEqual(t.interpolations[0].conversion, None) - self.assertEqual(t.interpolations[0].format_spec, "") + self.assertTStringEqual(t, ("Hello, ", ""), [(name, "name")]) self.assertEqual(f(t), "Hello, Python") # Multiple interpolations first = "Python" last = "Developer" t = t"{first} {last}" - self.assertEqual(t.strings, ("", " ", "")) - self.assertEqual(t.interpolations[0].value, first) - self.assertEqual(t.interpolations[0].expression, "first") - self.assertEqual(t.interpolations[0].conversion, None) - self.assertEqual(t.interpolations[0].format_spec, "") - self.assertEqual(t.interpolations[1].value, last) - self.assertEqual(t.interpolations[1].expression, "last") - self.assertEqual(t.interpolations[1].conversion, None) - self.assertEqual(t.interpolations[1].format_spec, "") + self.assertTStringEqual( + t, ("", " ", ""), [(first, 'first'), (last, 'last')] + ) self.assertEqual(f(t), "Python Developer") # Interpolation with expressions a = 10 b = 20 t = t"Sum: {a + b}" - self.assertEqual(t.strings, ("Sum: ", "")) - self.assertEqual(t.interpolations[0].value, a + b) - self.assertEqual(t.interpolations[0].expression, "a + b") - self.assertEqual(t.interpolations[0].conversion, None) - self.assertEqual(t.interpolations[0].format_spec, "") + self.assertTStringEqual(t, ("Sum: ", ""), [(a + b, "a + b")]) self.assertEqual(f(t), "Sum: 30") # Interpolation with function def square(x): return x * x t = t"Square: {square(5)}" - self.assertEqual(t.strings, ("Square: ", "")) - self.assertEqual(t.interpolations[0].value, square(5)) - self.assertEqual(t.interpolations[0].expression, "square(5)") - self.assertEqual(t.interpolations[0].conversion, None) - self.assertEqual(t.interpolations[0].format_spec, "") + self.assertTStringEqual( + t, ("Square: ", ""), [(square(5), "square(5)")] + ) self.assertEqual(f(t), "Square: 25") # Test attribute access in expressions @@ -81,75 +87,52 @@ def upper(self): person = Person("Alice") t = t"Name: {person.name}" - self.assertEqual(t.strings, ("Name: ", "")) - self.assertEqual(t.interpolations[0].value, person.name) - self.assertEqual(t.interpolations[0].expression, "person.name") - self.assertEqual(t.interpolations[0].conversion, None) - self.assertEqual(t.interpolations[0].format_spec, "") + self.assertTStringEqual( + t, ("Name: ", ""), [(person.name, "person.name")] + ) self.assertEqual(f(t), "Name: Alice") # Test method calls t = t"Name: {person.upper()}" - self.assertEqual(t.strings, ("Name: ", "")) - self.assertEqual(t.interpolations[0].value, person.upper()) - self.assertEqual(t.interpolations[0].expression, "person.upper()") - self.assertEqual(t.interpolations[0].conversion, None) - self.assertEqual(t.interpolations[0].format_spec, "") + self.assertTStringEqual( + t, ("Name: ", ""), [(person.upper(), "person.upper()")] + ) self.assertEqual(f(t), "Name: ALICE") # Test dictionary access data = {"name": "Bob", "age": 30} t = t"Name: {data['name']}, Age: {data['age']}" - self.assertEqual(t.strings, ("Name: ", ", Age: ", "")) - self.assertEqual(t.interpolations[0].value, data["name"]) - self.assertEqual(t.interpolations[0].expression, "data['name']") - self.assertEqual(t.interpolations[0].conversion, None) - self.assertEqual(t.interpolations[0].format_spec, "") - self.assertEqual(t.interpolations[1].value, data["age"]) - self.assertEqual(t.interpolations[1].expression, "data['age']") - self.assertEqual(t.interpolations[1].conversion, None) - self.assertEqual(t.interpolations[1].format_spec, "") + self.assertTStringEqual( + t, ("Name: ", ", Age: ", ""), + [(data["name"], "data['name']"), (data["age"], "data['age']")], + ) self.assertEqual(f(t), "Name: Bob, Age: 30") def test_format_specifiers(self): # Test basic format specifiers value = 3.14159 t = t"Pi: {value:.2f}" - self.assertEqual(t.strings, ("Pi: ", "")) - self.assertEqual(t.interpolations[0].value, value) - self.assertEqual(t.interpolations[0].expression, "value") - self.assertEqual(t.interpolations[0].conversion, None) - self.assertEqual(t.interpolations[0].format_spec, ".2f") + self.assertTStringEqual( + t, ("Pi: ", ""), [(value, "value", None, ".2f")] + ) self.assertEqual(f(t), "Pi: 3.14") def test_conversions(self): # Test !s conversion (str) obj = object() t = t"Object: {obj!s}" - self.assertEqual(t.strings, ("Object: ", "")) - self.assertEqual(t.interpolations[0].value, obj) - self.assertEqual(t.interpolations[0].expression, "obj") - self.assertEqual(t.interpolations[0].conversion, "s") - self.assertEqual(t.interpolations[0].format_spec, "") + self.assertTStringEqual(t, ("Object: ", ""), [(obj, "obj", "s")]) self.assertEqual(f(t), f"Object: {str(obj)}") # Test !r conversion (repr) t = t"Data: {obj!r}" - self.assertEqual(t.strings, ("Data: ", "")) - self.assertEqual(t.interpolations[0].value, obj) - self.assertEqual(t.interpolations[0].expression, "obj") - self.assertEqual(t.interpolations[0].conversion, "r") - self.assertEqual(t.interpolations[0].format_spec, "") + self.assertTStringEqual(t, ("Data: ", ""), [(obj, "obj", "r")]) self.assertEqual(f(t), f"Data: {repr(obj)}") # Test !a conversion (ascii) text = "Café" t = t"ASCII: {text!a}" - self.assertEqual(t.strings, ("ASCII: ", "")) - self.assertEqual(t.interpolations[0].value, text) - self.assertEqual(t.interpolations[0].expression, "text") - self.assertEqual(t.interpolations[0].conversion, "a") - self.assertEqual(t.interpolations[0].format_spec, "") + self.assertTStringEqual(t, ("ASCII: ", ""), [(text, "text", "a")]) self.assertEqual(f(t), f"ASCII: {ascii(text)}") # Test !z conversion (error) @@ -161,51 +144,40 @@ def test_debug_specifier(self): # Test debug specifier value = 42 t = t"Value: {value=}" - self.assertEqual(t.strings, ("Value: value=", "")) - self.assertEqual(t.interpolations[0].value, value) - self.assertEqual(t.interpolations[0].expression, "value") - self.assertEqual(t.interpolations[0].conversion, "r") - self.assertEqual(t.interpolations[0].format_spec, "") + self.assertTStringEqual( + t, ("Value: value=", ""), [(value, "value", "r")] + ) self.assertEqual(f(t), "Value: value=42") # Test debug specifier with format (conversion default to !r) t = t"Value: {value=:.2f}" - self.assertEqual(t.strings, ("Value: value=", "")) - self.assertEqual(t.interpolations[0].value, value) - self.assertEqual(t.interpolations[0].expression, "value") - self.assertEqual(t.interpolations[0].conversion, None) - self.assertEqual(t.interpolations[0].format_spec, ".2f") + self.assertTStringEqual( + t, ("Value: value=", ""), [(value, "value", None, ".2f")] + ) self.assertEqual(f(t), "Value: value=42.00") # Test debug specifier with conversion t = t"Value: {value=!s}" - self.assertEqual(t.strings, ("Value: value=", "")) - self.assertEqual(t.interpolations[0].value, value) - self.assertEqual(t.interpolations[0].expression, "value") - self.assertEqual(t.interpolations[0].conversion, "s") - self.assertEqual(t.interpolations[0].format_spec, "") + self.assertTStringEqual( + t, ("Value: value=", ""), [(value, "value", "s")] + ) # Test white space in debug specifier t = t"Value: {value = }" - self.assertEqual(t.strings, ("Value: value = ", "")) - self.assertEqual(t.interpolations[0].value, value) - self.assertEqual(t.interpolations[0].expression, "value") - self.assertEqual(t.interpolations[0].conversion, "r") - self.assertEqual(t.interpolations[0].format_spec, "") + self.assertTStringEqual( + t, ("Value: value = ", ""), [(value, "value", "r")] + ) self.assertEqual(f(t), "Value: value = 42") def test_raw_tstrings(self): path = r"C:\Users" t = rt"{path}\Documents" - self.assertEqual(t.strings, ("", r"\Documents",)) - self.assertEqual(t.interpolations[0].value, path) - self.assertEqual(t.interpolations[0].expression, "path") + self.assertTStringEqual(t, ("", r"\Documents"), [(path, "path")]) self.assertEqual(f(t), r"C:\Users\Documents") # Test alternative prefix t = tr"{path}\Documents" - self.assertEqual(t.strings, ("", r"\Documents",)) - self.assertEqual(t.interpolations[0].value, path) + self.assertTStringEqual(t, ("", r"\Documents"), [(path, "path")]) def test_template_concatenation(self): @@ -213,15 +185,13 @@ def test_template_concatenation(self): t1 = t"Hello, " t2 = t"world" combined = t1 + t2 - self.assertEqual(combined.strings, ("Hello, world",)) - self.assertEqual(len(combined.interpolations), 0) + self.assertTStringEqual(combined, ("Hello, world",), ()) self.assertEqual(f(combined), "Hello, world") # Test template + string t1 = t"Hello" combined = t1 + ", world" - self.assertEqual(combined.strings, ("Hello, world",)) - self.assertEqual(len(combined.interpolations), 0) + self.assertTStringEqual(combined, ("Hello, world",), ()) self.assertEqual(f(combined), "Hello, world") # Test template + template with interpolation @@ -229,20 +199,12 @@ def test_template_concatenation(self): t1 = t"Hello, " t2 = t"{name}" combined = t1 + t2 - self.assertEqual(combined.strings, ("Hello, ", "")) - self.assertEqual(combined.interpolations[0].value, name) - self.assertEqual(combined.interpolations[0].expression, "name") - self.assertEqual(combined.interpolations[0].conversion, None) - self.assertEqual(combined.interpolations[0].format_spec, "") + self.assertTStringEqual(combined, ("Hello, ", ""), [(name, "name")]) self.assertEqual(f(combined), "Hello, Python") # Test string + template t = "Hello, " + t"{name}" - self.assertEqual(t.strings, ("Hello, ", "")) - self.assertEqual(t.interpolations[0].value, name) - self.assertEqual(t.interpolations[0].expression, "name") - self.assertEqual(t.interpolations[0].conversion, None) - self.assertEqual(t.interpolations[0].format_spec, "") + self.assertTStringEqual(t, ("Hello, ", ""), [(name, "name")]) self.assertEqual(f(t), "Hello, Python") def test_nested_templates(self): @@ -251,15 +213,16 @@ def test_nested_templates(self): inner = t"{name}" t = t"Language: {inner}" + t_interp = t.interpolations[0] self.assertEqual(t.strings, ("Language: ", "")) - self.assertEqual(t.interpolations[0].value.strings, ("", "")) - self.assertEqual(t.interpolations[0].value.interpolations[0].value, name) - self.assertEqual(t.interpolations[0].value.interpolations[0].expression, "name") - self.assertEqual(t.interpolations[0].value.interpolations[0].conversion, None) - self.assertEqual(t.interpolations[0].value.interpolations[0].format_spec, "") - self.assertEqual(t.interpolations[0].expression, "inner") - self.assertEqual(t.interpolations[0].conversion, None) - self.assertEqual(t.interpolations[0].format_spec, "") + 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_ast_structure(self): # Test AST structure for simple t-string @@ -309,68 +272,51 @@ def test_runtime_errors(self): def test_literal_concatenation(self): # Test concatenation of t-string literals t = t"Hello, " t"world" - self.assertEqual(t.strings, ("Hello, world",)) - self.assertEqual(len(t.interpolations), 0) + self.assertTStringEqual(t, ("Hello, world",), ()) self.assertEqual(f(t), "Hello, world") # Test concatenation with interpolation name = "Python" t = t"Hello, " t"{name}" - self.assertEqual(t.strings, ("Hello, ", "")) - self.assertEqual(t.interpolations[0].value, name) - self.assertEqual(t.interpolations[0].expression, "name") - self.assertEqual(t.interpolations[0].conversion, None) - self.assertEqual(t.interpolations[0].format_spec, "") + self.assertTStringEqual(t, ("Hello, ", ""), [(name, "name")]) self.assertEqual(f(t), "Hello, Python") # Test concatenation with string literal name = "Python" t = t"Hello, {name}" "and welcome!" - self.assertEqual(t.strings, ("Hello, ", "and welcome!")) - self.assertEqual(t.interpolations[0].value, name) - self.assertEqual(t.interpolations[0].expression, "name") - self.assertEqual(t.interpolations[0].conversion, None) - self.assertEqual(t.interpolations[0].format_spec, "") + self.assertTStringEqual( + t, ("Hello, ", "and welcome!"), [(name, "name")] + ) self.assertEqual(f(t), "Hello, Pythonand welcome!") # Test concatenation with Unicode literal name = "Python" t = t"Hello, {name}" u"and welcome!" - self.assertEqual(t.strings, ("Hello, ", "and welcome!")) - self.assertEqual(t.interpolations[0].value, name) - self.assertEqual(t.interpolations[0].expression, "name") - self.assertEqual(t.interpolations[0].conversion, None) - self.assertEqual(t.interpolations[0].format_spec, "") + self.assertTStringEqual( + t, ("Hello, ", "and welcome!"), [(name, "name")] + ) self.assertEqual(f(t), "Hello, Pythonand welcome!") # Test concatenation with f-string literal tab = '\t' t = t"Tab: {tab}. " f"f-tab: {tab}." - self.assertEqual(t.strings, ("Tab: ", ". f-tab: \t.")) - self.assertEqual(t.interpolations[0].value, tab) - self.assertEqual(t.interpolations[0].expression, "tab") - self.assertEqual(t.interpolations[0].conversion, None) - self.assertEqual(t.interpolations[0].format_spec, "") + self.assertTStringEqual(t, ("Tab: ", ". f-tab: \t."), [(tab, "tab")]) self.assertEqual(f(t), "Tab: \t. f-tab: \t.") # Test concatenation with raw string literal tab = '\t' t = t"Tab: {tab}. " r"Raw tab: \t." - self.assertEqual(t.strings, ("Tab: ", r". Raw tab: \t.")) - self.assertEqual(t.interpolations[0].value, tab) - self.assertEqual(t.interpolations[0].expression, "tab") - self.assertEqual(t.interpolations[0].conversion, None) - self.assertEqual(t.interpolations[0].format_spec, "") + self.assertTStringEqual( + t, ("Tab: ", r". Raw tab: \t."), [(tab, "tab")] + ) self.assertEqual(f(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.assertEqual(t.strings, ("Tab: ", ". f-tab: \t. Raw tab: \\t.")) - self.assertEqual(t.interpolations[0].value, tab) - self.assertEqual(t.interpolations[0].expression, "tab") - self.assertEqual(t.interpolations[0].conversion, None) - self.assertEqual(t.interpolations[0].format_spec, "") + self.assertTStringEqual( + t, ("Tab: ", ". f-tab: \t. Raw tab: \\t."), [(tab, "tab")] + ) self.assertEqual(f(t), "Tab: \t. f-tab: \t. Raw tab: \\t.") what = 't' @@ -388,8 +334,9 @@ def test_triple_quoted(self): Hello, world """ - self.assertEqual(t.strings, ("\n Hello,\n world\n ",)) - self.assertEqual(len(t.interpolations), 0) + self.assertTStringEqual( + t, ("\n Hello,\n world\n ",), () + ) self.assertEqual(f(t), "\n Hello,\n world\n ") # Test triple-quoted with interpolation @@ -398,11 +345,9 @@ def test_triple_quoted(self): Hello, {name} """ - self.assertEqual(t.strings, ("\n Hello,\n ", "\n ")) - self.assertEqual(t.interpolations[0].value, name) - self.assertEqual(t.interpolations[0].expression, "name") - self.assertEqual(t.interpolations[0].conversion, None) - self.assertEqual(t.interpolations[0].format_spec, "") + self.assertTStringEqual( + t, ("\n Hello,\n ", "\n "), [(name, "name")] + ) self.assertEqual(f(t), "\n Hello,\n Python\n ") if __name__ == '__main__': From 856b499d60b158e99bae8c9cfc22452836f08472 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Sat, 19 Apr 2025 17:24:52 +0100 Subject: [PATCH 2/6] Add TStringTestCase to _support --- Lib/test/test_string/_support.py | 25 ++++++++ Lib/test/test_string/test_templatelib.py | 81 +++++++++--------------- Lib/test/test_tstring.py | 26 +------- 3 files changed, 56 insertions(+), 76 deletions(-) diff --git a/Lib/test/test_string/_support.py b/Lib/test/test_string/_support.py index fde89ee1b62ae4..1639685fb11d28 100644 --- a/Lib/test/test_string/_support.py +++ b/Lib/test/test_string/_support.py @@ -1,6 +1,31 @@ +import unittest from string.templatelib import Interpolation +class TStringTestCase(unittest.TestCase): + def assertTStringEqual(self, t, strings, interpolations): + """Test template string literal equality. + + The *strings* argument must be a tuple of strings equal to *t.strings*. + + The *interpolations* argument must be a sequence of tuples which are + compared against *t.interpolations*. Each tuple consists of + (value, expression, conversion, format_spec), though the final two + items may be omitted, and are assumed to be None and '' respectively. + """ + self.assertEqual(t.strings, strings) + self.assertEqual(len(t.interpolations), len(interpolations)) + + for actual, expected in zip(t.interpolations, interpolations, + strict=True): + if len(expected) == 2: + self.assertEqual(tuple(actual), expected + (None, '')) + elif len(expected) == 3: + self.assertEqual(tuple(actual), expected + ('',)) + else: + self.assertEqual(tuple(actual), expected) + + def convert(value, conversion): if conversion == "a": return ascii(value) diff --git a/Lib/test/test_string/test_templatelib.py b/Lib/test/test_string/test_templatelib.py index 677b4c29331b1d..415d2847bbf438 100644 --- a/Lib/test/test_string/test_templatelib.py +++ b/Lib/test/test_string/test_templatelib.py @@ -1,78 +1,55 @@ -import unittest from string.templatelib import Template, Interpolation -from test.test_string._support import f +from test.test_string._support import TStringTestCase, f -class TestTemplate(unittest.TestCase): +class TestTemplate(TStringTestCase): def test_basic_creation(self): # Simple t-string creation - t = t"Hello, world" + t = t'Hello, world' self.assertTrue(isinstance(t, Template)) - self.assertEqual(t.strings, ("Hello, world",)) - self.assertEqual(len(t.interpolations), 0) - self.assertEqual(f(t), "Hello, world") + self.assertTStringEqual(t, ('Hello, world',), ()) + self.assertEqual(f(t), 'Hello, world') # Empty t-string - t = t"" - self.assertEqual(t.strings, ("",)) - self.assertEqual(len(t.interpolations), 0) - self.assertEqual(f(t), "") + t = t'' + self.assertTStringEqual(t, ('',), ()) + self.assertEqual(f(t), '') # Multi-line t-string t = t"""Hello, world""" - self.assertEqual(t.strings, ("Hello,\nworld",)) + self.assertEqual(t.strings, ('Hello,\nworld',)) self.assertEqual(len(t.interpolations), 0) - self.assertEqual(f(t), "Hello,\nworld") + self.assertEqual(f(t), 'Hello,\nworld') def test_creation_interleaving(self): # Should add strings on either side - t = Template(Interpolation("Maria", "name", None, "")) - self.assertEqual(t.strings, ("", "")) - self.assertEqual(t.interpolations[0].value, "Maria") - self.assertEqual(t.interpolations[0].expression, "name") - self.assertEqual(t.interpolations[0].conversion, None) - self.assertEqual(t.interpolations[0].format_spec, "") - self.assertEqual(f(t), "Maria") + t = Template(Interpolation('Maria', 'name', None, '')) + self.assertTStringEqual(t, ('', ''), [('Maria', 'name')]) + self.assertEqual(f(t), 'Maria') # Should prepend empty string - t = Template(Interpolation("Maria", "name", None, ""), " is my name") - self.assertEqual(t.strings, ("", " is my name")) - self.assertEqual(t.interpolations[0].value, "Maria") - self.assertEqual(t.interpolations[0].expression, "name") - self.assertEqual(t.interpolations[0].conversion, None) - self.assertEqual(t.interpolations[0].format_spec, "") - self.assertEqual(f(t), "Maria is my name") + t = Template(Interpolation('Maria', 'name', None, ''), ' is my name') + self.assertTStringEqual(t, ('', ' is my name'), [('Maria', 'name')]) + self.assertEqual(f(t), 'Maria is my name') # Should append empty string - t = Template("Hello, ", Interpolation("Maria", "name", None, "")) - self.assertEqual(t.strings, ("Hello, ", "")) - self.assertEqual(t.interpolations[0].value, "Maria") - self.assertEqual(t.interpolations[0].expression, "name") - self.assertEqual(t.interpolations[0].conversion, None) - self.assertEqual(t.interpolations[0].format_spec, "") - self.assertEqual(f(t), "Hello, Maria") + t = Template('Hello, ', Interpolation('Maria', 'name', None, '')) + self.assertTStringEqual(t, ('Hello, ', ''), [('Maria', 'name')]) + self.assertEqual(f(t), 'Hello, Maria') # Should concatenate strings - t = Template("Hello", ", ", Interpolation("Maria", "name", None, ""), "!") - self.assertEqual(t.strings, ("Hello, ", "!")) - self.assertEqual(t.interpolations[0].value, "Maria") - self.assertEqual(t.interpolations[0].expression, "name") - self.assertEqual(t.interpolations[0].conversion, None) - self.assertEqual(t.interpolations[0].format_spec, "") - self.assertEqual(f(t), "Hello, Maria!") + t = Template('Hello', ', ', Interpolation('Maria', 'name', None, ''), + '!') + self.assertTStringEqual(t, ('Hello, ', '!'), [('Maria', 'name')]) + self.assertEqual(f(t), 'Hello, Maria!') # Should add strings on either side and in between - t = Template(Interpolation("Maria", "name", None, ""), Interpolation("Python", "language", None, "")) - self.assertEqual(t.strings, ("", "", "")) - self.assertEqual(t.interpolations[0].value, "Maria") - self.assertEqual(t.interpolations[0].expression, "name") - self.assertEqual(t.interpolations[0].conversion, None) - self.assertEqual(t.interpolations[0].format_spec, "") - self.assertEqual(t.interpolations[1].value, "Python") - self.assertEqual(t.interpolations[1].expression, "language") - self.assertEqual(t.interpolations[1].conversion, None) - self.assertEqual(t.interpolations[1].format_spec, "") - self.assertEqual(f(t), "MariaPython") + t = Template(Interpolation('Maria', 'name', None, ''), + Interpolation('Python', 'language', None, '')) + self.assertTStringEqual( + t, ('', '', ''), [('Maria', 'name'), ('Python', 'language')] + ) + self.assertEqual(f(t), 'MariaPython') diff --git a/Lib/test/test_tstring.py b/Lib/test/test_tstring.py index e55d56e21e4087..e4d90cc042ea03 100644 --- a/Lib/test/test_tstring.py +++ b/Lib/test/test_tstring.py @@ -1,38 +1,16 @@ import ast import unittest -from test.test_string._support import f +from test.test_string._support import TStringTestCase, f -class TestTString(unittest.TestCase): +class TestTString(TStringTestCase): def assertAllRaise(self, exception_type, regex, error_strings): for s in error_strings: with self.subTest(s=s): with self.assertRaisesRegex(exception_type, regex): eval(s) - def assertTStringEqual(self, t, strings, interpolations): - """Test template string literal equality. - - The *strings* argument must be a tuple of strings equal to *t.strings*. - - The *interpolations* argument must be a sequence of tuples which are - compared against *t.interpolations*. Each tuple consists of - (value, expression, conversion, format_spec), though the final two - items may be omitted, and are assumed to be None and '' respectively. - """ - self.assertEqual(t.strings, strings) - self.assertEqual(len(t.interpolations), len(interpolations)) - - for actual, expected in zip(t.interpolations, interpolations, - strict=True): - if len(expected) == 2: - self.assertEqual(tuple(actual), expected + (None, '')) - elif len(expected) == 3: - self.assertEqual(tuple(actual), expected + ('',)) - else: - self.assertEqual(tuple(actual), expected) - def test_string_representation(self): # Test __repr__ t = t"Hello" From 6fd298259c6e72e9f9f12436ffac364cfcd79fee Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Sat, 19 Apr 2025 17:31:35 +0100 Subject: [PATCH 3/6] assertIsInstance --- Lib/test/test_string/test_templatelib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_string/test_templatelib.py b/Lib/test/test_string/test_templatelib.py index 415d2847bbf438..d00560c7e91356 100644 --- a/Lib/test/test_string/test_templatelib.py +++ b/Lib/test/test_string/test_templatelib.py @@ -8,7 +8,7 @@ class TestTemplate(TStringTestCase): def test_basic_creation(self): # Simple t-string creation t = t'Hello, world' - self.assertTrue(isinstance(t, Template)) + self.assertIsInstance(t, Template) self.assertTStringEqual(t, ('Hello, world',), ()) self.assertEqual(f(t), 'Hello, world') From 2f19077859cd6587f8c6f1633739532fbc0121c9 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Sat, 19 Apr 2025 19:28:44 +0100 Subject: [PATCH 4/6] Fix comparison --- Lib/test/test_string/_support.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_string/_support.py b/Lib/test/test_string/_support.py index 1639685fb11d28..05b207993dec2e 100644 --- a/Lib/test/test_string/_support.py +++ b/Lib/test/test_string/_support.py @@ -16,14 +16,14 @@ def assertTStringEqual(self, t, strings, interpolations): self.assertEqual(t.strings, strings) self.assertEqual(len(t.interpolations), len(interpolations)) - for actual, expected in zip(t.interpolations, interpolations, - strict=True): + for i, expected in zip(t.interpolations, interpolations, strict=True): + actual = (i.value, i.expression, i.conversion, i.format_spec) if len(expected) == 2: - self.assertEqual(tuple(actual), expected + (None, '')) + self.assertEqual(actual, expected + (None, '')) elif len(expected) == 3: - self.assertEqual(tuple(actual), expected + ('',)) + self.assertEqual(actual, expected + ('',)) else: - self.assertEqual(tuple(actual), expected) + self.assertEqual(actual, expected) def convert(value, conversion): From 682399ed6aa8c4a055eaa4f69678dd57d6e95cdc Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Sat, 19 Apr 2025 19:33:30 +0100 Subject: [PATCH 5/6] Reorganise --- Lib/test/test_string/_support.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_string/_support.py b/Lib/test/test_string/_support.py index 05b207993dec2e..764f5f22808d68 100644 --- a/Lib/test/test_string/_support.py +++ b/Lib/test/test_string/_support.py @@ -16,14 +16,20 @@ def assertTStringEqual(self, t, strings, interpolations): self.assertEqual(t.strings, strings) self.assertEqual(len(t.interpolations), len(interpolations)) - for i, expected in zip(t.interpolations, interpolations, strict=True): - actual = (i.value, i.expression, i.conversion, i.format_spec) - if len(expected) == 2: - self.assertEqual(actual, expected + (None, '')) - elif len(expected) == 3: - self.assertEqual(actual, expected + ('',)) - else: - self.assertEqual(actual, expected) + for i, exp in zip(t.interpolations, interpolations, strict=True): + if len(exp) == 4: + actual = (i.value, i.expression, i.conversion, i.format_spec) + self.assertEqual(actual, exp) + continue + + if len(exp) == 3: + self.assertEqual((i.value, i.expression, i.conversion), exp) + self.assertEqual(i.format_spec, '') + continue + + self.assertEqual((i.value, i.expression), exp) + self.assertEqual(i.format_spec, '') + self.assertEqual(i.conversion, None) def convert(value, conversion): From c4857adfa14f7666488d80dc72f45c592a608f37 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Sat, 19 Apr 2025 19:33:56 +0100 Subject: [PATCH 6/6] assertIsNone --- Lib/test/test_string/_support.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_string/_support.py b/Lib/test/test_string/_support.py index 764f5f22808d68..c08392d0aa7945 100644 --- a/Lib/test/test_string/_support.py +++ b/Lib/test/test_string/_support.py @@ -29,7 +29,7 @@ def assertTStringEqual(self, t, strings, interpolations): self.assertEqual((i.value, i.expression), exp) self.assertEqual(i.format_spec, '') - self.assertEqual(i.conversion, None) + self.assertIsNone(i.conversion) def convert(value, conversion):