Screenshots to show off (in HTML).
[cnetworkmanager.git] / dbusclient / __init__.py
blob1ece9ae17cb5430201d3d98f4b5fb6ca5ccbab8a
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 def __init__(self, conn=None, bus_name=None, object_path=None, introspect=True, follow_name_owner_changes=False, **kwargs):
32 """Constructor.
34 kwargs may contain default_interface, to be used
35 if introspection does not provide it for a method/property
36 """
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.
46 """
47 # TODO cache
48 # iface = self._interface_cache.get(name)
49 # if iface == None:
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()
55 for im in methods:
56 (i, m) = im.rsplit(".", 1)
57 if m == name:
58 iface = i
59 # print "METHOD %s INTERFACE %s" %(name, iface)
60 callable = super(DBusMio, self).__getattr__(name)
61 return functools.partial(callable, dbus_interface=iface, byte_arrays=True)
63 # properties
64 def __getitem__(self, key):
65 """Proxies DBus properties as dictionary items.
67 a = DBusMio(...)
68 p = a["Prop"]
70 Uses default_interface (because dbus.proxies.ProxyObject
71 does not store introspection data for properties, boo. TODO.)
72 """
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, byte_arrays=True)
79 def __setitem__(self, key, value):
80 """Proxies DBus properties as dictionary items.
82 a = DBusMio(...)
83 a["Prop"] = "Hello"
85 Uses default_interface (because dbus.proxies.ProxyObject
86 does not store introspection data for properties, boo. TODO.)
87 """
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, byte_arrays=True)
94 def _mklist(x):
95 """Return a list.
97 Tuples are made into lists, everything else a singleton list.
98 """
99 if isinstance(x, list):
100 return x
101 elif isinstance(x, tuple):
102 return [i for i in x]
103 else:
104 return [x]
106 class DBusClient(DBusMio):
109 _adaptors = {
110 "methods": {},
111 "signals": {},
112 "properties": {},
116 @classmethod
117 def _get_adaptor(cls, kind, name):
118 # print "GET", cls, kind, name
119 try:
120 a = cls._adaptors[kind][name]
121 # print ">", a
122 # TODO cache somehow?
123 return a
124 except KeyError:
125 scls = cls.__mro__[1] # can use "super"? how?
126 try:
127 return scls._get_adaptor(kind, name)
128 except AttributeError: # no _get_adaptor there
129 raise KeyError(":".join((kind, name)))
131 @classmethod
132 def _add_adaptor(cls, kind, name, adaptor):
133 # print "ADD", cls, kind, name, adaptor
134 assert(isinstance(adaptor, Adaptor))
135 cls._adaptors[kind][name] = adaptor
137 @classmethod
138 def _add_adaptors_dict(cls, andict):
140 a nested dictionary of kind:name:adaptor,
142 if not cls.__dict__.has_key("_adaptors"):
143 # do not use inherited attribute
144 cls._adaptors = {"methods":{}, "properties":{}, "signals":{}}
146 for section in cls._adaptors.keys():
147 secsource = andict.pop(section, {})
148 for name, adaptor in secsource.iteritems():
149 cls._add_adaptor(section, name, adaptor)
150 assert len(andict) == 0
151 # print "AA", cls, cls._adaptors
153 @classmethod
154 def _add_adaptors(cls, **kwargs):
155 """kwargs: a *flat* dictionary of name: adaptor"""
156 adict = {"methods":{}, "properties":{}, "signals":{}}
157 for k, v in kwargs.iteritems():
158 kind = v.kind()
159 adict[kind][k] = v
160 cls._add_adaptors_dict(adict)
162 def __getattr__(self, name):
163 "Wrap return values"
165 callable = super(DBusClient, self).__getattr__(name)
166 try:
167 adaptor = self._get_adaptor("methods", name)
168 return adaptor.adapt(callable)
169 except KeyError:
170 return callable
172 # properties
173 def __getitem__(self, key):
174 value = super(DBusClient, self).__getitem__(key)
175 try:
176 adaptor = self._get_adaptor("properties", key)
177 return adaptor.adapt(value)
178 except KeyError:
179 return value
181 def __setitem__(self, key, value):
182 try:
183 adaptor = self._get_adaptor("properties", key)
184 value = adaptor.adapt_write(value)
185 except KeyError:
186 pass
187 return super(DBusClient, self).__setitem__(key, value)
190 # signals
191 # overrides a ProxyObject method
192 def _connect_to_signal(self, signame, handler, interface=None, **kwargs):
193 "Wrap signal handler, with arg adaptors"
195 # TODO also demarshal kwargs
196 adaptor = self._get_adaptor("signals", signame)
197 wrap_handler = adaptor.adapt(handler)
198 return self.connect_to_signal(signame, wrap_handler, interface, **kwargs)