[telepathy-doc/master] Implementing telepathy clients in Python
Danielle Madeley
danielle.madeley at collabora.co.uk
Sun Nov 8 19:22:32 PST 2009
---
docs/book/C/services.xml | 125 +++++++++++++++++++++++++-
docs/examples/python_mc5_clients/observer.py | 64 ++++++--------
2 files changed, 147 insertions(+), 42 deletions(-)
diff --git a/docs/book/C/services.xml b/docs/book/C/services.xml
index 64d23dc..a946789 100644
--- a/docs/book/C/services.xml
+++ b/docs/book/C/services.xml
@@ -10,10 +10,12 @@
<para>
Occasionally you may wish to implement your own Telepathy service, a
- program that implements a Telepathy API interface (most commonly this
- will be <interfacename>org.freedesktop.Telepathy.Client</interfacename>,
- but maybe you will be implement a Connection Manager, or some other
- Telepathy interface).
+ program that implements a Telepathy API interface. Most commonly this
+ will be <interfacename>org.freedesktop.Telepathy.Client</interfacename>
+ (see <xref linkend="sect.channel-dispatcher.clients"/> for the high-level
+ details on implementing Telepathy Clients),
+ but equally you could implement a Connection Manager, or some other
+ Telepathy interface.
</para>
<para>
This is done by implementing the D-Bus API for each interface as
@@ -275,4 +277,119 @@ observer_iface_init (gpointer g_iface, gpointer iface_data)
</sect2>
</sect1>
+
+ <sect1 id="sect.services.python">
+ <title>telepathy-python</title>
+ <para>
+ Services in <application>telepathy-python</application> are built off
+ the service publishing built into
+ <ulink url="http://dbus.freedesktop.org/doc/dbus-python/doc/tutorial.html#exporting-objects">dbus-python</ulink>.
+ However, to make implementing services easier,
+ <application>telepathy-python</application> already provides interfaces and
+ abstract base classes with all of the method decoration required already
+ supplied.
+ </para>
+
+ <sect2 id="sect.services.python.object">
+ <title>Implementing a D-Bus Object</title>
+ <para>
+ <application>telepathy-python</application> provides interfaces and
+ abstract base classes for most Telepathy services you need to implement
+ in the <literal>telepathy.server</literal> namespace.
+ To implement a D-Bus object which can be published on the bus, you just
+ need to subclass as many of these classes as required for the
+ interfaces provided by your particular object.
+ </para>
+
+ <para>
+ <xref linkend="ex.services.python.object"/> demonstrates publishing an
+ <link linkend="sect.channel-dispatcher.clients.impl.observer">Observer</link>
+ on the bus that implements
+ <interfacename>Client.Observer</interfacename> and
+ <interfacename>DBus.Properties</interfacename>.
+ </para>
+
+ <example id="ex.services.python.object"
+ file="python_mc5_clients/observer.py">
+ <title>Implementing Client.Observer and D-Bus Properties</title>
+ </example>
+
+ <para>
+ The <methodname>ObserveChannels</methodname> method is already
+ decorated with <methodname>dbus.service.method</methodname> by the
+ <classname>telepathy.server.ClientObserver</classname> parent
+ class, so doesn't need to be decorated in your subclass.
+ However, we can redecorate
+ the method if we want to change any of the parameters to
+ <methodname>dbus.service.method</methodname> (e.g. using
+ <parameter>async_callbacks</parameter>, which is useful when you need
+ to emit a signal after returning from a D-Bus call). When doing this,
+ the input and output signatures need to be provided again. See
+ <xref linkend="ex.services.python.method.redecorate"/>.
+ </para>
+
+ <tip>
+ <title>Emitting Signals after 'Returning'</title>
+ <para>
+ Many times, the Telepathy specification requires a service to return
+ from a method call before emitting any signals. The
+ <parameter>async_callbacks</parameter> parameter to the
+ <methodname>dbus.service.method</methodname> decorator is how to
+ implement this.
+ </para>
+ <para>
+ See <xref linkend="ex.services.python.method.redecorate"/> for an
+ example.
+ </para>
+ </tip>
+
+ <example id="ex.services.python.method.redecorate">
+ <title>Redecorating a Method Call</title>
+ <programlisting language="python"><![CDATA[@dbus.service.method(CONNECTION_INTERFACE_REQUESTS,
+ in_signature='a{sv}', out_signature='boa{sv}',
+ async_callbacks=('_success', '_error'))
+def EnsureChannel(self, request, _success, _error):
+
+ # code goes here
+
+ # return success over D-Bus
+ _success(yours, channel._object_path, props)
+
+ # emit a signal
+ self.signal_new_channels([channel])]]></programlisting>
+ </example>
+ </sect2>
+
+ <sect2 id="sect.services.python.publishing">
+ <title>Publishing an Object on the Bus</title>
+ <para>
+ Publishing a Telepathy object on the bus is just like publishing any
+ other object with <application>dbus-python</application>.
+ <xref linkend="ex.services.python.publishing"/> shows publishing our
+ example Observer on the session bus.
+ </para>
+
+ <example id="ex.services.python.publishing"
+ file="python_mc5_clients/observer.py">
+ <title>Publishing a Client on the Bus</title>
+ </example>
+ </sect2>
+
+ <sect2 id="sect.services.python.dbusproperties">
+ <title>D-Bus Properties</title>
+
+ <para>
+ D-Bus properties are handled via the
+ <classname>telepathy.server.DBusProperties</classname> base class,
+ which your class should inherit from.
+ </para>
+ <para>
+ Call the method <methodname>_implement_property_get</methodname> to
+ provide a dictionary of the properties for a given interface.
+ Properties are provided as Python callables, so static properties can
+ be defined using <methodname>lambda</methodname>. See
+ <xref linkend="ex.services.python.object"/> for an example.
+ </para>
+ </sect2>
+ </sect1>
</chapter>
diff --git a/docs/examples/python_mc5_clients/observer.py b/docs/examples/python_mc5_clients/observer.py
index 51a1f1d..9dd3adb 100644
--- a/docs/examples/python_mc5_clients/observer.py
+++ b/docs/examples/python_mc5_clients/observer.py
@@ -1,61 +1,49 @@
import dbus.glib
import gobject
-import telepathy
-from telepathy._generated.Client_Observer import ClientObserver
-from telepathy.server.properties import DBusProperties
+import telepathy
from telepathy.interfaces import CLIENT, \
CLIENT_OBSERVER, \
CHANNEL
-class ExampleObserver(ClientObserver, DBusProperties):
- properties = {
- CLIENT: {
- 'Interfaces': [ CLIENT_OBSERVER ],
- },
- CLIENT_OBSERVER: {
- 'ObserverChannelFilter': dbus.Array([
+# begin ex.services.python.object
+class ExampleObserver(telepathy.server.ClientObserver,
+ telepathy.server.DBusProperties):
+
+ def __init__(self, *args):
+ telepathy.server.ClientObserver.__init__(self, *args)
+ telepathy.server.DBusProperties.__init__(self)
+
+ self._implement_property_get(CLIENT, {
+ 'Interfaces': lambda: [ CLIENT_OBSERVER ],
+ })
+ self._implement_property_get(CLIENT_OBSERVER, {
+ 'ObserverChannelFilter': lambda: dbus.Array([
dbus.Dictionary({
}, signature='sv')
], signature='a{sv}')
- },
- }
-
- def __init__(self, client_name):
- bus_name = '.'.join ([CLIENT, client_name])
- object_path = '/' + bus_name.replace('.', '/')
-
- bus_name = dbus.service.BusName(bus_name, bus=dbus.SessionBus())
- ClientObserver.__init__(self, bus_name, object_path)
-
- def GetAll(self, interface):
- print "GetAll", interface
- if interface in self.properties:
- return self.properties[interface]
- else:
- return {}
-
- def Get(self, interface, property):
- print "Get", interface, property
- if interface in self.properties and \
- property in self.properties[interface]:
- return self.properties[interface][property]
- else:
- return 0
+ })
def ObserveChannels(self, account, connection, channels, dispatch_operation,
requests_satisfied, observer_info):
-
print "Incoming channels on %s:" % (connection)
for object, props in channels:
print " - %s :: %s" % (props[CHANNEL + '.ChannelType'],
props[CHANNEL + '.TargetID'])
+# end ex.services.python.object
+
+# begin ex.services.python.publishing
+def publish(client_name):
+ bus_name = '.'.join ([CLIENT, client_name])
+ object_path = '/' + bus_name.replace('.', '/')
+
+ bus_name = dbus.service.BusName(bus_name, bus=dbus.SessionBus())
-def start():
- ExampleObserver("ExampleObserver")
+ ExampleObserver(bus_name, object_path)
+# end ex.services.python.publishing
return False
if __name__ == '__main__':
- gobject.timeout_add(0, start)
+ gobject.timeout_add(0, publish, "ExampleObserver")
loop = gobject.MainLoop()
loop.run()
--
1.5.6.5
More information about the telepathy-commits
mailing list