Playing Around With the Bluez D-BUS Interface

  • Post author:
  • Post category:Uncategorized

In my previous entry about using the Maemo obex-module on the desktop, Johan Hedberg mentioned that bluez-utils 3.7 included equivalent interfaces to the osso-gwconnect daemon used by the method. Since then, the copy of bluez-utils in Edgy has been updated to 3.7, and the necessary interfaces are enabled in hcid by default.

Before trying to modify the VFS code, I thought I’d experiment a bit with the D-BUS interfaces via the D-BUS python bindings. Most of the interesting method calls exist on the org.bluez.Adapter interface. We can easily get the default adapter with the following code:

import dbus

bus = dbus.SystemBus()
manager = dbus.Interface(
    bus.get_object('org.bluez', '/org/bluez'),
    'org.bluez.Manager')

adapter = dbus.Interface(
    bus.get_object('org.bluez', manager.DefaultAdapter()),
    'org.bluez.Adapter')

At this point, it is possible to perform discovery:

import dbus.glib
import gtk

def remote_device_found(addr, class_, rssi):
    print 'Found:', addr
def discovery_complete():
    gtk.main_quit()

adapter.connect_to_signal('RemoteDeviceFound', remote_device_found)
adapter.connect_to_signal('DiscoveryCompleted', discovery_completed)

adapter.DiscoverDevices()
gtk.main()

It is also possible to configure periodic discovery, which will send signals about devices that get found, disappear, or change name, so we could easily implement the obex: directory listing that shows all the devices found that support OBEX-FTP. One thing that isn’t clear from the API documentation is what happens if multiple programs try to start or stop discovery at the same time. It looks like the second program will get a org.bluez.Error.InProgress error when it tries to begin discovery. Ideally discovery would stay active til the last program interested in the results closed. Maybe I am misunderstanding it a bit and you can actually use the interface in this mode.

When we want to actually do OBEX-FTP with the device, we can establish the rfcomm connection:

rfcomm = dbus.Interface(
    bus.get_object('org.bluez', manager.DefaultAdapter()),
    'org.bluez.RFCOMM')

# will return e.g. /dev/rfcomm0
devname = rfcomm.Connect(bluetooth_address, 'ftp')

# communicate with the phone via the new rfcomm device

rfcomm.Disconnect(devname)

So it should be possible to modify obex-method to function with only the daemons included in Ubuntu Edgy. All that’s left is to do the actual work 🙂.

Launchpad enterered into Python bug tracker competition

  • Post author:
  • Post category:Uncategorized

The Python developers have been looking for a new bug tracker, and essentially put out a tender for people interested in providing a bug tracker. Recently I have been working on getting Launchpad‘s entry ready, which mainly involved working on SourceForge import.

The entry is now up, and our demonstration server is up and running with a snapshot of the Python bug tracker data.

As a side effect of this, we’ve got fairly good SourceForge tracker import support now, which we should be able to use if other projects want to switch away from SF.

Re: Lazy loading

  • Post author:
  • Post category:Uncategorized

Emmanuel: if you are using a language like Python, you can let the language keep track of your state machine for something like that:

def load_items(treeview, liststore, items):
    for obj in items:
        liststore.append((obj.get_foo(),
                          obj.get_bar(),
                          obj.get_baz()))
        yield True
    treeview.set_model(liststore)
    yield False

def lazy_load_items(treeview, liststore, items):
    gobject.idle_add(load_items(treeview, liststore, item).next)

Here, load_items() is a generator that will iterate over a sequence like [True, True, ..., True, False]. The next() method is used to get the next value from the iterator. When used as an idle function with this particular generator, it results in one item being added to the list store per idle call til we get to the end of the generator body where the “yield False” statement results in the idle function being removed.

For a lot of algorithms, this removes the need to design and debug a state machine equivalent. Of course, it is possible to do similar things in C but that’s even more obscure 🙂.

pygpgme 0.1 released

  • Post author:
  • Post category:Uncategorized

Back in January I started working on a new Python wrapper for the GPGME library. I recently put out the first release:

http://cheeseshop.python.org/pypi/pygpgme/0.1

This library allows you to encrypt, decrypt, sign and verify messages in the OpenPGP format, using gpg as the backend. In general, it stays fairly close to the C API with the following changes:

  • Represent C structures as Python classes where appropriate (e.g. contexts, keys, etc). Operations on those data types are converted to methods.
  • The gpgme_data_t type is not exposed directly. Instead, any Python object that looks like a file object can be passed (including StringIO objects).
  • In cases where there are gpgme_op_XXXX() and gpgme_op_XXXX_result() function pairs, these have been replaced by a single gpgme.Context.XXXX() method. Errors are returned in the exception where appropriate.
  • No explicit memory management. As expected for a Python module, memory management is automatic.

The module also releases the global interpreter lock over calls that fork gpg subprocesses. This should make the module multithread friendly.

This code is being used inside Launchpad to verify incoming email and help manage users’ PGP public keys.

In other news, gnome-gpg 0.4 made it into dapper, so users of the next Ubuntu release can see the improvements.

Python class advisors

  • Post author:
  • Post category:Uncategorized

Anyone who has played with Zope 3 has probably seen the syntax used to declare what interfaces a particular class implements. It looks something like this:

class Foo:
    implements(IFoo, IBar)
    ...

This leads to the following question: how can a function call inside a class definition’s scope affect the resulting class? To understand how this works, a little knowledge of Python metaclasses is needed.

Metaclasses

In Python, classes are instances of metaclasses. For new-style classes, the default metaclass is type (which happens to be its own metaclass). When you create a new class or subclass, you are creating a new instance of the metaclass. The constructor for a metaclass takes three arguments: the class’s name, a tuple of the base classes and a dictionary attributes and methods. So the following two definitions of the class C are equivalent:

class C(object):
    a = 42

C = type('C', (object,), {'a': 42})

The metaclass for a particular class can be picked in a number of ways:

  • A __metaclass__ variable at module or class scope.
  • Use the same metaclass as the base class.

If no metaclass is specified through either of these means, an “old style” class is created. I won’t cover old style classes here.

Now in Python calling a function and creating a new instance look pretty similar. In fact the metaclass machinary doesn’t really care. The following two class definitions are also equivalent:

class C:
    __metaclass__ = type

def not_the_metaclass(name, bases, attrs):
    return type(name, bases, attrs)

class C:
    __metaclass__ = not_the_metaclass

So using a function or other callable object as the metaclass allows you to hook into the class creation without affecting the type of the resulting class.

Class Advisors

The tricks performed by the Zope implements() function are wrapped up in the zope.interface.advice module. It does so by making use of the fact that Python programs can inspect their execution stack at runtime.

  1. Walk up the stack to where the scope of the class being defined.
  2. Check to see if a “__metaclass__” variable has been set, which would indicate the that a metaclass has been specified for this particular class already.
  3. Check the module scope for a “__metaclass__” variable.
  4. Define a function advise(name, bases, cdict) that does the following:
    • Deduce the metaclass (either what __metaclass__ was set to in the class scope, the module scope, or check base classes).
    • Call the metaclass to create the new class.
    • Do something to the new class (in the case of Zope, it sets what interfaces the class implements).
  5. Set the “__metaclass__” variable in the class scope to this function.

The actual implementation is a little more complicated to handle the case of registering multiple class advisors for a single class. The actual interface provided is quite simple though:

from zope.interface.advice import addClassAdvisor

def setA():
    def advisor(cls):
        cls.a = 42
        return cls
    addClassAdvisor(advisor)

class C:
    setA()

This simply sets the attribute ‘a’ on the class after it has been created. Also, since method decorators are implemented as a single function call, they can add a class advisor as a way to perform some extra work on the class or method after the class has been constructed.