Added docstrings for DBusMio and others.
[cnetworkmanager.git] / dbusclient / __init__.py
blob9bf0ca7a50bfa86ec6a3ab59d4b9ae362e623217
1 "Convenience wrappers around dbus-python"
3 import dbus
4 import functools
5 from func import *
7 def object_path(o):
8 """Return the object path of o.
10 If o is a proxy object, use its appropriate attribute.
11 Otherwise assume that o already is an object path.
12 """
13 if isinstance(o, dbus.proxies.ProxyObject):
14 return o.object_path
15 # hope it is ok
16 return o
18 class DBusMio(dbus.proxies.ProxyObject):
19 """Multi-interface object.
21 Will look into introspection data to find which interface
22 to use for a method or a property, obviating the need for
23 dbus.proxies.Interface.
24 If introspection is not available, provide default_interface
25 to the constructor.
27 BUGS: 1st method call will block with introspection"""
29 def __init__(self, conn=None, bus_name=None, object_path=None, introspect=True, follow_name_owner_changes=False, **kwargs):
30 """Constructor.
32 kwargs may contain default_interface, to be used
33 if introspection does not provide it for a method/property
34 """
36 # FIXME common for this class, all classes?
37 self.__default_interface = kwargs.pop("default_interface", None)
38 super(DBusMio, self).__init__(conn, bus_name, object_path, introspect, follow_name_owner_changes, **kwargs)
40 def __getattr__(self, name):
41 """Proxied DBus methods.
43 Uses introspection or default_interface to find the interface.
44 """
45 # TODO cache
46 # iface = self._interface_cache.get(name)
47 # if iface == None:
48 iface = self.__default_interface
49 # _introspect_method_map comes from ProxyObject
50 # But it will be empty until the async introspection finishes
51 self._introspect_block() # FIXME makeit work with async methods
52 methods = self._introspect_method_map.keys()
53 for im in methods:
54 (i, m) = im.rsplit(".", 1)
55 if m == name:
56 iface = i
57 # print "METHOD %s INTERFACE %s" %(name, iface)
58 callable = super(DBusMio, self).__getattr__(name)
59 return functools.partial(callable, dbus_interface=iface)
61 # properties
62 def __getitem__(self, key):
63 """Proxies DBus properties as dictionary items.
65 a = DBusMio(...)
66 p = a["Prop"]
68 Uses default_interface (because dbus.proxies.ProxyObject
69 does not store introspection data for properties, boo. TODO.)
70 """
72 iface = self.__default_interface # TODO cache
73 # TODO _introspect_property_map
74 pmi = dbus.Interface(self, "org.freedesktop.DBus.Properties")
75 return pmi.Get(iface, key)
77 def __setitem__(self, key, value):
78 """Proxies DBus properties as dictionary items.
80 a = DBusMio(...)
81 a["Prop"] = "Hello"
83 Uses default_interface (because dbus.proxies.ProxyObject
84 does not store introspection data for properties, boo. TODO.)
85 """
87 iface = self.__default_interface # TODO cache
88 # TODO _introspect_property_map
89 pmi = dbus.Interface(self, "org.freedesktop.DBus.Properties")
90 return pmi.Set(iface, key, value)
92 def mklist(x):
93 if isinstance(x, list):
94 return x
95 elif isinstance(x, tuple):
96 return [i for i in x]
97 else:
98 return [x]
100 #class DBusClient(dbus.proxies.Interface):
101 class DBusClient(DBusMio):
102 _adaptors = {
103 "methods": {},
104 "signals": {},
105 "properties": {},
109 @classmethod
110 def _get_adaptor(cls, kind, name):
111 # print "GET", cls, kind, name
112 try:
113 a = cls._adaptors[kind][name]
114 # print ">", a
115 # TODO cache somehow?
116 return a
117 except KeyError:
118 scls = cls.__mro__[1] # can use "super"? how?
119 try:
120 return scls._get_adaptor(kind, name)
121 except AttributeError: # no _get_adaptor there
122 raise KeyError(":".join((kind, name)))
124 @classmethod
125 def _add_adaptor(cls, kind, name, adaptor):
126 # print "ADD", cls, kind, name, adaptor
127 adaptor = mklist(adaptor)
128 try:
129 args = adaptor[1]
130 except:
131 args = []
132 args = mklist(args)
133 try:
134 kwargs = adaptor[2]
135 except:
136 kwargs = {}
137 cls._adaptors[kind][name] = [adaptor[0], args, kwargs]
140 @classmethod
141 def _add_adaptors(cls, *args, **kwargs):
143 a nested dictionary of kind:name:adaptor,
144 either as kwargs (new) or as a single dict arg (old, newest)
146 if not cls.__dict__.has_key("_adaptors"):
147 # do not use inherited attribute
148 cls._adaptors = {"methods":{}, "properties":{}, "signals":{}}
149 if len(args) != 0:
150 assert len(kwargs) == 0
151 assert len(args) == 1
152 kwargs = args[0]
154 for section in cls._adaptors.keys():
155 secsource = kwargs.pop(section, {})
156 for name, adaptor in secsource.iteritems():
157 cls._add_adaptor(section, name, adaptor)
158 assert len(kwargs) == 0
159 # print "AA", cls, cls._adaptors
161 def __getattr__(self, name):
162 "Wrap return values"
164 callable = super(DBusClient, self).__getattr__(name)
165 try:
166 adaptor = self._get_adaptor("methods", name)
167 return async_callable_universal_adaptor(callable, adaptor)
168 except KeyError:
169 return callable
171 # properties
172 def __getitem__(self, key):
173 value = super(DBusClient, self).__getitem__(key)
174 try:
175 adaptor = self._get_adaptor("properties", key)[0]
176 except KeyError, IndexError:
177 adaptor = identity
178 return adaptor(value)
180 def __setitem__(self, key, value):
181 try:
182 adaptor = self._get_adaptor("properties", key)[1][0]
183 except KeyError, IndexError:
184 adaptor = identity
185 value = adaptor(value)
186 return super(DBusClient, self).__setitem__(key, value)
189 # signals
190 # overrides a ProxyObject method
191 def _connect_to_signal(self, signame, handler, interface=None, **kwargs):
192 "Wrap signal handler, with arg adaptors"
194 # TODO also demarshal kwargs
195 adaptor = self._get_adaptor("signals", signame)
196 wrap_handler = callable_universal_adaptor(handler, adaptor)
197 return self.connect_to_signal(signame, wrap_handler, interface, **kwargs)
199 #class ObjectAddress:
200 # """An object path, optionally with a service/connection where to find it"""
201 # pass