@ -17,6 +17,7 @@ from typing import (
Iterable ,
List ,
Optional ,
Sequence ,
Tuple ,
TypeVar ,
Union ,
@ -30,10 +31,19 @@ from .common import (
)
from . gen import QAPISchemaMonolithicCVisitor
from . schema import (
QAPISchema ,
QAPISchemaArrayType ,
QAPISchemaBuiltinType ,
QAPISchemaEntity ,
QAPISchemaEnumMember ,
QAPISchemaFeature ,
QAPISchemaObjectType ,
QAPISchemaObjectTypeMember ,
QAPISchemaType ,
QAPISchemaVariant ,
QAPISchemaVariants ,
)
from . source import QAPISourceInfo
# This module constructs a tree data structure that is used to
@ -58,6 +68,16 @@ _NonScalar = Union[Dict[str, _Stub], List[_Stub]]
_Value = Union [ _Scalar , _NonScalar ]
JSONValue = Union [ _Value , ' Annotated[_Value] ' ]
# These types are based on structures defined in QEMU's schema, so we
# lack precise types for them here. Python 3.6 does not offer
# TypedDict constructs, so they are broadly typed here as simple
# Python Dicts.
SchemaInfo = Dict [ str , object ]
SchemaInfoObject = Dict [ str , object ]
SchemaInfoObjectVariant = Dict [ str , object ]
SchemaInfoObjectMember = Dict [ str , object ]
SchemaInfoCommand = Dict [ str , object ]
_ValueT = TypeVar ( ' _ValueT ' , bound = _Value )
@ -77,9 +97,11 @@ class Annotated(Generic[_ValueT]):
self . ifcond : Tuple [ str , . . . ] = tuple ( ifcond )
def _tree_to_qlit ( obj , level = 0 , dict_value = False ) :
def _tree_to_qlit ( obj : JSONValue ,
level : int = 0 ,
dict_value : bool = False ) - > str :
def indent ( level ) :
def indent ( level : int ) - > str :
return level * 4 * ' '
if isinstance ( obj , Annotated ) :
@ -137,21 +159,21 @@ def _tree_to_qlit(obj, level=0, dict_value=False):
return ret
def to_c_string ( string ) :
def to_c_string ( string : str ) - > str :
return ' " ' + string . replace ( ' \\ ' , r ' \\ ' ) . replace ( ' " ' , r ' \ " ' ) + ' " '
class QAPISchemaGenIntrospectVisitor ( QAPISchemaMonolithicCVisitor ) :
def __init__ ( self , prefix , unmask ) :
def __init__ ( self , prefix : str , unmask : bool ) :
super ( ) . __init__ (
prefix , ' qapi-introspect ' ,
' * QAPI/QMP schema introspection ' , __doc__ )
self . _unmask = unmask
self . _schema = None
self . _trees = [ ]
self . _used_types = [ ]
self . _name_map = { }
self . _schema : Optional [ QAPISchema ] = None
self . _trees : List [ Annotated [ SchemaInfo ] ] = [ ]
self . _used_types : List [ QAPISchemaType ] = [ ]
self . _name_map : Dict [ str , str ] = { }
self . _genc . add ( mcgen ( '''
#include "qemu/osdep.h"
#include "%(prefix)sqapi-introspect.h"
@ -159,10 +181,10 @@ class QAPISchemaGenIntrospectVisitor(QAPISchemaMonolithicCVisitor):
''' ,
prefix = prefix ) )
def visit_begin ( self , schema ) :
def visit_begin ( self , schema : QAPISchema ) - > None :
self . _schema = schema
def visit_end ( self ) :
def visit_end ( self ) - > None :
# visit the types that are actually used
for typ in self . _used_types :
typ . visit ( self )
@ -184,18 +206,18 @@ const QLitObject %(c_name)s = %(c_string)s;
self . _used_types = [ ]
self . _name_map = { }
def visit_needed ( self , entity ) :
def visit_needed ( self , entity : QAPISchemaEntity ) - > bool :
# Ignore types on first pass; visit_end() will pick up used types
return not isinstance ( entity , QAPISchemaType )
def _name ( self , name ) :
def _name ( self , name : str ) - > str :
if self . _unmask :
return name
if name not in self . _name_map :
self . _name_map [ name ] = ' %d ' % len ( self . _name_map )
return self . _name_map [ name ]
def _use_type ( self , typ ) :
def _use_type ( self , typ : QAPISchemaType ) - > str :
assert self . _schema is not None
# Map the various integer types to plain int
@ -217,10 +239,13 @@ const QLitObject %(c_name)s = %(c_string)s;
return self . _name ( typ . name )
@staticmethod
def _gen_features ( features ) :
def _gen_features ( features : List [ QAPISchemaFeature ]
) - > List [ Annotated [ str ] ] :
return [ Annotated ( f . name , f . ifcond ) for f in features ]
def _gen_tree ( self , name , mtype , obj , ifcond , features ) :
def _gen_tree ( self , name : str , mtype : str , obj : Dict [ str , object ] ,
ifcond : Sequence [ str ] ,
features : Optional [ List [ QAPISchemaFeature ] ] ) - > None :
comment : Optional [ str ] = None
if mtype not in ( ' command ' , ' event ' , ' builtin ' , ' array ' ) :
if not self . _unmask :
@ -234,42 +259,65 @@ const QLitObject %(c_name)s = %(c_string)s;
obj [ ' features ' ] = self . _gen_features ( features )
self . _trees . append ( Annotated ( obj , ifcond , comment ) )
def _gen_member ( self , member ) :
obj = { ' name ' : member . name , ' type ' : self . _use_type ( member . type ) }
def _gen_member ( self , member : QAPISchemaObjectTypeMember
) - > Annotated [ SchemaInfoObjectMember ] :
obj : SchemaInfoObjectMember = {
' name ' : member . name ,
' type ' : self . _use_type ( member . type )
}
if member . optional :
obj [ ' default ' ] = None
if member . features :
obj [ ' features ' ] = self . _gen_features ( member . features )
return Annotated ( obj , member . ifcond )
def _gen_variant ( self , variant ) :
obj = { ' case ' : variant . name , ' type ' : self . _use_type ( variant . type ) }
def _gen_variant ( self , variant : QAPISchemaVariant
) - > Annotated [ SchemaInfoObjectVariant ] :
obj : SchemaInfoObjectVariant = {
' case ' : variant . name ,
' type ' : self . _use_type ( variant . type )
}
return Annotated ( obj , variant . ifcond )
def visit_builtin_type ( self , name , info , json_type ) :
def visit_builtin_type ( self , name : str , info : Optional [ QAPISourceInfo ] ,
json_type : str ) - > None :
self . _gen_tree ( name , ' builtin ' , { ' json-type ' : json_type } , [ ] , None )
def visit_enum_type ( self , name , info , ifcond , features , members , prefix ) :
def visit_enum_type ( self , name : str , info : Optional [ QAPISourceInfo ] ,
ifcond : Sequence [ str ] ,
features : List [ QAPISchemaFeature ] ,
members : List [ QAPISchemaEnumMember ] ,
prefix : Optional [ str ] ) - > None :
self . _gen_tree (
name , ' enum ' ,
{ ' values ' : [ Annotated ( m . name , m . ifcond ) for m in members ] } ,
ifcond , features
)
def visit_array_type ( self , name , info , ifcond , element_type ) :
def visit_array_type ( self , name : str , info : Optional [ QAPISourceInfo ] ,
ifcond : Sequence [ str ] ,
element_type : QAPISchemaType ) - > None :
element = self . _use_type ( element_type )
self . _gen_tree ( ' [ ' + element + ' ] ' , ' array ' , { ' element-type ' : element } ,
ifcond , None )
def visit_object_type_flat ( self , name , info , ifcond , features ,
members , variants ) :
obj = { ' members ' : [ self . _gen_member ( m ) for m in members ] }
def visit_object_type_flat ( self , name : str , info : Optional [ QAPISourceInfo ] ,
ifcond : Sequence [ str ] ,
features : List [ QAPISchemaFeature ] ,
members : List [ QAPISchemaObjectTypeMember ] ,
variants : Optional [ QAPISchemaVariants ] ) - > None :
obj : SchemaInfoObject = {
' members ' : [ self . _gen_member ( m ) for m in members ]
}
if variants :
obj [ ' tag ' ] = variants . tag_member . name
obj [ ' variants ' ] = [ self . _gen_variant ( v ) for v in variants . variants ]
self . _gen_tree ( name , ' object ' , obj , ifcond , features )
def visit_alternate_type ( self , name , info , ifcond , features , variants ) :
def visit_alternate_type ( self , name : str , info : Optional [ QAPISourceInfo ] ,
ifcond : Sequence [ str ] ,
features : List [ QAPISchemaFeature ] ,
variants : QAPISchemaVariants ) - > None :
self . _gen_tree (
name , ' alternate ' ,
{ ' members ' : [ Annotated ( { ' type ' : self . _use_type ( m . type ) } ,
@ -278,27 +326,38 @@ const QLitObject %(c_name)s = %(c_string)s;
ifcond , features
)
def visit_command ( self , name , info , ifcond , features ,
arg_type , ret_type , gen , success_response , boxed ,
allow_oob , allow_preconfig , coroutine ) :
def visit_command ( self , name : str , info : Optional [ QAPISourceInfo ] ,
ifcond : Sequence [ str ] ,
features : List [ QAPISchemaFeature ] ,
arg_type : Optional [ QAPISchemaObjectType ] ,
ret_type : Optional [ QAPISchemaType ] , gen : bool ,
success_response : bool , boxed : bool , allow_oob : bool ,
allow_preconfig : bool , coroutine : bool ) - > None :
assert self . _schema is not None
arg_type = arg_type or self . _schema . the_empty_object_type
ret_type = ret_type or self . _schema . the_empty_object_type
obj = { ' arg-type ' : self . _use_type ( arg_type ) ,
' ret-type ' : self . _use_type ( ret_type ) }
obj : SchemaInfoCommand = {
' arg-type ' : self . _use_type ( arg_type ) ,
' ret-type ' : self . _use_type ( ret_type )
}
if allow_oob :
obj [ ' allow-oob ' ] = allow_oob
self . _gen_tree ( name , ' command ' , obj , ifcond , features )
def visit_event ( self , name , info , ifcond , features , arg_type , boxed ) :
def visit_event ( self , name : str , info : Optional [ QAPISourceInfo ] ,
ifcond : Sequence [ str ] , features : List [ QAPISchemaFeature ] ,
arg_type : Optional [ QAPISchemaObjectType ] ,
boxed : bool ) - > None :
assert self . _schema is not None
arg_type = arg_type or self . _schema . the_empty_object_type
self . _gen_tree ( name , ' event ' , { ' arg-type ' : self . _use_type ( arg_type ) } ,
ifcond , features )
def gen_introspect ( schema , output_dir , prefix , opt_unmask ) :
def gen_introspect ( schema : QAPISchema , output_dir : str , prefix : str ,
opt_unmask : bool ) - > None :
vis = QAPISchemaGenIntrospectVisitor ( prefix , opt_unmask )
schema . visit ( vis )
vis . write ( output_dir )