qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: John Snow <jsnow@redhat.com>
To: Eduardo Habkost <ehabkost@redhat.com>
Cc: "Peter Maydell" <peter.maydell@linaro.org>,
	"Thomas Huth" <thuth@redhat.com>,
	qemu-devel@nongnu.org, "Markus Armbruster" <armbru@redhat.com>,
	"Cleber Rosa" <crosa@redhat.com>,
	"Alex Bennée" <alex.bennee@linaro.org>
Subject: Re: [PATCH v4 41/46] qapi/introspect.py: create a typed 'Node' data structure
Date: Wed, 30 Sep 2020 14:58:04 -0400	[thread overview]
Message-ID: <4282f45c-b5e2-6c60-8010-a9858098f395@redhat.com> (raw)
In-Reply-To: <20200930183918.GY3717385@habkost.net>

On 9/30/20 2:39 PM, Eduardo Habkost wrote:
> On Wed, Sep 30, 2020 at 12:31:45AM -0400, John Snow wrote:
>> This replaces _make_tree with Node.__init__(), effectively. By creating
>> it as a generic container, we can more accurately describe the exact
>> nature of this particular Node.
>>
>> Signed-off-by: John Snow <jsnow@redhat.com>
>> ---
>>   scripts/qapi/introspect.py | 77 +++++++++++++++++++-------------------
>>   1 file changed, 38 insertions(+), 39 deletions(-)
>>
>> diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py
>> index 43b6ba5df1f..86286e755ca 100644
>> --- a/scripts/qapi/introspect.py
>> +++ b/scripts/qapi/introspect.py
>> @@ -12,11 +12,12 @@
>>   
>>   from typing import (
>>       Dict,
>> +    Generic,
>> +    Iterable,
>>       List,
>>       Optional,
>>       Sequence,
>> -    Tuple,
>> -    Union,
>> +    TypeVar,
>>   )
>>   
>>   from .common import (
>> @@ -43,42 +44,42 @@
>>   
>>   
>>   # The correct type for TreeNode is actually:
>> -# Union[AnnotatedNode, List[TreeNode], Dict[str, TreeNode], str, bool]
>> +# Union[Node[TreeNode], List[TreeNode], Dict[str, TreeNode], str, bool]
>>   # but mypy does not support recursive types yet.
>>   TreeNode = object
>> +_NodeType = TypeVar('_NodeType', bound=TreeNode)
>>   _DObject = Dict[str, object]
>> -Extra = Dict[str, object]
>> -AnnotatedNode = Tuple[TreeNode, Extra]
> 
> Do you have plans to make Node replace TreeNode completely?
> 
> I'd understand this patch as a means to reach that goal, but I'm
> not sure the intermediate state of having both Node and TreeNode
> types (that can be confused with each other) is desirable.
> 

The problem is that _tree_to_qlit still accepts a broad array of types. 
The "TreeNode" comment above explains that those types are:

Node[TreeNode], List[TreeNode], Dict[str, TreeNode], str, bool

Three are containers, two are leaf values.
of the containers, the Node container is special in that it houses 
explicitly one of the four other types (but never itself.)

Even if I somehow always enforced Node[T] heading into _tree_to_qlit, I 
would still need to describe what 'T' is, which is another recursive 
type that I cannot exactly describe with mypy's current descriptive power:

INNER_TYPE = List[Node[INNER_TYPE]], Dict[str, Node[INNER_TYPE]], str, bool

And that's not really a huge win, or indeed any different to the 
existing TreeNode being an "object".

So ... no, I felt like I was going to stop here, where we have 
fundamentally:

1. Undecorated nodes (list, dict, str, bool) ("TreeNode")
2. Decorated nodes (Node[T])                 ("Node")

which leads to the question: Why bother swapping Tuple for Node at that 
point?

My answer is simply that having a strong type name allows us to 
distinguish this from garden-variety Tuples that might sneak in for 
other reasons in other data types.

Maybe we want a different nomenclature though, like Node vs AnnotatedNode?

--js

(Also: 'TreeNode' is just an alias for object, it doesn't mean anything 
grammatically. I could just as soon erase it entirely if you felt it 
provided no value. It doesn't enforce that it only takes objects we 
declared were 'TreeNode' types, for instance. It's just a preprocessor 
name, basically.)

>>   
>>   
>> -def _make_tree(obj: Union[_DObject, str], ifcond: List[str],
>> -               comment: Optional[str] = None) -> AnnotatedNode:
>> -    extra: Extra = {
>> -        'if': ifcond,
>> -        'comment': comment,
>> -    }
>> -    return (obj, extra)
>> +class Node(Generic[_NodeType]):
>> +    """
>> +    Node generally contains a SchemaInfo-like type (as a dict),
>> +    But it also used to wrap comments/ifconds around leaf value types.
>> +    """
>> +    # Remove after 3.7 adds @dataclass:
>> +    # pylint: disable=too-few-public-methods
>> +    def __init__(self, data: _NodeType, ifcond: Iterable[str],
>> +                 comment: Optional[str] = None):
>> +        self.data = data
>> +        self.comment: Optional[str] = comment
>> +        self.ifcond: Sequence[str] = tuple(ifcond)
>>   
>>   
>> -def _tree_to_qlit(obj: TreeNode,
>> -                  level: int = 0,
>> +def _tree_to_qlit(obj: TreeNode, level: int = 0,
>>                     suppress_first_indent: bool = False) -> str:
>>   
>>       def indent(level: int) -> str:
>>           return level * 4 * ' '
>>   
>> -    if isinstance(obj, tuple):
>> -        ifobj, extra = obj
>> -        ifcond = extra.get('if')
>> -        comment = extra.get('comment')
>> +    if isinstance(obj, Node):
>>           ret = ''
>> -        if comment:
>> -            ret += indent(level) + '/* %s */\n' % comment
>> -        if ifcond:
>> -            ret += gen_if(ifcond)
>> -        ret += _tree_to_qlit(ifobj, level)
>> -        if ifcond:
>> -            ret += '\n' + gen_endif(ifcond)
>> +        if obj.comment:
>> +            ret += indent(level) + '/* %s */\n' % obj.comment
>> +        if obj.ifcond:
>> +            ret += gen_if(obj.ifcond)
>> +        ret += _tree_to_qlit(obj.data, level)
>> +        if obj.ifcond:
>> +            ret += '\n' + gen_endif(obj.ifcond)
>>           return ret
>>   
>>       ret = ''
>> @@ -125,7 +126,7 @@ def __init__(self, prefix: str, unmask: bool):
>>               ' * QAPI/QMP schema introspection', __doc__)
>>           self._unmask = unmask
>>           self._schema: Optional[QAPISchema] = None
>> -        self._trees: List[AnnotatedNode] = []
>> +        self._trees: List[Node[_DObject]] = []
>>           self._used_types: List[QAPISchemaType] = []
>>           self._name_map: Dict[str, str] = {}
>>           self._genc.add(mcgen('''
>> @@ -192,9 +193,8 @@ def _use_type(self, typ: QAPISchemaType) -> str:
>>   
>>       @classmethod
>>       def _gen_features(cls,
>> -                      features: List[QAPISchemaFeature]
>> -                      ) -> List[AnnotatedNode]:
>> -        return [_make_tree(f.name, f.ifcond) for f in features]
>> +                      features: List[QAPISchemaFeature]) -> List[Node[str]]:
>> +        return [Node(f.name, f.ifcond) for f in features]
>>   
>>       def _gen_tree(self, name: str, mtype: str, obj: _DObject,
>>                     ifcond: List[str],
>> @@ -210,10 +210,10 @@ def _gen_tree(self, name: str, mtype: str, obj: _DObject,
>>           obj['meta-type'] = mtype
>>           if features:
>>               obj['features'] = self._gen_features(features)
>> -        self._trees.append(_make_tree(obj, ifcond, comment))
>> +        self._trees.append(Node(obj, ifcond, comment))
>>   
>>       def _gen_member(self,
>> -                    member: QAPISchemaObjectTypeMember) -> AnnotatedNode:
>> +                    member: QAPISchemaObjectTypeMember) -> Node[_DObject]:
>>           obj: _DObject = {
>>               'name': member.name,
>>               'type': self._use_type(member.type)
>> @@ -222,19 +222,19 @@ def _gen_member(self,
>>               obj['default'] = None
>>           if member.features:
>>               obj['features'] = self._gen_features(member.features)
>> -        return _make_tree(obj, member.ifcond)
>> +        return Node(obj, member.ifcond)
>>   
>>       def _gen_variants(self, tag_name: str,
>>                         variants: List[QAPISchemaVariant]) -> _DObject:
>>           return {'tag': tag_name,
>>                   'variants': [self._gen_variant(v) for v in variants]}
>>   
>> -    def _gen_variant(self, variant: QAPISchemaVariant) -> AnnotatedNode:
>> +    def _gen_variant(self, variant: QAPISchemaVariant) -> Node[_DObject]:
>>           obj: _DObject = {
>>               'case': variant.name,
>>               'type': self._use_type(variant.type)
>>           }
>> -        return _make_tree(obj, variant.ifcond)
>> +        return Node(obj, variant.ifcond)
>>   
>>       def visit_builtin_type(self, name: str, info: Optional[QAPISourceInfo],
>>                              json_type: str) -> None:
>> @@ -245,8 +245,7 @@ def visit_enum_type(self, name: str, info: QAPISourceInfo,
>>                           members: List[QAPISchemaEnumMember],
>>                           prefix: Optional[str]) -> None:
>>           self._gen_tree(name, 'enum',
>> -                       {'values': [_make_tree(m.name, m.ifcond, None)
>> -                                   for m in members]},
>> +                       {'values': [Node(m.name, m.ifcond) for m in members]},
>>                          ifcond, features)
>>   
>>       def visit_array_type(self, name: str, info: Optional[QAPISourceInfo],
>> @@ -274,9 +273,9 @@ def visit_alternate_type(self, name: str, info: QAPISourceInfo,
>>                                variants: QAPISchemaVariants) -> None:
>>           self._gen_tree(name, 'alternate',
>>                          {'members': [
>> -                           _make_tree({'type': self._use_type(m.type)},
>> -                                      m.ifcond, None)
>> -                           for m in variants.variants]},
>> +                           Node({'type': self._use_type(m.type)}, m.ifcond)
>> +                           for m in variants.variants
>> +                       ]},
>>                          ifcond, features)
>>   
>>       def visit_command(self, name: str, info: QAPISourceInfo, ifcond: List[str],
>> -- 
>> 2.26.2
>>
> 



  reply	other threads:[~2020-09-30 19:01 UTC|newest]

Thread overview: 69+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-09-30  4:31 [PATCH v4 00/46] qapi: static typing conversion, pt1 John Snow
2020-09-30  4:31 ` [PATCH v4 01/46] [DO-NOT-MERGE] docs: replace single backtick (`) with double-backtick (``) John Snow
2020-09-30  4:31 ` [PATCH v4 02/46] docs: repair broken references John Snow
2020-09-30  4:31 ` [PATCH v4 03/46] [DO-NOT-MERGE] docs/sphinx: change default role to "any" John Snow
2020-09-30  4:31 ` [PATCH v4 04/46] qapi: modify docstrings to be sphinx-compatible John Snow
2020-09-30  8:47   ` Markus Armbruster
2020-09-30 17:22     ` John Snow
2020-10-01  8:52       ` Markus Armbruster
2020-10-01 14:48         ` John Snow
2020-10-02  9:19           ` Markus Armbruster
2020-10-02 15:14             ` John Snow
2020-09-30 17:38     ` John Snow
2020-10-01  8:54       ` Markus Armbruster
2020-10-01 14:28         ` John Snow
2020-09-30  4:31 ` [PATCH v4 05/46] [DO-NOT-MERGE] docs: enable sphinx-autodoc for scripts/qapi John Snow
2020-09-30  4:31 ` [PATCH v4 06/46] qapi-gen: Separate arg-parsing from generation John Snow
2020-09-30  4:31 ` [PATCH v4 07/46] qapi: move generator entrypoint into module John Snow
2020-09-30  4:31 ` [PATCH v4 08/46] [DO-NOT-MERGE] docs: add scripts/qapi/main to python manual John Snow
2020-09-30  4:31 ` [PATCH v4 09/46] qapi: Prefer explicit relative imports John Snow
2020-09-30  4:31 ` [PATCH v4 10/46] qapi: Remove wildcard includes John Snow
2020-09-30  4:31 ` [PATCH v4 11/46] qapi: enforce import order/styling with isort John Snow
2020-09-30  4:31 ` [PATCH v4 12/46] qapi: delint using flake8 John Snow
2020-09-30  4:31 ` [PATCH v4 13/46] qapi: add pylintrc John Snow
2020-09-30  4:31 ` [PATCH v4 14/46] qapi/common.py: Remove python compatibility workaround John Snow
2020-09-30  4:31 ` [PATCH v4 15/46] qapi/common.py: Add indent manager John Snow
2020-09-30  4:31 ` [PATCH v4 16/46] qapi/common.py: delint with pylint John Snow
2020-09-30  4:31 ` [PATCH v4 17/46] qapi/common.py: Replace one-letter 'c' variable John Snow
2020-09-30  4:31 ` [PATCH v4 18/46] qapi/common.py: check with pylint John Snow
2020-09-30  4:31 ` [PATCH v4 19/46] qapi/common.py: add type hint annotations John Snow
2020-09-30  4:31 ` [PATCH v4 20/46] qapi/common.py: Convert comments into docstrings, and elaborate John Snow
2020-09-30  4:31 ` [PATCH v4 21/46] qapi/common.py: move build_params into gen.py John Snow
2020-09-30  4:31 ` [PATCH v4 22/46] qapi: establish mypy type-checking baseline John Snow
2020-09-30  4:31 ` [PATCH v4 23/46] qapi/events.py: add type hint annotations John Snow
2020-09-30  4:31 ` [PATCH v4 24/46] qapi/events.py: Move comments into docstrings John Snow
2020-09-30  4:31 ` [PATCH v4 25/46] qapi/commands.py: Don't re-bind to variable of different type John Snow
2020-09-30  4:31 ` [PATCH v4 26/46] qapi/commands.py: add type hint annotations John Snow
2020-09-30  4:31 ` [PATCH v4 27/46] qapi/commands.py: enable checking with mypy John Snow
2020-09-30  4:31 ` [PATCH v4 28/46] qapi/source.py: add type hint annotations John Snow
2020-09-30  4:31 ` [PATCH v4 29/46] qapi/source.py: delint with pylint John Snow
2020-09-30  4:31 ` [PATCH v4 30/46] qapi/gen.py: Fix edge-case of _is_user_module John Snow
2020-09-30 11:03   ` Eduardo Habkost
2020-09-30  4:31 ` [PATCH v4 31/46] qapi/gen.py: add type hint annotations John Snow
2020-09-30  4:31 ` [PATCH v4 32/46] qapi/gen.py: Enable checking with mypy John Snow
2020-09-30  4:31 ` [PATCH v4 33/46] qapi/gen.py: Remove unused parameter John Snow
2020-09-30  4:31 ` [PATCH v4 34/46] qapi/gen.py: update write() to be more idiomatic John Snow
2020-09-30  4:31 ` [PATCH v4 35/46] qapi/gen.py: delint with pylint John Snow
2020-09-30  4:31 ` [PATCH v4 36/46] qapi/introspect.py: assert obj is a dict when features are given John Snow
2020-09-30 11:04   ` Eduardo Habkost
2020-09-30  4:31 ` [PATCH v4 37/46] qapi/instrospect.py: add preliminary type hint annotations John Snow
2020-09-30 18:31   ` Eduardo Habkost
2020-09-30  4:31 ` [PATCH v4 38/46] qapi/introspect.py: add _gen_features helper John Snow
2020-09-30 17:21   ` Eduardo Habkost
2020-09-30  4:31 ` [PATCH v4 39/46] qapi/introspect.py: Unify return type of _make_tree() John Snow
2020-09-30 18:24   ` Eduardo Habkost
2020-09-30 18:32     ` John Snow
2020-09-30 18:57       ` Eduardo Habkost
2020-09-30 19:02         ` John Snow
2020-09-30  4:31 ` [PATCH v4 40/46] qapi/introspect.py: replace 'extra' dict with 'comment' argument John Snow
2020-09-30 18:24   ` Eduardo Habkost
2020-09-30  4:31 ` [PATCH v4 41/46] qapi/introspect.py: create a typed 'Node' data structure John Snow
2020-09-30 18:39   ` Eduardo Habkost
2020-09-30 18:58     ` John Snow [this message]
2020-09-30 19:52       ` Eduardo Habkost
2020-10-01 17:59         ` John Snow
2020-09-30  4:31 ` [PATCH v4 42/46] qapi/types.py: add type hint annotations John Snow
2020-09-30  4:31 ` [PATCH v4 43/46] qapi/types.py: remove one-letter variables John Snow
2020-09-30  4:31 ` [PATCH v4 44/46] qapi/visit.py: assert tag_member contains a QAPISchemaEnumType John Snow
2020-09-30  4:31 ` [PATCH v4 45/46] qapi/visit.py: remove unused parameters from gen_visit_object John Snow
2020-09-30  4:31 ` [PATCH v4 46/46] qapi/visit.py: add type hint annotations 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=4282f45c-b5e2-6c60-8010-a9858098f395@redhat.com \
    --to=jsnow@redhat.com \
    --cc=alex.bennee@linaro.org \
    --cc=armbru@redhat.com \
    --cc=crosa@redhat.com \
    --cc=ehabkost@redhat.com \
    --cc=peter.maydell@linaro.org \
    --cc=qemu-devel@nongnu.org \
    --cc=thuth@redhat.com \
    /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).