DBus is great, and so is Python—each for their own reasons. One mismatch is that everything passing over the bus must be a message or signal. You wind up with Python code that looks like
proxyobject.SetThis('foo')
bar = proxyobject.GetThat()
proxyobject.DoSomeOtherStuff()
But then you realize the dbus-python binding is very Pythonic, so you can extend it in a Pythonic way. Here's something I banged together in about half an hour:
import dbus
class FancyInterface(dbus.Interface):
"""A fancier DBus Interface.
Derived classes should define a list of strings __dbus_properties__ and a
_dbus_interface. For every listed property ('foo') the named interface
should support two methods:
* GetFoo() -- accepts nothing, returns a single value.
* SetFoo(..) -- returns nothing, accepts a single value of the same type
as GetFoo.
Then DerivedClass.foo may be accessed as if it were a normal variable.
"""
def __init__(self, object):
# next four lines from dbus.Interface.__init__()
if isinstance(object, dbus.Interface):
self._obj = object.proxy_object
else:
self._obj = object
# set up properties
for a in self.__dbus_properties__:
fget = lambda self: self._obj.get_dbus_method('Get' +
a.capitalize())()
fset = lambda self, value: self._obj.get_dbus_method('Set' +
a.capitalize())(value)
setattr(self.__class__, a, property(fget, fset))
class Sample(FancyInterface):
_dbus_interface = 'com.example.SampleInterface'
__dbus_properties__ = ('foo', 'bar',)
def __init__(self, object): FancyInterface.__init__(self, object)
# this class is 'empty' otherwise...
Using FancyInterface, it becomes even easier to forget you're talking to a proxy of a remote object:
bus = dbus.SessionBus()
o = Sample(bus.get_object('com.example.SampleService',
'/SomeObject'))
o.foo = 'Hello, '
o.bar = 'world!'
print o.foo, o.bar
Look for this stuff being used in GTG in the near future! (Also, fixes to my blog CSS...)
Comments
Some comments: - Looks like
Some comments:
- Looks like you're setting the class attributes on every instance creation of a Sample object (or any other type deriving from FancyInterface). I guess that's not the desired behaviour?
- Why defining __init__ in Sample if it only calls the super constructor?
- I'd rather implement this using a metaclass mixin... OTOH (not on a Linux system right now...), with a slight API change ('__dbus_interface__' instead of a protected '_dbus_interface' name):
class _FancyInterfaceMeta(type): def __new__(cls, name, bases, attrs): try: FancyInterface except NameError: return type.__new__(name, bases, attrs) interface = attrs.pop('__dbus_interface__') properties = attrs.pop('__dbus_properties__') assert '_dbus_interface' not in attrs attrs['_dbus_interface'] = interface for name in properties: assert name not in attrs name_ = name.capitalize() fget = lambda self: self._obj.get_dbus_method('Get%s' % name_) fset = lambda self, value: self._obj.get_dbus_method('Set%s' % name_)(value) prop = property(fget, fset, doc='Accessor to DBus property %s' % name) attrs[name] = prop return type.__new__(name, bases, attrs) class FancyInterface(object, dbus.Interface): __metaclass__ = _FancyInterfaceMeta def __init__(self, object_): if isinstance(object_, dbus.Interface): self._obj = object_.proxy_object else: self._obj = object_ class Sample(FancyInterface): __dbus_interface__ = 'com.example.SampleInterface' __dbus_properties__ = 'foo', 'bar',Please don't have GetFoo()
Please don't have GetFoo() and SetFoo() methods on your Dbus objects to access its properties; instead it should use the standard Dbus properties interface see http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfa...
@Nicolas: No, it's not,
@Nicolas:
_dbus_interfaceis used elsewhere in the parent classes, so I just reused it.@Staz: That's handy! So
GetAll()can obviate storing__dbus_properties__, andGet()andSet()will simplify defining the lambda functions forfgetandfset.Thanks both for your comments!
One problem with this
One problem with this proposal is that people expect attribute access to be fast, but will tolerate slow method calls. People usually code accordingly, which can lead to quite slow programs (e.g. repeatedly getting the property when it could do with a single call).
A D-Bus method call (which the standard property access interface entails) certainly doesn't count as fast, since it will block waiting for the response.