hush.
[platform/upstream/dbus.git] / python / dbus.py
1
2 """Module for high-level communication over the FreeDesktop.org Bus (DBus)
3
4 DBus allows you to share and access remote objects between processes
5 running on the desktop, and also to access system services (such as
6 the print spool).
7
8 To use DBus, first get a Bus object, which provides a connection to one
9 of a few standard dbus-daemon instances that might be running. From the
10 Bus you can get a RemoteService. A service is provided by an application or
11 process connected to the Bus, and represents a set of objects. Once you
12 have a RemoteService you can get a RemoteObject that implements a specific interface
13 (an interface is just a standard group of member functions). Then you can call
14 those member functions directly.
15
16 You can think of a complete method call as looking something like:
17
18 Bus:SESSION -> Service:org.gnome.Evolution -> Object:/org/gnome/Evolution/Inbox -> Interface: org.gnome.Evolution.MailFolder -> Method: Forward('message1', 'seth@gnome.org')
19
20 This communicates over the SESSION Bus to the org.gnome.Evolution process to call the
21 Forward method of the /org/gnome/Evolution/Inbox object (which provides the
22 org.gnome.Evolution.MailFolder interface) with two string arguments.
23
24 For example, the dbus-daemon itself provides a service and some objects:
25
26 # Get a connection to the desktop-wide SESSION bus
27 bus = dbus.Bus(dbus.Bus.TYPE_SESSION)
28
29 # Get the service provided by the dbus-daemon named org.freedesktop.DBus
30 dbus_service = bus.get_service('org.freedesktop.DBus')
31
32 # Get a reference to the desktop bus' standard object, denoted
33 # by the path /org/freedesktop/DBus. The object /org/freedesktop/DBus
34 # implements the 'org.freedesktop.DBus' interface
35 dbus_object = dbus_service.get_object('/org/freedesktop/DBus',
36                                        'org.freedesktop.DBus')
37
38 # One of the member functions in the org.freedesktop.DBus interface
39 # is ListServices(), which provides a list of all the other services
40 # registered on this bus. Call it, and print the list.
41 print(dbus_object.ListServices())
42 """
43
44 import dbus_bindings
45
46 class Bus:
47     """A connection to a DBus daemon.
48
49     One of three possible standard buses, the SESSION, SYSTEM,
50     or ACTIVATION bus
51     """
52     TYPE_SESSION    = dbus_bindings.BUS_SESSION
53     TYPE_SYSTEM     = dbus_bindings.BUS_SYSTEM
54     TYPE_ACTIVATION = dbus_bindings.BUS_ACTIVATION
55
56     def __init__(self, bus_type=TYPE_SESSION, glib_mainloop=True):
57         self._connection = dbus_bindings.bus_get(bus_type)
58
59         self._connection.add_filter(self._signal_func)
60         self._match_rule_to_receivers = { }
61         if (glib_mainloop):
62             self._connection.setup_with_g_main()
63
64     def get_service(self, service_name="org.freedesktop.Broadcast"):
65         """Get one of the RemoteServices connected to this Bus. service_name
66         is just a string of the form 'com.widgetcorp.MyService'
67         """
68         return RemoteService(self._connection, service_name)
69
70     def add_signal_receiver(self, receiver, interface=None, service=None, path=None):
71         match_rule = self._get_match_rule(interface, service, path)
72         
73         if (not self._match_rule_to_receivers.has_key(match_rule)):
74             self._match_rule_to_receivers[match_rule] = [ ]
75         self._match_rule_to_receivers[match_rule].append(receiver)
76         
77         dbus_bindings.bus_add_match(self._connection, match_rule)
78
79     def remove_signal_receiver(self, receiver, interface=None, service=None, path=None):
80         match_rule = self._get_match_rule(interface, service, path)
81
82         if self._match_rule_to_receivers.has_key(match_rule):
83             if self._match_rule_to_receivers[match_rule].__contains__(receiver):
84                 self._match_rule_to_receivers[match_rule].remove(receiver)
85                 dbus_bindings.bus_remove_match(self._connection, match_rule)
86
87     def get_connection(self):
88         """Get the dbus_bindings.Connection object associated with this Bus"""
89         return self._connection
90
91     def _get_match_rule(self, interface, service, path):
92 ##        if (interface):
93 ##            match_rule = match_rule + ",interface='%s'" % (interface)
94 ##        if (service):
95 ##            match_rule = match_rule + ",service='%s'" % (service)
96 ##        if (path):
97 ##            match_rule = match_rule + ",path='%s'" % (path)
98         # FIXME: use the service here too!!!
99         return "type='signal',interface='%s',path='%s'" % (interface, path)
100     
101     def _signal_func(self, connection, message):
102         if (message.get_type() != dbus_bindings.MESSAGE_TYPE_SIGNAL):
103             return
104         
105         interface = message.get_interface()
106         service   = message.get_sender()
107         path      = message.get_path()
108         member    = message.get_member()
109
110         match_rule = self._get_match_rule(interface, service, path)
111
112         if (self._match_rule_to_receivers.has_key(match_rule)):
113             receivers = self._match_rule_to_receivers[match_rule]
114             args = [interface, member, service, path, message]
115             for receiver in receivers:
116                 receiver(*args)
117         
118
119 class RemoteObject:
120     """A remote Object.
121
122     A RemoteObject is provided by a RemoteService on a particular Bus. RemoteObjects
123     have member functions, and can be called like normal Python objects.
124     """
125     def __init__(self, connection, service_name, object_path, interface):
126         self._connection   = connection
127         self._service_name = service_name
128         self._object_path  = object_path
129         self._interface    = interface
130
131     def __getattr__(self, member):
132         if member == '__call__':
133             return object.__call__
134         else:
135             return RemoteMethod(self._connection, self._service_name,
136                                 self._object_path, self._interface, member)
137
138
139 class RemoteMethod:
140     """A remote Method.
141
142     Typically a member of a RemoteObject. Calls to the
143     method produce messages that travel over the Bus and are routed
144     to a specific Service.
145     """
146     def __init__(self, connection, service_name, object_path, interface, method_name):
147         self._connection   = connection
148         self._service_name = service_name
149         self._object_path  = object_path
150         self._interface    = interface
151         self._method_name  = method_name
152
153     def __call__(self, *args):
154         message = dbus_bindings.MethodCall(self._object_path, self._interface, self._method_name)
155         message.set_destination(self._service_name)
156         
157         # Add the arguments to the function
158         iter = message.get_iter()
159         for arg in args:
160             iter.append(arg)
161
162         reply_message = self._connection.send_with_reply_and_block(message, 5000)
163
164         args_tuple = reply_message.get_args_list()
165         if len(args_tuple) == 0:
166             return
167         elif len(args_tuple) == 1:
168             return args_tuple[0]
169         else:
170             return args_tuple
171
172 class Service:
173     """A base class for exporting your own Services across the Bus
174
175     Just inherit from Service, providing the name of your service
176     (e.g. org.designfu.SampleService).
177     """
178     def __init__(self, service_name, bus=None):
179         self._service_name = service_name
180                              
181         if bus == None:
182             # Get the default bus
183             self._bus = Bus()
184         else:
185             self._bus = bus
186
187         dbus_bindings.bus_acquire_service(self._bus.get_connection(), service_name)
188
189     def get_bus(self):
190         """Get the Bus this Service is on"""
191         return self._bus
192
193     def get_service_name(self):
194         """Get the name of this service"""
195         return self._service_name
196
197 class Object:
198     """A base class for exporting your own Objects across the Bus.
199
200     Just inherit from Object and provide a list of methods to share
201     across the Bus. These will appear as member functions of your
202     ServiceObject.
203     """
204     def __init__(self, object_path, methods_to_share, service):
205         self._object_path = object_path
206         self._service = service
207         self._bus = service.get_bus()
208         self._connection = self._bus.get_connection()
209
210         self._method_name_to_method = self._build_method_dictionary(methods_to_share)
211         
212         self._connection.register_object_path(object_path, self._unregister_cb, self._message_cb)
213
214     def broadcast_signal(self, interface, signal_name):
215         message = dbus_bindings.Signal(self._object_path, interface, signal_name)
216         #FIXME: need to set_sender, but it always disconnects when we do this
217         #message.set_sender(self._service.get_service_name())
218         self._connection.send(message)
219
220     def _unregister_cb(self, connection):
221         print ("Unregister")
222         
223     def _message_cb(self, connection, message):
224         target_method_name = message.get_member()
225         target_method = self._method_name_to_method[target_method_name]
226         args = message.get_args_list()
227         
228         try:
229             retval = target_method(*args)
230         except Exception, e:
231             if e.__module__ == '__main__':
232                 # FIXME: is it right to use .__name__ here?
233                 error_name = e.__class__.__name__
234             else:
235                 error_name = e.__module__ + '.' + str(e.__class__.__name__)
236             error_contents = str(e)
237             reply = dbus_bindings.Error(message, error_name, error_contents)
238         else:
239             reply = dbus_bindings.MethodReturn(message)
240             if retval != None:
241                 iter = reply.get_iter()
242                 iter.append(retval)
243                 
244         self._connection.send(reply)
245
246     def _build_method_dictionary(self, methods):
247         method_dict = {}
248         for method in methods:
249             if method_dict.has_key(method.__name__):
250                 print ('WARNING: registering DBus Object methods, already have a method named %s' % (method.__name__))
251             method_dict[method.__name__] = method
252         return method_dict
253
254 class RemoteService:
255     """A remote service providing objects.
256
257     A service is typically a process or application that provides
258     remote objects, but can also be the broadcast service that
259     receives signals from all applications on the Bus.
260     """
261     
262     def __init__(self, connection, service_name):
263         self._connection     = connection
264         self._service_name   = service_name
265
266     def get_object(self, object_path, interface):
267         """Get an object provided by this Service that implements a
268         particular interface. object_path is a string of the form
269         '/com/widgetcorp/MyService/MyObject1'. interface looks a lot
270         like a service_name (they're often the same) and is of the form,
271         'com.widgetcorp.MyInterface', and mostly just defines the
272         set of member functions that will be present in the object.
273         """
274         return RemoteObject(self._connection, self._service_name, object_path, interface)
275