c41f523c058445691afe05f45b127f3f0394e803
[platform/upstream/python-gobject.git] / gi / overrides / GLib.py
1 # -*- Mode: Python; py-indent-offset: 4 -*-
2 # vim: tabstop=4 shiftwidth=4 expandtab
3 #
4 # Copyright (C) 2010 Tomeu Vizoso <tomeu.vizoso@collabora.co.uk>
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 ..importer import modules
22 from .._gi import variant_new_tuple, variant_type_from_string
23
24 GLib = modules['GLib']._introspection_module
25
26 __all__ = []
27
28
29 class _VariantCreator(object):
30
31     _LEAF_CONSTRUCTORS = {
32         'b': GLib.Variant.new_boolean,
33         'y': GLib.Variant.new_byte,
34         'n': GLib.Variant.new_int16,
35         'q': GLib.Variant.new_uint16,
36         'i': GLib.Variant.new_int32,
37         'u': GLib.Variant.new_uint32,
38         'x': GLib.Variant.new_int64,
39         't': GLib.Variant.new_uint64,
40         'h': GLib.Variant.new_handle,
41         'd': GLib.Variant.new_double,
42         's': GLib.Variant.new_string,
43         'o': GLib.Variant.new_object_path,
44         'g': GLib.Variant.new_signature,
45         'v': GLib.Variant.new_variant,
46     }
47
48     def _create(self, format, args):
49         '''Create a GVariant object from given format and argument list.
50
51         This method recursively calls itself for complex structures (arrays,
52         dictionaries, boxed).
53
54         Return a tuple (variant, rest_format, rest_args) with the generated
55         GVariant, the remainder of the format string, and the remainder of the
56         arguments.
57
58         If args is None, then this won't actually consume any arguments, and
59         just parse the format string and generate empty GVariant structures.
60         This is required for creating empty dictionaries or arrays.
61         '''
62         # leaves (simple types)
63         constructor = self._LEAF_CONSTRUCTORS.get(format[0])
64         if constructor:
65             if args is not None:
66                 if not args:
67                     raise TypeError('not enough arguments for GVariant format string')
68                 v = constructor(args[0])
69                 return (v, format[1:], args[1:])
70             else:
71                 return (None, format[1:], None)
72
73         if format[0] == '(':
74             return self._create_tuple(format, args)
75
76         if format.startswith('a{'):
77             return self._create_dict(format, args)
78
79         if format[0] == 'a':
80             return self._create_array(format, args)
81
82         raise NotImplementedError('cannot handle GVariant type ' + format)
83
84     def _create_tuple(self, format, args):
85         '''Handle the case where the outermost type of format is a tuple.'''
86
87         format = format[1:]  # eat the '('
88         builder = GLib.VariantBuilder.new(variant_type_from_string('r'))
89         if args is not None:
90             if not args or not isinstance(args[0], tuple):
91                 raise TypeError('expected tuple argument')
92
93             for i in range(len(args[0])):
94                 if format.startswith(')'):
95                     raise TypeError('too many arguments for tuple signature')
96
97                 (v, format, _) = self._create(format, args[0][i:])
98                 builder.add_value(v)
99             args = args[1:]
100         return (builder.end(), format[1:], args)
101
102     def _create_dict(self, format, args):
103         '''Handle the case where the outermost type of format is a dict.'''
104
105         builder = None
106         if args is None or not args[0]:
107             # empty value: we need to call _create() to parse the subtype,
108             # and specify the element type precisely
109             rest_format = self._create(format[2:], None)[1]
110             rest_format = self._create(rest_format, None)[1]
111             if not rest_format.startswith('}'):
112                 raise ValueError('dictionary type string not closed with }')
113             rest_format = rest_format[1:]  # eat the}
114             element_type = format[:len(format) - len(rest_format)]
115             builder = GLib.VariantBuilder.new(variant_type_from_string(element_type))
116         else:
117             builder = GLib.VariantBuilder.new(variant_type_from_string('a{?*}'))
118             for k, v in args[0].items():
119                 (key_v, rest_format, _) = self._create(format[2:], [k])
120                 (val_v, rest_format, _) = self._create(rest_format, [v])
121
122                 if not rest_format.startswith('}'):
123                     raise ValueError('dictionary type string not closed with }')
124                 rest_format = rest_format[1:]  # eat the}
125
126                 entry = GLib.VariantBuilder.new(variant_type_from_string('{?*}'))
127                 entry.add_value(key_v)
128                 entry.add_value(val_v)
129                 builder.add_value(entry.end())
130
131         if args is not None:
132             args = args[1:]
133         return (builder.end(), rest_format, args)
134
135     def _create_array(self, format, args):
136         '''Handle the case where the outermost type of format is an array.'''
137
138         builder = None
139         if args is None or not args[0]:
140             # empty value: we need to call _create() to parse the subtype,
141             # and specify the element type precisely
142             rest_format = self._create(format[1:], None)[1]
143             element_type = format[:len(format) - len(rest_format)]
144             builder = GLib.VariantBuilder.new(variant_type_from_string(element_type))
145         else:
146             builder = GLib.VariantBuilder.new(variant_type_from_string('a*'))
147             for i in range(len(args[0])):
148                 (v, rest_format, _) = self._create(format[1:], args[0][i:])
149                 builder.add_value(v)
150         if args is not None:
151             args = args[1:]
152         return (builder.end(), rest_format, args)
153
154
155 class Variant(GLib.Variant):
156     def __new__(cls, format_string, value):
157         '''Create a GVariant from a native Python object.
158
159         format_string is a standard GVariant type signature, value is a Python
160         object whose structure has to match the signature.
161
162         Examples:
163           GLib.Variant('i', 1)
164           GLib.Variant('(is)', (1, 'hello'))
165           GLib.Variant('(asa{sv})', ([], {'foo': GLib.Variant('b', True),
166                                           'bar': GLib.Variant('i', 2)}))
167         '''
168         creator = _VariantCreator()
169         (v, rest_format, _) = creator._create(format_string, [value])
170         if rest_format:
171             raise TypeError('invalid remaining format string: "%s"' % rest_format)
172         v.format_string = format_string
173         return v
174
175     def __del__(self):
176         self.unref()
177
178     def __str__(self):
179         return self.print_(True)
180
181     def __repr__(self):
182         return "GLib.Variant('%s', %s)" % (self.format_string, self.print_(True))
183
184     def __eq__(self, other):
185         try:
186             return self.equal(other)
187         except TypeError:
188             return False
189
190     def __ne__(self, other):
191         try:
192             return not self.equal(other)
193         except TypeError:
194             return True
195
196     def __hash__(self):
197         # We're not using just hash(self.unpack()) because otherwise we'll have
198         # hash collisions between the same content in different variant types,
199         # which will cause a performance issue in set/dict/etc.
200         return hash((self.get_type_string(), self.unpack()))
201
202     def unpack(self):
203         '''Decompose a GVariant into a native Python object.'''
204
205         LEAF_ACCESSORS = {
206             'b': self.get_boolean,
207             'y': self.get_byte,
208             'n': self.get_int16,
209             'q': self.get_uint16,
210             'i': self.get_int32,
211             'u': self.get_uint32,
212             'x': self.get_int64,
213             't': self.get_uint64,
214             'h': self.get_handle,
215             'd': self.get_double,
216             's': self.get_string,
217             'o': self.get_string,  # object path
218             'g': self.get_string,  # signature
219         }
220
221         # simple values
222         la = LEAF_ACCESSORS.get(self.get_type_string())
223         if la:
224             return la()
225
226         # tuple
227         if self.get_type_string().startswith('('):
228             res = [self.get_child_value(i).unpack()
229                    for i in range(self.n_children())]
230             return tuple(res)
231
232         # dictionary
233         if self.get_type_string().startswith('a{'):
234             res = {}
235             for i in range(self.n_children()):
236                 v = self.get_child_value(i)
237                 res[v.get_child_value(0).unpack()] = v.get_child_value(1).unpack()
238             return res
239
240         # array
241         if self.get_type_string().startswith('a'):
242             return [self.get_child_value(i).unpack()
243                     for i in range(self.n_children())]
244
245         # variant (just unbox transparently)
246         if self.get_type_string().startswith('v'):
247             return self.get_variant().unpack()
248
249         raise NotImplementedError('unsupported GVariant type ' + self.get_type_string())
250
251     @classmethod
252     def split_signature(klass, signature):
253         '''Return a list of the element signatures of the topmost signature tuple.
254
255         If the signature is not a tuple, it returns one element with the entire
256         signature. If the signature is an empty tuple, the result is [].
257
258         This is useful for e. g. iterating over method parameters which are
259         passed as a single Variant.
260         '''
261         if signature == '()':
262             return []
263
264         if not signature.startswith('('):
265             return [signature]
266
267         result = []
268         head = ''
269         tail = signature[1:-1]  # eat the surrounding ()
270         while tail:
271             c = tail[0]
272             head += c
273             tail = tail[1:]
274
275             if c in ('m', 'a'):
276                 # prefixes, keep collecting
277                 continue
278             if c in ('(', '{'):
279                 # consume until corresponding )/}
280                 level = 1
281                 up = c
282                 if up == '(':
283                     down = ')'
284                 else:
285                     down = '}'
286                 while level > 0:
287                     c = tail[0]
288                     head += c
289                     tail = tail[1:]
290                     if c == up:
291                         level += 1
292                     elif c == down:
293                         level -= 1
294
295             # otherwise we have a simple type
296             result.append(head)
297             head = ''
298
299         return result
300
301     #
302     # Pythonic iterators
303     #
304
305     def __len__(self):
306         if self.get_type_string() in ['s', 'o', 'g']:
307             return len(self.get_string())
308         # Array, dict, tuple
309         if self.get_type_string().startswith('a') or self.get_type_string().startswith('('):
310             return self.n_children()
311         raise TypeError('GVariant type %s does not have a length' % self.get_type_string())
312
313     def __getitem__(self, key):
314         # dict
315         if self.get_type_string().startswith('a{'):
316             try:
317                 val = self.lookup_value(key, variant_type_from_string('*'))
318                 if val is None:
319                     raise KeyError(key)
320                 return val.unpack()
321             except TypeError:
322                 # lookup_value() only works for string keys, which is certainly
323                 # the common case; we have to do painful iteration for other
324                 # key types
325                 for i in range(self.n_children()):
326                     v = self.get_child_value(i)
327                     if v.get_child_value(0).unpack() == key:
328                         return v.get_child_value(1).unpack()
329                 raise KeyError(key)
330
331         # array/tuple
332         if self.get_type_string().startswith('a') or self.get_type_string().startswith('('):
333             key = int(key)
334             if key < 0:
335                 key = self.n_children() + key
336             if key < 0 or key >= self.n_children():
337                 raise IndexError('list index out of range')
338             return self.get_child_value(key).unpack()
339
340         # string
341         if self.get_type_string() in ['s', 'o', 'g']:
342             return self.get_string().__getitem__(key)
343
344         raise TypeError('GVariant type %s is not a container' % self.get_type_string())
345
346     #
347     # Pythonic bool operations
348     #
349
350     def __nonzero__(self):
351         return self.__bool__()
352
353     def __bool__(self):
354         if self.get_type_string() in ['y', 'n', 'q', 'i', 'u', 'x', 't', 'h', 'd']:
355             return self.unpack() != 0
356         if self.get_type_string() in ['b']:
357             return self.get_boolean()
358         if self.get_type_string() in ['s', 'o', 'g']:
359             return len(self.get_string()) != 0
360         # Array, dict, tuple
361         if self.get_type_string().startswith('a') or self.get_type_string().startswith('('):
362             return self.n_children() != 0
363         if self.get_type_string() in ['v']:
364             # unpack works recursively, hence bool also works recursively
365             return bool(self.unpack())
366         # Everything else is True
367         return True
368
369     def keys(self):
370         if not self.get_type_string().startswith('a{'):
371             return TypeError, 'GVariant type %s is not a dictionary' % self.get_type_string()
372
373         res = []
374         for i in range(self.n_children()):
375             v = self.get_child_value(i)
376             res.append(v.get_child_value(0).unpack())
377         return res
378
379
380 @classmethod
381 def new_tuple(cls, *elements):
382     return variant_new_tuple(elements)
383
384
385 def get_string(self):
386     value, length = GLib.Variant.get_string(self)
387     return value
388
389 setattr(Variant, 'new_tuple', new_tuple)
390 setattr(Variant, 'get_string', get_string)
391
392 __all__.append('Variant')