On Wed, Nov 22, 2023, 7:00 AM Markus Armbruster <arm...@redhat.com> wrote:
> John Snow <js...@redhat.com> writes: > > > On Tue, Nov 21, 2023, 9:09 AM Markus Armbruster <arm...@redhat.com> > wrote: > > > >> John Snow <js...@redhat.com> writes: > >> > >> > We already take care to perform some type narrowing for arg_type and > >> > ret_type, but not in a way where mypy can utilize the result. A simple > >> > change to use a temporary variable helps the medicine go down. > >> > > >> > Signed-off-by: John Snow <js...@redhat.com> > >> > --- > >> > scripts/qapi/schema.py | 17 +++++++++-------- > >> > 1 file changed, 9 insertions(+), 8 deletions(-) > >> > > >> > diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py > >> > index 4600a566005..a1094283828 100644 > >> > --- a/scripts/qapi/schema.py > >> > +++ b/scripts/qapi/schema.py > >> > @@ -825,13 +825,14 @@ def __init__(self, name, info, doc, ifcond, > features, > >> > def check(self, schema): > >> > super().check(schema) > >> > if self._arg_type_name: > >> > - self.arg_type = schema.resolve_type( > >> > + arg_type = schema.resolve_type( > >> > self._arg_type_name, self.info, "command's 'data'") > >> > - if not isinstance(self.arg_type, QAPISchemaObjectType): > >> > + if not isinstance(arg_type, QAPISchemaObjectType): > >> > raise QAPISemError( > >> > self.info, > >> > "command's 'data' cannot take %s" > >> > - % self.arg_type.describe()) > >> > + % arg_type.describe()) > >> > + self.arg_type = arg_type > >> > if self.arg_type.variants and not self.boxed: > >> > raise QAPISemError( > >> > self.info, > > Same story as for QAPISchemaEvent.check() below. Correct? > Yep. > >> > @@ -848,8 +849,7 @@ def check(self, schema): > >> > if self.name not in > self.info.pragma.command_returns_exceptions: > >> > typ = self.ret_type > >> > if isinstance(typ, QAPISchemaArrayType): > >> > - typ = self.ret_type.element_type > >> > - assert typ > >> > + typ = typ.element_type > >> > > > > In this case, we've narrowed typ but not self.ret_type and mypy is not > sure > > they're synonymous here (lack of power in mypy's model, maybe?). Work in > > terms of the temporary type we've already narrowed so mypy knows we have > an > > element_type field. > > The conditional ensures @typ is QAPISchemaArrayType. > > In mypy's view, @typ is QAPISchemaArrayType, but self.ret_type is only > Optional[QAPISchemaType]. > > Therefore, it chokes on self.ret_type.element_type, but is happy with > typ.element_type. > > Correct? > I think so, yes. In this conditional block, we need to work in terms of typ, which has been narrowed. The broader type doesn't have .element_type. > Why delete the assertion? Oh! Hmm, should the deletion go into PATCH > 10? > Yeah, just a patch-splitting goof. I'll repair that. > >> if not isinstance(typ, QAPISchemaObjectType): > >> > raise QAPISemError( > >> > self.info, > >> > @@ -885,13 +885,14 @@ def __init__(self, name, info, doc, ifcond, > features, arg_type, boxed): > >> > def check(self, schema): > >> > super().check(schema) > >> > if self._arg_type_name: > >> > - self.arg_type = schema.resolve_type( > >> > + typ = schema.resolve_type( > >> > self._arg_type_name, self.info, "event's 'data'") > >> > - if not isinstance(self.arg_type, QAPISchemaObjectType): > >> > + if not isinstance(typ, QAPISchemaObjectType): > >> > raise QAPISemError( > >> > self.info, > >> > "event's 'data' cannot take %s" > >> > - % self.arg_type.describe()) > >> > + % typ.describe()) > >> > + self.arg_type = typ > >> > if self.arg_type.variants and not self.boxed: > >> > raise QAPISemError( > >> > self.info, > >> > >> Harmless enough. I can't quite see the mypy problem, though. Care to > >> elaborate a bit? > >> > > > > self.arg_type has a narrower type- or, it WILL at the end of this series > - > > so we need to narrow a temporary variable first before assigning it to > the > > object state. > > > > We already perform the necessary check/narrowing, so this is really just > > pointing out that it's a bad idea to assign the state before the type > > check. Now we type check before assigning state. > > After PATCH 16, .resolve_type() will return QAPISchemaType, and > self.arg_type will be Optional[QAPISchemaObjectType]. Correct? > Sounds right. Sometimes it's a little hard to see what the error is before the rest of the types go in, a hazard of needing all patches to bisect without regression. Do you want a more elaborate commit message? --js