From ce9820f94ecc59707af6dfe14831a09c5fbfb49e Mon Sep 17 00:00:00 2001 From: Marko Kohtala Date: Fri, 17 Jun 2011 15:47:24 +0300 Subject: [PATCH] Add support for properties. --- dbus/decorators.py | 94 +++++++++++++++++++++++++++++++++++++++++ dbus/service.py | 118 +++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 206 insertions(+), 6 deletions(-) diff --git a/dbus/decorators.py b/dbus/decorators.py index a5ca281..3977c68 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 + + 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 + + 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 + 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 getter(self, fget): + # TODO Could check the name of fget and do similar to builtin property() + if self.__name__ is None: + property_name = fget.__name__ + validate_member_name(property_name) + self.__name__ = property_name + self.fget = fget + return self + + def setter(self, fset): + # TODO Could check the name of fset and do similar to builtin property() + if self.__name__ is None: + property_name = fset.__name__ + validate_member_name(property_name) + self.__name__ = property_name + self.fset = fset + return self diff --git a/dbus/service.py b/dbus/service.py index b92d840..76cc443 100644 --- a/dbus/service.py +++ b/dbus/service.py @@ -37,8 +37,10 @@ except ImportError: 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 +_builtin_property = property +from dbus.decorators import method, signal, property from dbus.exceptions import DBusException, \ NameExistsException, \ UnknownMethodException @@ -296,6 +298,18 @@ def _method_reply_error(connection, message, exception): class InterfaceType(type): def __init__(cls, name, bases, dct): + # Verify the user remembered to inherit PropertiesInterface when using properties + # TODO Could we add the base class here automagically? + # TODO Should we check the signatures here to be ok for a variant. + for func in dct.values(): + if isinstance(func, property): + for b in bases: + if issubclass(b, PropertiesInterface): + break + else: + raise ValueError("A property defined without base class 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 +334,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 +382,99 @@ 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 = '