All of lore.kernel.org
 help / color / mirror / Atom feed
From: Markus Armbruster <armbru@redhat.com>
To: John Snow <jsnow@redhat.com>
Cc: qemu-devel@nongnu.org,  Michael Roth <michael.roth@amd.com>,
	 Peter Maydell <peter.maydell@linaro.org>
Subject: Re: [PATCH v2 13/19] qapi/schema: split "checked" field into "checking" and "checked"
Date: Tue, 16 Jan 2024 15:58:29 +0100	[thread overview]
Message-ID: <87o7dlqq96.fsf@pond.sub.org> (raw)
In-Reply-To: <20240112222945.3033854-14-jsnow@redhat.com> (John Snow's message of "Fri, 12 Jan 2024 17:29:39 -0500")

John Snow <jsnow@redhat.com> writes:

> differentiate between "actively in the process of checking" and
> "checking has completed". This allows us to clean up the types of some
> internal fields such as QAPISchemaObjectType's members field which
> currently uses "None" as a test for determining if check has been run
> already or not.
>
> This simplifies the typing from a cumbersome Optional[List[T]] to merely
> a List[T], which is more pythonic: it is safe to iterate over an empty
> list with "for x in []" whereas with an Optional[List[T]] you have to
> rely on the more cumbersome "if L: for x in L: ..."

Does this cumbersome form exist?

> Note that it is valid to have an empty members list, see the internal
> q_empty object as an example.

Yes.

.members becomes valid only in .check().  Before the patch, .__init__()
initializes it to None, and .check() sets it to the real value.  We use
assert .members is not None to catch invalid use.  We can also hope
invalid use without an assert would crash.  for m in .members would.

We've seen this pattern before: PATCH 4+5.  There, we change .__init__()
to declare the attribute without initializing it.  Use before it becomes
valid now certainly crashes, which is an improvement.  Why can't we do
the same here?

> Signed-off-by: John Snow <jsnow@redhat.com>
> ---
>  scripts/qapi/schema.py | 24 +++++++++++++++---------
>  1 file changed, 15 insertions(+), 9 deletions(-)
>
> diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
> index eefa58a798b..0d9a70ab4cb 100644
> --- a/scripts/qapi/schema.py
> +++ b/scripts/qapi/schema.py
> @@ -20,7 +20,7 @@
>  from collections import OrderedDict
>  import os
>  import re
> -from typing import List, Optional
> +from typing import List, Optional, cast
>  
>  from .common import (
>      POINTER_SUFFIX,
> @@ -457,22 +457,24 @@ def __init__(self, name, info, doc, ifcond, features,
>          self.base = None
>          self.local_members = local_members
>          self.variants = variants
> -        self.members = None
> +        self.members: List[QAPISchemaObjectTypeMember] = []
> +        self._checking = False
>  
>      def check(self, schema):
>          # This calls another type T's .check() exactly when the C
>          # struct emitted by gen_object() contains that T's C struct
>          # (pointers don't count).
> -        if self.members is not None:
> -            # A previous .check() completed: nothing to do
> -            return
> -        if self._checked:
> +        if self._checking:
>              # Recursed: C struct contains itself
>              raise QAPISemError(self.info,
>                                 "object %s contains itself" % self.name)
> +        if self._checked:
> +            # A previous .check() completed: nothing to do
> +            return
>  
> +        self._checking = True
>          super().check(schema)
> -        assert self._checked and self.members is None
> +        assert self._checked and not self.members
>  
>          seen = OrderedDict()
>          if self._base_name:
> @@ -489,13 +491,17 @@ def check(self, schema):
>          for m in self.local_members:
>              m.check(schema)
>              m.check_clash(self.info, seen)
> -        members = seen.values()
> +
> +        # check_clash is abstract, but local_members is asserted to be
> +        # List[QAPISchemaObjectTypeMember]. Cast to the narrower type.
> +        members = cast(List[QAPISchemaObjectTypeMember], list(seen.values()))
>  
>          if self.variants:
>              self.variants.check(schema, seen)
>              self.variants.check_clash(self.info, seen)
>  
> -        self.members = members  # mark completed
> +        self.members = members
> +        self._checking = False  # mark completed
>  
>      # Check that the members of this type do not cause duplicate JSON members,
>      # and update seen to track the members seen so far. Report any errors

I think you missed these:

       def is_empty(self):
           assert self.members is not None
           return not self.members and not self.variants

       def has_conditional_members(self):
           assert self.members is not None
           return any(m.ifcond.is_present() for m in self.members)

The assertions no longer work.  I figure you want to assert .checked
instead.

Consider splitting the patch: first add .checking, and replace use of
.members by use of .checking where possible.  Then change .members.  The
split may or may not be easier to describe and digest.  Use your
judgement.



  reply	other threads:[~2024-01-16 14:59 UTC|newest]

Thread overview: 46+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-01-12 22:29 [PATCH v2 00/19] qapi: statically type schema.py John Snow
2024-01-12 22:29 ` [PATCH v2 01/19] qapi: sort pylint suppressions John Snow
2024-01-15 12:18   ` Markus Armbruster
2024-01-15 19:58     ` John Snow
2024-01-16  6:48       ` Markus Armbruster
2024-01-12 22:29 ` [PATCH v2 02/19] qapi/schema: add " John Snow
2024-01-12 22:29 ` [PATCH v2 03/19] qapi: create QAPISchemaDefinition John Snow
2024-01-15 13:16   ` Markus Armbruster
2024-01-15 21:23     ` John Snow
2024-01-16  7:22       ` Markus Armbruster
2024-01-16 15:49         ` John Snow
2024-01-12 22:29 ` [PATCH v2 04/19] qapi/schema: declare type for QAPISchemaObjectTypeMember.type John Snow
2024-01-15 13:53   ` Markus Armbruster
2024-01-16 15:55     ` John Snow
2024-01-12 22:29 ` [PATCH v2 05/19] qapi/schema: declare type for QAPISchemaArrayType.element_type John Snow
2024-01-15 13:59   ` Markus Armbruster
2024-01-17 16:06     ` John Snow
2024-01-12 22:29 ` [PATCH v2 06/19] qapi/schema: make c_type() and json_type() abstract methods John Snow
2024-01-15 14:03   ` Markus Armbruster
2024-01-17 16:11     ` John Snow
2024-01-12 22:29 ` [PATCH v2 07/19] qapi/schema: adjust type narrowing for mypy's benefit John Snow
2024-01-12 22:29 ` [PATCH v2 08/19] qapi/schema: add type narrowing to lookup_type() John Snow
2024-01-12 22:29 ` [PATCH v2 09/19] qapi/schema: allow resolve_type to be used for built-in types John Snow
2024-01-16 11:09   ` Markus Armbruster
2024-01-17 16:44     ` John Snow
2024-01-22 13:12       ` Markus Armbruster
2024-01-31 22:28         ` John Snow
2024-01-31 23:04         ` John Snow
2024-01-12 22:29 ` [PATCH v2 10/19] qapi: use schema.resolve_type instead of schema.lookup_type John Snow
2024-01-12 22:29 ` [PATCH v2 11/19] qapi/schema: fix QAPISchemaArrayType.check's call to resolve_type John Snow
2024-01-16 12:17   ` Markus Armbruster
2024-01-17 16:48     ` John Snow
2024-01-17 20:00       ` Markus Armbruster
2024-01-12 22:29 ` [PATCH v2 12/19] qapi/schema: assert info is present when necessary John Snow
2024-01-16 13:37   ` Markus Armbruster
2024-01-12 22:29 ` [PATCH v2 13/19] qapi/schema: split "checked" field into "checking" and "checked" John Snow
2024-01-16 14:58   ` Markus Armbruster [this message]
2024-02-01 19:41     ` John Snow
2024-02-01 19:57       ` John Snow
2024-01-12 22:29 ` [PATCH v2 14/19] qapi/schema: fix typing for QAPISchemaVariants.tag_member John Snow
2024-01-17  8:22   ` Markus Armbruster
2024-01-12 22:29 ` [PATCH v2 15/19] qapi/schema: assert inner type of QAPISchemaVariants in check_clash() John Snow
2024-01-12 22:29 ` [PATCH v2 16/19] qapi/parser: demote QAPIExpression to Dict[str, Any] John Snow
2024-01-12 22:29 ` [PATCH v2 17/19] qapi/schema: add type hints John Snow
2024-01-12 22:29 ` [PATCH v2 18/19] qapi/schema: turn on mypy strictness John Snow
2024-01-12 22:29 ` [PATCH v2 19/19] qapi/schema: remove unnecessary asserts John Snow

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=87o7dlqq96.fsf@pond.sub.org \
    --to=armbru@redhat.com \
    --cc=jsnow@redhat.com \
    --cc=michael.roth@amd.com \
    --cc=peter.maydell@linaro.org \
    --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.