All of lore.kernel.org
 help / color / mirror / Atom feed
From: Markus Armbruster <armbru@redhat.com>
To: John Snow <jsnow@redhat.com>
Cc: Michael Roth <michael.roth@amd.com>,
	Cleber Rosa <crosa@redhat.com>,
	qemu-devel <qemu-devel@nongnu.org>,
	Eduardo Habkost <ehabkost@redhat.com>
Subject: Re: [PATCH v2 3/6] qapi/parser: add type hint annotations (QAPIDoc)
Date: Thu, 30 Sep 2021 12:04:22 +0200	[thread overview]
Message-ID: <87bl4aqt09.fsf@dusky.pond.sub.org> (raw)
In-Reply-To: <CAFn=p-Y4uqnLEvemPkTwYAdnFfoPLojyjRsWbD2zsEBpfvpqhQ@mail.gmail.com> (John Snow's message of "Tue, 28 Sep 2021 19:25:29 -0400")

John Snow <jsnow@redhat.com> writes:

> On Tue, Sep 7, 2021 at 6:44 AM Markus Armbruster <armbru@redhat.com> wrote:
>
>> John Snow <jsnow@redhat.com> writes:
>>
>> > Annotations do not change runtime behavior.
>> >
>> > This commit adds mostly annotations, but uses a TYPE_CHECKING runtime
>> > check to conditionally import dependencies, which only triggers during
>> > runs of mypy.
>>
>> Please add a reference to
>> https://mypy.readthedocs.io/en/latest/runtime_troubles.html#import-cycles
>>
>>
> OK.
>
>
>> > Signed-off-by: John Snow <jsnow@redhat.com>
>> >
>> > ---
>> >
>> > TopLevelExpr, an idea from previous drafts, makes a return here in order
>> > to give a semantic meaning to check_expr(). The type is intended to be
>> > used more in forthcoming commits (pt5c), but I opted to include it now
>> > instead of creating yet-another Dict[str, object] type hint that I would
>> > forget to change later.
>> >
>> > Signed-off-by: John Snow <jsnow@redhat.com>
>> > ---
>> >  scripts/qapi/parser.py | 77 ++++++++++++++++++++++++++----------------
>> >  1 file changed, 48 insertions(+), 29 deletions(-)
>> >
>> > diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py
>> > index 3ddde318376..b1e2fa5c577 100644
>> > --- a/scripts/qapi/parser.py
>> > +++ b/scripts/qapi/parser.py
>> > @@ -18,6 +18,7 @@
>> >  import os
>> >  import re
>> >  from typing import (
>> > +    TYPE_CHECKING,
>> >      Dict,
>> >      List,
>> >      Optional,
>> > @@ -30,6 +31,15 @@
>> >  from .source import QAPISourceInfo
>> >
>> >
>> > +if TYPE_CHECKING:
>> > +    # pylint: disable=cyclic-import
>> > +    # TODO: Remove cycle. [schema -> expr -> parser -> schema]
>>
>
> WRT this todo: you mentioned that you'd prefer having some idea or plan for
> how to eliminate the cycle in order to let this band-aid fly. How about
> adding a 'doc' member to e.g. QAPISchemaFeature and doing the connection
> entirely inside of schema.py, and dropping connect_member() and
> connect_feature()?
>
> Would that be serviceable?

I guess it would.  One way to find out.

>> > +    from .schema import QAPISchemaFeature, QAPISchemaMember
>> > +
>> > +
>> > +#: Represents a single Top Level QAPI schema expression.
>> > +TopLevelExpr = Dict[str, object]
>>
>> Related: _ExprValue below, and _JSONObject in expr.py.  The latter's
>> comment gives the best rationale (except I don't get "the purpose of
>> this module is to interrogate that type").
>>
>>
> in expr.py, the purpose of that module (expr) is explicitly to interrogate
> (check, validate) the shape of arbitrary JSON objects. I am saying that a
> more strict definition specifically here in expr.py is not necessary
> because the entire purpose of expr.py is to, at runtime, verify the shape
> of any such variables that might be annotated that way. I am drawing some
> distinction to introspect.py, where we're generating that data ourselves --
> the stronger types are more viable there, because we know what they are
> already.

Let me try to write a clearer comment:

    # Deserialized JSON objects as returned by the parser.
    # This is a actually Dict[str, _JSONValue], where _JSONValue is
    # Union[bool, str, List[Self], Dict[str, Self]].  Since mypy lacks
    # recursive types, we can't define _JSONValue, and use object
    # instead.  Sad.
    _JSONObject = Dict[str, object]

> (again, sorry about mypy's lack of recursive typing, I hate it too, I
> promise)

We got to play the hand we've been dealt.

>> I think we'd like to have
>>
>> * A recursive type for JSON value (in our bastardized version of JSON)
>>
>>   This is Union of bool, str, List[Self], Dict[str, Self].  It's what
>>   .get_expr() returns.
>>
>>   Since mypy can't do recursive, we approximate with _ExprValue.
>>
>> * A recursive type for JSON object
>>
>>   This is the Dict branch of the above.  It's what check_keys() &
>>   friends take as argument.
>>
>>   We approximate with _JSONObject.
>>
>>   Same for the List branch would make sense if we had a use for the
>>   type.
>>
>> * A recursive type for TOP-LEVEL-EXPR
>>
>>   Actually the same type as the previous one, to be used only for the
>>   schema's top-level expressions.  It's the elements of
>>   QAPISchemaParser.exprs[], and what check_exprs() takes as argument.
>>
>>   We approximate with TopLevelExpr, but so far use it only for
>>   check_exprs().
>>
>>   Doesn't really improve type checking, but may serve as documentation.
>>
>>
> That's the intended effect here -- to help highlight which functions
> operate on those top-level expressions, and which might be invoked in more
> arbitrary cases.
> Consider also a hypothetical TOP-LEVEL-EXPR that is actually a bona-fide
> object with additional metadata, too. In these cases, the type really will
> be different.
>
>
>> Shouldn't these types be all defined in one place, namely right here?
>> Bonus: we need to explain the mypy sadness just once then.
>>
>> Shouldn't their names be more systematic?  _ExprValue, _JSONObject and
>> TopLevelExpr hardly suggest any relation...
>>
>>
> I drop _JSONObject in pt5c, it was a stop-gap. For the rest, I'll see about
> a more rigorous consolidation now that we're this far in.
>
> pt5c was intended as a "cleanup" step that did some of that consolidation
> of nearly-redundant types; but I wanted all of parser.py under the mypy gun
> *first*.
> Will you take a raincheck here and we'll focus on the consolidation in the
> next series? I agree it's worth doing.

Works for me.

> (I can add a 'FIXME' that will 100% need to be fixed before I move
> scripts/qapi under python/qemu/qapi -- the linter config there prohibits
> them, so you can be sure I can't ignore it.)

Makes sense.



  reply	other threads:[~2021-09-30 10:06 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-05-20 22:57 [PATCH v2 0/6] qapi: static typing conversion, pt5b John Snow
2021-05-20 22:57 ` [PATCH v2 1/6] qapi/parser: fix unused check_args_section arguments John Snow
2021-05-20 22:57 ` [PATCH v2 2/6] qapi/parser: Allow empty QAPIDoc Sections John Snow
2021-09-07  8:28   ` Markus Armbruster
2021-09-24 22:37     ` John Snow
2021-05-20 22:57 ` [PATCH v2 3/6] qapi/parser: add type hint annotations (QAPIDoc) John Snow
2021-09-07 10:44   ` Markus Armbruster
2021-09-28 23:25     ` John Snow
2021-09-30 10:04       ` Markus Armbruster [this message]
2021-05-20 22:57 ` [PATCH v2 4/6] qapi/parser: enable mypy checks John Snow
2021-05-20 22:57 ` [PATCH v2 5/6] qapi/parser: Silence too-few-public-methods warning John Snow
2021-05-20 22:57 ` [PATCH v2 6/6] qapi/parser: enable pylint checks John Snow
2021-08-05  0:20 ` [PATCH v2 0/6] qapi: static typing conversion, pt5b John Snow
2021-09-07 10:56 ` Markus Armbruster

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87bl4aqt09.fsf@dusky.pond.sub.org \
    --to=armbru@redhat.com \
    --cc=crosa@redhat.com \
    --cc=ehabkost@redhat.com \
    --cc=jsnow@redhat.com \
    --cc=michael.roth@amd.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.