1 # -*- Mode: Python; py-indent-offset: 4 -*-
2 # vim: tabstop=4 shiftwidth=4 expandtab
4 # Copyright (C) 2010 Tomeu Vizoso <tomeu.vizoso@collabora.co.uk>
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 ..importer import modules
22 from .._gi import variant_new_tuple, variant_type_from_string
24 GLib = modules['GLib']._introspection_module
29 class _VariantCreator(object):
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,
48 def _create(self, format, args):
49 '''Create a GVariant object from given format and argument list.
51 This method recursively calls itself for complex structures (arrays,
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
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.
62 # leaves (simple types)
63 constructor = self._LEAF_CONSTRUCTORS.get(format[0])
67 raise TypeError('not enough arguments for GVariant format string')
68 v = constructor(args[0])
69 return (v, format[1:], args[1:])
71 return (None, format[1:], None)
74 return self._create_tuple(format, args)
76 if format.startswith('a{'):
77 return self._create_dict(format, args)
80 return self._create_array(format, args)
82 raise NotImplementedError('cannot handle GVariant type ' + format)
84 def _create_tuple(self, format, args):
85 '''Handle the case where the outermost type of format is a tuple.'''
87 format = format[1:] # eat the '('
88 builder = GLib.VariantBuilder.new(variant_type_from_string('r'))
90 if not args or not isinstance(args[0], tuple):
91 raise TypeError('expected tuple argument')
93 for i in range(len(args[0])):
94 if format.startswith(')'):
95 raise TypeError('too many arguments for tuple signature')
97 (v, format, _) = self._create(format, args[0][i:])
100 return (builder.end(), format[1:], args)
102 def _create_dict(self, format, args):
103 '''Handle the case where the outermost type of format is a dict.'''
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))
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])
122 if not rest_format.startswith('}'):
123 raise ValueError('dictionary type string not closed with }')
124 rest_format = rest_format[1:] # eat the}
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())
133 return (builder.end(), rest_format, args)
135 def _create_array(self, format, args):
136 '''Handle the case where the outermost type of format is an array.'''
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))
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:])
152 return (builder.end(), rest_format, args)
155 class Variant(GLib.Variant):
156 def __new__(cls, format_string, value):
157 '''Create a GVariant from a native Python object.
159 format_string is a standard GVariant type signature, value is a Python
160 object whose structure has to match the signature.
164 GLib.Variant('(is)', (1, 'hello'))
165 GLib.Variant('(asa{sv})', ([], {'foo': GLib.Variant('b', True),
166 'bar': GLib.Variant('i', 2)}))
168 creator = _VariantCreator()
169 (v, rest_format, _) = creator._create(format_string, [value])
171 raise TypeError('invalid remaining format string: "%s"' % rest_format)
172 v.format_string = format_string
179 return self.print_(True)
182 return "GLib.Variant('%s', %s)" % (self.format_string, self.print_(True))
184 def __eq__(self, other):
186 return self.equal(other)
190 def __ne__(self, other):
192 return not self.equal(other)
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()))
203 '''Decompose a GVariant into a native Python object.'''
206 'b': self.get_boolean,
209 'q': self.get_uint16,
211 'u': self.get_uint32,
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
222 la = LEAF_ACCESSORS.get(self.get_type_string())
227 if self.get_type_string().startswith('('):
228 res = [self.get_child_value(i).unpack()
229 for i in range(self.n_children())]
233 if self.get_type_string().startswith('a{'):
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()
241 if self.get_type_string().startswith('a'):
242 return [self.get_child_value(i).unpack()
243 for i in range(self.n_children())]
245 # variant (just unbox transparently)
246 if self.get_type_string().startswith('v'):
247 return self.get_variant().unpack()
249 raise NotImplementedError('unsupported GVariant type ' + self.get_type_string())
252 def split_signature(klass, signature):
253 '''Return a list of the element signatures of the topmost signature tuple.
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 [].
258 This is useful for e. g. iterating over method parameters which are
259 passed as a single Variant.
261 if signature == '()':
264 if not signature.startswith('('):
269 tail = signature[1:-1] # eat the surrounding ()
276 # prefixes, keep collecting
279 # consume until corresponding )/}
295 # otherwise we have a simple type
306 if self.get_type_string() in ['s', 'o', 'g']:
307 return len(self.get_string())
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())
313 def __getitem__(self, key):
315 if self.get_type_string().startswith('a{'):
317 val = self.lookup_value(key, variant_type_from_string('*'))
322 # lookup_value() only works for string keys, which is certainly
323 # the common case; we have to do painful iteration for other
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()
332 if self.get_type_string().startswith('a') or self.get_type_string().startswith('('):
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()
341 if self.get_type_string() in ['s', 'o', 'g']:
342 return self.get_string().__getitem__(key)
344 raise TypeError('GVariant type %s is not a container' % self.get_type_string())
347 # Pythonic bool operations
350 def __nonzero__(self):
351 return self.__bool__()
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
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
370 if not self.get_type_string().startswith('a{'):
371 return TypeError, 'GVariant type %s is not a dictionary' % self.get_type_string()
374 for i in range(self.n_children()):
375 v = self.get_child_value(i)
376 res.append(v.get_child_value(0).unpack())
381 def new_tuple(cls, *elements):
382 return variant_new_tuple(elements)
385 def get_string(self):
386 value, length = GLib.Variant.get_string(self)
389 setattr(Variant, 'new_tuple', new_tuple)
390 setattr(Variant, 'get_string', get_string)
392 __all__.append('Variant')