On Mon, May 4, 2026 at 5:54 AM Markus Armbruster <[email protected]> wrote:
>
> John Snow <[email protected]> writes:
>
> > These fields are used to provide error checking and internal logistics
> > and should not be used by a user of the library to directly access
> > documentation sections, so make them private.
> >
> > The "since" field alone is left public, as the qapidoc generator does
> > use this field to pull that section out of the regular flow of the
> > document.
> >
> > Signed-off-by: John Snow <[email protected]>
> > ---
> >  scripts/qapi/parser.py | 54 +++++++++++++++++++++++-------------------
> >  1 file changed, 29 insertions(+), 25 deletions(-)
> >
> > diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py
> > index b33edbba74f..da4756a7424 100644
> > --- a/scripts/qapi/parser.py
> > +++ b/scripts/qapi/parser.py
> > @@ -555,7 +555,7 @@ def get_doc(self) -> 'QAPIDoc':
> >                      break
> >                  # Non-blank line, first of a section
> >                  if line == 'Features:':
> > -                    if doc.features:
> > +                    if doc.has_features:
> >                          raise QAPIParseError(
> >                              self, "duplicated 'Features:' line")
> >                      self.accept(False)
> > @@ -570,7 +570,7 @@ def get_doc(self) -> 'QAPIDoc':
> >                          if text:
> >                              doc.append_line(text)
> >                          line = self.get_doc_indented(doc)
> > -                    if not doc.features:
> > +                    if not doc.has_features:
> >                          raise QAPIParseError(
> >                              self, 'feature descriptions expected')
> >                      no_more_args = True
>
> These two hunks along with the one adding .has_features() below make the
> patch not purely mechanical.  Separate patch, please.

And here I thought I already split this all to the Nth degree. :)

>
> Have you considered using a local bool variable instead?

I prefer having less state and more logic, to avoid having to keep
related state synchronized.

>
> > @@ -733,14 +733,18 @@ def __init__(self, info: QAPISourceInfo, symbol: 
> > Optional[str] = None):
> >              QAPIDoc.Section(info, QAPIDoc.Kind.PLAIN)
> >          ]
> >          # dicts mapping parameter/feature names to their description
> > -        self.args: Dict[str, QAPIDoc.ArgSection] = {}
> > -        self.features: Dict[str, QAPIDoc.ArgSection] = {}
> > +        self._args: Dict[str, QAPIDoc.ArgSection] = {}
> > +        self._features: Dict[str, QAPIDoc.ArgSection] = {}
> >          # a command's "Returns" and "Errors" section
> > -        self.returns: Optional[QAPIDoc.Section] = None
> > -        self.errors: Optional[QAPIDoc.Section] = None
> > +        self._returns: Optional[QAPIDoc.Section] = None
> > +        self._errors: Optional[QAPIDoc.Section] = None
> >          # "Since" section
> >          self.since: Optional[QAPIDoc.Section] = None
> >
> > +    @property
> > +    def has_features(self) -> bool:
> > +        return bool(self._features)
> > +
> >      def end(self) -> None:
> >          for section in self.all_sections:
> >              section.text = section.text.strip('\n')
> > @@ -771,15 +775,15 @@ def new_tagged_section(
> >      ) -> None:
> >          section = self.Section(info, kind)
> >          if kind == QAPIDoc.Kind.RETURNS:
> > -            if self.returns:
> > +            if self._returns:
> >                  raise QAPISemError(
> >                      info, "duplicated '%s' section" % kind)
> > -            self.returns = section
> > +            self._returns = section
> >          elif kind == QAPIDoc.Kind.ERRORS:
> > -            if self.errors:
> > +            if self._errors:
> >                  raise QAPISemError(
> >                      info, "duplicated '%s' section" % kind)
> > -            self.errors = section
> > +            self._errors = section
> >          elif kind == QAPIDoc.Kind.SINCE:
> >              if self.since:
> >                  raise QAPISemError(
> > @@ -803,16 +807,16 @@ def _new_description(
> >          desc[name] = section
> >
> >      def new_argument(self, info: QAPISourceInfo, name: str) -> None:
> > -        self._new_description(info, name, QAPIDoc.Kind.MEMBER, self.args)
> > +        self._new_description(info, name, QAPIDoc.Kind.MEMBER, self._args)
> >
> >      def new_feature(self, info: QAPISourceInfo, name: str) -> None:
> > -        self._new_description(info, name, QAPIDoc.Kind.FEATURE, 
> > self.features)
> > +        self._new_description(info, name, QAPIDoc.Kind.FEATURE, 
> > self._features)
> >
> >      def append_line(self, line: str) -> None:
> >          self.all_sections[-1].append_line(line)
> >
> >      def connect_member(self, member: 'QAPISchemaMember') -> None:
> > -        if member.name not in self.args:
> > +        if member.name not in self._args:
> >              assert member.info
> >              if self.symbol not in 
> > member.info.pragma.documentation_exceptions:
> >                  raise QAPISemError(member.info,
> > @@ -823,7 +827,7 @@ def connect_member(self, member: 'QAPISchemaMember') -> 
> > None:
> >
> >              section = QAPIDoc.ArgSection(
> >                  self.info, QAPIDoc.Kind.MEMBER, member.name)
> > -            self.args[member.name] = section
> > +            self._args[member.name] = section
> >
> >              # Determine where to insert stub doc - it should go at the
> >              # end of the members section(s), if any. Note that index 0
> > @@ -835,14 +839,14 @@ def connect_member(self, member: 'QAPISchemaMember') 
> > -> None:
> >                      index += 1
> >              self.all_sections.insert(index, section)
> >
> > -        self.args[member.name].connect(member)
> > +        self._args[member.name].connect(member)
> >
> >      def connect_feature(self, feature: 'QAPISchemaFeature') -> None:
> > -        if feature.name not in self.features:
> > +        if feature.name not in self._features:
> >              raise QAPISemError(feature.info,
> >                                 "feature '%s' lacks documentation"
> >                                 % feature.name)
> > -        self.features[feature.name].connect(feature)
> > +        self._features[feature.name].connect(feature)
> >
> >      def ensure_returns(self, info: QAPISourceInfo) -> None:
> >
> > @@ -883,18 +887,18 @@ def _insert_near_kind(
> >
> >      def check_expr(self, expr: QAPIExpression) -> None:
> >          if 'command' in expr:
> > -            if self.returns and 'returns' not in expr:
> > +            if self._returns and 'returns' not in expr:
> >                  raise QAPISemError(
> > -                    self.returns.info,
> > +                    self._returns.info,
> >                      "'Returns' section, but command doesn't return 
> > anything")
> >          else:
> > -            if self.returns:
> > +            if self._returns:
> >                  raise QAPISemError(
> > -                    self.returns.info,
> > +                    self._returns.info,
> >                      "'Returns' section is only valid for commands")
> > -            if self.errors:
> > +            if self._errors:
> >                  raise QAPISemError(
> > -                    self.errors.info,
> > +                    self._errors.info,
> >                      "'Errors' section is only valid for commands")
> >
> >      def check(self) -> None:
> > @@ -914,5 +918,5 @@ def check_args_section(
> >                          "do" if len(bogus) > 1 else "does"
> >                      ))
> >
> > -        check_args_section(self.args, 'member')
> > -        check_args_section(self.features, 'feature')
> > +        check_args_section(self._args, 'member')
> > +        check_args_section(self._features, 'feature')
>


Reply via email to