[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