Skip to content

Commit

Permalink
gh-104683: Improve consistency and test coverage of argument-clinic `…
Browse files Browse the repository at this point in the history
…__repr__` functions (#107667)
  • Loading branch information
AlexWaygood committed Aug 5, 2023
1 parent 4a5b422 commit 6996b40
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 13 deletions.
94 changes: 91 additions & 3 deletions Lib/test/test_clinic.py
Expand Up @@ -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()
Expand Down Expand Up @@ -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: 'ü', 'á', 'ß'
""")
Expand Down Expand Up @@ -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()
20 changes: 12 additions & 8 deletions Tools/clinic/clinic.py
Expand Up @@ -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:
Expand Down Expand Up @@ -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':
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
7 changes: 5 additions & 2 deletions Tools/clinic/cpp.py
Expand Up @@ -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()
Expand Down

0 comments on commit 6996b40

Please sign in to comment.