1# Copyright 2015 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import dbus
6import dbus.service
7
8
9class NoSuchPropertyException(Exception):
10    """Raised when someone requests a property that has not been registered."""
11    pass
12
13
14class DBusPropertyExposer(dbus.service.Object):
15    """Exports the org.freedesktop.DBus.Properties interface."""
16
17    def __init__(self, bus, path, interface_name):
18        super(DBusPropertyExposer, self).__init__(bus, path)
19        self.interface_name = interface_name
20        self._properties = dict()
21
22
23    def register_property(self, property_name, property_getter):
24        """Subclasses should call this function for each exposed property.
25
26        @param property_name: string name of property to expose.
27        @param property_getter: function that takes no arguments and returns
28                a value for the property, in its proper DBus typing.
29
30        """
31        self._properties[property_name] = property_getter
32
33
34    def on_property_changed(self, property_name):
35        """Subclasses should call this function when property values change.
36
37        @param property_name: string name of property that changed.
38
39        """
40        self.PropertiesChanged(dbus.String(self.interface_name),
41                               self.property_getter(),
42                               dbus.Array([], 's'))
43
44
45    def property_getter(self):
46        """Method suitable for giving to the ObjectManager.
47
48        @return map of DBus strings to variants.
49
50        """
51        results = dbus.Dictionary(dict(), 'sv')
52        for property_name, property_getter in self._properties.iteritems():
53            results[dbus.String(property_name)] = property_getter()
54        return results
55
56
57    @dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE,
58                         in_signature='ss', out_signature='v')
59    def Get(self, interface_name, requested_property_name):
60        """Implements org.freedesktop.DBus.Properties.Get().
61
62        @param interface_name: string interface to get properties of.
63        @param requested_property_name: string, self explanatory.
64        @return variant value of the given property.
65
66        """
67        if interface_name != self.interface_name:
68            return ''
69        for property_name, property_getter in self._properties.iteritems():
70            if property_name == requested_property_name:
71                return property_getter()
72        raise NoSuchPropertyException('Could not find property %s' %
73                                      requested_property_name)
74
75
76    @dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE,
77                         in_signature='s', out_signature='a{sv}')
78    def GetAll(self, interface_name):
79        """Implements org.freedesktop.DBus.Properties.GetAll().
80
81        @param interface_name: string interface to get properties of.
82        @return dict which maps property names to values.
83
84        """
85        results = dbus.Dictionary(dict(), 'sv')
86        if interface_name != self.interface_name:
87            return results
88        for property_name, property_getter in self._properties.iteritems():
89            results[dbus.String(property_name)] = property_getter()
90        return results
91
92
93    @dbus.service.signal(dbus_interface=dbus.PROPERTIES_IFACE,
94                         signature='sa{sv}as')
95    def PropertiesChanged(self, interface_name, properties, invalid_properties):
96        """Implementation of DBus interface signal.
97
98        org.freedesktop.DBus.Properties.PropertiesChanged (
99            STRING interface_name,
100            DICT<STRING,VARIANT> changed_properties,
101            ARRAY<STRING> invalidated_properties);
102
103        @param interface_name: See above.
104        @param properties: See above.
105        @param invalid_properties: See above.
106
107        """
108