]>
Commit | Line | Data |
---|---|---|
2668dc7b MAL |
1 | # Based from "GDBus - GLib D-Bus Library": |
2 | # | |
3 | # Copyright (C) 2008-2011 Red Hat, Inc. | |
4 | # | |
5 | # This library is free software; you can redistribute it and/or | |
6 | # modify it under the terms of the GNU Lesser General Public | |
7 | # License as published by the Free Software Foundation; either | |
8 | # version 2.1 of the License, or (at your option) any later version. | |
9 | # | |
10 | # This library is distributed in the hope that it will be useful, | |
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | # Lesser General Public License for more details. | |
14 | # | |
15 | # You should have received a copy of the GNU Lesser General | |
16 | # Public License along with this library; if not, see <http://www.gnu.org/licenses/>. | |
17 | # | |
18 | # Author: David Zeuthen <davidz@redhat.com> | |
19 | ||
20 | import xml.parsers.expat | |
21 | ||
22 | ||
23 | class Annotation: | |
24 | def __init__(self, key, value): | |
25 | self.key = key | |
26 | self.value = value | |
27 | self.annotations = [] | |
28 | self.since = "" | |
29 | ||
30 | ||
31 | class Arg: | |
32 | def __init__(self, name, signature): | |
33 | self.name = name | |
34 | self.signature = signature | |
35 | self.annotations = [] | |
36 | self.doc_string = "" | |
37 | self.since = "" | |
38 | ||
39 | ||
40 | class Method: | |
41 | def __init__(self, name, h_type_implies_unix_fd=True): | |
42 | self.name = name | |
43 | self.h_type_implies_unix_fd = h_type_implies_unix_fd | |
44 | self.in_args = [] | |
45 | self.out_args = [] | |
46 | self.annotations = [] | |
47 | self.doc_string = "" | |
48 | self.since = "" | |
49 | self.deprecated = False | |
50 | self.unix_fd = False | |
51 | ||
52 | ||
53 | class Signal: | |
54 | def __init__(self, name): | |
55 | self.name = name | |
56 | self.args = [] | |
57 | self.annotations = [] | |
58 | self.doc_string = "" | |
59 | self.since = "" | |
60 | self.deprecated = False | |
61 | ||
62 | ||
63 | class Property: | |
64 | def __init__(self, name, signature, access): | |
65 | self.name = name | |
66 | self.signature = signature | |
67 | self.access = access | |
68 | self.annotations = [] | |
69 | self.arg = Arg("value", self.signature) | |
70 | self.arg.annotations = self.annotations | |
71 | self.readable = False | |
72 | self.writable = False | |
73 | if self.access == "readwrite": | |
74 | self.readable = True | |
75 | self.writable = True | |
76 | elif self.access == "read": | |
77 | self.readable = True | |
78 | elif self.access == "write": | |
79 | self.writable = True | |
80 | else: | |
81 | raise ValueError('Invalid access type "{}"'.format(self.access)) | |
82 | self.doc_string = "" | |
83 | self.since = "" | |
84 | self.deprecated = False | |
85 | self.emits_changed_signal = True | |
86 | ||
87 | ||
88 | class Interface: | |
89 | def __init__(self, name): | |
90 | self.name = name | |
91 | self.methods = [] | |
92 | self.signals = [] | |
93 | self.properties = [] | |
94 | self.annotations = [] | |
95 | self.doc_string = "" | |
96 | self.doc_string_brief = "" | |
97 | self.since = "" | |
98 | self.deprecated = False | |
99 | ||
100 | ||
101 | class DBusXMLParser: | |
102 | STATE_TOP = "top" | |
103 | STATE_NODE = "node" | |
104 | STATE_INTERFACE = "interface" | |
105 | STATE_METHOD = "method" | |
106 | STATE_SIGNAL = "signal" | |
107 | STATE_PROPERTY = "property" | |
108 | STATE_ARG = "arg" | |
109 | STATE_ANNOTATION = "annotation" | |
110 | STATE_IGNORED = "ignored" | |
111 | ||
112 | def __init__(self, xml_data, h_type_implies_unix_fd=True): | |
113 | self._parser = xml.parsers.expat.ParserCreate() | |
114 | self._parser.CommentHandler = self.handle_comment | |
115 | self._parser.CharacterDataHandler = self.handle_char_data | |
116 | self._parser.StartElementHandler = self.handle_start_element | |
117 | self._parser.EndElementHandler = self.handle_end_element | |
118 | ||
119 | self.parsed_interfaces = [] | |
120 | self._cur_object = None | |
121 | ||
122 | self.state = DBusXMLParser.STATE_TOP | |
123 | self.state_stack = [] | |
124 | self._cur_object = None | |
125 | self._cur_object_stack = [] | |
126 | ||
127 | self.doc_comment_last_symbol = "" | |
128 | ||
129 | self._h_type_implies_unix_fd = h_type_implies_unix_fd | |
130 | ||
131 | self._parser.Parse(xml_data) | |
132 | ||
133 | COMMENT_STATE_BEGIN = "begin" | |
134 | COMMENT_STATE_PARAMS = "params" | |
135 | COMMENT_STATE_BODY = "body" | |
136 | COMMENT_STATE_SKIP = "skip" | |
137 | ||
138 | def handle_comment(self, data): | |
139 | comment_state = DBusXMLParser.COMMENT_STATE_BEGIN | |
140 | lines = data.split("\n") | |
141 | symbol = "" | |
142 | body = "" | |
143 | in_para = False | |
144 | params = {} | |
145 | for line in lines: | |
146 | orig_line = line | |
147 | line = line.lstrip() | |
148 | if comment_state == DBusXMLParser.COMMENT_STATE_BEGIN: | |
149 | if len(line) > 0: | |
150 | colon_index = line.find(": ") | |
151 | if colon_index == -1: | |
152 | if line.endswith(":"): | |
153 | symbol = line[0 : len(line) - 1] | |
154 | comment_state = DBusXMLParser.COMMENT_STATE_PARAMS | |
155 | else: | |
156 | comment_state = DBusXMLParser.COMMENT_STATE_SKIP | |
157 | else: | |
158 | symbol = line[0:colon_index] | |
159 | rest_of_line = line[colon_index + 2 :].strip() | |
160 | if len(rest_of_line) > 0: | |
161 | body += rest_of_line + "\n" | |
162 | comment_state = DBusXMLParser.COMMENT_STATE_PARAMS | |
163 | elif comment_state == DBusXMLParser.COMMENT_STATE_PARAMS: | |
164 | if line.startswith("@"): | |
165 | colon_index = line.find(": ") | |
166 | if colon_index == -1: | |
167 | comment_state = DBusXMLParser.COMMENT_STATE_BODY | |
168 | if not in_para: | |
169 | in_para = True | |
170 | body += orig_line + "\n" | |
171 | else: | |
172 | param = line[1:colon_index] | |
173 | docs = line[colon_index + 2 :] | |
174 | params[param] = docs | |
175 | else: | |
176 | comment_state = DBusXMLParser.COMMENT_STATE_BODY | |
177 | if len(line) > 0: | |
178 | if not in_para: | |
179 | in_para = True | |
180 | body += orig_line + "\n" | |
181 | elif comment_state == DBusXMLParser.COMMENT_STATE_BODY: | |
182 | if len(line) > 0: | |
183 | if not in_para: | |
184 | in_para = True | |
185 | body += orig_line + "\n" | |
186 | else: | |
187 | if in_para: | |
188 | body += "\n" | |
189 | in_para = False | |
190 | if in_para: | |
191 | body += "\n" | |
192 | ||
193 | if symbol != "": | |
194 | self.doc_comment_last_symbol = symbol | |
195 | self.doc_comment_params = params | |
196 | self.doc_comment_body = body | |
197 | ||
198 | def handle_char_data(self, data): | |
199 | # print 'char_data=%s'%data | |
200 | pass | |
201 | ||
202 | def handle_start_element(self, name, attrs): | |
203 | old_state = self.state | |
204 | old_cur_object = self._cur_object | |
205 | if self.state == DBusXMLParser.STATE_IGNORED: | |
206 | self.state = DBusXMLParser.STATE_IGNORED | |
207 | elif self.state == DBusXMLParser.STATE_TOP: | |
208 | if name == DBusXMLParser.STATE_NODE: | |
209 | self.state = DBusXMLParser.STATE_NODE | |
210 | else: | |
211 | self.state = DBusXMLParser.STATE_IGNORED | |
212 | elif self.state == DBusXMLParser.STATE_NODE: | |
213 | if name == DBusXMLParser.STATE_INTERFACE: | |
214 | self.state = DBusXMLParser.STATE_INTERFACE | |
215 | iface = Interface(attrs["name"]) | |
216 | self._cur_object = iface | |
217 | self.parsed_interfaces.append(iface) | |
218 | elif name == DBusXMLParser.STATE_ANNOTATION: | |
219 | self.state = DBusXMLParser.STATE_ANNOTATION | |
220 | anno = Annotation(attrs["name"], attrs["value"]) | |
221 | self._cur_object.annotations.append(anno) | |
222 | self._cur_object = anno | |
223 | else: | |
224 | self.state = DBusXMLParser.STATE_IGNORED | |
225 | ||
226 | # assign docs, if any | |
227 | if "name" in attrs and self.doc_comment_last_symbol == attrs["name"]: | |
228 | self._cur_object.doc_string = self.doc_comment_body | |
229 | if "short_description" in self.doc_comment_params: | |
230 | short_description = self.doc_comment_params["short_description"] | |
231 | self._cur_object.doc_string_brief = short_description | |
232 | if "since" in self.doc_comment_params: | |
233 | self._cur_object.since = self.doc_comment_params["since"].strip() | |
234 | ||
235 | elif self.state == DBusXMLParser.STATE_INTERFACE: | |
236 | if name == DBusXMLParser.STATE_METHOD: | |
237 | self.state = DBusXMLParser.STATE_METHOD | |
238 | method = Method( | |
239 | attrs["name"], h_type_implies_unix_fd=self._h_type_implies_unix_fd | |
240 | ) | |
241 | self._cur_object.methods.append(method) | |
242 | self._cur_object = method | |
243 | elif name == DBusXMLParser.STATE_SIGNAL: | |
244 | self.state = DBusXMLParser.STATE_SIGNAL | |
245 | signal = Signal(attrs["name"]) | |
246 | self._cur_object.signals.append(signal) | |
247 | self._cur_object = signal | |
248 | elif name == DBusXMLParser.STATE_PROPERTY: | |
249 | self.state = DBusXMLParser.STATE_PROPERTY | |
250 | prop = Property(attrs["name"], attrs["type"], attrs["access"]) | |
251 | self._cur_object.properties.append(prop) | |
252 | self._cur_object = prop | |
253 | elif name == DBusXMLParser.STATE_ANNOTATION: | |
254 | self.state = DBusXMLParser.STATE_ANNOTATION | |
255 | anno = Annotation(attrs["name"], attrs["value"]) | |
256 | self._cur_object.annotations.append(anno) | |
257 | self._cur_object = anno | |
258 | else: | |
259 | self.state = DBusXMLParser.STATE_IGNORED | |
260 | ||
261 | # assign docs, if any | |
262 | if "name" in attrs and self.doc_comment_last_symbol == attrs["name"]: | |
263 | self._cur_object.doc_string = self.doc_comment_body | |
264 | if "since" in self.doc_comment_params: | |
265 | self._cur_object.since = self.doc_comment_params["since"].strip() | |
266 | ||
267 | elif self.state == DBusXMLParser.STATE_METHOD: | |
268 | if name == DBusXMLParser.STATE_ARG: | |
269 | self.state = DBusXMLParser.STATE_ARG | |
270 | arg_name = None | |
271 | if "name" in attrs: | |
272 | arg_name = attrs["name"] | |
273 | arg = Arg(arg_name, attrs["type"]) | |
274 | direction = attrs.get("direction", "in") | |
275 | if direction == "in": | |
276 | self._cur_object.in_args.append(arg) | |
277 | elif direction == "out": | |
278 | self._cur_object.out_args.append(arg) | |
279 | else: | |
280 | raise ValueError('Invalid direction "{}"'.format(direction)) | |
281 | self._cur_object = arg | |
282 | elif name == DBusXMLParser.STATE_ANNOTATION: | |
283 | self.state = DBusXMLParser.STATE_ANNOTATION | |
284 | anno = Annotation(attrs["name"], attrs["value"]) | |
285 | self._cur_object.annotations.append(anno) | |
286 | self._cur_object = anno | |
287 | else: | |
288 | self.state = DBusXMLParser.STATE_IGNORED | |
289 | ||
290 | # assign docs, if any | |
291 | if self.doc_comment_last_symbol == old_cur_object.name: | |
292 | if "name" in attrs and attrs["name"] in self.doc_comment_params: | |
293 | doc_string = self.doc_comment_params[attrs["name"]] | |
294 | if doc_string is not None: | |
295 | self._cur_object.doc_string = doc_string | |
296 | if "since" in self.doc_comment_params: | |
297 | self._cur_object.since = self.doc_comment_params[ | |
298 | "since" | |
299 | ].strip() | |
300 | ||
301 | elif self.state == DBusXMLParser.STATE_SIGNAL: | |
302 | if name == DBusXMLParser.STATE_ARG: | |
303 | self.state = DBusXMLParser.STATE_ARG | |
304 | arg_name = None | |
305 | if "name" in attrs: | |
306 | arg_name = attrs["name"] | |
307 | arg = Arg(arg_name, attrs["type"]) | |
308 | self._cur_object.args.append(arg) | |
309 | self._cur_object = arg | |
310 | elif name == DBusXMLParser.STATE_ANNOTATION: | |
311 | self.state = DBusXMLParser.STATE_ANNOTATION | |
312 | anno = Annotation(attrs["name"], attrs["value"]) | |
313 | self._cur_object.annotations.append(anno) | |
314 | self._cur_object = anno | |
315 | else: | |
316 | self.state = DBusXMLParser.STATE_IGNORED | |
317 | ||
318 | # assign docs, if any | |
319 | if self.doc_comment_last_symbol == old_cur_object.name: | |
320 | if "name" in attrs and attrs["name"] in self.doc_comment_params: | |
321 | doc_string = self.doc_comment_params[attrs["name"]] | |
322 | if doc_string is not None: | |
323 | self._cur_object.doc_string = doc_string | |
324 | if "since" in self.doc_comment_params: | |
325 | self._cur_object.since = self.doc_comment_params[ | |
326 | "since" | |
327 | ].strip() | |
328 | ||
329 | elif self.state == DBusXMLParser.STATE_PROPERTY: | |
330 | if name == DBusXMLParser.STATE_ANNOTATION: | |
331 | self.state = DBusXMLParser.STATE_ANNOTATION | |
332 | anno = Annotation(attrs["name"], attrs["value"]) | |
333 | self._cur_object.annotations.append(anno) | |
334 | self._cur_object = anno | |
335 | else: | |
336 | self.state = DBusXMLParser.STATE_IGNORED | |
337 | ||
338 | elif self.state == DBusXMLParser.STATE_ARG: | |
339 | if name == DBusXMLParser.STATE_ANNOTATION: | |
340 | self.state = DBusXMLParser.STATE_ANNOTATION | |
341 | anno = Annotation(attrs["name"], attrs["value"]) | |
342 | self._cur_object.annotations.append(anno) | |
343 | self._cur_object = anno | |
344 | else: | |
345 | self.state = DBusXMLParser.STATE_IGNORED | |
346 | ||
347 | elif self.state == DBusXMLParser.STATE_ANNOTATION: | |
348 | if name == DBusXMLParser.STATE_ANNOTATION: | |
349 | self.state = DBusXMLParser.STATE_ANNOTATION | |
350 | anno = Annotation(attrs["name"], attrs["value"]) | |
351 | self._cur_object.annotations.append(anno) | |
352 | self._cur_object = anno | |
353 | else: | |
354 | self.state = DBusXMLParser.STATE_IGNORED | |
355 | ||
356 | else: | |
357 | raise ValueError( | |
358 | 'Unhandled state "{}" while entering element with name "{}"'.format( | |
359 | self.state, name | |
360 | ) | |
361 | ) | |
362 | ||
363 | self.state_stack.append(old_state) | |
364 | self._cur_object_stack.append(old_cur_object) | |
365 | ||
366 | def handle_end_element(self, name): | |
367 | self.state = self.state_stack.pop() | |
368 | self._cur_object = self._cur_object_stack.pop() | |
369 | ||
370 | ||
371 | def parse_dbus_xml(xml_data): | |
372 | parser = DBusXMLParser(xml_data, True) | |
373 | return parser.parsed_interfaces |