Skip to content

Commit 6996b40

Browse files
authored
gh-104683: Improve consistency and test coverage of argument-clinic __repr__ functions (#107667)
1 parent 4a5b422 commit 6996b40

File tree

3 files changed

+108
-13
lines changed

3 files changed

+108
-13
lines changed

Diff for: Lib/test/test_clinic.py

+91-3
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def __init__(self):
6969
self.converters = FakeConvertersDict()
7070
self.legacy_converters = FakeConvertersDict()
7171
self.language = clinic.CLanguage(None)
72-
self.filename = None
72+
self.filename = "clinic_tests"
7373
self.destination_buffers = {}
7474
self.block_parser = clinic.BlockParser('', self.language)
7575
self.modules = collections.OrderedDict()
@@ -1849,10 +1849,10 @@ def test_non_ascii_character_in_docstring(self):
18491849
self.parse(block)
18501850
# The line numbers are off; this is a known limitation.
18511851
expected = dedent("""\
1852-
Warning on line 0:
1852+
Warning in file 'clinic_tests' on line 0:
18531853
Non-ascii characters are not allowed in docstrings: 'á'
18541854
1855-
Warning on line 0:
1855+
Warning in file 'clinic_tests' on line 0:
18561856
Non-ascii characters are not allowed in docstrings: 'ü', 'á', 'ß'
18571857
18581858
""")
@@ -3030,5 +3030,93 @@ def test_suffix_all_lines(self):
30303030
self.assertEqual(out, expected)
30313031

30323032

3033+
class ClinicReprTests(unittest.TestCase):
3034+
def test_Block_repr(self):
3035+
block = clinic.Block("foo")
3036+
expected_repr = "<clinic.Block 'text' input='foo' output=None>"
3037+
self.assertEqual(repr(block), expected_repr)
3038+
3039+
block2 = clinic.Block("bar", "baz", [], "eggs", "spam")
3040+
expected_repr_2 = "<clinic.Block 'baz' input='bar' output='eggs'>"
3041+
self.assertEqual(repr(block2), expected_repr_2)
3042+
3043+
block3 = clinic.Block(
3044+
input="longboi_" * 100,
3045+
dsl_name="wow_so_long",
3046+
signatures=[],
3047+
output="very_long_" * 100,
3048+
indent=""
3049+
)
3050+
expected_repr_3 = (
3051+
"<clinic.Block 'wow_so_long' input='longboi_longboi_longboi_l...' output='very_long_very_long_very_...'>"
3052+
)
3053+
self.assertEqual(repr(block3), expected_repr_3)
3054+
3055+
def test_Destination_repr(self):
3056+
destination = clinic.Destination(
3057+
"foo", type="file", clinic=FakeClinic(), args=("eggs",)
3058+
)
3059+
self.assertEqual(
3060+
repr(destination), "<clinic.Destination 'foo' type='file' file='eggs'>"
3061+
)
3062+
3063+
destination2 = clinic.Destination("bar", type="buffer", clinic=FakeClinic())
3064+
self.assertEqual(repr(destination2), "<clinic.Destination 'bar' type='buffer'>")
3065+
3066+
def test_Module_repr(self):
3067+
module = clinic.Module("foo", FakeClinic())
3068+
self.assertRegex(repr(module), r"<clinic.Module 'foo' at \d+>")
3069+
3070+
def test_Class_repr(self):
3071+
cls = clinic.Class("foo", FakeClinic(), None, 'some_typedef', 'some_type_object')
3072+
self.assertRegex(repr(cls), r"<clinic.Class 'foo' at \d+>")
3073+
3074+
def test_FunctionKind_repr(self):
3075+
self.assertEqual(
3076+
repr(clinic.FunctionKind.INVALID), "<clinic.FunctionKind.INVALID>"
3077+
)
3078+
self.assertEqual(
3079+
repr(clinic.FunctionKind.CLASS_METHOD), "<clinic.FunctionKind.CLASS_METHOD>"
3080+
)
3081+
3082+
def test_Function_and_Parameter_reprs(self):
3083+
function = clinic.Function(
3084+
name='foo',
3085+
module=FakeClinic(),
3086+
cls=None,
3087+
c_basename=None,
3088+
full_name='foofoo',
3089+
return_converter=clinic.init_return_converter(),
3090+
kind=clinic.FunctionKind.METHOD_INIT,
3091+
coexist=False
3092+
)
3093+
self.assertEqual(repr(function), "<clinic.Function 'foo'>")
3094+
3095+
converter = clinic.self_converter('bar', 'bar', function)
3096+
parameter = clinic.Parameter(
3097+
"bar",
3098+
kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
3099+
function=function,
3100+
converter=converter
3101+
)
3102+
self.assertEqual(repr(parameter), "<clinic.Parameter 'bar'>")
3103+
3104+
def test_Monitor_repr(self):
3105+
monitor = clinic.cpp.Monitor()
3106+
self.assertRegex(repr(monitor), r"<clinic.Monitor \d+ line=0 condition=''>")
3107+
3108+
monitor.line_number = 42
3109+
monitor.stack.append(("token1", "condition1"))
3110+
self.assertRegex(
3111+
repr(monitor), r"<clinic.Monitor \d+ line=42 condition='condition1'>"
3112+
)
3113+
3114+
monitor.stack.append(("token2", "condition2"))
3115+
self.assertRegex(
3116+
repr(monitor),
3117+
r"<clinic.Monitor \d+ line=42 condition='condition1 && condition2'>"
3118+
)
3119+
3120+
30333121
if __name__ == "__main__":
30343122
unittest.main()

Diff for: Tools/clinic/clinic.py

+12-8
Original file line numberDiff line numberDiff line change
@@ -1728,8 +1728,12 @@ def summarize(s: object) -> str:
17281728
if len(s) > 30:
17291729
return s[:26] + "..." + s[0]
17301730
return s
1731-
return "".join((
1732-
"<Block ", dsl_name, " input=", summarize(self.input), " output=", summarize(self.output), ">"))
1731+
parts = (
1732+
repr(dsl_name),
1733+
f"input={summarize(self.input)}",
1734+
f"output={summarize(self.output)}"
1735+
)
1736+
return f"<clinic.Block {' '.join(parts)}>"
17331737

17341738

17351739
class BlockParser:
@@ -2037,10 +2041,10 @@ def __post_init__(self, args: tuple[str, ...]) -> None:
20372041

20382042
def __repr__(self) -> str:
20392043
if self.type == 'file':
2040-
file_repr = " " + repr(self.filename)
2044+
type_repr = f"type='file' file={self.filename!r}"
20412045
else:
2042-
file_repr = ''
2043-
return "".join(("<Destination ", self.name, " ", self.type, file_repr, ">"))
2046+
type_repr = f"type={self.type!r}"
2047+
return f"<clinic.Destination {self.name!r} {type_repr}>"
20442048

20452049
def clear(self) -> None:
20462050
if self.type != 'buffer':
@@ -2500,7 +2504,7 @@ def new_or_init(self) -> bool:
25002504
return self in {FunctionKind.METHOD_INIT, FunctionKind.METHOD_NEW}
25012505

25022506
def __repr__(self) -> str:
2503-
return f"<FunctionKind.{self.name}>"
2507+
return f"<clinic.FunctionKind.{self.name}>"
25042508

25052509

25062510
INVALID: Final = FunctionKind.INVALID
@@ -2577,7 +2581,7 @@ def methoddef_flags(self) -> str | None:
25772581
return '|'.join(flags)
25782582

25792583
def __repr__(self) -> str:
2580-
return '<clinic.Function ' + self.name + '>'
2584+
return f'<clinic.Function {self.name!r}>'
25812585

25822586
def copy(self, **overrides: Any) -> Function:
25832587
f = dc.replace(self, **overrides)
@@ -2605,7 +2609,7 @@ class Parameter:
26052609
right_bracket_count: int = dc.field(init=False, default=0)
26062610

26072611
def __repr__(self) -> str:
2608-
return '<clinic.Parameter ' + self.name + '>'
2612+
return f'<clinic.Parameter {self.name!r}>'
26092613

26102614
def is_keyword_only(self) -> bool:
26112615
return self.kind == inspect.Parameter.KEYWORD_ONLY

Diff for: Tools/clinic/cpp.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,12 @@ def __post_init__(self) -> None:
4343
self.line_number = 0
4444

4545
def __repr__(self) -> str:
46-
return (
47-
f"<Monitor {id(self)} line={self.line_number} condition={self.condition()!r}>"
46+
parts = (
47+
str(id(self)),
48+
f"line={self.line_number}",
49+
f"condition={self.condition()!r}"
4850
)
51+
return f"<clinic.Monitor {' '.join(parts)}>"
4952

5053
def status(self) -> str:
5154
return str(self.line_number).rjust(4) + ": " + self.condition()

0 commit comments

Comments
 (0)