I recently ported indicator-jenkins to Gtk3 using the python GObject Introspection Repository (gir) bindings. Ted Gould did most of the work, I just cleaned some bits up and made sure everything worked. One issue that puzzled me for a while is that the GObject library changed the way it's "notify" signal works between GObject 2 and GObject 3. I've not seen any documentation of this change, so I'll describe it here.
For this example, let's make a very simple class that has a single property:
import gobject
class MyClass(gobject.GObject):
prop = gobject.property(type=int)
...and a very simple callback function that we want to call whenever the value of 'prop' changes:
def cb(sender, prop):
print "property '%s' changed on %r." % (prop.name, sender)
Finally, with GObject 2 we can create an instance of 'MyClass' and connect to the 'notify' signal like this:
inst = MyClass()
inst.connect("notify", cb)
inst.prop = 42
When we run this simple program we get the following output:
property 'prop' changed on
... which is what we expected. However, if we port this code to GObject 3, it should look like this:
from gi.repository import GObject
class MyClass(GObject.GObject):
prop = GObject.property(type=int)
def cb(sender, prop):
print "property '%s' changed on %r." % (prop.name, sender)
inst = MyClass()
inst.connect("notify", cb)
inst.prop = 42
However, running this gives an error:
/usr/lib/python2.7/dist-packages/gi/_gobject/propertyhelper.py:171: Warning: g_value_get_object: assertion `G_VALUE_HOLDS_OBJECT (value)' failed
instance.set_property(self.name, value)
Traceback (most recent call last):
File "gobject3.py", line 8, in cb
print "property '%s' changed on %r." % (prop.name, sender)
AttributeError: 'NoneType' object has no attribute 'name'
The 'prop' parameter in the callback is set to None.
There is a solution however - connecting the callback to a more specific notification signal works as expected:
from gi.repository import GObject
class MyClass(GObject.GObject):
prop = GObject.property(type=int)
def cb(sender, prop):
print "property '%s' changed on %r." % (prop.name, sender)
inst = MyClass()
inst.connect("notify::prop", cb)
inst.prop = 42
It took me a while to figure this out - hopefully I've saved someone else that work.
Python GObject Introspection oddness
Indicator-jenkins is now even more awesome
My latest hobby project, indicator-jenkins is now even better (I wrote about this previously, in case you missed it).
New features since my last blog post:
- The code is now much nicer, and will be much easier to extend. My long-term goal is to support other types of CI servers (I'll probably have to change the project name I guess).
- Desktop notifications are generated for each new build of a monitored project. The notification includes the status and health report of the last build.
- LOTS of bug-fixes, especially around the settings UI. I'm still not happy with the settings dialog UI, but it's at least usable now.
Then launch indicator-jenkins from the unity dash or command line (Note: Launching it from the command line generates a LOT of debug output - I will turn this off in future releases).
Introducing: indicator-jenkins
For my day job I've been monitoring a jenkins instance (specifically, the public Ubuntu QA jenkins instance) using my web browser. This is obviously suboptimal - I'd like to be able to see the state of the jenkins jobs I'm interested at a glance, without having to open my web browser.
I couldn't find a solution to my problem, so I created one: indicator-jenkins is a panel indicator for your desktop manager of choice. It allows you to select one or more jobs from a jenkins server, and follow the job state without having to open your browser.
The project is hosted on launchpad, and is built daily into my PPA. To install it (I'm assuming you're running Ubuntu):
Once it's installed you can launch it in a couple of different ways:
- From a terminal - jut run indicator-jenkins. If you run it on the terminal you'll get a lot of debugging output (useful if you want to submit a patch, or figure out why it's not working).
- From the unity dash - open the dash, and search for 'jenkins' - it's probably going to be the first link.
Once it's running you should see the jenkins icon in your panel. To set it up, click the icon to open the settings dialog, enter the URL of the jenkins instance you want to look at, hit the refresh button, and pick the job(s) you want to monitor. Click OK and you're done! It's simpler than it sounds - see for yourself:
Right now it's pretty rough-and-ready - there are many features I'd like to add:
- Integrate desktop notifications, so you can be alerted when a job state changes.
- Customise panel icon based on job state (i.e.- show a red icon if any of the monitored jobs are failing).
- Allow user to customise refresh period.
- Allow user to monitor jobs from more than one jenkins server.
- Show more information about a jenkins job. For a start, show stability as well as current status, and maybe in the future show unity test pass/failure rates for projects that have that information.
The entire application is written in python. We make use of several python modules:
- The python-appindicator package gives us the ability to create an icon on the menu, and the python gtk2 bindings are used to create the menu and settings dialog.
- The python 'multiprocessing' module is used to spin up worker processes to fetch the data from jenkins. Initially the application used threads for this, but python's threading support isn't great, and it was taking too long.
- The python 'json' module is used to save and load settings.
- The python-jenkins package is used to communicate with the jenkins server.