On Wed, Feb 8, 2023, 11:17 AM Markus Armbruster <arm...@redhat.com> wrote:
> John Snow <js...@redhat.com> writes: > > > This is an immutable, named, typed tuple. It's arguably nicer than > > arbitrary dicts for passing data around when using strict typing. > > > > This patch turns parser.exprs into a list of ParsedExpressions instead, > > and adjusts expr.py to match. > > > > This allows the types we specify in parser.py to be "remembered" all the > > way through expr.py and into schema.py. Several assertions around > > packing and unpacking this data can be removed as a result. > > > > Signed-off-by: John Snow <js...@redhat.com> > > --- > > scripts/qapi/expr.py | 29 +++++++++-------------------- > > scripts/qapi/parser.py | 29 ++++++++++++++++++----------- > > scripts/qapi/schema.py | 6 +++--- > > 3 files changed, 30 insertions(+), 34 deletions(-) > > > > diff --git a/scripts/qapi/expr.py b/scripts/qapi/expr.py > > index d01543006d8..293f830fe9d 100644 > > --- a/scripts/qapi/expr.py > > +++ b/scripts/qapi/expr.py > > @@ -44,7 +44,7 @@ > > > > from .common import c_name > > from .error import QAPISemError > > -from .parser import QAPIDoc > > +from .parser import ParsedExpression > > from .source import QAPISourceInfo > > > > > > @@ -595,29 +595,17 @@ def check_event(expr: _JSONObject, info: > QAPISourceInfo) -> None: > > check_type(args, info, "'data'", allow_dict=not boxed) > > > > > > -def check_expr(expr_elem: _JSONObject) -> None: > > +def check_expr(pexpr: ParsedExpression) -> None: > > """ > > - Validate and normalize a parsed QAPI schema expression. > > + Validate and normalize a `ParsedExpression`. > > Doc comment style question: what's our attitude towards repeating (parts > of) the function signature in its doc comment? > > In general, I dislike comments that merely rephrase code as prose. > Me too, I've never found a doc system that gets it exactly right... I don't like the duplication. For the sake of tooltips and help docs that might be removed from code, I often feel compelled to write something. Sometimes I value the consistency over the DRY violation. I dunno. I struggle with it. > Untyped Python builds a habit of mentioning the types in the doc > comment. That's additional information. In typed Python, it's > rephrased information. > > Keeping the old line would be just fine with me. > The only reason I have this habit is because I have an obsession with cross-references, really. I love clickables. love 'em! Though it's probably true that the type signature would suffice for giving you a clickable. Looking at https://qemu.readthedocs.io/projects/python-qemu-qmp/en/latest/overview.html ... yeah, the signatures are clickable. OK, I can cut it out. probably. I guess. perhaps. > > > > - :param expr_elem: The parsed expression to normalize and validate. > > + :param pexpr: The parsed expression to normalize and validate. > > > > :raise QAPISemError: When this expression fails validation. > > - :return: None, ``expr`` is normalized in-place as needed. > > + :return: None, ``pexpr`` is normalized in-place as needed. > > """ > > - # Expression > > - assert isinstance(expr_elem['expr'], dict) > > - for key in expr_elem['expr'].keys(): > > - assert isinstance(key, str) > > - expr: _JSONObject = expr_elem['expr'] > > - > > - # QAPISourceInfo > > - assert isinstance(expr_elem['info'], QAPISourceInfo) > > - info: QAPISourceInfo = expr_elem['info'] > > - > > - # Optional[QAPIDoc] > > - tmp = expr_elem.get('doc') > > - assert tmp is None or isinstance(tmp, QAPIDoc) > > - doc: Optional[QAPIDoc] = tmp > > + expr = pexpr.expr > > + info = pexpr.info > > > > if 'include' in expr: > > return > > @@ -637,6 +625,7 @@ def check_expr(expr_elem: _JSONObject) -> None: > > info.set_defn(meta, name) > > check_defn_name_str(name, info, meta) > > > > + doc = pexpr.doc > > if doc: > > if doc.symbol != name: > > raise QAPISemError( > > @@ -688,7 +677,7 @@ def check_expr(expr_elem: _JSONObject) -> None: > > check_flags(expr, info) > > > > > > -def check_exprs(exprs: List[_JSONObject]) -> List[_JSONObject]: > > +def check_exprs(exprs: List[ParsedExpression]) -> > List[ParsedExpression]: > > """ > > Validate and normalize a list of parsed QAPI schema expressions. > > > > diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py > > index 1b006cdc133..f897dffbfd4 100644 > > --- a/scripts/qapi/parser.py > > +++ b/scripts/qapi/parser.py > > @@ -21,6 +21,7 @@ > > TYPE_CHECKING, > > Dict, > > List, > > + NamedTuple, > > Optional, > > Set, > > Union, > > @@ -48,6 +49,12 @@ > > # several modules. > > > > > > +class ParsedExpression(NamedTuple): > > + expr: TopLevelExpr > > + info: QAPISourceInfo > > + doc: Optional['QAPIDoc'] > > + > > + > > class QAPIParseError(QAPISourceError): > > """Error class for all QAPI schema parsing errors.""" > > def __init__(self, parser: 'QAPISchemaParser', msg: str): > > @@ -100,7 +107,7 @@ def __init__(self, > > self.line_pos = 0 > > > > # Parser output: > > - self.exprs: List[Dict[str, object]] = [] > > + self.exprs: List[ParsedExpression] = [] > > self.docs: List[QAPIDoc] = [] > > > > # Showtime! > > @@ -147,8 +154,7 @@ def _parse(self) -> None: > > "value of 'include' must be a > string") > > incl_fname = os.path.join(os.path.dirname(self._fname), > > include) > > - self.exprs.append({'expr': {'include': incl_fname}, > > - 'info': info}) > > + self._add_expr(OrderedDict({'include': incl_fname}), > info) > > exprs_include = self._include(include, info, incl_fname, > > self._included) > > if exprs_include: > > @@ -165,17 +171,18 @@ def _parse(self) -> None: > > for name, value in pragma.items(): > > self._pragma(name, value, info) > > else: > > - expr_elem = {'expr': expr, > > - 'info': info} > > - if cur_doc: > > - if not cur_doc.symbol: > > - raise QAPISemError( > > - cur_doc.info, "definition documentation > required") > > - expr_elem['doc'] = cur_doc > > - self.exprs.append(expr_elem) > > + if cur_doc and not cur_doc.symbol: > > + raise QAPISemError( > > + cur_doc.info, "definition documentation > required") > > + self._add_expr(expr, info, cur_doc) > > cur_doc = None > > self.reject_expr_doc(cur_doc) > > > > + def _add_expr(self, expr: TopLevelExpr, > > + info: QAPISourceInfo, > > + doc: Optional['QAPIDoc'] = None) -> None: > > + self.exprs.append(ParsedExpression(expr, info, doc)) > > + > > @staticmethod > > def reject_expr_doc(doc: Optional['QAPIDoc']) -> None: > > if doc and doc.symbol: > > diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py > > index cd8661125cd..89f8e60db36 100644 > > --- a/scripts/qapi/schema.py > > +++ b/scripts/qapi/schema.py > > @@ -1168,9 +1168,9 @@ def _def_event(self, expr, info, doc): > > > > def _def_exprs(self, exprs): > > for expr_elem in exprs: > > Rename @expr_elem to @pexpr for consistency with check_expr()? > OK, will address when reordering/squashing > > - expr = expr_elem['expr'] > > - info = expr_elem['info'] > > - doc = expr_elem.get('doc') > > + expr = expr_elem.expr > > + info = expr_elem.info > > + doc = expr_elem.doc > > if 'enum' in expr: > > self._def_enum_type(expr, info, doc) > > elif 'struct' in expr: > >