7f6e5c7325834ea88565d454cbd048b26324f075
[platform/upstream/python-gobject.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
22 from ..importer import modules
23
24 from gi.repository import GLib
25
26 import sys
27
28 Gio = modules['Gio']._introspection_module
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     def __init__(self, schema, path=None, backend=None, **kwargs):
67         Gio.Settings.__init__(self, schema=schema, backend=backend, path=path, **kwargs)
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         else:
105             raise NotImplementedError('Cannot handle allowed type range class' + str(type_))
106
107         self.set_value(key, GLib.Variant(type_str, value))
108
109     def keys(self):
110         return self.list_keys()
111
112 Settings = override(Settings)
113 __all__.append('Settings')
114
115
116 class _DBusProxyMethodCall:
117     '''Helper class to implement DBusProxy method calls.'''
118
119     def __init__(self, dbus_proxy, method_name):
120         self.dbus_proxy = dbus_proxy
121         self.method_name = method_name
122
123     def __async_result_handler(self, obj, result, user_data):
124         (result_callback, error_callback, real_user_data) = user_data
125         try:
126             ret = obj.call_finish(result)
127         except Exception:
128             etype, e = sys.exc_info()[:2]
129             # return exception as value
130             if error_callback:
131                 error_callback(obj, e, real_user_data)
132             else:
133                 result_callback(obj, e, real_user_data)
134             return
135
136         result_callback(obj, self._unpack_result(ret), real_user_data)
137
138     def __call__(self, *args, **kwargs):
139         # the first positional argument is the signature, unless we are calling
140         # a method without arguments; then signature is implied to be '()'.
141         if args:
142             signature = args[0]
143             args = args[1:]
144             if not isinstance(signature, str):
145                 raise TypeError('first argument must be the method signature string: %r' % signature)
146         else:
147             signature = '()'
148
149         arg_variant = GLib.Variant(signature, tuple(args))
150
151         if 'result_handler' in kwargs:
152             # asynchronous call
153             user_data = (kwargs['result_handler'],
154                          kwargs.get('error_handler'),
155                          kwargs.get('user_data'))
156             self.dbus_proxy.call(self.method_name, arg_variant,
157                                  kwargs.get('flags', 0), kwargs.get('timeout', -1), None,
158                                  self.__async_result_handler, user_data)
159         else:
160             # synchronous call
161             result = self.dbus_proxy.call_sync(self.method_name, arg_variant,
162                                                kwargs.get('flags', 0),
163                                                kwargs.get('timeout', -1),
164                                                None)
165             return self._unpack_result(result)
166
167     @classmethod
168     def _unpack_result(klass, result):
169         '''Convert a D-BUS return variant into an appropriate return value'''
170
171         result = result.unpack()
172
173         # to be compatible with standard Python behaviour, unbox
174         # single-element tuples and return None for empty result tuples
175         if len(result) == 1:
176             result = result[0]
177         elif len(result) == 0:
178             result = None
179
180         return result
181
182
183 class DBusProxy(Gio.DBusProxy):
184     '''Provide comfortable and pythonic method calls.
185
186     This marshalls the method arguments into a GVariant, invokes the
187     call_sync() method on the DBusProxy object, and unmarshalls the result
188     GVariant back into a Python tuple.
189
190     The first argument always needs to be the D-Bus signature tuple of the
191     method call. Example:
192
193       proxy = Gio.DBusProxy.new_sync(...)
194       result = proxy.MyMethod('(is)', 42, 'hello')
195
196     The exception are methods which take no arguments, like
197     proxy.MyMethod('()'). For these you can omit the signature and just write
198     proxy.MyMethod().
199
200     Optional keyword arguments:
201
202     - timeout: timeout for the call in milliseconds (default to D-Bus timeout)
203
204     - flags: Combination of Gio.DBusCallFlags.*
205
206     - result_handler: Do an asynchronous method call and invoke
207          result_handler(proxy_object, result, user_data) when it finishes.
208
209     - error_handler: If the asynchronous call raises an exception,
210       error_handler(proxy_object, exception, user_data) is called when it
211       finishes. If error_handler is not given, result_handler is called with
212       the exception object as result instead.
213
214     - user_data: Optional user data to pass to result_handler for
215       asynchronous calls.
216
217     Example for asynchronous calls:
218
219       def mymethod_done(proxy, result, user_data):
220           if isinstance(result, Exception):
221               # handle error
222           else:
223               # do something with result
224
225       proxy.MyMethod('(is)', 42, 'hello',
226           result_handler=mymethod_done, user_data='data')
227     '''
228     def __getattr__(self, name):
229         return _DBusProxyMethodCall(self, name)
230
231 DBusProxy = override(DBusProxy)
232 __all__.append('DBusProxy')