Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 2 commits into from
Aug 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 91 additions & 3 deletions Lib/test/test_clinic.py
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Loading