PageRenderTime 55ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/server/dbus-example-server.py

https://gitlab.com/jouyouyun/trying-python-gdbus
Python | 299 lines | 275 code | 8 blank | 16 comment | 1 complexity | 012b44a63330c901d812165c7eebfab0 MD5 | raw file
  1. #!/usr/bin/env python3
  2. # Copyright (C) 2015 Red Hat, Inc.
  3. # This copyrighted material is made available to anyone wishing to use,
  4. # modify, copy, or redistribute it subject to the terms and conditions of
  5. # the GNU General Public License v.2.
  6. #
  7. # This application is distributed in the hope that it will be useful,
  8. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  10. # General Public License for more details.
  11. #
  12. # Authors:
  13. # Guy Streeter <streeter@redhat.com>
  14. # /glib/gio/tests/gdbus-example-server.c
  15. from gi.repository import Gio, GObject, GLib
  16. from common import OBJECT_PATH, dump_args, BUS_NAME, INTERFACE_NAME
  17. import sys
  18. introspection_xml = '''
  19. <node>
  20. <interface name='org.gtk.GDBus.TestInterface'>
  21. <annotation name='org.gtk.GDBus.Annotation' value='OnInterface'/>
  22. <annotation name='org.gtk.GDBus.Annotation' value='AlsoOnInterface'/>
  23. <method name='HelloWorld'>
  24. <annotation name='org.gtk.GDBus.Annotation' value='OnMethod'/>
  25. <arg type='s' name='greeting' direction='in'/>
  26. <arg type='s' name='response' direction='out'/>
  27. </method>
  28. <method name='EmitSignal'>
  29. <arg type='d' name='speed_in_mph' direction='in'>
  30. <annotation name='org.gtk.GDBus.Annotation' value='OnArg'/>
  31. </arg>
  32. </method>
  33. <method name='GimmeStdout'/>
  34. <signal name='VelocityChanged'>
  35. <annotation name='org.gtk.GDBus.Annotation' value='Onsignal'/>
  36. <arg type='d' name='speed_in_mph'/>
  37. <arg type='s' name='speed_as_string'>
  38. <annotation name='org.gtk.GDBus.Annotation' value='OnArg_NonFirst'/>
  39. </arg>
  40. </signal>
  41. <property type='s' name='FluxCapacitorName' access='read'>
  42. <annotation name='org.gtk.GDBus.Annotation' value='OnProperty'>
  43. <annotation name='org.gtk.GDBus.Annotation' value='OnAnnotation_YesThisIsCrazy'/>
  44. </annotation>
  45. </property>
  46. <property type='s' name='Title' access='readwrite'/>
  47. <property type='s' name='ReadingAlwaysThrowsError' access='read'/>
  48. <property type='s' name='WritingAlwaysThrowsError' access='readwrite'/>
  49. <property type='s' name='OnlyWritable' access='write'/>
  50. <property type='s' name='Foo' access='read'/>
  51. <property type='s' name='Bar' access='read'/>
  52. </interface>
  53. </node>
  54. '''
  55. introspection_data = Gio.DBusNodeInfo.new_for_xml(introspection_xml)
  56. print(repr(introspection_data.interfaces))
  57. for iface in introspection_data.interfaces:
  58. print(repr(iface))
  59. interface_info = introspection_data.lookup_interface(INTERFACE_NAME)
  60. print('if', interface_info)
  61. # dbus main loop is handled by Gio
  62. main_loop = GObject.MainLoop()
  63. def static_vars(**kwargs):
  64. def decorate(func):
  65. for k, v in kwargs.items():
  66. setattr(func, k, v)
  67. return func
  68. return decorate
  69. FooValue = 'Tick'
  70. BarValue = 'Tock'
  71. GlobalTitle = None
  72. @static_vars(swap_it=0)
  73. def on_timeout_cb(connection):
  74. global FooValue, BarValue
  75. def swapped_tick_tock():
  76. tick_tock = ('Tick', 'Tock')
  77. foo = tick_tock[on_timeout_cb.swap_it]
  78. on_timeout_cb.swap_it = (on_timeout_cb.swap_it + 1) % 2
  79. bar = tick_tock[on_timeout_cb.swap_it]
  80. return foo, bar
  81. FooValue, BarValue = swapped_tick_tock()
  82. # tuple(interface name, changed_properties, invalidated properties)
  83. #
  84. # Interface name is a string.
  85. # Changed properties are a dict with string keys and variant values
  86. # where the key is the property name and the value is the new
  87. # property value.
  88. # Invalidated properties are a list of property name strings. They
  89. # represent properties that have changed but the new values are not
  90. # provided. They must be fetched separately.
  91. values_dict = { 'Foo' : GLib.Variant('s', FooValue),
  92. 'Bar' : GLib.Variant('s', BarValue)
  93. }
  94. variant = GLib.Variant('(sa{sv}as)',
  95. ('org.gtk.GDBus.TestInterface',
  96. values_dict, ()))
  97. # http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties
  98. assert connection.emit_signal(None,
  99. OBJECT_PATH,
  100. 'org.freedesktop.DBus.Properties',
  101. 'PropertiesChanged',
  102. variant)
  103. return True
  104. @dump_args
  105. def handle_method_call(connection, sender, object_path, interface_name,
  106. method_name, parameters, invocation):
  107. if interface_name == 'org.freedesktop.DBus.Properties':
  108. if method_name == 'Get':
  109. get_property(connection, sender, object_path, parameters,
  110. invocation)
  111. return
  112. if method_name == 'GetAll':
  113. get_all(connection, sender, object_path, parameters,
  114. invocation)
  115. return
  116. assert method_name == 'Set'
  117. set_property(connection, sender, object_path, parameters,
  118. invocation)
  119. return
  120. assert interface_name == INTERFACE_NAME
  121. if method_name == 'HelloWorld':
  122. print(repr(parameters), parameters.unpack())
  123. greeting = parameters.unpack()[0]
  124. if greeting == 'Return Unregistered':
  125. response = "As requested, here's a GError not registered (G_IO_ERROR_FAILED_HANDLED)"
  126. invocation.return_error_literal(Gio.io_error_quark(),
  127. Gio.IOErrorEnum.FAILED_HANDLED,
  128. response)
  129. elif greeting == 'Return Registered':
  130. response = "As requested, here's a GError that is registered (G_DBUS_ERROR_MATCH_RULE_NOT_FOUND"
  131. invocation.return_error_literal(Gio.dbus_error_quark(),
  132. Gio.DBusError.MATCH_RULE_NOT_FOUND,
  133. response)
  134. elif greeting == 'Return Raw':
  135. response = "As requested, here's a raw D-Bus error"
  136. invocation.return_dbus_error('org.gtk.GDBus.SomeErrorName',
  137. response)
  138. else:
  139. response = "You greeted me with '%s'. Thanks!" % (greeting,)
  140. invocation.return_value(GLib.Variant('(s)', (response,)))
  141. elif method_name == 'EmitSignal':
  142. speed_in_mph = parameters.unpack()[0]
  143. speed_as_string = '%g mph!' % (speed_in_mph,)
  144. variant = GLib.Variant('(ds)', (speed_in_mph, speed_as_string))
  145. assert connection.emit_signal(None,
  146. object_path,
  147. interface_name,
  148. 'VelocityChanged',
  149. variant)
  150. invocation.return_value(None)
  151. elif method_name == 'GimmeStdout':
  152. print(repr(connection.get_capabilities()))
  153. if connection.get_capabilities() & Gio.DBusCapabilityFlags.UNIX_FD_PASSING:
  154. fd_list = Gio.UnixFDList.new()
  155. assert fd_list.append(sys.stdout.fileno()) >= 0
  156. reply = invocation.get_message().new_method_reply()
  157. reply.set_unix_fd_list(fd_list)
  158. assert connection.send_message(reply,
  159. Gio.DBusSendMessageFlags.NONE)
  160. else:
  161. response = 'Your message bus daemon does not support file descriptor passing (need D-Bus >= 1.3.0)'
  162. invocation.return_dbus_error('org.gtk.GDBus.Failed', response)
  163. else:
  164. assert False
  165. @dump_args
  166. def get_property(connection, sender, object_path, parameters,
  167. invocation):
  168. global GlobalTitle
  169. ret_string = None
  170. interface_name, property_name = parameters.unpack()
  171. assert interface_name == INTERFACE_NAME
  172. if property_name == 'FluxCapacitorName':
  173. ret_string = 'DeLorean'
  174. elif property_name == 'Title':
  175. if not GlobalTitle:
  176. GlobalTitle = 'Back To Python!'
  177. ret_string = GlobalTitle
  178. elif property_name == 'ReadingAlwaysThrowsError':
  179. response = 'Hello %s. I thought I said reading this property always results in an error. kthxbye' % (sender,)
  180. invocation.return_dbus_error('org.freedesktop.DBus.Error.Failed',
  181. response)
  182. return
  183. elif property_name == 'WritingAlwaysThrowsError':
  184. ret_string = "There's no home like home"
  185. elif property_name == 'Foo':
  186. ret_string = FooValue
  187. elif property_name == 'Bar':
  188. ret_string = BarValue
  189. else:
  190. response = 'No such property: ' + property_name
  191. invocation.return_dbus_error('org.freedesktop.DBus.Error.InvalidArgs',
  192. response)
  193. return
  194. assert ret_string
  195. # return from Get is a tuple of variants
  196. ret_var = GLib.Variant('s', ret_string)
  197. invocation.return_value(GLib.Variant('(v)', (ret_var,)))
  198. @dump_args
  199. def get_all(connection, sender, object_path, parameters, invocation):
  200. print(parameters.unpack())
  201. interface_name, = parameters.unpack()
  202. assert interface_name == 'org.gtk.GDBus.TestInterface'
  203. global GlobalTitle, FooValue, BarValue
  204. title = GlobalTitle if GlobalTitle else ''
  205. values = {'FluxCapacitorName' : GLib.Variant('s', 'DeLorean'),
  206. 'Title' : GLib.Variant('s', title),
  207. 'WritingAlwaysThrowsError' : GLib.Variant('s', 'Return to Python!'),
  208. 'Foo' : GLib.Variant('s', FooValue),
  209. 'Bar' : GLib.Variant('s', BarValue)}
  210. invocation.return_value(GLib.Variant('(a{sv})', (values,)))
  211. @dump_args
  212. def set_property(connection, sender, object_path, parameters,
  213. invocation):
  214. global GlobalTitle
  215. interface_name, property_name, new_value = parameters.unpack()
  216. assert interface_name == INTERFACE_NAME
  217. if property_name == 'Title':
  218. GlobalTitle = new_value
  219. values_dict = { 'Title' : GLib.Variant('s', GlobalTitle) }
  220. variant = GLib.Variant('(sa{sv}as)',
  221. ('org.gtk.GDBus.TestInterface',
  222. values_dict, ()))
  223. assert connection.emit_signal(None,
  224. OBJECT_PATH,
  225. 'org.freedesktop.DBus.Properties',
  226. 'PropertiesChanged',
  227. variant)
  228. invocation.return_value(None)
  229. return
  230. if property_name == 'OnlyWritable':
  231. values_dict = { 'OnlyWritable' : GLib.Variant('s', new_value) }
  232. variant = GLib.Variant('(sa{sv}as)',
  233. ('org.gtk.GDBus.TestInterface',
  234. values_dict, ()))
  235. assert connection.emit_signal(None,
  236. OBJECT_PATH,
  237. 'org.freedesktop.DBus.Properties',
  238. 'PropertiesChanged',
  239. variant)
  240. invocation.return_value(None)
  241. return
  242. if property_name == 'WritingAlwaysThrowsError':
  243. response = 'Hello %s. I thought I said writing this property always results in an error. kthxbye' % (sender,)
  244. invocation.return_error_literal(Gio.io_error_quark(),
  245. Gio.IOErrorEnum.FAILED,
  246. response)
  247. return
  248. assert False
  249. @dump_args
  250. def bus_acquired_handler(connection, name):
  251. # From https://lazka.github.io/pgi-docs/Gio-2.0/structs/DBusInterfaceVTable.html
  252. # Since 2.38, if you want to handle getting/setting D-Bus properties
  253. # asynchronously, give None as your get_property() or set_property()
  254. # function. The D-Bus call will be directed to your method_call
  255. # function, with the provided interface_name set to
  256. # org.freedesktop.DBus.Properties.
  257. #
  258. # Specifying the handlers here works, but they don't have a way to
  259. # return an error.
  260. registration_id = connection.register_object(OBJECT_PATH,
  261. interface_info,
  262. handle_method_call,
  263. None,
  264. None)
  265. assert registration_id > 0
  266. # swap the properties of Foo and Bar every two seconds
  267. GLib.timeout_add_seconds(2, on_timeout_cb, connection)
  268. def name_acquired_handler(connection, name):
  269. print('name acquired', name)
  270. def name_lost_handler(connection, name):
  271. print('name lost', name)
  272. main_loop.quit()
  273. owner_id = Gio.bus_own_name(Gio.BusType.SESSION,
  274. BUS_NAME,
  275. Gio.BusNameOwnerFlags.NONE,
  276. bus_acquired_handler,
  277. name_acquired_handler,
  278. name_lost_handler)
  279. main_loop.run()
  280. Gio.bus_unown_name(owner_id)