From c2a34e28e5ecb6ba28cf56297647381466325a87 Mon Sep 17 00:00:00 2001 From: Marko Kohtala Date: Mon, 20 Jun 2011 13:03:16 +0300 Subject: [PATCH] Add support for properties. --- dbus/decorators.py | 94 ++++++++++++++++++++++++++++++++++ dbus/service.py | 142 +++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 228 insertions(+), 8 deletions(-) diff --git a/dbus/decorators.py b/dbus/decorators.py index a5ca281..5f024a1 100644 --- a/dbus/decorators.py +++ b/dbus/decorators.py @@ -338,3 +338,97 @@ def signal(dbus_interface, signature=None, path_keyword=None, return emit_signal return decorator + +class property(object): + """A decorator used to mark properties of a `dbus.service.Object`. + """ + + def __init__(self, dbus_interface=None, signature=None, + property_name=None, emits_changed_signal=None, + fget=None, fset=None, doc=None): + """Initialize the decorator used to mark properties of a + `dbus.service.Object`. + + :Parameters: + `dbus_interface` : str + The D-Bus interface owning the property + + `signature` : str + The signature of the property in the usual D-Bus notation. The + signature must be suitable to be carried in a variant. + + `property_name` : str + A name for the property. Defaults to the name of the getter or + setter function. + + `emits_changed_signal` : True, False, "invalidates", or None + Tells for introspection if the object emits PropertiesChanged + signal. + + `fget` : func + Getter function taking the instance from which to read the + property. + + `fset` : func + Setter function taking the instance to which set the property + and the property value. + + `doc` : str + Documentation string for the property. Defaults to documentation + string of getter function. + + :Since: 0.85.0 + """ + validate_interface_name(dbus_interface) + self._dbus_interface = dbus_interface + + self._init_property_name = property_name + if property_name is None: + if fget is not None: + property_name = fget.__name__ + elif fset is not None: + property_name = fset.__name__ + if property_name: + validate_member_name(property_name) + self.__name__ = property_name + + self._init_doc = doc + if doc is None and fget is not None: + doc = getattr(fget, "__doc__", None) + self.fget = fget + self.fset = fset + self.__doc__ = doc + + self._emits_changed_signal = emits_changed_signal + if len(tuple(Signature(signature))) != 1: + raise ValueError, 'signature must have only one item' + self._dbus_signature = signature + + def __get__(self, inst, type=None): + if inst is None: + return self + if self.fget is None: + raise AttributeError, "unreadable attribute" + return self.fget(inst) + + def __set__(self, inst, value): + if self.fset is None: + raise AttributeError, "can't set attribute" + self.fset(inst, value) + + def __call__(self, fget): + return self.getter(fget) + + def _copy(self, fget=None, fset=None): + return property(dbus_interface=self._dbus_interface, + signature=self._dbus_signature, + property_name=self._init_property_name, + emits_changed_signal=self._emits_changed_signal, + fget=fget or self.fget, fset=fset or self.fset, + doc=self._init_doc) + + def getter(self, fget): + return self._copy(fget=fget) + + def setter(self, fset): + return self._copy(fset=fset) diff --git a/dbus/service.py b/dbus/service.py index b92d840..9bf43fe 100644 --- a/dbus/service.py +++ b/dbus/service.py @@ -34,11 +34,13 @@ try: import thread except ImportError: import dummy_thread as thread +import __builtin__ import _dbus_bindings from dbus import SessionBus, Signature, Struct, validate_bus_name, \ - validate_object_path, INTROSPECTABLE_IFACE, ObjectPath -from dbus.decorators import method, signal + validate_object_path, INTROSPECTABLE_IFACE, \ + PROPERTIES_IFACE, ObjectPath +from dbus.decorators import method, signal, property from dbus.exceptions import DBusException, \ NameExistsException, \ UnknownMethodException @@ -296,6 +298,16 @@ def _method_reply_error(connection, message, exception): class InterfaceType(type): def __init__(cls, name, bases, dct): + # Properties require the PropertiesInterface base. + for func in dct.values(): + if isinstance(func, property): + for b in bases: + if issubclass(b, PropertiesInterface): + break + else: + bases += (PropertiesInterface,) + break + # these attributes are shared between all instances of the Interface # object, so this has to be a dictionary that maps class names to # the per-class introspection/interface data @@ -320,7 +332,7 @@ class InterfaceType(type): super(InterfaceType, cls).__init__(name, bases, dct) - # methods are different to signals, so we have two functions... :) + # methods are different to signals and properties, so we have three functions... :) def _reflect_on_method(cls, func): args = func._dbus_args @@ -368,9 +380,104 @@ class InterfaceType(type): return reflection_data + def _reflect_on_property(cls, descriptor): + signature = descriptor._dbus_signature + if signature is None: + signature = 'v' + + if descriptor.fget: + if descriptor.fset: + access = "readwrite" + else: + access = "read" + elif descriptor.fset: + access = "write" + else: + return "" + reflection_data = '