mirror of
				https://github.com/qemu/qemu.git
				synced 2025-10-31 12:07:31 +00:00 
			
		
		
		
	 2668dc7b5d
			
		
	
	
		2668dc7b5d
		
	
	
	
	
		
			
			Add a new dbus-doc directive to import D-Bus interfaces documentation from the introspection XML. The comments annotations follow the gtkdoc/kerneldoc style, and should be formatted with reST. Note: I realize after the fact that I was implementing those modules with sphinx 4, and that we have much lower requirements. Instead of lowering the features and code (removing type annotations etc), let's have a warning in the documentation when the D-Bus modules can't be used, and point to the source XML file in that case. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Acked-by: Gerd Hoffmann <kraxel@redhat.com>
		
			
				
	
	
		
			374 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			374 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # Based from "GDBus - GLib D-Bus Library":
 | |
| #
 | |
| # Copyright (C) 2008-2011 Red Hat, Inc.
 | |
| #
 | |
| # This library is free software; you can redistribute it and/or
 | |
| # modify it under the terms of the GNU Lesser General Public
 | |
| # License as published by the Free Software Foundation; either
 | |
| # version 2.1 of the License, or (at your option) any later version.
 | |
| #
 | |
| # This library is distributed in the hope that it will be useful,
 | |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
| # Lesser General Public License for more details.
 | |
| #
 | |
| # You should have received a copy of the GNU Lesser General
 | |
| # Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
 | |
| #
 | |
| # Author: David Zeuthen <davidz@redhat.com>
 | |
| 
 | |
| import xml.parsers.expat
 | |
| 
 | |
| 
 | |
| class Annotation:
 | |
|     def __init__(self, key, value):
 | |
|         self.key = key
 | |
|         self.value = value
 | |
|         self.annotations = []
 | |
|         self.since = ""
 | |
| 
 | |
| 
 | |
| class Arg:
 | |
|     def __init__(self, name, signature):
 | |
|         self.name = name
 | |
|         self.signature = signature
 | |
|         self.annotations = []
 | |
|         self.doc_string = ""
 | |
|         self.since = ""
 | |
| 
 | |
| 
 | |
| class Method:
 | |
|     def __init__(self, name, h_type_implies_unix_fd=True):
 | |
|         self.name = name
 | |
|         self.h_type_implies_unix_fd = h_type_implies_unix_fd
 | |
|         self.in_args = []
 | |
|         self.out_args = []
 | |
|         self.annotations = []
 | |
|         self.doc_string = ""
 | |
|         self.since = ""
 | |
|         self.deprecated = False
 | |
|         self.unix_fd = False
 | |
| 
 | |
| 
 | |
| class Signal:
 | |
|     def __init__(self, name):
 | |
|         self.name = name
 | |
|         self.args = []
 | |
|         self.annotations = []
 | |
|         self.doc_string = ""
 | |
|         self.since = ""
 | |
|         self.deprecated = False
 | |
| 
 | |
| 
 | |
| class Property:
 | |
|     def __init__(self, name, signature, access):
 | |
|         self.name = name
 | |
|         self.signature = signature
 | |
|         self.access = access
 | |
|         self.annotations = []
 | |
|         self.arg = Arg("value", self.signature)
 | |
|         self.arg.annotations = self.annotations
 | |
|         self.readable = False
 | |
|         self.writable = False
 | |
|         if self.access == "readwrite":
 | |
|             self.readable = True
 | |
|             self.writable = True
 | |
|         elif self.access == "read":
 | |
|             self.readable = True
 | |
|         elif self.access == "write":
 | |
|             self.writable = True
 | |
|         else:
 | |
|             raise ValueError('Invalid access type "{}"'.format(self.access))
 | |
|         self.doc_string = ""
 | |
|         self.since = ""
 | |
|         self.deprecated = False
 | |
|         self.emits_changed_signal = True
 | |
| 
 | |
| 
 | |
| class Interface:
 | |
|     def __init__(self, name):
 | |
|         self.name = name
 | |
|         self.methods = []
 | |
|         self.signals = []
 | |
|         self.properties = []
 | |
|         self.annotations = []
 | |
|         self.doc_string = ""
 | |
|         self.doc_string_brief = ""
 | |
|         self.since = ""
 | |
|         self.deprecated = False
 | |
| 
 | |
| 
 | |
| class DBusXMLParser:
 | |
|     STATE_TOP = "top"
 | |
|     STATE_NODE = "node"
 | |
|     STATE_INTERFACE = "interface"
 | |
|     STATE_METHOD = "method"
 | |
|     STATE_SIGNAL = "signal"
 | |
|     STATE_PROPERTY = "property"
 | |
|     STATE_ARG = "arg"
 | |
|     STATE_ANNOTATION = "annotation"
 | |
|     STATE_IGNORED = "ignored"
 | |
| 
 | |
|     def __init__(self, xml_data, h_type_implies_unix_fd=True):
 | |
|         self._parser = xml.parsers.expat.ParserCreate()
 | |
|         self._parser.CommentHandler = self.handle_comment
 | |
|         self._parser.CharacterDataHandler = self.handle_char_data
 | |
|         self._parser.StartElementHandler = self.handle_start_element
 | |
|         self._parser.EndElementHandler = self.handle_end_element
 | |
| 
 | |
|         self.parsed_interfaces = []
 | |
|         self._cur_object = None
 | |
| 
 | |
|         self.state = DBusXMLParser.STATE_TOP
 | |
|         self.state_stack = []
 | |
|         self._cur_object = None
 | |
|         self._cur_object_stack = []
 | |
| 
 | |
|         self.doc_comment_last_symbol = ""
 | |
| 
 | |
|         self._h_type_implies_unix_fd = h_type_implies_unix_fd
 | |
| 
 | |
|         self._parser.Parse(xml_data)
 | |
| 
 | |
|     COMMENT_STATE_BEGIN = "begin"
 | |
|     COMMENT_STATE_PARAMS = "params"
 | |
|     COMMENT_STATE_BODY = "body"
 | |
|     COMMENT_STATE_SKIP = "skip"
 | |
| 
 | |
|     def handle_comment(self, data):
 | |
|         comment_state = DBusXMLParser.COMMENT_STATE_BEGIN
 | |
|         lines = data.split("\n")
 | |
|         symbol = ""
 | |
|         body = ""
 | |
|         in_para = False
 | |
|         params = {}
 | |
|         for line in lines:
 | |
|             orig_line = line
 | |
|             line = line.lstrip()
 | |
|             if comment_state == DBusXMLParser.COMMENT_STATE_BEGIN:
 | |
|                 if len(line) > 0:
 | |
|                     colon_index = line.find(": ")
 | |
|                     if colon_index == -1:
 | |
|                         if line.endswith(":"):
 | |
|                             symbol = line[0 : len(line) - 1]
 | |
|                             comment_state = DBusXMLParser.COMMENT_STATE_PARAMS
 | |
|                         else:
 | |
|                             comment_state = DBusXMLParser.COMMENT_STATE_SKIP
 | |
|                     else:
 | |
|                         symbol = line[0:colon_index]
 | |
|                         rest_of_line = line[colon_index + 2 :].strip()
 | |
|                         if len(rest_of_line) > 0:
 | |
|                             body += rest_of_line + "\n"
 | |
|                         comment_state = DBusXMLParser.COMMENT_STATE_PARAMS
 | |
|             elif comment_state == DBusXMLParser.COMMENT_STATE_PARAMS:
 | |
|                 if line.startswith("@"):
 | |
|                     colon_index = line.find(": ")
 | |
|                     if colon_index == -1:
 | |
|                         comment_state = DBusXMLParser.COMMENT_STATE_BODY
 | |
|                         if not in_para:
 | |
|                             in_para = True
 | |
|                         body += orig_line + "\n"
 | |
|                     else:
 | |
|                         param = line[1:colon_index]
 | |
|                         docs = line[colon_index + 2 :]
 | |
|                         params[param] = docs
 | |
|                 else:
 | |
|                     comment_state = DBusXMLParser.COMMENT_STATE_BODY
 | |
|                     if len(line) > 0:
 | |
|                         if not in_para:
 | |
|                             in_para = True
 | |
|                         body += orig_line + "\n"
 | |
|             elif comment_state == DBusXMLParser.COMMENT_STATE_BODY:
 | |
|                 if len(line) > 0:
 | |
|                     if not in_para:
 | |
|                         in_para = True
 | |
|                     body += orig_line + "\n"
 | |
|                 else:
 | |
|                     if in_para:
 | |
|                         body += "\n"
 | |
|                         in_para = False
 | |
|         if in_para:
 | |
|             body += "\n"
 | |
| 
 | |
|         if symbol != "":
 | |
|             self.doc_comment_last_symbol = symbol
 | |
|             self.doc_comment_params = params
 | |
|             self.doc_comment_body = body
 | |
| 
 | |
|     def handle_char_data(self, data):
 | |
|         # print 'char_data=%s'%data
 | |
|         pass
 | |
| 
 | |
|     def handle_start_element(self, name, attrs):
 | |
|         old_state = self.state
 | |
|         old_cur_object = self._cur_object
 | |
|         if self.state == DBusXMLParser.STATE_IGNORED:
 | |
|             self.state = DBusXMLParser.STATE_IGNORED
 | |
|         elif self.state == DBusXMLParser.STATE_TOP:
 | |
|             if name == DBusXMLParser.STATE_NODE:
 | |
|                 self.state = DBusXMLParser.STATE_NODE
 | |
|             else:
 | |
|                 self.state = DBusXMLParser.STATE_IGNORED
 | |
|         elif self.state == DBusXMLParser.STATE_NODE:
 | |
|             if name == DBusXMLParser.STATE_INTERFACE:
 | |
|                 self.state = DBusXMLParser.STATE_INTERFACE
 | |
|                 iface = Interface(attrs["name"])
 | |
|                 self._cur_object = iface
 | |
|                 self.parsed_interfaces.append(iface)
 | |
|             elif name == DBusXMLParser.STATE_ANNOTATION:
 | |
|                 self.state = DBusXMLParser.STATE_ANNOTATION
 | |
|                 anno = Annotation(attrs["name"], attrs["value"])
 | |
|                 self._cur_object.annotations.append(anno)
 | |
|                 self._cur_object = anno
 | |
|             else:
 | |
|                 self.state = DBusXMLParser.STATE_IGNORED
 | |
| 
 | |
|             # assign docs, if any
 | |
|             if "name" in attrs and self.doc_comment_last_symbol == attrs["name"]:
 | |
|                 self._cur_object.doc_string = self.doc_comment_body
 | |
|                 if "short_description" in self.doc_comment_params:
 | |
|                     short_description = self.doc_comment_params["short_description"]
 | |
|                     self._cur_object.doc_string_brief = short_description
 | |
|                 if "since" in self.doc_comment_params:
 | |
|                     self._cur_object.since = self.doc_comment_params["since"].strip()
 | |
| 
 | |
|         elif self.state == DBusXMLParser.STATE_INTERFACE:
 | |
|             if name == DBusXMLParser.STATE_METHOD:
 | |
|                 self.state = DBusXMLParser.STATE_METHOD
 | |
|                 method = Method(
 | |
|                     attrs["name"], h_type_implies_unix_fd=self._h_type_implies_unix_fd
 | |
|                 )
 | |
|                 self._cur_object.methods.append(method)
 | |
|                 self._cur_object = method
 | |
|             elif name == DBusXMLParser.STATE_SIGNAL:
 | |
|                 self.state = DBusXMLParser.STATE_SIGNAL
 | |
|                 signal = Signal(attrs["name"])
 | |
|                 self._cur_object.signals.append(signal)
 | |
|                 self._cur_object = signal
 | |
|             elif name == DBusXMLParser.STATE_PROPERTY:
 | |
|                 self.state = DBusXMLParser.STATE_PROPERTY
 | |
|                 prop = Property(attrs["name"], attrs["type"], attrs["access"])
 | |
|                 self._cur_object.properties.append(prop)
 | |
|                 self._cur_object = prop
 | |
|             elif name == DBusXMLParser.STATE_ANNOTATION:
 | |
|                 self.state = DBusXMLParser.STATE_ANNOTATION
 | |
|                 anno = Annotation(attrs["name"], attrs["value"])
 | |
|                 self._cur_object.annotations.append(anno)
 | |
|                 self._cur_object = anno
 | |
|             else:
 | |
|                 self.state = DBusXMLParser.STATE_IGNORED
 | |
| 
 | |
|             # assign docs, if any
 | |
|             if "name" in attrs and self.doc_comment_last_symbol == attrs["name"]:
 | |
|                 self._cur_object.doc_string = self.doc_comment_body
 | |
|                 if "since" in self.doc_comment_params:
 | |
|                     self._cur_object.since = self.doc_comment_params["since"].strip()
 | |
| 
 | |
|         elif self.state == DBusXMLParser.STATE_METHOD:
 | |
|             if name == DBusXMLParser.STATE_ARG:
 | |
|                 self.state = DBusXMLParser.STATE_ARG
 | |
|                 arg_name = None
 | |
|                 if "name" in attrs:
 | |
|                     arg_name = attrs["name"]
 | |
|                 arg = Arg(arg_name, attrs["type"])
 | |
|                 direction = attrs.get("direction", "in")
 | |
|                 if direction == "in":
 | |
|                     self._cur_object.in_args.append(arg)
 | |
|                 elif direction == "out":
 | |
|                     self._cur_object.out_args.append(arg)
 | |
|                 else:
 | |
|                     raise ValueError('Invalid direction "{}"'.format(direction))
 | |
|                 self._cur_object = arg
 | |
|             elif name == DBusXMLParser.STATE_ANNOTATION:
 | |
|                 self.state = DBusXMLParser.STATE_ANNOTATION
 | |
|                 anno = Annotation(attrs["name"], attrs["value"])
 | |
|                 self._cur_object.annotations.append(anno)
 | |
|                 self._cur_object = anno
 | |
|             else:
 | |
|                 self.state = DBusXMLParser.STATE_IGNORED
 | |
| 
 | |
|             # assign docs, if any
 | |
|             if self.doc_comment_last_symbol == old_cur_object.name:
 | |
|                 if "name" in attrs and attrs["name"] in self.doc_comment_params:
 | |
|                     doc_string = self.doc_comment_params[attrs["name"]]
 | |
|                     if doc_string is not None:
 | |
|                         self._cur_object.doc_string = doc_string
 | |
|                     if "since" in self.doc_comment_params:
 | |
|                         self._cur_object.since = self.doc_comment_params[
 | |
|                             "since"
 | |
|                         ].strip()
 | |
| 
 | |
|         elif self.state == DBusXMLParser.STATE_SIGNAL:
 | |
|             if name == DBusXMLParser.STATE_ARG:
 | |
|                 self.state = DBusXMLParser.STATE_ARG
 | |
|                 arg_name = None
 | |
|                 if "name" in attrs:
 | |
|                     arg_name = attrs["name"]
 | |
|                 arg = Arg(arg_name, attrs["type"])
 | |
|                 self._cur_object.args.append(arg)
 | |
|                 self._cur_object = arg
 | |
|             elif name == DBusXMLParser.STATE_ANNOTATION:
 | |
|                 self.state = DBusXMLParser.STATE_ANNOTATION
 | |
|                 anno = Annotation(attrs["name"], attrs["value"])
 | |
|                 self._cur_object.annotations.append(anno)
 | |
|                 self._cur_object = anno
 | |
|             else:
 | |
|                 self.state = DBusXMLParser.STATE_IGNORED
 | |
| 
 | |
|             # assign docs, if any
 | |
|             if self.doc_comment_last_symbol == old_cur_object.name:
 | |
|                 if "name" in attrs and attrs["name"] in self.doc_comment_params:
 | |
|                     doc_string = self.doc_comment_params[attrs["name"]]
 | |
|                     if doc_string is not None:
 | |
|                         self._cur_object.doc_string = doc_string
 | |
|                     if "since" in self.doc_comment_params:
 | |
|                         self._cur_object.since = self.doc_comment_params[
 | |
|                             "since"
 | |
|                         ].strip()
 | |
| 
 | |
|         elif self.state == DBusXMLParser.STATE_PROPERTY:
 | |
|             if name == DBusXMLParser.STATE_ANNOTATION:
 | |
|                 self.state = DBusXMLParser.STATE_ANNOTATION
 | |
|                 anno = Annotation(attrs["name"], attrs["value"])
 | |
|                 self._cur_object.annotations.append(anno)
 | |
|                 self._cur_object = anno
 | |
|             else:
 | |
|                 self.state = DBusXMLParser.STATE_IGNORED
 | |
| 
 | |
|         elif self.state == DBusXMLParser.STATE_ARG:
 | |
|             if name == DBusXMLParser.STATE_ANNOTATION:
 | |
|                 self.state = DBusXMLParser.STATE_ANNOTATION
 | |
|                 anno = Annotation(attrs["name"], attrs["value"])
 | |
|                 self._cur_object.annotations.append(anno)
 | |
|                 self._cur_object = anno
 | |
|             else:
 | |
|                 self.state = DBusXMLParser.STATE_IGNORED
 | |
| 
 | |
|         elif self.state == DBusXMLParser.STATE_ANNOTATION:
 | |
|             if name == DBusXMLParser.STATE_ANNOTATION:
 | |
|                 self.state = DBusXMLParser.STATE_ANNOTATION
 | |
|                 anno = Annotation(attrs["name"], attrs["value"])
 | |
|                 self._cur_object.annotations.append(anno)
 | |
|                 self._cur_object = anno
 | |
|             else:
 | |
|                 self.state = DBusXMLParser.STATE_IGNORED
 | |
| 
 | |
|         else:
 | |
|             raise ValueError(
 | |
|                 'Unhandled state "{}" while entering element with name "{}"'.format(
 | |
|                     self.state, name
 | |
|                 )
 | |
|             )
 | |
| 
 | |
|         self.state_stack.append(old_state)
 | |
|         self._cur_object_stack.append(old_cur_object)
 | |
| 
 | |
|     def handle_end_element(self, name):
 | |
|         self.state = self.state_stack.pop()
 | |
|         self._cur_object = self._cur_object_stack.pop()
 | |
| 
 | |
| 
 | |
| def parse_dbus_xml(xml_data):
 | |
|     parser = DBusXMLParser(xml_data, True)
 | |
|     return parser.parsed_interfaces
 |