/server/dbus-example-server.py
Python | 299 lines | 275 code | 8 blank | 16 comment | 1 complexity | 012b44a63330c901d812165c7eebfab0 MD5 | raw file
- #!/usr/bin/env python3
- # Copyright (C) 2015 Red Hat, Inc.
- # This copyrighted material is made available to anyone wishing to use,
- # modify, copy, or redistribute it subject to the terms and conditions of
- # the GNU General Public License v.2.
- #
- # This application 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
- # General Public License for more details.
- #
- # Authors:
- # Guy Streeter <streeter@redhat.com>
- # /glib/gio/tests/gdbus-example-server.c
- from gi.repository import Gio, GObject, GLib
- from common import OBJECT_PATH, dump_args, BUS_NAME, INTERFACE_NAME
- import sys
- introspection_xml = '''
- <node>
- <interface name='org.gtk.GDBus.TestInterface'>
- <annotation name='org.gtk.GDBus.Annotation' value='OnInterface'/>
- <annotation name='org.gtk.GDBus.Annotation' value='AlsoOnInterface'/>
- <method name='HelloWorld'>
- <annotation name='org.gtk.GDBus.Annotation' value='OnMethod'/>
- <arg type='s' name='greeting' direction='in'/>
- <arg type='s' name='response' direction='out'/>
- </method>
- <method name='EmitSignal'>
- <arg type='d' name='speed_in_mph' direction='in'>
- <annotation name='org.gtk.GDBus.Annotation' value='OnArg'/>
- </arg>
- </method>
- <method name='GimmeStdout'/>
- <signal name='VelocityChanged'>
- <annotation name='org.gtk.GDBus.Annotation' value='Onsignal'/>
- <arg type='d' name='speed_in_mph'/>
- <arg type='s' name='speed_as_string'>
- <annotation name='org.gtk.GDBus.Annotation' value='OnArg_NonFirst'/>
- </arg>
- </signal>
- <property type='s' name='FluxCapacitorName' access='read'>
- <annotation name='org.gtk.GDBus.Annotation' value='OnProperty'>
- <annotation name='org.gtk.GDBus.Annotation' value='OnAnnotation_YesThisIsCrazy'/>
- </annotation>
- </property>
- <property type='s' name='Title' access='readwrite'/>
- <property type='s' name='ReadingAlwaysThrowsError' access='read'/>
- <property type='s' name='WritingAlwaysThrowsError' access='readwrite'/>
- <property type='s' name='OnlyWritable' access='write'/>
- <property type='s' name='Foo' access='read'/>
- <property type='s' name='Bar' access='read'/>
- </interface>
- </node>
- '''
- introspection_data = Gio.DBusNodeInfo.new_for_xml(introspection_xml)
- print(repr(introspection_data.interfaces))
- for iface in introspection_data.interfaces:
- print(repr(iface))
- interface_info = introspection_data.lookup_interface(INTERFACE_NAME)
- print('if', interface_info)
- # dbus main loop is handled by Gio
- main_loop = GObject.MainLoop()
- def static_vars(**kwargs):
- def decorate(func):
- for k, v in kwargs.items():
- setattr(func, k, v)
- return func
- return decorate
- FooValue = 'Tick'
- BarValue = 'Tock'
- GlobalTitle = None
- @static_vars(swap_it=0)
- def on_timeout_cb(connection):
- global FooValue, BarValue
- def swapped_tick_tock():
- tick_tock = ('Tick', 'Tock')
- foo = tick_tock[on_timeout_cb.swap_it]
- on_timeout_cb.swap_it = (on_timeout_cb.swap_it + 1) % 2
- bar = tick_tock[on_timeout_cb.swap_it]
- return foo, bar
- FooValue, BarValue = swapped_tick_tock()
- # tuple(interface name, changed_properties, invalidated properties)
- #
- # Interface name is a string.
- # Changed properties are a dict with string keys and variant values
- # where the key is the property name and the value is the new
- # property value.
- # Invalidated properties are a list of property name strings. They
- # represent properties that have changed but the new values are not
- # provided. They must be fetched separately.
- values_dict = { 'Foo' : GLib.Variant('s', FooValue),
- 'Bar' : GLib.Variant('s', BarValue)
- }
- variant = GLib.Variant('(sa{sv}as)',
- ('org.gtk.GDBus.TestInterface',
- values_dict, ()))
- # http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties
- assert connection.emit_signal(None,
- OBJECT_PATH,
- 'org.freedesktop.DBus.Properties',
- 'PropertiesChanged',
- variant)
- return True
- @dump_args
- def handle_method_call(connection, sender, object_path, interface_name,
- method_name, parameters, invocation):
- if interface_name == 'org.freedesktop.DBus.Properties':
- if method_name == 'Get':
- get_property(connection, sender, object_path, parameters,
- invocation)
- return
- if method_name == 'GetAll':
- get_all(connection, sender, object_path, parameters,
- invocation)
- return
- assert method_name == 'Set'
- set_property(connection, sender, object_path, parameters,
- invocation)
- return
- assert interface_name == INTERFACE_NAME
- if method_name == 'HelloWorld':
- print(repr(parameters), parameters.unpack())
- greeting = parameters.unpack()[0]
- if greeting == 'Return Unregistered':
- response = "As requested, here's a GError not registered (G_IO_ERROR_FAILED_HANDLED)"
- invocation.return_error_literal(Gio.io_error_quark(),
- Gio.IOErrorEnum.FAILED_HANDLED,
- response)
- elif greeting == 'Return Registered':
- response = "As requested, here's a GError that is registered (G_DBUS_ERROR_MATCH_RULE_NOT_FOUND"
- invocation.return_error_literal(Gio.dbus_error_quark(),
- Gio.DBusError.MATCH_RULE_NOT_FOUND,
- response)
- elif greeting == 'Return Raw':
- response = "As requested, here's a raw D-Bus error"
- invocation.return_dbus_error('org.gtk.GDBus.SomeErrorName',
- response)
- else:
- response = "You greeted me with '%s'. Thanks!" % (greeting,)
- invocation.return_value(GLib.Variant('(s)', (response,)))
- elif method_name == 'EmitSignal':
- speed_in_mph = parameters.unpack()[0]
- speed_as_string = '%g mph!' % (speed_in_mph,)
- variant = GLib.Variant('(ds)', (speed_in_mph, speed_as_string))
- assert connection.emit_signal(None,
- object_path,
- interface_name,
- 'VelocityChanged',
- variant)
- invocation.return_value(None)
- elif method_name == 'GimmeStdout':
- print(repr(connection.get_capabilities()))
- if connection.get_capabilities() & Gio.DBusCapabilityFlags.UNIX_FD_PASSING:
- fd_list = Gio.UnixFDList.new()
- assert fd_list.append(sys.stdout.fileno()) >= 0
- reply = invocation.get_message().new_method_reply()
- reply.set_unix_fd_list(fd_list)
- assert connection.send_message(reply,
- Gio.DBusSendMessageFlags.NONE)
- else:
- response = 'Your message bus daemon does not support file descriptor passing (need D-Bus >= 1.3.0)'
- invocation.return_dbus_error('org.gtk.GDBus.Failed', response)
- else:
- assert False
- @dump_args
- def get_property(connection, sender, object_path, parameters,
- invocation):
- global GlobalTitle
- ret_string = None
- interface_name, property_name = parameters.unpack()
- assert interface_name == INTERFACE_NAME
- if property_name == 'FluxCapacitorName':
- ret_string = 'DeLorean'
- elif property_name == 'Title':
- if not GlobalTitle:
- GlobalTitle = 'Back To Python!'
- ret_string = GlobalTitle
- elif property_name == 'ReadingAlwaysThrowsError':
- response = 'Hello %s. I thought I said reading this property always results in an error. kthxbye' % (sender,)
- invocation.return_dbus_error('org.freedesktop.DBus.Error.Failed',
- response)
- return
- elif property_name == 'WritingAlwaysThrowsError':
- ret_string = "There's no home like home"
- elif property_name == 'Foo':
- ret_string = FooValue
- elif property_name == 'Bar':
- ret_string = BarValue
- else:
- response = 'No such property: ' + property_name
- invocation.return_dbus_error('org.freedesktop.DBus.Error.InvalidArgs',
- response)
- return
- assert ret_string
- # return from Get is a tuple of variants
- ret_var = GLib.Variant('s', ret_string)
- invocation.return_value(GLib.Variant('(v)', (ret_var,)))
- @dump_args
- def get_all(connection, sender, object_path, parameters, invocation):
- print(parameters.unpack())
- interface_name, = parameters.unpack()
- assert interface_name == 'org.gtk.GDBus.TestInterface'
- global GlobalTitle, FooValue, BarValue
- title = GlobalTitle if GlobalTitle else ''
- values = {'FluxCapacitorName' : GLib.Variant('s', 'DeLorean'),
- 'Title' : GLib.Variant('s', title),
- 'WritingAlwaysThrowsError' : GLib.Variant('s', 'Return to Python!'),
- 'Foo' : GLib.Variant('s', FooValue),
- 'Bar' : GLib.Variant('s', BarValue)}
- invocation.return_value(GLib.Variant('(a{sv})', (values,)))
- @dump_args
- def set_property(connection, sender, object_path, parameters,
- invocation):
- global GlobalTitle
- interface_name, property_name, new_value = parameters.unpack()
- assert interface_name == INTERFACE_NAME
- if property_name == 'Title':
- GlobalTitle = new_value
- values_dict = { 'Title' : GLib.Variant('s', GlobalTitle) }
- variant = GLib.Variant('(sa{sv}as)',
- ('org.gtk.GDBus.TestInterface',
- values_dict, ()))
- assert connection.emit_signal(None,
- OBJECT_PATH,
- 'org.freedesktop.DBus.Properties',
- 'PropertiesChanged',
- variant)
- invocation.return_value(None)
- return
- if property_name == 'OnlyWritable':
- values_dict = { 'OnlyWritable' : GLib.Variant('s', new_value) }
- variant = GLib.Variant('(sa{sv}as)',
- ('org.gtk.GDBus.TestInterface',
- values_dict, ()))
- assert connection.emit_signal(None,
- OBJECT_PATH,
- 'org.freedesktop.DBus.Properties',
- 'PropertiesChanged',
- variant)
- invocation.return_value(None)
- return
- if property_name == 'WritingAlwaysThrowsError':
- response = 'Hello %s. I thought I said writing this property always results in an error. kthxbye' % (sender,)
- invocation.return_error_literal(Gio.io_error_quark(),
- Gio.IOErrorEnum.FAILED,
- response)
- return
- assert False
- @dump_args
- def bus_acquired_handler(connection, name):
- # From https://lazka.github.io/pgi-docs/Gio-2.0/structs/DBusInterfaceVTable.html
- # Since 2.38, if you want to handle getting/setting D-Bus properties
- # asynchronously, give None as your get_property() or set_property()
- # function. The D-Bus call will be directed to your method_call
- # function, with the provided interface_name set to
- # “org.freedesktop.DBus.Properties”.
- #
- # Specifying the handlers here works, but they don't have a way to
- # return an error.
- registration_id = connection.register_object(OBJECT_PATH,
- interface_info,
- handle_method_call,
- None,
- None)
- assert registration_id > 0
- # swap the properties of Foo and Bar every two seconds
- GLib.timeout_add_seconds(2, on_timeout_cb, connection)
- def name_acquired_handler(connection, name):
- print('name acquired', name)
- def name_lost_handler(connection, name):
- print('name lost', name)
- main_loop.quit()
- owner_id = Gio.bus_own_name(Gio.BusType.SESSION,
- BUS_NAME,
- Gio.BusNameOwnerFlags.NONE,
- bus_acquired_handler,
- name_acquired_handler,
- name_lost_handler)
- main_loop.run()
- Gio.bus_unown_name(owner_id)