1 # -*- Mode: Python; py-indent-offset: 4 -*-
2 # vim: tabstop=4 shiftwidth=4 expandtab
4 # Copyright (C) 2010 Ignacio Casal Quinteiro <icq@gnome.org>
6 # This library is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU Lesser General Public
8 # License as published by the Free Software Foundation; either
9 # version 2.1 of the License, or (at your option) any later version.
11 # This library is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # Lesser General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with this library; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
21 from ..overrides import override
22 from ..module import get_introspection_module
24 from gi.repository import GLib
28 Gio = get_introspection_module('Gio')
33 class FileEnumerator(Gio.FileEnumerator):
38 file_info = self.next_file(None)
40 if file_info is not None:
45 # python 2 compat for the iter protocol
49 FileEnumerator = override(FileEnumerator)
50 __all__.append('FileEnumerator')
53 class MenuItem(Gio.MenuItem):
54 def set_attribute(self, attributes):
55 for (name, format_string, value) in attributes:
56 self.set_attribute_value(name, GLib.Variant(format_string, value))
59 MenuItem = override(MenuItem)
60 __all__.append('MenuItem')
63 class Settings(Gio.Settings):
64 '''Provide dictionary-like access to GLib.Settings.'''
66 def __init__(self, schema, path=None, backend=None, **kwargs):
67 Gio.Settings.__init__(self, schema=schema, backend=backend, path=path, **kwargs)
69 def __contains__(self, key):
70 return key in self.list_keys()
73 return len(self.list_keys())
76 # for "if mysettings" we don't want a dictionary-like test here, just
77 # if the object isn't None
80 # alias for Python 2.x object protocol
81 __nonzero__ = __bool__
83 def __getitem__(self, key):
84 # get_value() aborts the program on an unknown key
86 raise KeyError('unknown key: %r' % (key,))
88 return self.get_value(key).unpack()
90 def __setitem__(self, key, value):
91 # set_value() aborts the program on an unknown key
93 raise KeyError('unknown key: %r' % (key,))
95 # determine type string of this key
96 range = self.get_range(key)
97 type_ = range.get_child_value(0).get_string()
98 v = range.get_child_value(1)
100 # v is boxed empty array, type of its elements is the allowed value type
101 type_str = v.get_child_value(0).get_type_string()
102 assert type_str.startswith('a')
103 type_str = type_str[1:]
104 elif type_ == 'enum':
105 # v is an array with the allowed values
106 assert v.get_child_value(0).get_type_string().startswith('a')
107 type_str = v.get_child_value(0).get_child_value(0).get_type_string()
109 if value not in allowed:
110 raise ValueError('value %s is not an allowed enum (%s)' % (value, allowed))
112 raise NotImplementedError('Cannot handle allowed type range class ' + str(type_))
114 self.set_value(key, GLib.Variant(type_str, value))
117 return self.list_keys()
119 Settings = override(Settings)
120 __all__.append('Settings')
123 class _DBusProxyMethodCall:
124 '''Helper class to implement DBusProxy method calls.'''
126 def __init__(self, dbus_proxy, method_name):
127 self.dbus_proxy = dbus_proxy
128 self.method_name = method_name
130 def __async_result_handler(self, obj, result, user_data):
131 (result_callback, error_callback, real_user_data) = user_data
133 ret = obj.call_finish(result)
135 etype, e = sys.exc_info()[:2]
136 # return exception as value
138 error_callback(obj, e, real_user_data)
140 result_callback(obj, e, real_user_data)
143 result_callback(obj, self._unpack_result(ret), real_user_data)
145 def __call__(self, *args, **kwargs):
146 # the first positional argument is the signature, unless we are calling
147 # a method without arguments; then signature is implied to be '()'.
151 if not isinstance(signature, str):
152 raise TypeError('first argument must be the method signature string: %r' % signature)
156 arg_variant = GLib.Variant(signature, tuple(args))
158 if 'result_handler' in kwargs:
160 user_data = (kwargs['result_handler'],
161 kwargs.get('error_handler'),
162 kwargs.get('user_data'))
163 self.dbus_proxy.call(self.method_name, arg_variant,
164 kwargs.get('flags', 0), kwargs.get('timeout', -1), None,
165 self.__async_result_handler, user_data)
168 result = self.dbus_proxy.call_sync(self.method_name, arg_variant,
169 kwargs.get('flags', 0),
170 kwargs.get('timeout', -1),
172 return self._unpack_result(result)
175 def _unpack_result(klass, result):
176 '''Convert a D-BUS return variant into an appropriate return value'''
178 result = result.unpack()
180 # to be compatible with standard Python behaviour, unbox
181 # single-element tuples and return None for empty result tuples
184 elif len(result) == 0:
190 class DBusProxy(Gio.DBusProxy):
191 '''Provide comfortable and pythonic method calls.
193 This marshalls the method arguments into a GVariant, invokes the
194 call_sync() method on the DBusProxy object, and unmarshalls the result
195 GVariant back into a Python tuple.
197 The first argument always needs to be the D-Bus signature tuple of the
198 method call. Example:
200 proxy = Gio.DBusProxy.new_sync(...)
201 result = proxy.MyMethod('(is)', 42, 'hello')
203 The exception are methods which take no arguments, like
204 proxy.MyMethod('()'). For these you can omit the signature and just write
207 Optional keyword arguments:
209 - timeout: timeout for the call in milliseconds (default to D-Bus timeout)
211 - flags: Combination of Gio.DBusCallFlags.*
213 - result_handler: Do an asynchronous method call and invoke
214 result_handler(proxy_object, result, user_data) when it finishes.
216 - error_handler: If the asynchronous call raises an exception,
217 error_handler(proxy_object, exception, user_data) is called when it
218 finishes. If error_handler is not given, result_handler is called with
219 the exception object as result instead.
221 - user_data: Optional user data to pass to result_handler for
224 Example for asynchronous calls:
226 def mymethod_done(proxy, result, user_data):
227 if isinstance(result, Exception):
230 # do something with result
232 proxy.MyMethod('(is)', 42, 'hello',
233 result_handler=mymethod_done, user_data='data')
235 def __getattr__(self, name):
236 return _DBusProxyMethodCall(self, name)
238 DBusProxy = override(DBusProxy)
239 __all__.append('DBusProxy')