dbusparser.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  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. import xml.parsers.expat
  20. class Annotation:
  21. def __init__(self, key, value):
  22. self.key = key
  23. self.value = value
  24. self.annotations = []
  25. self.since = ""
  26. class Arg:
  27. def __init__(self, name, signature):
  28. self.name = name
  29. self.signature = signature
  30. self.annotations = []
  31. self.doc_string = ""
  32. self.since = ""
  33. class Method:
  34. def __init__(self, name, h_type_implies_unix_fd=True):
  35. self.name = name
  36. self.h_type_implies_unix_fd = h_type_implies_unix_fd
  37. self.in_args = []
  38. self.out_args = []
  39. self.annotations = []
  40. self.doc_string = ""
  41. self.since = ""
  42. self.deprecated = False
  43. self.unix_fd = False
  44. class Signal:
  45. def __init__(self, name):
  46. self.name = name
  47. self.args = []
  48. self.annotations = []
  49. self.doc_string = ""
  50. self.since = ""
  51. self.deprecated = False
  52. class Property:
  53. def __init__(self, name, signature, access):
  54. self.name = name
  55. self.signature = signature
  56. self.access = access
  57. self.annotations = []
  58. self.arg = Arg("value", self.signature)
  59. self.arg.annotations = self.annotations
  60. self.readable = False
  61. self.writable = False
  62. if self.access == "readwrite":
  63. self.readable = True
  64. self.writable = True
  65. elif self.access == "read":
  66. self.readable = True
  67. elif self.access == "write":
  68. self.writable = True
  69. else:
  70. raise ValueError('Invalid access type "{}"'.format(self.access))
  71. self.doc_string = ""
  72. self.since = ""
  73. self.deprecated = False
  74. self.emits_changed_signal = True
  75. class Interface:
  76. def __init__(self, name):
  77. self.name = name
  78. self.methods = []
  79. self.signals = []
  80. self.properties = []
  81. self.annotations = []
  82. self.doc_string = ""
  83. self.doc_string_brief = ""
  84. self.since = ""
  85. self.deprecated = False
  86. class DBusXMLParser:
  87. STATE_TOP = "top"
  88. STATE_NODE = "node"
  89. STATE_INTERFACE = "interface"
  90. STATE_METHOD = "method"
  91. STATE_SIGNAL = "signal"
  92. STATE_PROPERTY = "property"
  93. STATE_ARG = "arg"
  94. STATE_ANNOTATION = "annotation"
  95. STATE_IGNORED = "ignored"
  96. def __init__(self, xml_data, h_type_implies_unix_fd=True):
  97. self._parser = xml.parsers.expat.ParserCreate()
  98. self._parser.CommentHandler = self.handle_comment
  99. self._parser.CharacterDataHandler = self.handle_char_data
  100. self._parser.StartElementHandler = self.handle_start_element
  101. self._parser.EndElementHandler = self.handle_end_element
  102. self.parsed_interfaces = []
  103. self._cur_object = None
  104. self.state = DBusXMLParser.STATE_TOP
  105. self.state_stack = []
  106. self._cur_object = None
  107. self._cur_object_stack = []
  108. self.doc_comment_last_symbol = ""
  109. self._h_type_implies_unix_fd = h_type_implies_unix_fd
  110. self._parser.Parse(xml_data)
  111. COMMENT_STATE_BEGIN = "begin"
  112. COMMENT_STATE_PARAMS = "params"
  113. COMMENT_STATE_BODY = "body"
  114. COMMENT_STATE_SKIP = "skip"
  115. def handle_comment(self, data):
  116. comment_state = DBusXMLParser.COMMENT_STATE_BEGIN
  117. lines = data.split("\n")
  118. symbol = ""
  119. body = ""
  120. in_para = False
  121. params = {}
  122. for line in lines:
  123. orig_line = line
  124. line = line.lstrip()
  125. if comment_state == DBusXMLParser.COMMENT_STATE_BEGIN:
  126. if len(line) > 0:
  127. colon_index = line.find(": ")
  128. if colon_index == -1:
  129. if line.endswith(":"):
  130. symbol = line[0 : len(line) - 1]
  131. comment_state = DBusXMLParser.COMMENT_STATE_PARAMS
  132. else:
  133. comment_state = DBusXMLParser.COMMENT_STATE_SKIP
  134. else:
  135. symbol = line[0:colon_index]
  136. rest_of_line = line[colon_index + 2 :].strip()
  137. if len(rest_of_line) > 0:
  138. body += rest_of_line + "\n"
  139. comment_state = DBusXMLParser.COMMENT_STATE_PARAMS
  140. elif comment_state == DBusXMLParser.COMMENT_STATE_PARAMS:
  141. if line.startswith("@"):
  142. colon_index = line.find(": ")
  143. if colon_index == -1:
  144. comment_state = DBusXMLParser.COMMENT_STATE_BODY
  145. if not in_para:
  146. in_para = True
  147. body += orig_line + "\n"
  148. else:
  149. param = line[1:colon_index]
  150. docs = line[colon_index + 2 :]
  151. params[param] = docs
  152. else:
  153. comment_state = DBusXMLParser.COMMENT_STATE_BODY
  154. if len(line) > 0:
  155. if not in_para:
  156. in_para = True
  157. body += orig_line + "\n"
  158. elif comment_state == DBusXMLParser.COMMENT_STATE_BODY:
  159. if len(line) > 0:
  160. if not in_para:
  161. in_para = True
  162. body += orig_line + "\n"
  163. else:
  164. if in_para:
  165. body += "\n"
  166. in_para = False
  167. if in_para:
  168. body += "\n"
  169. if symbol != "":
  170. self.doc_comment_last_symbol = symbol
  171. self.doc_comment_params = params
  172. self.doc_comment_body = body
  173. def handle_char_data(self, data):
  174. # print 'char_data=%s'%data
  175. pass
  176. def handle_start_element(self, name, attrs):
  177. old_state = self.state
  178. old_cur_object = self._cur_object
  179. if self.state == DBusXMLParser.STATE_IGNORED:
  180. self.state = DBusXMLParser.STATE_IGNORED
  181. elif self.state == DBusXMLParser.STATE_TOP:
  182. if name == DBusXMLParser.STATE_NODE:
  183. self.state = DBusXMLParser.STATE_NODE
  184. else:
  185. self.state = DBusXMLParser.STATE_IGNORED
  186. elif self.state == DBusXMLParser.STATE_NODE:
  187. if name == DBusXMLParser.STATE_INTERFACE:
  188. self.state = DBusXMLParser.STATE_INTERFACE
  189. iface = Interface(attrs["name"])
  190. self._cur_object = iface
  191. self.parsed_interfaces.append(iface)
  192. elif name == DBusXMLParser.STATE_ANNOTATION:
  193. self.state = DBusXMLParser.STATE_ANNOTATION
  194. anno = Annotation(attrs["name"], attrs["value"])
  195. self._cur_object.annotations.append(anno)
  196. self._cur_object = anno
  197. else:
  198. self.state = DBusXMLParser.STATE_IGNORED
  199. # assign docs, if any
  200. if "name" in attrs and self.doc_comment_last_symbol == attrs["name"]:
  201. self._cur_object.doc_string = self.doc_comment_body
  202. if "short_description" in self.doc_comment_params:
  203. short_description = self.doc_comment_params["short_description"]
  204. self._cur_object.doc_string_brief = short_description
  205. if "since" in self.doc_comment_params:
  206. self._cur_object.since = self.doc_comment_params["since"].strip()
  207. elif self.state == DBusXMLParser.STATE_INTERFACE:
  208. if name == DBusXMLParser.STATE_METHOD:
  209. self.state = DBusXMLParser.STATE_METHOD
  210. method = Method(
  211. attrs["name"], h_type_implies_unix_fd=self._h_type_implies_unix_fd
  212. )
  213. self._cur_object.methods.append(method)
  214. self._cur_object = method
  215. elif name == DBusXMLParser.STATE_SIGNAL:
  216. self.state = DBusXMLParser.STATE_SIGNAL
  217. signal = Signal(attrs["name"])
  218. self._cur_object.signals.append(signal)
  219. self._cur_object = signal
  220. elif name == DBusXMLParser.STATE_PROPERTY:
  221. self.state = DBusXMLParser.STATE_PROPERTY
  222. prop = Property(attrs["name"], attrs["type"], attrs["access"])
  223. self._cur_object.properties.append(prop)
  224. self._cur_object = prop
  225. elif name == DBusXMLParser.STATE_ANNOTATION:
  226. self.state = DBusXMLParser.STATE_ANNOTATION
  227. anno = Annotation(attrs["name"], attrs["value"])
  228. self._cur_object.annotations.append(anno)
  229. self._cur_object = anno
  230. else:
  231. self.state = DBusXMLParser.STATE_IGNORED
  232. # assign docs, if any
  233. if "name" in attrs and self.doc_comment_last_symbol == attrs["name"]:
  234. self._cur_object.doc_string = self.doc_comment_body
  235. if "since" in self.doc_comment_params:
  236. self._cur_object.since = self.doc_comment_params["since"].strip()
  237. elif self.state == DBusXMLParser.STATE_METHOD:
  238. if name == DBusXMLParser.STATE_ARG:
  239. self.state = DBusXMLParser.STATE_ARG
  240. arg_name = None
  241. if "name" in attrs:
  242. arg_name = attrs["name"]
  243. arg = Arg(arg_name, attrs["type"])
  244. direction = attrs.get("direction", "in")
  245. if direction == "in":
  246. self._cur_object.in_args.append(arg)
  247. elif direction == "out":
  248. self._cur_object.out_args.append(arg)
  249. else:
  250. raise ValueError('Invalid direction "{}"'.format(direction))
  251. self._cur_object = arg
  252. elif name == DBusXMLParser.STATE_ANNOTATION:
  253. self.state = DBusXMLParser.STATE_ANNOTATION
  254. anno = Annotation(attrs["name"], attrs["value"])
  255. self._cur_object.annotations.append(anno)
  256. self._cur_object = anno
  257. else:
  258. self.state = DBusXMLParser.STATE_IGNORED
  259. # assign docs, if any
  260. if self.doc_comment_last_symbol == old_cur_object.name:
  261. if "name" in attrs and attrs["name"] in self.doc_comment_params:
  262. doc_string = self.doc_comment_params[attrs["name"]]
  263. if doc_string is not None:
  264. self._cur_object.doc_string = doc_string
  265. if "since" in self.doc_comment_params:
  266. self._cur_object.since = self.doc_comment_params[
  267. "since"
  268. ].strip()
  269. elif self.state == DBusXMLParser.STATE_SIGNAL:
  270. if name == DBusXMLParser.STATE_ARG:
  271. self.state = DBusXMLParser.STATE_ARG
  272. arg_name = None
  273. if "name" in attrs:
  274. arg_name = attrs["name"]
  275. arg = Arg(arg_name, attrs["type"])
  276. self._cur_object.args.append(arg)
  277. self._cur_object = arg
  278. elif name == DBusXMLParser.STATE_ANNOTATION:
  279. self.state = DBusXMLParser.STATE_ANNOTATION
  280. anno = Annotation(attrs["name"], attrs["value"])
  281. self._cur_object.annotations.append(anno)
  282. self._cur_object = anno
  283. else:
  284. self.state = DBusXMLParser.STATE_IGNORED
  285. # assign docs, if any
  286. if self.doc_comment_last_symbol == old_cur_object.name:
  287. if "name" in attrs and attrs["name"] in self.doc_comment_params:
  288. doc_string = self.doc_comment_params[attrs["name"]]
  289. if doc_string is not None:
  290. self._cur_object.doc_string = doc_string
  291. if "since" in self.doc_comment_params:
  292. self._cur_object.since = self.doc_comment_params[
  293. "since"
  294. ].strip()
  295. elif self.state == DBusXMLParser.STATE_PROPERTY:
  296. if name == DBusXMLParser.STATE_ANNOTATION:
  297. self.state = DBusXMLParser.STATE_ANNOTATION
  298. anno = Annotation(attrs["name"], attrs["value"])
  299. self._cur_object.annotations.append(anno)
  300. self._cur_object = anno
  301. else:
  302. self.state = DBusXMLParser.STATE_IGNORED
  303. elif self.state == DBusXMLParser.STATE_ARG:
  304. if name == DBusXMLParser.STATE_ANNOTATION:
  305. self.state = DBusXMLParser.STATE_ANNOTATION
  306. anno = Annotation(attrs["name"], attrs["value"])
  307. self._cur_object.annotations.append(anno)
  308. self._cur_object = anno
  309. else:
  310. self.state = DBusXMLParser.STATE_IGNORED
  311. elif self.state == DBusXMLParser.STATE_ANNOTATION:
  312. if name == DBusXMLParser.STATE_ANNOTATION:
  313. self.state = DBusXMLParser.STATE_ANNOTATION
  314. anno = Annotation(attrs["name"], attrs["value"])
  315. self._cur_object.annotations.append(anno)
  316. self._cur_object = anno
  317. else:
  318. self.state = DBusXMLParser.STATE_IGNORED
  319. else:
  320. raise ValueError(
  321. 'Unhandled state "{}" while entering element with name "{}"'.format(
  322. self.state, name
  323. )
  324. )
  325. self.state_stack.append(old_state)
  326. self._cur_object_stack.append(old_cur_object)
  327. def handle_end_element(self, name):
  328. self.state = self.state_stack.pop()
  329. self._cur_object = self._cur_object_stack.pop()
  330. def parse_dbus_xml(xml_data):
  331. parser = DBusXMLParser(xml_data, True)
  332. return parser.parsed_interfaces