qapi: Rewrite parsing of doc comment section symbols and tags

To recognize a line starting with a section symbol and or tag, we
first split it at the first space, then examine the part left of the
space.  We can just as well examine the unsplit line, so do that.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20230428105429.1687850-13-armbru@redhat.com>
Reviewed-by: Juan Quintela <quintela@redhat.com>
[Work around lack of walrus operator in Python 3.7 and older]
This commit is contained in:
Markus Armbruster 2023-04-28 12:54:24 +02:00
parent 9b2c6746d3
commit 3e32dca3f0
1 changed files with 27 additions and 28 deletions

View File

@ -560,12 +560,12 @@ class QAPIDoc:
self._switch_section(QAPIDoc.NullSection(self._parser)) self._switch_section(QAPIDoc.NullSection(self._parser))
@staticmethod @staticmethod
def _is_section_tag(name: str) -> bool: def _match_at_name_colon(string: str) -> re.Match:
return name in ('Returns:', 'Since:', return re.match(r'@([^:]*): *', string)
# those are often singular or plural
'Note:', 'Notes:', @staticmethod
'Example:', 'Examples:', def _match_section_tag(string: str) -> re.Match:
'TODO:') return re.match(r'(Returns|Since|Notes?|Examples?|TODO): *', string)
def _append_body_line(self, line: str) -> None: def _append_body_line(self, line: str) -> None:
""" """
@ -581,7 +581,6 @@ class QAPIDoc:
Else, append the line to the current section. Else, append the line to the current section.
""" """
name = line.split(' ', 1)[0]
# FIXME not nice: things like '# @foo:' and '# @foo: ' aren't # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
# recognized, and get silently treated as ordinary text # recognized, and get silently treated as ordinary text
if not self.symbol and not self.body.text and line.startswith('@'): if not self.symbol and not self.body.text and line.startswith('@'):
@ -595,12 +594,12 @@ class QAPIDoc:
self._parser, "name required after '@'") self._parser, "name required after '@'")
elif self.symbol: elif self.symbol:
# This is a definition documentation block # This is a definition documentation block
if name.startswith('@') and name.endswith(':'): if self._match_at_name_colon(line):
self._append_line = self._append_args_line self._append_line = self._append_args_line
self._append_args_line(line) self._append_args_line(line)
elif line == 'Features:': elif line == 'Features:':
self._append_line = self._append_features_line self._append_line = self._append_features_line
elif self._is_section_tag(name): elif self._match_section_tag(line):
self._append_line = self._append_various_line self._append_line = self._append_various_line
self._append_various_line(line) self._append_various_line(line)
else: else:
@ -621,16 +620,16 @@ class QAPIDoc:
Else, append the line to the current section. Else, append the line to the current section.
""" """
name = line.split(' ', 1)[0] match = self._match_at_name_colon(line)
if match:
if name.startswith('@') and name.endswith(':'):
# If line is "@arg: first line of description", find # If line is "@arg: first line of description", find
# the index of 'f', which is the indent we expect for any # the index of 'f', which is the indent we expect for any
# following lines. We then remove the leading "@arg:" # following lines. We then remove the leading "@arg:"
# from line and replace it with spaces so that 'f' has the # from line and replace it with spaces so that 'f' has the
# same index as it did in the original line and can be # same index as it did in the original line and can be
# handled the same way we will handle following lines. # handled the same way we will handle following lines.
indent = must_match(r'@\S*:\s*', line).end() name = match.group(1)
indent = match.end()
line = line[indent:] line = line[indent:]
if not line: if not line:
# Line was just the "@arg:" header # Line was just the "@arg:" header
@ -638,8 +637,8 @@ class QAPIDoc:
indent = -1 indent = -1
else: else:
line = ' ' * indent + line line = ' ' * indent + line
self._start_args_section(name[1:-1], indent) self._start_args_section(name, indent)
elif self._is_section_tag(name): elif self._match_section_tag(line):
self._append_line = self._append_various_line self._append_line = self._append_various_line
self._append_various_line(line) self._append_various_line(line)
return return
@ -656,16 +655,16 @@ class QAPIDoc:
self._append_freeform(line) self._append_freeform(line)
def _append_features_line(self, line: str) -> None: def _append_features_line(self, line: str) -> None:
name = line.split(' ', 1)[0] match = self._match_at_name_colon(line)
if match:
if name.startswith('@') and name.endswith(':'):
# If line is "@arg: first line of description", find # If line is "@arg: first line of description", find
# the index of 'f', which is the indent we expect for any # the index of 'f', which is the indent we expect for any
# following lines. We then remove the leading "@arg:" # following lines. We then remove the leading "@arg:"
# from line and replace it with spaces so that 'f' has the # from line and replace it with spaces so that 'f' has the
# same index as it did in the original line and can be # same index as it did in the original line and can be
# handled the same way we will handle following lines. # handled the same way we will handle following lines.
indent = must_match(r'@\S*:\s*', line).end() name = match.group(1)
indent = match.end()
line = line[indent:] line = line[indent:]
if not line: if not line:
# Line was just the "@arg:" header # Line was just the "@arg:" header
@ -673,8 +672,8 @@ class QAPIDoc:
indent = -1 indent = -1
else: else:
line = ' ' * indent + line line = ' ' * indent + line
self._start_features_section(name[1:-1], indent) self._start_features_section(name, indent)
elif self._is_section_tag(name): elif self._match_section_tag(line):
self._append_line = self._append_various_line self._append_line = self._append_various_line
self._append_various_line(line) self._append_various_line(line)
return return
@ -698,13 +697,13 @@ class QAPIDoc:
Else, append the line to the current section. Else, append the line to the current section.
""" """
name = line.split(' ', 1)[0] match = self._match_at_name_colon(line)
if match:
if name.startswith('@') and name.endswith(':'):
raise QAPIParseError(self._parser, raise QAPIParseError(self._parser,
"'%s' can't follow '%s' section" "'@%s:' can't follow '%s' section"
% (name, self.sections[0].name)) % (match.group(1), self.sections[0].name))
if self._is_section_tag(name): match = self._match_section_tag(line)
if match:
# If line is "Section: first line of description", find # If line is "Section: first line of description", find
# the index of 'f', which is the indent we expect for any # the index of 'f', which is the indent we expect for any
# following lines. We then remove the leading "Section:" # following lines. We then remove the leading "Section:"
@ -719,7 +718,7 @@ class QAPIDoc:
indent = 0 indent = 0
else: else:
line = ' ' * indent + line line = ' ' * indent + line
self._start_section(name[:-1], indent) self._start_section(match.group(1), indent)
self._append_freeform(line) self._append_freeform(line)