# 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 . # # Author: David Zeuthen 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