5d338e0aefa5a0366695e9258bd68634847ac0d7
[cnetworkmanager.git] / dbusclient / __init__.py
blob5d338e0aefa5a0366695e9258bd68634847ac0d7
1 "Convenience wrappers around dbus-python"
3 import dbus
4 import functools
6 # TODO rename to adaptors
7 from func import Adaptor, MethodAdaptor, PropertyAdaptor, SignalAdaptor
9 def object_path(o):
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.
14 """
15 if isinstance(o, dbus.proxies.ProxyObject):
16 return o.object_path
17 # hope it is ok
18 return o
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
27 to the constructor.
29 BUGS: 1st method call will block with introspection"""
31 API_OPTIONS = {
32 "byte_arrays": True,
35 def __init__(self, conn=None, bus_name=None, object_path=None, introspect=True, follow_name_owner_changes=False, **kwargs):
36 """Constructor.
38 kwargs may contain default_interface, to be used
39 if introspection does not provide it for a method/property
40 """
42 # FIXME common for this class, all classes?
43 self.__default_interface = kwargs.pop("default_interface", None)
44 # print "OP:", object_path
45 super(DBusMio, self).__init__(conn, bus_name, object_path, introspect, follow_name_owner_changes, **kwargs)
47 def __getattr__(self, name):
48 """Proxied DBus methods.
50 Uses introspection or default_interface to find the interface.
51 """
52 # TODO cache
53 # iface = self._interface_cache.get(name)
54 # if iface == None:
55 iface = self.__default_interface
56 # _introspect_method_map comes from ProxyObject
57 # But it will be empty until the async introspection finishes
58 self._introspect_block() # FIXME makeit work with async methods
59 methods = self._introspect_method_map.keys()
60 for im in methods:
61 (i, m) = im.rsplit(".", 1)
62 if m == name:
63 iface = i
64 # print "METHOD %s INTERFACE %s" %(name, iface)
65 callable = super(DBusMio, self).__getattr__(name)
66 return functools.partial(callable, dbus_interface=iface, **DBusMio.API_OPTIONS)
68 # properties
69 def __getitem__(self, key):
70 """Proxies DBus properties as dictionary items.
72 a = DBusMio(...)
73 p = a["Prop"]
75 Uses default_interface (because dbus.proxies.ProxyObject
76 does not store introspection data for properties, boo. TODO.)
77 """
79 iface = self.__default_interface # TODO cache
80 # TODO _introspect_property_map
81 pmi = dbus.Interface(self, "org.freedesktop.DBus.Properties")
82 return pmi.Get(iface, key, **DBusMio.API_OPTIONS)
84 def __setitem__(self, key, value):
85 """Proxies DBus properties as dictionary items.
87 a = DBusMio(...)
88 a["Prop"] = "Hello"
90 Uses default_interface (because dbus.proxies.ProxyObject
91 does not store introspection data for properties, boo. TODO.)
92 """
94 iface = self.__default_interface # TODO cache
95 # TODO _introspect_property_map
96 pmi = dbus.Interface(self, "org.freedesktop.DBus.Properties")
97 return pmi.Set(iface, key, value, **DBusMio.API_OPTIONS)
99 def _mklist(x):
100 """Return a list.
102 Tuples are made into lists, everything else a singleton list.
104 if isinstance(x, list):
105 return x
106 elif isinstance(x, tuple):
107 return [i for i in x]
108 else:
109 return [x]
111 class DBusClient(DBusMio):
114 _adaptors = {
115 "methods": {},
116 "signals": {},
117 "properties": {},
121 @classmethod
122 def _get_adaptor(cls, kind, name):
123 # print "GET", cls, kind, name
124 try:
125 a = cls._adaptors[kind][name]
126 # print ">", a
127 # TODO cache somehow?
128 return a
129 except KeyError:
130 scls = cls.__mro__[1] # can use "super"? how?
131 try:
132 return scls._get_adaptor(kind, name)
133 except AttributeError: # no _get_adaptor there
134 raise KeyError(":".join((kind, name)))
136 @classmethod
137 def _add_adaptor(cls, kind, name, adaptor):
138 # print "ADD", cls, kind, name, adaptor
139 assert(isinstance(adaptor, Adaptor))
140 cls._adaptors[kind][name] = adaptor
142 @classmethod
143 def _add_adaptors_dict(cls, andict):
145 a nested dictionary of kind:name:adaptor,
147 if not cls.__dict__.has_key("_adaptors"):
148 # do not use inherited attribute
149 cls._adaptors = {"methods":{}, "properties":{}, "signals":{}}
151 for section in cls._adaptors.keys():
152 secsource = andict.pop(section, {})
153 for name, adaptor in secsource.iteritems():
154 cls._add_adaptor(section, name, adaptor)
155 assert len(andict) == 0
156 # print "AA", cls, cls._adaptors
158 @classmethod
159 def _add_adaptors(cls, **kwargs):
160 """kwargs: a *flat* dictionary of name: adaptor"""
161 adict = {"methods":{}, "properties":{}, "signals":{}}
162 for k, v in kwargs.iteritems():
163 kind = v.kind()
164 adict[kind][k] = v
165 cls._add_adaptors_dict(adict)
167 def __getattr__(self, name):
168 "Wrap return values"
170 callable = super(DBusClient, self).__getattr__(name)
171 try:
172 adaptor = self._get_adaptor("methods", name)
173 return adaptor.adapt(callable)
174 except KeyError:
175 return callable
177 # properties
178 def __getitem__(self, key):
179 value = super(DBusClient, self).__getitem__(key)
180 try:
181 adaptor = self._get_adaptor("properties", key)
182 return adaptor.adapt(value)
183 except KeyError:
184 return value
186 def __setitem__(self, key, value):
187 try:
188 adaptor = self._get_adaptor("properties", key)
189 value = adaptor.adapt_write(value)
190 except KeyError:
191 pass
192 return super(DBusClient, self).__setitem__(key, value)
195 # signals
196 # overrides a ProxyObject method
197 def _connect_to_signal(self, signame, handler, interface=None, **kwargs):
198 "Wrap signal handler, with arg adaptors"
200 # TODO also demarshal kwargs
201 adaptor = self._get_adaptor("signals", signame)
202 wrap_handler = adaptor.adapt(handler)
203 return self.connect_to_signal(signame, wrap_handler, interface, **kwargs)