1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
|
# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 Tiger Soldier
#
# This file is part of OSD Lyrics.
#
# OSD Lyrics is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# OSD Lyrics is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with OSD Lyrics. If not, see <https://www.gnu.org/licenses/>.
#
import dbus.exceptions
class AccessDeniedError(dbus.exceptions.DBusException):
def __init__(self, *args):
dbus.exceptions.DBusException.__init__(self, dbus_error_name='org.osdlyrics.Error.AccessDenied', *args)
class Property:
""" DBus property class
"""
def __init__(self, dbus_interface, type_signature,
emit_change=True, readable=True, writeable=True,
name=None, fget=None, fset=None, dbus_set=None):
"""
Arguments:
- `type_signature`: (string) Type signature of this property. This parameter
is used for introspection only
- `dbus_interface`: (string) The DBus interface of this property
- `emit_change`: (boolean or string) Whether to emit change with
`PropertiesChanged` D-Bus signal when the property is set.
Possible values are boolean value True or False, or a string
'invalidates'.
- `readable`: Whether the property is able to visit with `Get` D-Bus method.
- `writeable`: Whether the property is able to write with `Set` D-Bus method.
A property is writeable only when `writeable` is set to True
and a setter function is set.
"""
self._type_signature = type_signature
# CAVEAT: python-dbus uses "_dbus_interface" internally (in service.py)
self._interface = dbus_interface
self._fset = fset
self._fget = fget
self.__name__ = name
self._dbusset = dbus_set
if emit_change not in [True, False, 'invalidates']:
raise ValueError('Value of emit_change must be one of True, False, or \'invalidates\'')
self._emit_change = emit_change
self._readable = readable
self._writeable = writeable
@property
def interface(self):
""" Return the dbus interface of this property
"""
return self._interface
@property
def readable(self):
return self._readable
@property
def writeable(self):
return self._writeable
@property
def emit_change(self):
return str(self._emit_change).lower()
@property
def type_signature(self):
return self._type_signature
def __get__(self, obj, objtype=None):
if obj is None:
return self
if self._fget is None:
raise AttributeError("unreadable attribute")
return wrap_dbus_type(self._type_signature, self._fget(obj))
def __set__(self, obj, value):
if self._fset is None:
raise AttributeError("can't set attribute")
self._set_value(obj, value, self._fset)
def dbus_set(self, obj, value):
"""
Set a property through D-Bus.
This is intended to called by dbusext.service.Object to implement D-Bus
property features. Don't call it directly.
If dbus setter is set by `dbus_setter` decorator, the dbus setter will be
used the handle this request. Otherwise, if `writeable` is set to `True`,
the general setter set by `setter` decorator will be used.
"""
if callable(self._dbusset):
self._set_value(obj, value, self._dbusset)
elif self.writeable and callable(self._fset):
self._set_value(obj, value, self._fset)
else:
raise AccessDeniedError('Property %s is not writeable' % self.__name__)
def _set_value(self, obj, value, setter):
changed = setter(obj, value)
if not self._emit_change:
return
if changed is None or changed:
changed = True
else:
changed = False
if changed and getattr(self, '__name__', None) and getattr(obj, '_property_set', None):
obj._property_set(self.__name__, self._emit_change is True)
def setter(self, fset):
"""
Sets the setter function of the property. Return the property object itself.
The setter function should return a boolean value to tell if the value
is changed. Returning None is considered as True.
When ``emit_change`` is True in constructor, the property will emit changes
when value is set. If the owner object has a function named
``_property_set``, and the ``__name__`` attribute of the property object is
set by the class, the function will be invoked to notify the property has
been set when the setter is called, with the name of property as the first
argument, and an boolean as the second argument to tell whether the value
is changed.
This is usually used as an decorator::
class A(osdlyrics.dbus.Object):
@osdlyrics.dbus.property(type='s', dbus_interface='example.property')
def x(self):
return self._x
@x.setter
def x(self, value):
if self._x != value:
self._x = value
return True
else:
return False
@x.dbus_setter
def x(self, value):
if self._do_something(value):
self._x = value
return True
return False
By default, the setter is used as both python property setter and dbus
property setter. If you want a different set-handler to handle dbus property
set request, use `dbus_setter`.
"""
self._fset = fset
return self
def dbus_setter(self, fset):
"""
A decorator to create a D-Bus property set handler.
The new value set through D-Bus interface is passed to this setter. If dbus
setter not set, the general setter set by `Property.setter` decorator is
used as D-Bus property set handler.
Arguments:
- `fset`: The setter handler. Takes a parameter as the new value, and return
a boolean value to indicate whether the property is changed. If
None is returned, it is treated as True. See `setter` for example.
"""
self._dbusset = fset
return self
DBUS_TYPE_MAP = {
'y': dbus.Byte,
'b': dbus.Boolean,
'n': dbus.Int16,
'q': dbus.UInt16,
'i': dbus.Int32,
'u': dbus.UInt32,
'x': dbus.Int64,
't': dbus.UInt64,
'd': dbus.Double,
's': dbus.String,
'o': dbus.ObjectPath,
'g': dbus.Signature,
}
def wrap_dbus_type(signature, value):
if signature in DBUS_TYPE_MAP:
dbustype = DBUS_TYPE_MAP[signature]
if isinstance(value, dbustype):
return value
else:
return dbustype(value)
elif signature.startswith('a{'):
if isinstance(value, dbus.Dictionary):
return value
else:
return dbus.Dictionary(value, signature=signature)
elif signature.startswith('a'):
if isinstance(value, dbus.Array):
return value
else:
return dbus.Array(value, signature=signature)
elif signature.startswith('('):
if isinstance(value, dbus.Struct):
return value
else:
return dbus.Struct(value, signature=signature)
|