1 "Convenience wrappers around dbus-python"
6 # TODO rename to adaptors
7 from func
import Adaptor
, MethodAdaptor
, PropertyAdaptor
, SignalAdaptor
10 """Return the object path of o.
12 If o is a proxy object, use its appropriate attribute.
13 Otherwise assume that o already is an object path.
15 if isinstance(o
, dbus
.proxies
.ProxyObject
):
20 class DBusMio(dbus
.proxies
.ProxyObject
):
21 """Multi-interface object.
23 Will look into introspection data to find which interface
24 to use for a method or a property, obviating the need for
25 dbus.proxies.Interface.
26 If introspection is not available, provide default_interface
29 BUGS: 1st method call will block with introspection"""
31 def __init__(self
, conn
=None, bus_name
=None, object_path
=None, introspect
=True, follow_name_owner_changes
=False, **kwargs
):
34 kwargs may contain default_interface, to be used
35 if introspection does not provide it for a method/property
38 # FIXME common for this class, all classes?
39 self
.__default
_interface
= kwargs
.pop("default_interface", None)
40 super(DBusMio
, self
).__init
__(conn
, bus_name
, object_path
, introspect
, follow_name_owner_changes
, **kwargs
)
42 def __getattr__(self
, name
):
43 """Proxied DBus methods.
45 Uses introspection or default_interface to find the interface.
48 # iface = self._interface_cache.get(name)
50 iface
= self
.__default
_interface
51 # _introspect_method_map comes from ProxyObject
52 # But it will be empty until the async introspection finishes
53 self
._introspect
_block
() # FIXME makeit work with async methods
54 methods
= self
._introspect
_method
_map
.keys()
56 (i
, m
) = im
.rsplit(".", 1)
59 # print "METHOD %s INTERFACE %s" %(name, iface)
60 callable = super(DBusMio
, self
).__getattr
__(name
)
61 return functools
.partial(callable, dbus_interface
=iface
)
64 def __getitem__(self
, key
):
65 """Proxies DBus properties as dictionary items.
70 Uses default_interface (because dbus.proxies.ProxyObject
71 does not store introspection data for properties, boo. TODO.)
74 iface
= self
.__default
_interface
# TODO cache
75 # TODO _introspect_property_map
76 pmi
= dbus
.Interface(self
, "org.freedesktop.DBus.Properties")
77 return pmi
.Get(iface
, key
)
79 def __setitem__(self
, key
, value
):
80 """Proxies DBus properties as dictionary items.
85 Uses default_interface (because dbus.proxies.ProxyObject
86 does not store introspection data for properties, boo. TODO.)
89 iface
= self
.__default
_interface
# TODO cache
90 # TODO _introspect_property_map
91 pmi
= dbus
.Interface(self
, "org.freedesktop.DBus.Properties")
92 return pmi
.Set(iface
, key
, value
)
97 Tuples are made into lists, everything else a singleton list.
99 if isinstance(x
, list):
101 elif isinstance(x
, tuple):
102 return [i
for i
in x
]
106 class DBusClient(DBusMio
):
117 def _get_adaptor(cls
, kind
, name
):
118 # print "GET", cls, kind, name
120 a
= cls
._adaptors
[kind
][name
]
122 # TODO cache somehow?
125 scls
= cls
.__mro
__[1] # can use "super"? how?
127 return scls
._get
_adaptor
(kind
, name
)
128 except AttributeError: # no _get_adaptor there
129 raise KeyError(":".join((kind
, name
)))
132 def _add_adaptor(cls
, kind
, name
, adaptor
):
133 # print "ADD", cls, kind, name, adaptor
134 if isinstance(adaptor
, Adaptor
):
135 cls
._adaptors
[kind
][name
] = adaptor
138 # this adjusts the (too) flexible list syntax
139 adaptor
= _mklist(adaptor
)
151 if kind
== "methods":
152 adaptor
= MethodAdaptor(ret
, *args
)
153 elif kind
== "properties":
154 setter
= args
[0] if len(args
) else None
155 adaptor
= PropertyAdaptor(ret
, setter
)
156 elif kind
== "signals":
157 adaptor
= SignalAdaptor(*args
)
158 cls
._adaptors
[kind
][name
] = adaptor
162 def _add_adaptors(cls
, *args
, **kwargs
):
164 a nested dictionary of kind:name:adaptor,
165 either as kwargs (new) or as a single dict arg (old, newest)
167 if not cls
.__dict
__.has_key("_adaptors"):
168 # do not use inherited attribute
169 cls
._adaptors
= {"methods":{}, "properties":{}, "signals":{}}
171 assert len(kwargs
) == 0
172 assert len(args
) == 1
175 for section
in cls
._adaptors
.keys():
176 secsource
= kwargs
.pop(section
, {})
177 for name
, adaptor
in secsource
.iteritems():
178 cls
._add
_adaptor
(section
, name
, adaptor
)
179 assert len(kwargs
) == 0
180 # print "AA", cls, cls._adaptors
183 def _add_adaptors2(cls
, **kwargs
):
184 """kwargs: a *flat* dictionary of name: adaptor"""
185 adict
= {"methods":{}, "properties":{}, "signals":{}}
186 for k
, v
in kwargs
.iteritems():
187 if isinstance(v
, MethodAdaptor
):
188 adict
["methods"][k
] = v
189 elif isinstance(v
, PropertyAdaptor
):
190 adict
["properties"][k
] = v
191 elif isinstance(v
, SignalAdaptor
):
192 adict
["signals"][k
] = v
193 cls
._add
_adaptors
(adict
)
195 def __getattr__(self
, name
):
198 callable = super(DBusClient
, self
).__getattr
__(name
)
200 adaptor
= self
._get
_adaptor
("methods", name
)
201 return adaptor
.adapt(callable)
206 def __getitem__(self
, key
):
207 value
= super(DBusClient
, self
).__getitem
__(key
)
209 adaptor
= self
._get
_adaptor
("properties", key
)
210 return adaptor
.adapt(value
)
214 def __setitem__(self
, key
, value
):
216 adaptor
= self
._get
_adaptor
("properties", key
)
217 value
= adaptor
.adapt_write(value
)
220 return super(DBusClient
, self
).__setitem
__(key
, value
)
224 # overrides a ProxyObject method
225 def _connect_to_signal(self
, signame
, handler
, interface
=None, **kwargs
):
226 "Wrap signal handler, with arg adaptors"
228 # TODO also demarshal kwargs
229 adaptor
= self
._get
_adaptor
("signals", signame
)
230 wrap_handler
= adaptor
.adapt(handler
)
231 return self
.connect_to_signal(signame
, wrap_handler
, interface
, **kwargs
)