Imported Upstream version 3.13.1
[platform/upstream/pygobject2.git] / gi / overrides / Gio.py
1 # -*- Mode: Python; py-indent-offset: 4 -*-
2 # vim: tabstop=4 shiftwidth=4 expandtab
3 #
4 # Copyright (C) 2010 Ignacio Casal Quinteiro <icq@gnome.org>
5 #
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.
10 #
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.
15 #
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
19 # USA
20
21 from ..overrides import override, deprecated_init
22 from ..module import get_introspection_module
23
24 from gi.repository import GLib
25
26 import sys
27
28 Gio = get_introspection_module('Gio')
29
30 __all__ = []
31
32
33 class FileEnumerator(Gio.FileEnumerator):
34     def __iter__(self):
35         return self
36
37     def __next__(self):
38         file_info = self.next_file(None)
39
40         if file_info is not None:
41             return file_info
42         else:
43             raise StopIteration
44
45     # python 2 compat for the iter protocol
46     next = __next__
47
48
49 FileEnumerator = override(FileEnumerator)
50 __all__.append('FileEnumerator')
51
52
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))
57
58
59 MenuItem = override(MenuItem)
60 __all__.append('MenuItem')
61
62
63 class Settings(Gio.Settings):
64     '''Provide dictionary-like access to GLib.Settings.'''
65
66     __init__ = deprecated_init(Gio.Settings.__init__,
67                                arg_names=('schema', 'path', 'backend'))
68
69     def __contains__(self, key):
70         return key in self.list_keys()
71
72     def __len__(self):
73         return len(self.list_keys())
74
75     def __bool__(self):
76         # for "if mysettings" we don't want a dictionary-like test here, just
77         # if the object isn't None
78         return True
79
80     # alias for Python 2.x object protocol
81     __nonzero__ = __bool__
82
83     def __getitem__(self, key):
84         # get_value() aborts the program on an unknown key
85         if not key in self:
86             raise KeyError('unknown key: %r' % (key,))
87
88         return self.get_value(key).unpack()
89
90     def __setitem__(self, key, value):
91         # set_value() aborts the program on an unknown key
92         if not key in self:
93             raise KeyError('unknown key: %r' % (key,))
94
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)
99         if type_ == 'type':
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()
108             allowed = v.unpack()
109             if value not in allowed:
110                 raise ValueError('value %s is not an allowed enum (%s)' % (value, allowed))
111         else:
112             raise NotImplementedError('Cannot handle allowed type range class ' + str(type_))
113
114         self.set_value(key, GLib.Variant(type_str, value))
115
116     def keys(self):
117         return self.list_keys()
118
119 Settings = override(Settings)
120 __all__.append('Settings')
121
122
123 class _DBusProxyMethodCall:
124     '''Helper class to implement DBusProxy method calls.'''
125
126     def __init__(self, dbus_proxy, method_name):
127         self.dbus_proxy = dbus_proxy
128         self.method_name = method_name
129
130     def __async_result_handler(self, obj, result, user_data):
131         (result_callback, error_callback, real_user_data) = user_data
132         try:
133             ret = obj.call_finish(result)
134         except Exception:
135             etype, e = sys.exc_info()[:2]
136             # return exception as value
137             if error_callback:
138                 error_callback(obj, e, real_user_data)
139             else:
140                 result_callback(obj, e, real_user_data)
141             return
142
143         result_callback(obj, self._unpack_result(ret), real_user_data)
144
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 '()'.
148         if args:
149             signature = args[0]
150             args = args[1:]
151             if not isinstance(signature, str):
152                 raise TypeError('first argument must be the method signature string: %r' % signature)
153         else:
154             signature = '()'
155
156         arg_variant = GLib.Variant(signature, tuple(args))
157
158         if 'result_handler' in kwargs:
159             # asynchronous call
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)
166         else:
167             # synchronous call
168             result = self.dbus_proxy.call_sync(self.method_name, arg_variant,
169                                                kwargs.get('flags', 0),
170                                                kwargs.get('timeout', -1),
171                                                None)
172             return self._unpack_result(result)
173
174     @classmethod
175     def _unpack_result(klass, result):
176         '''Convert a D-BUS return variant into an appropriate return value'''
177
178         result = result.unpack()
179
180         # to be compatible with standard Python behaviour, unbox
181         # single-element tuples and return None for empty result tuples
182         if len(result) == 1:
183             result = result[0]
184         elif len(result) == 0:
185             result = None
186
187         return result
188
189
190 class DBusProxy(Gio.DBusProxy):
191     '''Provide comfortable and pythonic method calls.
192
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.
196
197     The first argument always needs to be the D-Bus signature tuple of the
198     method call. Example:
199
200       proxy = Gio.DBusProxy.new_sync(...)
201       result = proxy.MyMethod('(is)', 42, 'hello')
202
203     The exception are methods which take no arguments, like
204     proxy.MyMethod('()'). For these you can omit the signature and just write
205     proxy.MyMethod().
206
207     Optional keyword arguments:
208
209     - timeout: timeout for the call in milliseconds (default to D-Bus timeout)
210
211     - flags: Combination of Gio.DBusCallFlags.*
212
213     - result_handler: Do an asynchronous method call and invoke
214          result_handler(proxy_object, result, user_data) when it finishes.
215
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.
220
221     - user_data: Optional user data to pass to result_handler for
222       asynchronous calls.
223
224     Example for asynchronous calls:
225
226       def mymethod_done(proxy, result, user_data):
227           if isinstance(result, Exception):
228               # handle error
229           else:
230               # do something with result
231
232       proxy.MyMethod('(is)', 42, 'hello',
233           result_handler=mymethod_done, user_data='data')
234     '''
235     def __getattr__(self, name):
236         return _DBusProxyMethodCall(self, name)
237
238 DBusProxy = override(DBusProxy)
239 __all__.append('DBusProxy')