From 7137a96099644734cd6045313823840d4cecd5e8 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 9 Oct 2020 12:15:27 -0400 Subject: qapi: Prefer explicit relative imports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All of the QAPI include statements are changed to be package-aware, as explicit relative imports. A quirk of Python packages is that the name of the package exists only *outside* of the package. This means that to a module inside of the qapi folder, there is inherently no such thing as the "qapi" package. The reason these imports work is because the "qapi" package exists in the context of the caller -- the execution shim, where sys.path includes a directory that has a 'qapi' folder in it. When we write "from qapi import sibling", we are NOT referencing the folder 'qapi', but rather "any package named qapi in sys.path". If you should so happen to have a 'qapi' package in your path, it will use *that* package. When we write "from .sibling import foo", we always reference explicitly our sibling module; guaranteeing consistency in *where* we are importing these modules from. This can be useful when working with virtual environments and packages in development mode. In development mode, a package is installed as a series of symlinks that forwards to your same source files. The problem arises because code quality checkers will follow "import qapi.x" to the "installed" version instead of the sibling file and -- even though they are the same file -- they have different module paths, and this causes cyclic import problems, false positive type mismatch errors, and more. It can also be useful when dealing with hierarchical packages, e.g. if we allow qemu.core.qmp, qemu.qapi.parser, etc. Signed-off-by: John Snow Reviewed-by: Eduardo Habkost Reviewed-by: Cleber Rosa Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20201009161558.107041-6-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- scripts/qapi/types.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'scripts/qapi/types.py') diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index 3640f17cd6..ca9a5aacb3 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -13,9 +13,9 @@ This work is licensed under the terms of the GNU GPL, version 2. # See the COPYING file in the top-level directory. """ -from qapi.common import * -from qapi.gen import QAPISchemaModularCVisitor, ifcontext -from qapi.schema import QAPISchemaEnumMember, QAPISchemaObjectType +from .common import * +from .gen import QAPISchemaModularCVisitor, ifcontext +from .schema import QAPISchemaEnumMember, QAPISchemaObjectType # variants must be emitted before their container; track what has already -- cgit 1.4.1 From 5af8263d40c698c47befd4c0bed3d6c452b56d82 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 9 Oct 2020 12:15:28 -0400 Subject: qapi: Remove wildcard includes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wildcard includes become hard to manage when refactoring and dealing with circular dependencies with strictly typed mypy. flake8 also flags each one as a warning, as it is not smart enough to know which names exist in the imported file. Remove them and include things explicitly by name instead. Signed-off-by: John Snow Reviewed-by: Eduardo Habkost Reviewed-by: Cleber Rosa Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20201009161558.107041-7-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- scripts/qapi/commands.py | 2 +- scripts/qapi/events.py | 7 ++++++- scripts/qapi/gen.py | 12 +++++++++--- scripts/qapi/introspect.py | 7 ++++++- scripts/qapi/types.py | 8 +++++++- scripts/qapi/visit.py | 10 +++++++++- 6 files changed, 38 insertions(+), 8 deletions(-) (limited to 'scripts/qapi/types.py') diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index 1f43a0a34e..e06c10afcd 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -13,7 +13,7 @@ This work is licensed under the terms of the GNU GPL, version 2. See the COPYING file in the top-level directory. """ -from .common import * +from .common import build_params, c_name, mcgen from .gen import QAPIGenCCode, QAPISchemaModularCVisitor, ifcontext diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py index 0467272438..6b3afa14d7 100644 --- a/scripts/qapi/events.py +++ b/scripts/qapi/events.py @@ -12,7 +12,12 @@ This work is licensed under the terms of the GNU GPL, version 2. See the COPYING file in the top-level directory. """ -from .common import * +from .common import ( + build_params, + c_enum_const, + c_name, + mcgen, +) from .gen import QAPISchemaModularCVisitor, ifcontext from .schema import QAPISchemaEnumMember from .types import gen_enum, gen_enum_lookup diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py index fc57fdca5b..1fed712b43 100644 --- a/scripts/qapi/gen.py +++ b/scripts/qapi/gen.py @@ -11,13 +11,19 @@ # This work is licensed under the terms of the GNU GPL, version 2. # See the COPYING file in the top-level directory. - +from contextlib import contextmanager import errno import os import re -from contextlib import contextmanager -from .common import * +from .common import ( + c_fname, + gen_endif, + gen_if, + guardend, + guardstart, + mcgen, +) from .schema import QAPISchemaVisitor diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index 6c82d9d95f..42016a7e66 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -10,7 +10,12 @@ This work is licensed under the terms of the GNU GPL, version 2. See the COPYING file in the top-level directory. """ -from .common import * +from .common import ( + c_name, + gen_endif, + gen_if, + mcgen, +) from .gen import QAPISchemaMonolithicCVisitor from .schema import (QAPISchemaArrayType, QAPISchemaBuiltinType, QAPISchemaType) diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index ca9a5aacb3..53b47f9e58 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -13,7 +13,13 @@ This work is licensed under the terms of the GNU GPL, version 2. # See the COPYING file in the top-level directory. """ -from .common import * +from .common import ( + c_enum_const, + c_name, + gen_endif, + gen_if, + mcgen, +) from .gen import QAPISchemaModularCVisitor, ifcontext from .schema import QAPISchemaEnumMember, QAPISchemaObjectType diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py index 7850f6e848..ea277e7704 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -13,7 +13,15 @@ This work is licensed under the terms of the GNU GPL, version 2. See the COPYING file in the top-level directory. """ -from .common import * +from .common import ( + c_enum_const, + c_name, + gen_endif, + gen_if, + mcgen, + pop_indent, + push_indent, +) from .gen import QAPISchemaModularCVisitor, ifcontext from .schema import QAPISchemaObjectType -- cgit 1.4.1 From cd073c8fb0e86f135dbc573c2651863867183a75 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 9 Oct 2020 12:15:54 -0400 Subject: qapi/types.py: add type hint annotations Annotations do not change runtime behavior. This commit *only* adds annotations. Signed-off-by: John Snow Reviewed-by: Eduardo Habkost Reviewed-by: Cleber Rosa Message-Id: <20201009161558.107041-33-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- scripts/qapi/mypy.ini | 5 --- scripts/qapi/types.py | 86 ++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 64 insertions(+), 27 deletions(-) (limited to 'scripts/qapi/types.py') diff --git a/scripts/qapi/mypy.ini b/scripts/qapi/mypy.ini index c6960ff2db..83f1981355 100644 --- a/scripts/qapi/mypy.ini +++ b/scripts/qapi/mypy.ini @@ -29,11 +29,6 @@ disallow_untyped_defs = False disallow_incomplete_defs = False check_untyped_defs = False -[mypy-qapi.types] -disallow_untyped_defs = False -disallow_incomplete_defs = False -check_untyped_defs = False - [mypy-qapi.visit] disallow_untyped_defs = False disallow_incomplete_defs = False diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index 53b47f9e58..766822feb3 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -13,6 +13,8 @@ This work is licensed under the terms of the GNU GPL, version 2. # See the COPYING file in the top-level directory. """ +from typing import List, Optional + from .common import ( c_enum_const, c_name, @@ -21,7 +23,16 @@ from .common import ( mcgen, ) from .gen import QAPISchemaModularCVisitor, ifcontext -from .schema import QAPISchemaEnumMember, QAPISchemaObjectType +from .schema import ( + QAPISchema, + QAPISchemaEnumMember, + QAPISchemaFeature, + QAPISchemaObjectType, + QAPISchemaObjectTypeMember, + QAPISchemaType, + QAPISchemaVariants, +) +from .source import QAPISourceInfo # variants must be emitted before their container; track what has already @@ -29,7 +40,9 @@ from .schema import QAPISchemaEnumMember, QAPISchemaObjectType objects_seen = set() -def gen_enum_lookup(name, members, prefix=None): +def gen_enum_lookup(name: str, + members: List[QAPISchemaEnumMember], + prefix: Optional[str] = None) -> str: ret = mcgen(''' const QEnumLookup %(c_name)s_lookup = { @@ -54,7 +67,9 @@ const QEnumLookup %(c_name)s_lookup = { return ret -def gen_enum(name, members, prefix=None): +def gen_enum(name: str, + members: List[QAPISchemaEnumMember], + prefix: Optional[str] = None) -> str: # append automatically generated _MAX value enum_members = members + [QAPISchemaEnumMember('_MAX', None)] @@ -88,7 +103,7 @@ extern const QEnumLookup %(c_name)s_lookup; return ret -def gen_fwd_object_or_array(name): +def gen_fwd_object_or_array(name: str) -> str: return mcgen(''' typedef struct %(c_name)s %(c_name)s; @@ -96,7 +111,7 @@ typedef struct %(c_name)s %(c_name)s; c_name=c_name(name)) -def gen_array(name, element_type): +def gen_array(name: str, element_type: QAPISchemaType) -> str: return mcgen(''' struct %(c_name)s { @@ -107,7 +122,7 @@ struct %(c_name)s { c_name=c_name(name), c_type=element_type.c_type()) -def gen_struct_members(members): +def gen_struct_members(members: List[QAPISchemaObjectTypeMember]) -> str: ret = '' for memb in members: ret += gen_if(memb.ifcond) @@ -124,7 +139,10 @@ def gen_struct_members(members): return ret -def gen_object(name, ifcond, base, members, variants): +def gen_object(name: str, ifcond: List[str], + base: Optional[QAPISchemaObjectType], + members: List[QAPISchemaObjectTypeMember], + variants: Optional[QAPISchemaVariants]) -> str: if name in objects_seen: return '' objects_seen.add(name) @@ -178,7 +196,7 @@ struct %(c_name)s { return ret -def gen_upcast(name, base): +def gen_upcast(name: str, base: QAPISchemaObjectType) -> str: # C makes const-correctness ugly. We have to cast away const to let # this function work for both const and non-const obj. return mcgen(''' @@ -191,7 +209,7 @@ static inline %(base)s *qapi_%(c_name)s_base(const %(c_name)s *obj) c_name=c_name(name), base=base.c_name()) -def gen_variants(variants): +def gen_variants(variants: QAPISchemaVariants) -> str: ret = mcgen(''' union { /* union tag is @%(c_name)s */ ''', @@ -215,7 +233,7 @@ def gen_variants(variants): return ret -def gen_type_cleanup_decl(name): +def gen_type_cleanup_decl(name: str) -> str: ret = mcgen(''' void qapi_free_%(c_name)s(%(c_name)s *obj); @@ -225,7 +243,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(%(c_name)s, qapi_free_%(c_name)s) return ret -def gen_type_cleanup(name): +def gen_type_cleanup(name: str) -> str: ret = mcgen(''' void qapi_free_%(c_name)s(%(c_name)s *obj) @@ -247,12 +265,12 @@ void qapi_free_%(c_name)s(%(c_name)s *obj) class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): - def __init__(self, prefix): + def __init__(self, prefix: str): super().__init__( prefix, 'qapi-types', ' * Schema-defined QAPI types', ' * Built-in QAPI types', __doc__) - def _begin_system_module(self, name): + def _begin_system_module(self, name: None) -> None: self._genc.preamble_add(mcgen(''' #include "qemu/osdep.h" #include "qapi/dealloc-visitor.h" @@ -263,7 +281,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): #include "qapi/util.h" ''')) - def _begin_user_module(self, name): + def _begin_user_module(self, name: str) -> None: types = self._module_basename('qapi-types', name) visit = self._module_basename('qapi-visit', name) self._genc.preamble_add(mcgen(''' @@ -277,27 +295,43 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): #include "qapi/qapi-builtin-types.h" ''')) - def visit_begin(self, schema): + def visit_begin(self, schema: QAPISchema) -> None: # gen_object() is recursive, ensure it doesn't visit the empty type objects_seen.add(schema.the_empty_object_type.name) - def _gen_type_cleanup(self, name): + def _gen_type_cleanup(self, name: str) -> None: self._genh.add(gen_type_cleanup_decl(name)) self._genc.add(gen_type_cleanup(name)) - def visit_enum_type(self, name, info, ifcond, features, members, prefix): + def visit_enum_type(self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: List[str], + features: List[QAPISchemaFeature], + members: List[QAPISchemaEnumMember], + prefix: Optional[str]) -> None: with ifcontext(ifcond, self._genh, self._genc): self._genh.preamble_add(gen_enum(name, members, prefix)) self._genc.add(gen_enum_lookup(name, members, prefix)) - def visit_array_type(self, name, info, ifcond, element_type): + def visit_array_type(self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: List[str], + element_type: QAPISchemaType) -> None: with ifcontext(ifcond, self._genh, self._genc): self._genh.preamble_add(gen_fwd_object_or_array(name)) self._genh.add(gen_array(name, element_type)) self._gen_type_cleanup(name) - def visit_object_type(self, name, info, ifcond, features, - base, members, variants): + def visit_object_type(self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: List[str], + features: List[QAPISchemaFeature], + base: Optional[QAPISchemaObjectType], + members: List[QAPISchemaObjectTypeMember], + variants: Optional[QAPISchemaVariants]) -> None: # Nothing to do for the special empty builtin if name == 'q_empty': return @@ -313,7 +347,12 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): # implicit types won't be directly allocated/freed self._gen_type_cleanup(name) - def visit_alternate_type(self, name, info, ifcond, features, variants): + def visit_alternate_type(self, + name: str, + info: QAPISourceInfo, + ifcond: List[str], + features: List[QAPISchemaFeature], + variants: QAPISchemaVariants) -> None: with ifcontext(ifcond, self._genh): self._genh.preamble_add(gen_fwd_object_or_array(name)) self._genh.add(gen_object(name, ifcond, None, @@ -322,7 +361,10 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): self._gen_type_cleanup(name) -def gen_types(schema, output_dir, prefix, opt_builtins): +def gen_types(schema: QAPISchema, + output_dir: str, + prefix: str, + opt_builtins: bool) -> None: vis = QAPISchemaGenTypeVisitor(prefix) schema.visit(vis) vis.write(output_dir, opt_builtins) -- cgit 1.4.1 From dec44d3d659446c333c52e0c95c996b6881f94d6 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 9 Oct 2020 12:15:55 -0400 Subject: qapi/types.py: remove one-letter variables "John, if pylint told you to jump off a bridge, would you?" Hey, if it looked like fun, I might. Now that this file is clean, enable pylint checks on this file. Signed-off-by: John Snow Reviewed-by: Eduardo Habkost Reviewed-by: Cleber Rosa Message-Id: <20201009161558.107041-34-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- scripts/qapi/pylintrc | 1 - scripts/qapi/types.py | 29 +++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) (limited to 'scripts/qapi/types.py') diff --git a/scripts/qapi/pylintrc b/scripts/qapi/pylintrc index 8badcb11cd..b3c4cf46db 100644 --- a/scripts/qapi/pylintrc +++ b/scripts/qapi/pylintrc @@ -6,7 +6,6 @@ ignore-patterns=error.py, expr.py, parser.py, schema.py, - types.py, visit.py, diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index 766822feb3..2b4916cdaa 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -49,14 +49,14 @@ const QEnumLookup %(c_name)s_lookup = { .array = (const char *const[]) { ''', c_name=c_name(name)) - for m in members: - ret += gen_if(m.ifcond) - index = c_enum_const(name, m.name, prefix) + for memb in members: + ret += gen_if(memb.ifcond) + index = c_enum_const(name, memb.name, prefix) ret += mcgen(''' [%(index)s] = "%(name)s", ''', - index=index, name=m.name) - ret += gen_endif(m.ifcond) + index=index, name=memb.name) + ret += gen_endif(memb.ifcond) ret += mcgen(''' }, @@ -79,13 +79,13 @@ typedef enum %(c_name)s { ''', c_name=c_name(name)) - for m in enum_members: - ret += gen_if(m.ifcond) + for memb in enum_members: + ret += gen_if(memb.ifcond) ret += mcgen(''' %(c_enum)s, ''', - c_enum=c_enum_const(name, m.name, prefix)) - ret += gen_endif(m.ifcond) + c_enum=c_enum_const(name, memb.name, prefix)) + ret += gen_endif(memb.ifcond) ret += mcgen(''' } %(c_name)s; @@ -148,11 +148,12 @@ def gen_object(name: str, ifcond: List[str], objects_seen.add(name) ret = '' - if variants: - for v in variants.variants: - if isinstance(v.type, QAPISchemaObjectType): - ret += gen_object(v.type.name, v.type.ifcond, v.type.base, - v.type.local_members, v.type.variants) + for var in variants.variants if variants else (): + obj = var.type + if not isinstance(obj, QAPISchemaObjectType): + continue + ret += gen_object(obj.name, obj.ifcond, obj.base, + obj.local_members, obj.variants) ret += mcgen(''' -- cgit 1.4.1