qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: John Snow <jsnow@redhat.com>
To: Markus Armbruster <armbru@redhat.com>
Cc: Michael Roth <michael.roth@amd.com>,
	Cleber Rosa <crosa@redhat.com>, Eric Blake <eblake@redhat.com>,
	qemu-devel <qemu-devel@nongnu.org>,
	Eduardo Habkost <ehabkost@redhat.com>
Subject: Re: [PATCH v3 09/13] qapi/parser: add import cycle workaround
Date: Thu, 30 Sep 2021 13:11:55 -0400	[thread overview]
Message-ID: <CAFn=p-ZhF0wpbwL0X5Xn7YLKNSth+Bhsm9ScZF=MJtKHa4cWXQ@mail.gmail.com> (raw)
In-Reply-To: <87o88aqtw4.fsf@dusky.pond.sub.org>

[-- Attachment #1: Type: text/plain, Size: 5099 bytes --]

On Thu, Sep 30, 2021 at 5:45 AM Markus Armbruster <armbru@redhat.com> wrote:

> John Snow <jsnow@redhat.com> writes:
>
> > There is a cycle that exists in the QAPI generator: [schema -> expr ->
>
> "There is" or "there will be once we add strong type hints"?
>
>
"There exists in my mind-palace a cycle where, ..."

(Will adjust the commit message.)


> > parser -> schema]. It exists because the QAPIDoc class needs the names
> > of types defined by the schema module, but the schema module needs to
> > import both expr.py/parser.py to do its actual parsing.
> >
> > Ultimately, the layering violation is that parser.py should not have any
> > knowledge of specifics of the Schema. QAPIDoc performs double-duty here
> > both as a parser *and* as a finalized object that is part of the schema.
> >
> > I see three paths here:
> >
> > (1) Just use the TYPE_CHECKING trick to eliminate the cycle which is only
> >     present during static analysis.
> >
> > (2) Don't bother to annotate connect_member() et al, give them 'object'
> >     or 'Any'. I don't particularly like this, because it diminishes the
> >     usefulness of type hints for documentation purposes. Still, it's an
> >     extremely quick fix.
> >
> > (3) Reimplement doc <--> definition correlation directly in schema.py,
> >     integrating doc fields directly into QAPISchemaMember and relieving
> >     the QAPIDoc class of the responsibility. Users of the information
> >     would instead visit the members first and retrieve their
> >     documentation instead of the inverse operation -- visiting the
> >     documentation and retrieving their members.
> >
> > I prefer (3), but (1) is the easiest way to have my cake (strong type
> > hints) and eat it too (Not have import cycles). Do (1) for now, but plan
> > for (3). See also:
> >
> https://mypy.readthedocs.io/en/latest/runtime_troubles.html#import-cycles
> >
> > Signed-off-by: John Snow <jsnow@redhat.com>
> > ---
> >  scripts/qapi/parser.py | 15 +++++++++++----
> >  1 file changed, 11 insertions(+), 4 deletions(-)
> >
> > diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py
> > index 123fc2f099c..30b1d98df0b 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,12 @@
> >  from .source import QAPISourceInfo
> >
> >
> > +if TYPE_CHECKING:
> > +    # pylint: disable=cyclic-import
> > +    # TODO: Remove cycle. [schema -> expr -> parser -> schema]
> > +    from .schema import QAPISchemaFeature, QAPISchemaMember
> > +
> > +
> >  # Return value alias for get_expr().
> >  _ExprValue = Union[List[object], Dict[str, object], str, bool]
> >
> > @@ -473,9 +480,9 @@ def append(self, line):
> >      class ArgSection(Section):
> >          def __init__(self, parser, name, indent=0):
> >              super().__init__(parser, name, indent)
> > -            self.member = None
> > +            self.member: Optional['QAPISchemaMember'] = None
> >
> > -        def connect(self, member):
> > +        def connect(self, member: 'QAPISchemaMember') -> None:
> >              self.member = member
> >
> >      class NullSection(Section):
> > @@ -750,14 +757,14 @@ def _append_freeform(self, line):
> >                                   % match.group(1))
> >          self._section.append(line)
> >
> > -    def connect_member(self, member):
> > +    def connect_member(self, member: 'QAPISchemaMember') -> None:
> >          if member.name not in self.args:
> >              # Undocumented TODO outlaw
> >              self.args[member.name] = QAPIDoc.ArgSection(self._parser,
> >                                                          member.name)
> >          self.args[member.name].connect(member)
> >
> > -    def connect_feature(self, feature):
> > +    def connect_feature(self, feature: 'QAPISchemaFeature') -> None:
> >          if feature.name not in self.features:
> >              raise QAPISemError(feature.info,
> >                                 "feature '%s' lacks documentation"
>
> This adds just the type hints that cause the cycle.  I like that,
> because it illustrates the cycle.  Would be nice if the commit message
> mentioned this, perhaps
>
>   I prefer (3), but (1) is the easiest way to have my cake (strong type
>   hints) and eat it too (Not have import cycles). Do (1) for now, but plan
>   for (3). Also add the type hints that cause the cycle right away to
>   illustrate. See also:
>
> https://mypy.readthedocs.io/en/latest/runtime_troubles.html#import-cycles
>
> Slightly nicer, I think, would be swapping this and the next patch.
> Then that one's commit message needs to say something like "except for a
> few problematic ones, which the next commit will add".  Worthwhile?  Up
> to you.
>
>
Doing it the other way around means you can't squash the mypy patch into
the bulk-type-hints patch, but I think the git log usefulness is not better
or worse either way around. (Reviewer usefulness is maybe a ship that has
sailed, by now?)

--js

[-- Attachment #2: Type: text/html, Size: 7567 bytes --]

  reply	other threads:[~2021-09-30 17:13 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-09-29 19:44 [PATCH v3 00/13] qapi: static typing conversion, pt5b John Snow
2021-09-29 19:44 ` [PATCH v3 01/13] qapi/pylintrc: ignore 'consider-using-f-string' warning John Snow
2021-09-29 19:44 ` [PATCH v3 02/13] qapi/gen: use dict.items() to iterate over _modules John Snow
2021-09-29 19:44 ` [PATCH v3 03/13] qapi/parser: fix unused check_args_section arguments John Snow
2021-09-29 19:44 ` [PATCH v3 04/13] qapi: Add spaces after symbol declaration for consistency John Snow
2021-09-29 19:44 ` [PATCH v3 05/13] qapi/parser: improve detection of '@symbol:' preface John Snow
2021-09-30  8:42   ` Markus Armbruster
2021-09-30 17:43     ` John Snow
2021-09-29 19:44 ` [PATCH v3 06/13] qapi/parser: remove FIXME comment from _append_body_line John Snow
2021-09-30  8:47   ` Markus Armbruster
2021-09-30 17:20     ` John Snow
2021-09-29 19:44 ` [PATCH v3 07/13] qapi/parser: Simplify _end_section() John Snow
2021-09-29 19:44 ` [PATCH v3 08/13] qapi/parser: Introduce NullSection John Snow
2021-09-30  9:34   ` Markus Armbruster
2021-09-30 16:59     ` John Snow
2021-09-29 19:44 ` [PATCH v3 09/13] qapi/parser: add import cycle workaround John Snow
2021-09-30  9:45   ` Markus Armbruster
2021-09-30 17:11     ` John Snow [this message]
2021-09-29 19:44 ` [PATCH v3 10/13] qapi/parser: add type hint annotations (QAPIDoc) John Snow
2021-09-29 19:44 ` [PATCH v3 11/13] qapi/parser: enable mypy checks John Snow
2021-09-29 19:44 ` [PATCH v3 12/13] qapi/parser: Silence too-few-public-methods warning John Snow
2021-09-29 19:44 ` [PATCH v3 13/13] qapi/parser: enable pylint checks John Snow
2021-09-30 10:08 ` [PATCH v3 00/13] qapi: static typing conversion, pt5b 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='CAFn=p-ZhF0wpbwL0X5Xn7YLKNSth+Bhsm9ScZF=MJtKHa4cWXQ@mail.gmail.com' \
    --to=jsnow@redhat.com \
    --cc=armbru@redhat.com \
    --cc=crosa@redhat.com \
    --cc=eblake@redhat.com \
    --cc=ehabkost@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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).