[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH v3 5/6] qapi: Add support for aliases
From: |
Markus Armbruster |
Subject: |
Re: [PATCH v3 5/6] qapi: Add support for aliases |
Date: |
Thu, 16 Sep 2021 09:49:48 +0200 |
User-agent: |
Gnus/5.13 (Gnus v5.13) Emacs/27.2 (gnu/linux) |
Kevin Wolf <kwolf@redhat.com> writes:
> Introduce alias definitions for object types (structs and unions). This
> allows using the same QAPI type and visitor for many syntax variations
> that exist in the external representation, like between QMP and the
> command line. It also provides a new tool for evolving the schema while
> maintaining backwards compatibility during a deprecation period.
>
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> ---
> docs/devel/qapi-code-gen.rst | 104 +++++++++++++++++++++-
> docs/sphinx/qapidoc.py | 2 +-
> scripts/qapi/expr.py | 47 +++++++++-
> scripts/qapi/schema.py | 116 +++++++++++++++++++++++--
> scripts/qapi/types.py | 4 +-
> scripts/qapi/visit.py | 34 +++++++-
> tests/qapi-schema/test-qapi.py | 7 +-
> tests/qapi-schema/double-type.err | 2 +-
> tests/qapi-schema/unknown-expr-key.err | 2 +-
> 9 files changed, 297 insertions(+), 21 deletions(-)
>
> diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst
> index 26c62b0e7b..c0883507a8 100644
> --- a/docs/devel/qapi-code-gen.rst
> +++ b/docs/devel/qapi-code-gen.rst
> @@ -262,7 +262,8 @@ Syntax::
> 'data': MEMBERS,
> '*base': STRING,
> '*if': COND,
> - '*features': FEATURES }
> + '*features': FEATURES,
> + '*aliases': ALIASES }
> MEMBERS = { MEMBER, ... }
> MEMBER = STRING : TYPE-REF
> | STRING : { 'type': TYPE-REF,
> @@ -312,6 +313,9 @@ the schema`_ below for more on this.
> The optional 'features' member specifies features. See Features_
> below for more on this.
>
> +The optional 'aliases' member specifies aliases. See Aliases_ below
> +for more on this.
> +
>
> Union types
> -----------
> @@ -321,13 +325,15 @@ Syntax::
> UNION = { 'union': STRING,
> 'data': BRANCHES,
> '*if': COND,
> - '*features': FEATURES }
> + '*features': FEATURES,
> + '*aliases': ALIASES }
> | { 'union': STRING,
> 'data': BRANCHES,
> 'base': ( MEMBERS | STRING ),
> 'discriminator': STRING,
> '*if': COND,
> - '*features': FEATURES }
> + '*features': FEATURES,
> + '*aliases': ALIASES }
> BRANCHES = { BRANCH, ... }
> BRANCH = STRING : TYPE-REF
> | STRING : { 'type': TYPE-REF, '*if': COND }
> @@ -437,6 +443,9 @@ the schema`_ below for more on this.
> The optional 'features' member specifies features. See Features_
> below for more on this.
>
> +The optional 'aliases' member specifies aliases. See Aliases_ below
> +for more on this.
> +
>
> Alternate types
> ---------------
> @@ -888,6 +897,95 @@ shows a conditional entity only when the condition is
> satisfied in
> this particular build.
>
>
> +Aliases
> +-------
> +
> +Object types, including structs and unions, can contain alias
> +definitions.
> +
> +Aliases define alternative member names that may be used in wire input
> +to provide a value for a member in the same object or in a nested
> +object.
> +
> +Syntax::
> +
> + ALIASES = [ ALIAS, ... ]
> + ALIAS = { '*name': STRING,
> + 'source': [ STRING, ... ] }
> +
> +If ``name`` is present, then the single member referred to by ``source``
> +is made accessible with the name given by ``name`` in the type where the
> +alias definition is specified.
> +
> +If ``name`` is not present, then this is a wildcard alias and all
> +members in the object referred to by ``source`` are made accessible in
> +the type where the alias definition is specified with the same name as
> +they have in ``source``.
> +
> +``source`` is a non-empty list of member names representing the path to
> +an object member. The first name is resolved in the same object. Each
> +subsequent member is resolved in the object named by the preceding
> +member.
> +
> +Do not use optional objects in the path of a wildcard alias unless there
> +is no semantic difference between an empty object and an absent object.
> +Absent objects are implicitly turned into empty ones if an alias could
> +apply and provide a value in the nested object, which is always the case
> +for wildcard aliases.
Is this still correct?
> +
> +Example: Alternative name for a member in the same object ::
> +
> + { 'struct': 'File',
> + 'data': { 'path': 'str' },
> + 'aliases': [ { 'name': 'filename', 'source': ['path'] } ] }
> +
> +The member ``path`` may instead be given through its alias ``filename``
> +in input.
> +
> +Example: Alias for a member in a nested object ::
> +
> + { 'struct': 'A',
> + 'data': { 'zahl': 'int' } }
> + { 'struct': 'B',
> + 'data': { 'drei': 'A' } }
> + { 'struct': 'C',
> + 'data': { 'zwei': 'B' } }
> + { 'struct': 'D',
> + 'data': { 'eins': 'C' },
> + 'aliases': [ { 'name': 'number',
> + 'source': ['eins', 'zwei', 'drei', 'zahl' ] },
> + { 'name': 'the_B',
> + 'source': ['eins','zwei'] } ] }
> +
> +With this definition, each of the following inputs for ``D`` mean the
> +same::
> +
> + { 'eins': { 'zwei': { 'drei': { 'zahl': 42 } } } }
> +
> + { 'the_B': { 'drei': { 'zahl': 42 } } }
> +
> + { 'number': 42 }
> +
> +Example: Flattening a simple union with a wildcard alias that maps all
> +members of ``data`` to the top level ::
> +
> + { 'union': 'SocketAddress',
> + 'data': {
> + 'inet': 'InetSocketAddress',
> + 'unix': 'UnixSocketAddress' },
> + 'aliases': [ { 'source': ['data'] } ] }
> +
> +Aliases are transitive: ``source`` may refer to another alias name. In
> +this case, the alias is effectively an alternative name for the source
> +of the other alias.
> +
> +In order to accommodate unions where variants differ in structure, it
> +is allowed to use a path that doesn't necessarily match an existing
> +member in every variant; in this case, the alias remains unused. The
> +QAPI generator checks that there is at least one branch for which an
> +alias could match.
> +
> +
> Documentation comments
> ----------------------
>
> diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py
> index 87c67ab23f..68340b8529 100644
> --- a/docs/sphinx/qapidoc.py
> +++ b/docs/sphinx/qapidoc.py
> @@ -313,7 +313,7 @@ def visit_enum_type(self, name, info, ifcond, features,
> members, prefix):
> + self._nodes_for_if_section(ifcond))
>
> def visit_object_type(self, name, info, ifcond, features,
> - base, members, variants):
> + base, members, variants, aliases):
> doc = self._cur_doc
> if base and base.is_implicit():
> base = None
[...]