decorator factory for dbus-python methods

This is a crazy idea I had; that I want to share with people.

When you're implementing an object in dbus-python, you decorate your published method calls like this:

class ExampleObserver(dbus.service.Object):
    ...

    @dbus.service.method(dbus_interface=telepathy.interfaces.CLIENT_OBSERVER,
                         in_signature='ooa(oa{sv})oaoa{sv}',
                         out_signature='')
    def ObserveChannels(self, account, connection, channels, dispatch_operation,
                        requests_satisfied, observer_info):
        ...

The input and output signatures are incredibly easy to get wrong. The thing is, most D-Bus APIs (e.g. Telepathy) have a specification that contains these arguments. Some APIs (e.g. Telepathy-Python) provide generated code including interface names and constants. So why can't we do something more like?

class ExampleObserver(dbus.service.Object):
    ...

    @telepathy.decorators.Telepathy.Client.Observer.ObserveChannels
    def ObserveChannels(self, account, connection, channels, dispatch_operation,
                        requests_satisfied, observer_info):
        ...

With a decorator factory that looks up the parameters and then wraps the dbus.service.method factory.

Well, I just wrote a proof-of-concept. It looks something like this:

class Decorators(object):
    methods = {
        'org.freedesktop.DBus.Properties.GetAll': [ 's', 'a{sv}' ],
        'org.freedesktop.DBus.Properties.Get': [ 'ss', 'v' ],
        'org.freedesktop.Telepathy.Client.Observer.ObserveChannels': [ 'ooa(oa{sv})oaoa{sv}', '' ],
    }

    def __init__(self, namespace):
        self._namespace = namespace

    def __getattr__(self, key):
        return Decorators('%s.%s' % (self._namespace, key))

    def __call__(self, func):
        iface = self._namespace.rsplit('.', 1)[0]
        in_sig, out_sig = self.methods[self._namespace]
        return dbus.service.method(dbus_interface=iface,
                                   in_signature=in_sig,
                                   out_signature=out_sig)(func)

    def __str__(self):
        return self._namespace

decorators = Decorators('org.freedesktop')

Obviously in the real version, it would have a generated map of functions, or map of interfaces each with a map of functions, and a way to handle signals, but neat huh?

streaming to Airtunes (Airport Express) with PulseAudio

So, being back in Australia, the sun is shining, I'm sat on my sofa and I can't help but feel like I'm on summer holidays. So I want to crank some Uh Huh Her on the stereo.

We have an Airport Express, but using would normally require using Steph's computer, or the computer downstairs. So instead I thought I'd give the support in PulseAudio a go.

It's pretty easy to set up. Install the (Debian/Ubuntu) packages paprefs pavucontrol pulseaudio-module-raop pulseaudio-module-zeroconf (Fedora packages might have different names). Run paprefs and tick the option to discover Airtunes devices.

paprefs
Then using pavucontrol you can set the output device for an application.

pavucontrol
It's not perfect yet, it can sometimes get stuck skipping, and you have to move the stream to your local computer and back to the Airport (apparently the buffering is just implemented using windowing, but maybe needs to be smarter?), but it's working pretty good. Still, it's insanely neat, and because it's in PulseAudio, you can redirect any stream you like (not just from your music player).

It's not just Airtunes either. You can stream to other PulseAudio servers (discovered over mDNS) as well as UPnP media renderers.

Upgrading to Ubuntu Karmic

Upgraded to Ubuntu Karmic pre-release today on my Thinkpad X200s. Had two issues:

  1. machine wouldn't prompt for the key to unencrypt the hard disk so it didn't boot. This seems to be Launchpad bug 446591. I followed this fix.
  2. forgot that I had downgraded my Intel graphics driver to v2.4, causing my consoles to break, suspend to RAM to break and only support a broken version of EXA. apt-get install xserver-xorg-video-intel fixed this.

Things seem much better now than the mess I had this morning.

improving your code by fixing warnings

I don't know why, but there is a class of software programmer who seem to believe that compiler and runtime warnings are somehow optional, or something that can be cleaned up at a later date. This class of programmer is very common in the commercial software world.

I've never really been sure why people think these warnings can be ignored. Compilers are pretty smart these days, and if a the compiler says you have a type-mismatch for your pointer assignment… there's a good chance you did something silly.

Library developers don't just add all of those pesky assertions to their methods for fun. If your code is outputting critical warnings, you probably shouldn't be surprised that it's crashing shortly afterwards. Something clearly went wrong.

So what can you do? Compiling your code with warnings-as-errors when developing it is a very good step (-Werror in gcc). For runtime warnings, you can probably set an environment variable to make the program abort when reached (e.g. for GLib you can export G_DEBUG=fatal-warnings or fatal-criticals) — this allows you to attach a debugger when the warning is reached.

Simple steps for much better code.

and now for something from the school of pure evil (setting a background on a GtkTextView)

This is something that comes up from time to time, someone wants to set some attractive background on a GtkTextView. Well, unfortunately for you, the theme engine isn't able to help you here because GtkTextView never calls gtk_paint_box() or friends. Still, today I stared at the code for a bit and came up with a particularly hacky solution: see for yourself. You have to handle everything yourself, resizing (hook into size-allocate), scaling whatever is needed. You could probably cheat though and call something like gtk_paint_box() and then let the theme engine do that bit of the work for you… I've not tried.

Just to prove it works:


This depends on what is basically an implementation detail of the widget, and I make no promises that it will continue working in the future. With that warning in mind, do what you will with the code.

P.S. the background image is a photo of the art in my hotel room. If I was Google, I'd totally sue.

key maintenance

For those who care about things like GPG keys, I generated a new GPG key today. My old key wasn't compromised, but it's been around for a long time and is full of lots of old subkeys and my old name and stuff. I've signed my new key with my old key and set the expiry for my old key to tomorrow (I've not revoked it — should I?).

Update: it turns out that everyone in Debian is now generating 4096 RSA+RSA keys (Debian instructions). May as well be future proof, so I've revoked the key from this afternoon and generated another one. Thanks to Sjoerd for the tip.

I've pushed the keys to the keyserver, but for reference:

pub   4096R/4EE4E30F 2009-09-22 [expires: 2011-09-22]
      Key fingerprint = 5ED7 A3C0 F83A D937 300B  DA54 82EC F846 4EE4 E30F

uid                  Danielle Madeley <danielle@madeley.id.au>
uid                  Danielle Madeley <danielle.madeley@collabora.co.uk>

-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.4.9 (GNU/Linux)

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
=Nihe
-----END PGP PUBLIC KEY BLOCK-----

Please don't sign a key from someone you aren't sure is the real person, yadda yadda.

quick and dirty icon theme viewer

So very often I find myself trying to find the name of an icon in the icon theme, and I end up having to search through my icon theme with a combination of find and eog. Usually I miss the icon on my first pass through.

Today I cracked, and decided to hammer together a quick and dirty viewer and searcher for the icon theme which looks something like this:

icon theme viewer
It's written in PyGTK and so far only has the single feature of 'Search' (this is my 90% case). It could be a lot more advanced if people wanted to hack on it: it could select themes, show whether an icon is in this theme or a parent theme, what sizes are available, etc. I will certainly accept patches!

Hopefully other people find this useful too. Source code is here.

GTK+ client-side-windows and threading

Some people (including me) had noticed that recent Empathy's video calling was breaking with recent GTK+ builds. Some people (not including me) were smart enough to work out the bug was related to client-side-windows.

In CSW-enabled GTK+, calls to GDK_WINDOW_XID() — the function with returns the XID of a GdkWindow — implicitly makes a call to gdk_window_ensure_native() before retrieving the XID. It does this so that there is a window on the X server to return the XID for, and thus ideally no one's application will break under CSW. The problem in Empathy was that the first call to GDK_WINDOW_XID() happened in a thread, which in the olden days would have been harmless, but now it can magically result in your window being allocated server side.

The fix for Empathy (and maybe your program to) is to call gdk_window_ensure_native() as soon as the widget is realized. In Empathy we're calling GDK_WINDOW_XID(), because we don't want to depend on GTK+ 2.18, which looks a lot like this.