[Python-checkins] gh-104683: Improve consistency and test coverage of argument-clinic `__repr__` functions (#107667)

AlexWaygood webhook-mailer at python.org
Sat Aug 5 16:58:42 EDT 2023


https://github.com/python/cpython/commit/6996b406bcf9f6d85a59e539c743ef9126c3cc5d
commit: 6996b406bcf9f6d85a59e539c743ef9126c3cc5d
branch: main
author: Alex Waygood <Alex.Waygood at Gmail.com>
committer: AlexWaygood <Alex.Waygood at Gmail.com>
date: 2023-08-05T21:58:38+01:00
summary:

gh-104683: Improve consistency and test coverage of argument-clinic `__repr__` functions (#107667)

files:
M Lib/test/test_clinic.py
M Tools/clinic/clinic.py
M Tools/clinic/cpp.py

diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py
index 84b6a193ecf91..f30fad2126940 100644
--- a/Lib/test/test_clinic.py
+++ b/Lib/test/test_clinic.py
@@ -69,7 +69,7 @@ def __init__(self):
         self.converters = FakeConvertersDict()
         self.legacy_converters = FakeConvertersDict()
         self.language = clinic.CLanguage(None)
-        self.filename = None
+        self.filename = "clinic_tests"
         self.destination_buffers = {}
         self.block_parser = clinic.BlockParser('', self.language)
         self.modules = collections.OrderedDict()
@@ -1849,10 +1849,10 @@ def test_non_ascii_character_in_docstring(self):
             self.parse(block)
         # The line numbers are off; this is a known limitation.
         expected = dedent("""\
-            Warning on line 0:
+            Warning in file 'clinic_tests' on line 0:
             Non-ascii characters are not allowed in docstrings: 'á'
 
-            Warning on line 0:
+            Warning in file 'clinic_tests' on line 0:
             Non-ascii characters are not allowed in docstrings: 'ü', 'á', 'ß'
 
         """)
@@ -3030,5 +3030,93 @@ def test_suffix_all_lines(self):
         self.assertEqual(out, expected)
 
 
+class ClinicReprTests(unittest.TestCase):
+    def test_Block_repr(self):
+        block = clinic.Block("foo")
+        expected_repr = "<clinic.Block 'text' input='foo' output=None>"
+        self.assertEqual(repr(block), expected_repr)
+
+        block2 = clinic.Block("bar", "baz", [], "eggs", "spam")
+        expected_repr_2 = "<clinic.Block 'baz' input='bar' output='eggs'>"
+        self.assertEqual(repr(block2), expected_repr_2)
+
+        block3 = clinic.Block(
+            input="longboi_" * 100,
+            dsl_name="wow_so_long",
+            signatures=[],
+            output="very_long_" * 100,
+            indent=""
+        )
+        expected_repr_3 = (
+            "<clinic.Block 'wow_so_long' input='longboi_longboi_longboi_l...' output='very_long_very_long_very_...'>"
+        )
+        self.assertEqual(repr(block3), expected_repr_3)
+
+    def test_Destination_repr(self):
+        destination = clinic.Destination(
+            "foo", type="file", clinic=FakeClinic(), args=("eggs",)
+        )
+        self.assertEqual(
+            repr(destination), "<clinic.Destination 'foo' type='file' file='eggs'>"
+        )
+
+        destination2 = clinic.Destination("bar", type="buffer", clinic=FakeClinic())
+        self.assertEqual(repr(destination2), "<clinic.Destination 'bar' type='buffer'>")
+
+    def test_Module_repr(self):
+        module = clinic.Module("foo", FakeClinic())
+        self.assertRegex(repr(module), r"<clinic.Module 'foo' at \d+>")
+
+    def test_Class_repr(self):
+        cls = clinic.Class("foo", FakeClinic(), None, 'some_typedef', 'some_type_object')
+        self.assertRegex(repr(cls), r"<clinic.Class 'foo' at \d+>")
+
+    def test_FunctionKind_repr(self):
+        self.assertEqual(
+            repr(clinic.FunctionKind.INVALID), "<clinic.FunctionKind.INVALID>"
+        )
+        self.assertEqual(
+            repr(clinic.FunctionKind.CLASS_METHOD), "<clinic.FunctionKind.CLASS_METHOD>"
+        )
+
+    def test_Function_and_Parameter_reprs(self):
+        function = clinic.Function(
+            name='foo',
+            module=FakeClinic(),
+            cls=None,
+            c_basename=None,
+            full_name='foofoo',
+            return_converter=clinic.init_return_converter(),
+            kind=clinic.FunctionKind.METHOD_INIT,
+            coexist=False
+        )
+        self.assertEqual(repr(function), "<clinic.Function 'foo'>")
+
+        converter = clinic.self_converter('bar', 'bar', function)
+        parameter = clinic.Parameter(
+            "bar",
+            kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
+            function=function,
+            converter=converter
+        )
+        self.assertEqual(repr(parameter), "<clinic.Parameter 'bar'>")
+
+    def test_Monitor_repr(self):
+        monitor = clinic.cpp.Monitor()
+        self.assertRegex(repr(monitor), r"<clinic.Monitor \d+ line=0 condition=''>")
+
+        monitor.line_number = 42
+        monitor.stack.append(("token1", "condition1"))
+        self.assertRegex(
+            repr(monitor), r"<clinic.Monitor \d+ line=42 condition='condition1'>"
+        )
+
+        monitor.stack.append(("token2", "condition2"))
+        self.assertRegex(
+            repr(monitor),
+            r"<clinic.Monitor \d+ line=42 condition='condition1 && condition2'>"
+        )
+
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py
index 7fbae1e0d870a..423cdfb939c16 100755
--- a/Tools/clinic/clinic.py
+++ b/Tools/clinic/clinic.py
@@ -1728,8 +1728,12 @@ def summarize(s: object) -> str:
             if len(s) > 30:
                 return s[:26] + "..." + s[0]
             return s
-        return "".join((
-            "<Block ", dsl_name, " input=", summarize(self.input), " output=", summarize(self.output), ">"))
+        parts = (
+            repr(dsl_name),
+            f"input={summarize(self.input)}",
+            f"output={summarize(self.output)}"
+        )
+        return f"<clinic.Block {' '.join(parts)}>"
 
 
 class BlockParser:
@@ -2037,10 +2041,10 @@ def __post_init__(self, args: tuple[str, ...]) -> None:
 
     def __repr__(self) -> str:
         if self.type == 'file':
-            file_repr = " " + repr(self.filename)
+            type_repr = f"type='file' file={self.filename!r}"
         else:
-            file_repr = ''
-        return "".join(("<Destination ", self.name, " ", self.type, file_repr, ">"))
+            type_repr = f"type={self.type!r}"
+        return f"<clinic.Destination {self.name!r} {type_repr}>"
 
     def clear(self) -> None:
         if self.type != 'buffer':
@@ -2500,7 +2504,7 @@ def new_or_init(self) -> bool:
         return self in {FunctionKind.METHOD_INIT, FunctionKind.METHOD_NEW}
 
     def __repr__(self) -> str:
-        return f"<FunctionKind.{self.name}>"
+        return f"<clinic.FunctionKind.{self.name}>"
 
 
 INVALID: Final = FunctionKind.INVALID
@@ -2577,7 +2581,7 @@ def methoddef_flags(self) -> str | None:
         return '|'.join(flags)
 
     def __repr__(self) -> str:
-        return '<clinic.Function ' + self.name + '>'
+        return f'<clinic.Function {self.name!r}>'
 
     def copy(self, **overrides: Any) -> Function:
         f = dc.replace(self, **overrides)
@@ -2605,7 +2609,7 @@ class Parameter:
     right_bracket_count: int = dc.field(init=False, default=0)
 
     def __repr__(self) -> str:
-        return '<clinic.Parameter ' + self.name + '>'
+        return f'<clinic.Parameter {self.name!r}>'
 
     def is_keyword_only(self) -> bool:
         return self.kind == inspect.Parameter.KEYWORD_ONLY
diff --git a/Tools/clinic/cpp.py b/Tools/clinic/cpp.py
index 21a1b02e862c1..876105120c97f 100644
--- a/Tools/clinic/cpp.py
+++ b/Tools/clinic/cpp.py
@@ -43,9 +43,12 @@ def __post_init__(self) -> None:
         self.line_number = 0
 
     def __repr__(self) -> str:
-        return (
-            f"<Monitor {id(self)} line={self.line_number} condition={self.condition()!r}>"
+        parts = (
+            str(id(self)),
+            f"line={self.line_number}",
+            f"condition={self.condition()!r}"
         )
+        return f"<clinic.Monitor {' '.join(parts)}>"
 
     def status(self) -> str:
         return str(self.line_number).rjust(4) + ": " + self.condition()



More information about the Python-checkins mailing list