Add a semantic tag to paragraphs that appear *before* tagged
sections/members/features and those that appear after. This will control
how they are inlined when doc sections are merged and flattened.
Signed-off-by: John Snow <jsnow@redhat.com>
---
scripts/qapi/parser.py | 22 +++++++++++++++++-----
1 file changed, 17 insertions(+), 5 deletions(-)
diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py
index cf4cbca1c1f..b1794f71e12 100644
--- a/scripts/qapi/parser.py
+++ b/scripts/qapi/parser.py
@@ -503,6 +503,10 @@ def get_doc(self) -> 'QAPIDoc':
self.accept(False)
line = self.get_doc_line()
no_more_args = False
+ # Paragraphs before members/features/tagged are "intro" paragraphs.
+ # Any appearing subsequently are "outro" paragraphs.
+ # This is only semantic metadata for the doc generator.
+ intro = True
while line is not None:
# Blank lines
@@ -532,6 +536,7 @@ def get_doc(self) -> 'QAPIDoc':
raise QAPIParseError(
self, 'feature descriptions expected')
no_more_args = True
+ intro = False
elif match := self._match_at_name_colon(line):
# description
if no_more_args:
@@ -547,6 +552,7 @@ def get_doc(self) -> 'QAPIDoc':
doc.append_line(text)
line = self.get_doc_indented(doc)
no_more_args = True
+ intro = False
elif match := re.match(
r'(Returns|Errors|Since|Notes?|Examples?|TODO): *',
line):
@@ -557,13 +563,14 @@ def get_doc(self) -> 'QAPIDoc':
doc.append_line(text)
line = self.get_doc_indented(doc)
no_more_args = True
+ intro = False
elif line.startswith('='):
raise QAPIParseError(
self,
"unexpected '=' markup in definition documentation")
else:
# tag-less paragraph
- doc.ensure_untagged_section(self.info)
+ doc.ensure_untagged_section(self.info, intro)
doc.append_line(line)
line = self.get_doc_paragraph(doc)
else:
@@ -617,7 +624,7 @@ def __init__(
self,
info: QAPISourceInfo,
tag: Optional[str] = None,
- kind: str = 'paragraph',
+ kind: str = 'intro-paragraph',
):
# section source info, i.e. where it begins
self.info = info
@@ -625,7 +632,7 @@ def __init__(
self.tag = tag
# section text without tag
self.text = ''
- # section type - {paragraph, feature, member, tagged}
+ # section type - {<intro|outro>-paragraph, feature, member, tagged}
self.kind = kind
def append_line(self, line: str) -> None:
@@ -666,7 +673,11 @@ def end(self) -> None:
raise QAPISemError(
section.info, "text required after '%s:'" % section.tag)
- def ensure_untagged_section(self, info: QAPISourceInfo) -> None:
+ def ensure_untagged_section(
+ self,
+ info: QAPISourceInfo,
+ intro: bool = True,
+ ) -> None:
if self.all_sections and not self.all_sections[-1].tag:
section = self.all_sections[-1]
# Section is empty so far; update info to start *here*.
@@ -677,7 +688,8 @@ def ensure_untagged_section(self, info: QAPISourceInfo) -> None:
self.all_sections[-1].text += '\n'
return
# start new section
- section = self.Section(info)
+ kind = ("intro" if intro else "outro") + "-paragraph"
+ section = self.Section(info, kind=kind)
self.sections.append(section)
self.all_sections.append(section)
--
2.44.0
John Snow <jsnow@redhat.com> writes: > Add a semantic tag to paragraphs that appear *before* tagged > sections/members/features and those that appear after. This will control > how they are inlined when doc sections are merged and flattened. This future use is not obvious to me now. I guess the effective way to help me see it is actual patches, which will come in due time. > Signed-off-by: John Snow <jsnow@redhat.com> > --- > scripts/qapi/parser.py | 22 +++++++++++++++++----- > 1 file changed, 17 insertions(+), 5 deletions(-) > > diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py > index cf4cbca1c1f..b1794f71e12 100644 > --- a/scripts/qapi/parser.py > +++ b/scripts/qapi/parser.py > @@ -503,6 +503,10 @@ def get_doc(self) -> 'QAPIDoc': > self.accept(False) > line = self.get_doc_line() > no_more_args = False > + # Paragraphs before members/features/tagged are "intro" paragraphs. > + # Any appearing subsequently are "outro" paragraphs. > + # This is only semantic metadata for the doc generator. Not sure about the last sentence. Isn't it true for almost everything around here? Also, long line. > + intro = True > > while line is not None: > # Blank lines > @@ -532,6 +536,7 @@ def get_doc(self) -> 'QAPIDoc': > raise QAPIParseError( > self, 'feature descriptions expected') > no_more_args = True > + intro = False After feature descriptions. > elif match := self._match_at_name_colon(line): > # description > if no_more_args: > @@ -547,6 +552,7 @@ def get_doc(self) -> 'QAPIDoc': > doc.append_line(text) > line = self.get_doc_indented(doc) > no_more_args = True > + intro = False Or after member descriptions. > elif match := re.match( > r'(Returns|Errors|Since|Notes?|Examples?|TODO): *', > line): > @@ -557,13 +563,14 @@ def get_doc(self) -> 'QAPIDoc': > doc.append_line(text) > line = self.get_doc_indented(doc) > no_more_args = True > + intro = False Or after the first tagged section. Okay, it does what it says on the tin. > elif line.startswith('='): > raise QAPIParseError( > self, > "unexpected '=' markup in definition documentation") > else: > # tag-less paragraph > - doc.ensure_untagged_section(self.info) > + doc.ensure_untagged_section(self.info, intro) > doc.append_line(line) > line = self.get_doc_paragraph(doc) > else: > @@ -617,7 +624,7 @@ def __init__( > self, > info: QAPISourceInfo, > tag: Optional[str] = None, > - kind: str = 'paragraph', > + kind: str = 'intro-paragraph', The question "why is this optional?" crossed my mind when reviewing the previous patch. I left it unasked, because I felt challenging the overlap between @kind and @tag was more useful. However, the new default value 'intro-paragraph' feels more arbitrary to me than the old one 'paragraph', and that makes the question pop right back into my mind. Unless I'm mistaken, all calls but one @tag and @kind. Making that one pass it too feels simpler to me. Moot if we fuse @tag and @kind, of course. > ): > # section source info, i.e. where it begins > self.info = info > @@ -625,7 +632,7 @@ def __init__( > self.tag = tag > # section text without tag > self.text = '' > - # section type - {paragraph, feature, member, tagged} > + # section type - {<intro|outro>-paragraph, feature, member, tagged} Long line. > self.kind = kind > > def append_line(self, line: str) -> None: > @@ -666,7 +673,11 @@ def end(self) -> None: > raise QAPISemError( > section.info, "text required after '%s:'" % section.tag) > > - def ensure_untagged_section(self, info: QAPISourceInfo) -> None: > + def ensure_untagged_section( > + self, > + info: QAPISourceInfo, > + intro: bool = True, Two callers, one passes @info, one doesn't. Passing it always might be simpler. > + ) -> None: > if self.all_sections and not self.all_sections[-1].tag: > section = self.all_sections[-1] > # Section is empty so far; update info to start *here*. > @@ -677,7 +688,8 @@ def ensure_untagged_section(self, info: QAPISourceInfo) -> None: > self.all_sections[-1].text += '\n' > return > # start new section > - section = self.Section(info) > + kind = ("intro" if intro else "outro") + "-paragraph" > + section = self.Section(info, kind=kind) > self.sections.append(section) > self.all_sections.append(section)
On Thu, May 16, 2024, 5:34 AM Markus Armbruster <armbru@redhat.com> wrote: > John Snow <jsnow@redhat.com> writes: > > > Add a semantic tag to paragraphs that appear *before* tagged > > sections/members/features and those that appear after. This will control > > how they are inlined when doc sections are merged and flattened. > > This future use is not obvious to me now. I guess the effective way to > help me see it is actual patches, which will come in due time. > Head recursion and tail recursion, respectively :) * intro * inherited intro * members [ancestor-descendent] * features [ancestor-descendent] * inherited outro * outro Child gets the first and final words. Inherited stuff goes in the sandwich fillings. It feels like a simple rule that's easy to internalize. As a bonus, you can explain it by analogy to Americans as a burger, which is the only metaphor we understand. > > Signed-off-by: John Snow <jsnow@redhat.com> > > --- > > scripts/qapi/parser.py | 22 +++++++++++++++++----- > > 1 file changed, 17 insertions(+), 5 deletions(-) > > > > diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py > > index cf4cbca1c1f..b1794f71e12 100644 > > --- a/scripts/qapi/parser.py > > +++ b/scripts/qapi/parser.py > > @@ -503,6 +503,10 @@ def get_doc(self) -> 'QAPIDoc': > > self.accept(False) > > line = self.get_doc_line() > > no_more_args = False > > + # Paragraphs before members/features/tagged are "intro" > paragraphs. > > + # Any appearing subsequently are "outro" paragraphs. > > + # This is only semantic metadata for the doc generator. > > Not sure about the last sentence. Isn't it true for almost everything > around here? > I guess I was trying to say "There's no real difference between the two mechanically, it's purely based on where it appears in the doc block, which offers only a heuristic for its semantic value- introductory statements or additional detail." In my mind: the other "kind" values have some more mechanical difference to them, but intro/outro don't. > Also, long line. > > > + intro = True > > > > while line is not None: > > # Blank lines > > @@ -532,6 +536,7 @@ def get_doc(self) -> 'QAPIDoc': > > raise QAPIParseError( > > self, 'feature descriptions expected') > > no_more_args = True > > + intro = False > > After feature descriptions. > > > elif match := self._match_at_name_colon(line): > > # description > > if no_more_args: > > @@ -547,6 +552,7 @@ def get_doc(self) -> 'QAPIDoc': > > doc.append_line(text) > > line = self.get_doc_indented(doc) > > no_more_args = True > > + intro = False > > Or after member descriptions. > > > elif match := re.match( > > r'(Returns|Errors|Since|Notes?|Examples?|TODO): > *', > > line): > > @@ -557,13 +563,14 @@ def get_doc(self) -> 'QAPIDoc': > > doc.append_line(text) > > line = self.get_doc_indented(doc) > > no_more_args = True > > + intro = False > > Or after the first tagged section. > > Okay, it does what it says on the tin. > > > elif line.startswith('='): > > raise QAPIParseError( > > self, > > "unexpected '=' markup in definition > documentation") > > else: > > # tag-less paragraph > > - doc.ensure_untagged_section(self.info) > > + doc.ensure_untagged_section(self.info, intro) > > doc.append_line(line) > > line = self.get_doc_paragraph(doc) > > else: > > @@ -617,7 +624,7 @@ def __init__( > > self, > > info: QAPISourceInfo, > > tag: Optional[str] = None, > > - kind: str = 'paragraph', > > + kind: str = 'intro-paragraph', > > The question "why is this optional?" crossed my mind when reviewing the > previous patch. I left it unasked, because I felt challenging the > overlap between @kind and @tag was more useful. However, the new > default value 'intro-paragraph' feels more arbitrary to me than the old > one 'paragraph', and that makes the question pop right back into my > mind. > Just "don't break API" habit, nothing more. I can make it mandatory. > Unless I'm mistaken, all calls but one @tag and @kind. Making that one > pass it too feels simpler to me. > > Moot if we fuse @tag and @kind, of course. > > ): > > # section source info, i.e. where it begins > > self.info = info > > @@ -625,7 +632,7 @@ def __init__( > > self.tag = tag > > # section text without tag > > self.text = '' > > - # section type - {paragraph, feature, member, tagged} > > + # section type - {<intro|outro>-paragraph, feature, member, > tagged} > > Long line. Oops, default for black is longer. Forgot to enable the "I still use email patches as part of my penance" setting > > self.kind = kind > > > > def append_line(self, line: str) -> None: > > @@ -666,7 +673,11 @@ def end(self) -> None: > > raise QAPISemError( > > section.info, "text required after '%s:'" % > section.tag) > > > > - def ensure_untagged_section(self, info: QAPISourceInfo) -> None: > > + def ensure_untagged_section( > > + self, > > + info: QAPISourceInfo, > > + intro: bool = True, > > Two callers, one passes @info, one doesn't. Passing it always might be > simpler. > Okeydokey. > > + ) -> None: > > if self.all_sections and not self.all_sections[-1].tag: > > section = self.all_sections[-1] > > # Section is empty so far; update info to start *here*. > > @@ -677,7 +688,8 @@ def ensure_untagged_section(self, info: > QAPISourceInfo) -> None: > > self.all_sections[-1].text += '\n' > > return > > # start new section > > - section = self.Section(info) > > + kind = ("intro" if intro else "outro") + "-paragraph" > > + section = self.Section(info, kind=kind) > > self.sections.append(section) > > self.all_sections.append(section) > >
On Thu, May 16, 2024 at 11:06 AM John Snow <jsnow@redhat.com> wrote: > > > On Thu, May 16, 2024, 5:34 AM Markus Armbruster <armbru@redhat.com> wrote: > >> John Snow <jsnow@redhat.com> writes: >> >> > Add a semantic tag to paragraphs that appear *before* tagged >> > sections/members/features and those that appear after. This will control >> > how they are inlined when doc sections are merged and flattened. >> >> This future use is not obvious to me now. I guess the effective way to >> help me see it is actual patches, which will come in due time. >> > > Head recursion and tail recursion, respectively :) > > * intro > * inherited intro > * members [ancestor-descendent] > * features [ancestor-descendent] > * inherited outro > * outro > > Child gets the first and final words. Inherited stuff goes in the sandwich > fillings. > > It feels like a simple rule that's easy to internalize. As a bonus, you > can explain it by analogy to Americans as a burger, which is the only > metaphor we understand. > > >> > Signed-off-by: John Snow <jsnow@redhat.com> >> > --- >> > scripts/qapi/parser.py | 22 +++++++++++++++++----- >> > 1 file changed, 17 insertions(+), 5 deletions(-) >> > >> > diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py >> > index cf4cbca1c1f..b1794f71e12 100644 >> > --- a/scripts/qapi/parser.py >> > +++ b/scripts/qapi/parser.py >> > @@ -503,6 +503,10 @@ def get_doc(self) -> 'QAPIDoc': >> > self.accept(False) >> > line = self.get_doc_line() >> > no_more_args = False >> > + # Paragraphs before members/features/tagged are "intro" >> paragraphs. >> > + # Any appearing subsequently are "outro" paragraphs. >> > + # This is only semantic metadata for the doc generator. >> >> Not sure about the last sentence. Isn't it true for almost everything >> around here? >> > > I guess I was trying to say "There's no real difference between the two > mechanically, it's purely based on where it appears in the doc block, which > offers only a heuristic for its semantic value- introductory statements or > additional detail." > > In my mind: the other "kind" values have some more mechanical difference > to them, but intro/outro don't. > > >> Also, long line. >> >> > + intro = True >> > >> > while line is not None: >> > # Blank lines >> > @@ -532,6 +536,7 @@ def get_doc(self) -> 'QAPIDoc': >> > raise QAPIParseError( >> > self, 'feature descriptions expected') >> > no_more_args = True >> > + intro = False >> >> After feature descriptions. >> >> > elif match := self._match_at_name_colon(line): >> > # description >> > if no_more_args: >> > @@ -547,6 +552,7 @@ def get_doc(self) -> 'QAPIDoc': >> > doc.append_line(text) >> > line = self.get_doc_indented(doc) >> > no_more_args = True >> > + intro = False >> >> Or after member descriptions. >> >> > elif match := re.match( >> > >> r'(Returns|Errors|Since|Notes?|Examples?|TODO): *', >> > line): >> > @@ -557,13 +563,14 @@ def get_doc(self) -> 'QAPIDoc': >> > doc.append_line(text) >> > line = self.get_doc_indented(doc) >> > no_more_args = True >> > + intro = False >> >> Or after the first tagged section. >> >> Okay, it does what it says on the tin. >> >> > elif line.startswith('='): >> > raise QAPIParseError( >> > self, >> > "unexpected '=' markup in definition >> documentation") >> > else: >> > # tag-less paragraph >> > - doc.ensure_untagged_section(self.info) >> > + doc.ensure_untagged_section(self.info, intro) >> > doc.append_line(line) >> > line = self.get_doc_paragraph(doc) >> > else: >> > @@ -617,7 +624,7 @@ def __init__( >> > self, >> > info: QAPISourceInfo, >> > tag: Optional[str] = None, >> > - kind: str = 'paragraph', >> > + kind: str = 'intro-paragraph', >> >> The question "why is this optional?" crossed my mind when reviewing the >> previous patch. I left it unasked, because I felt challenging the >> overlap between @kind and @tag was more useful. However, the new >> default value 'intro-paragraph' feels more arbitrary to me than the old >> one 'paragraph', and that makes the question pop right back into my >> mind. >> > > Just "don't break API" habit, nothing more. I can make it mandatory. > > >> Unless I'm mistaken, all calls but one @tag and @kind. Making that one >> pass it too feels simpler to me. >> >> Moot if we fuse @tag and @kind, of course. > > >> > ): >> > # section source info, i.e. where it begins >> > self.info = info >> > @@ -625,7 +632,7 @@ def __init__( >> > self.tag = tag >> > # section text without tag >> > self.text = '' >> > - # section type - {paragraph, feature, member, tagged} >> > + # section type - {<intro|outro>-paragraph, feature, >> member, tagged} >> >> Long line. > > > Oops, default for black is longer. Forgot to enable the "I still use email > patches as part of my penance" setting > Oh, no, this is actually 79 columns on the button. Not picked up by *any* of our linters. Ehm... can you make the tools enforce them to your preference instead, please...? What's a "long line"? > > >> > self.kind = kind >> > >> > def append_line(self, line: str) -> None: >> > @@ -666,7 +673,11 @@ def end(self) -> None: >> > raise QAPISemError( >> > section.info, "text required after '%s:'" % >> section.tag) >> > >> > - def ensure_untagged_section(self, info: QAPISourceInfo) -> None: >> > + def ensure_untagged_section( >> > + self, >> > + info: QAPISourceInfo, >> > + intro: bool = True, >> >> Two callers, one passes @info, one doesn't. Passing it always might be >> simpler. >> > > Okeydokey. > > >> > + ) -> None: >> > if self.all_sections and not self.all_sections[-1].tag: >> > section = self.all_sections[-1] >> > # Section is empty so far; update info to start *here*. >> > @@ -677,7 +688,8 @@ def ensure_untagged_section(self, info: >> QAPISourceInfo) -> None: >> > self.all_sections[-1].text += '\n' >> > return >> > # start new section >> > - section = self.Section(info) >> > + kind = ("intro" if intro else "outro") + "-paragraph" >> > + section = self.Section(info, kind=kind) >> > self.sections.append(section) >> > self.all_sections.append(section) >> >>
© 2016 - 2024 Red Hat, Inc.