| # 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 |