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