372d6d4165c8c0299e5dfa9a589a459048db3617
[platform/upstream/pygobject2.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 # Copyright (C) 2011, 2012 Canonical Ltd.
6 #
7 # This library is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU Lesser General Public
9 # License as published by the Free Software Foundation; either
10 # version 2.1 of the License, or (at your option) any later version.
11 #
12 # This library is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 # Lesser General Public License for more details.
16 #
17 # You should have received a copy of the GNU Lesser General Public
18 # License along with this library; if not, write to the Free Software
19 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
20 # USA
21
22 import signal
23 import warnings
24 import sys
25 import socket
26
27 from ..module import get_introspection_module
28 from .._gi import (variant_type_from_string, source_new,
29                    source_set_callback, io_channel_read)
30 from ..overrides import override, deprecated, deprecated_attr
31 from gi import PyGIDeprecationWarning, version_info
32
33 GLib = get_introspection_module('GLib')
34
35 __all__ = []
36
37 from gi import _option as option
38 option  # pyflakes
39 __all__.append('option')
40
41
42 # Types and functions still needed from static bindings
43 from gi import _gi
44 from gi._error import GError
45
46 Error = GError
47 OptionContext = _gi.OptionContext
48 OptionGroup = _gi.OptionGroup
49 Pid = _gi.Pid
50 spawn_async = _gi.spawn_async
51
52
53 def threads_init():
54     warnings.warn('Since version 3.11, calling threads_init is no longer needed. '
55                   'See: https://wiki.gnome.org/PyGObject/Threading',
56                   PyGIDeprecationWarning, stacklevel=2)
57
58
59 def gerror_matches(self, domain, code):
60     # Handle cases where self.domain was set to an integer for compatibility
61     # with the introspected GLib.Error.
62     if isinstance(self.domain, str):
63         self_domain_quark = GLib.quark_from_string(self.domain)
64     else:
65         self_domain_quark = self.domain
66     return (self_domain_quark, self.code) == (domain, code)
67
68
69 def gerror_new_literal(domain, message, code):
70     domain_quark = GLib.quark_to_string(domain)
71     return GError(message, domain_quark, code)
72
73
74 # Monkey patch methods that rely on GLib introspection to be loaded at runtime.
75 Error.__name__ = 'Error'
76 Error.__module__ = 'GLib'
77 Error.__gtype__ = GLib.Error.__gtype__
78 Error.matches = gerror_matches
79 Error.new_literal = staticmethod(gerror_new_literal)
80
81
82 __all__ += ['GError', 'Error', 'OptionContext', 'OptionGroup', 'Pid',
83             'spawn_async', 'threads_init']
84
85
86 class _VariantCreator(object):
87
88     _LEAF_CONSTRUCTORS = {
89         'b': GLib.Variant.new_boolean,
90         'y': GLib.Variant.new_byte,
91         'n': GLib.Variant.new_int16,
92         'q': GLib.Variant.new_uint16,
93         'i': GLib.Variant.new_int32,
94         'u': GLib.Variant.new_uint32,
95         'x': GLib.Variant.new_int64,
96         't': GLib.Variant.new_uint64,
97         'h': GLib.Variant.new_handle,
98         'd': GLib.Variant.new_double,
99         's': GLib.Variant.new_string,
100         'o': GLib.Variant.new_object_path,
101         'g': GLib.Variant.new_signature,
102         'v': GLib.Variant.new_variant,
103     }
104
105     def _create(self, format, args):
106         """Create a GVariant object from given format and argument list.
107
108         This method recursively calls itself for complex structures (arrays,
109         dictionaries, boxed).
110
111         Return a tuple (variant, rest_format, rest_args) with the generated
112         GVariant, the remainder of the format string, and the remainder of the
113         arguments.
114
115         If args is None, then this won't actually consume any arguments, and
116         just parse the format string and generate empty GVariant structures.
117         This is required for creating empty dictionaries or arrays.
118         """
119         # leaves (simple types)
120         constructor = self._LEAF_CONSTRUCTORS.get(format[0])
121         if constructor:
122             if args is not None:
123                 if not args:
124                     raise TypeError('not enough arguments for GVariant format string')
125                 v = constructor(args[0])
126                 return (v, format[1:], args[1:])
127             else:
128                 return (None, format[1:], None)
129
130         if format[0] == '(':
131             return self._create_tuple(format, args)
132
133         if format.startswith('a{'):
134             return self._create_dict(format, args)
135
136         if format[0] == 'a':
137             return self._create_array(format, args)
138
139         raise NotImplementedError('cannot handle GVariant type ' + format)
140
141     def _create_tuple(self, format, args):
142         """Handle the case where the outermost type of format is a tuple."""
143
144         format = format[1:]  # eat the '('
145         if args is None:
146             # empty value: we need to call _create() to parse the subtype
147             rest_format = format
148             while rest_format:
149                 if rest_format.startswith(')'):
150                     break
151                 rest_format = self._create(rest_format, None)[1]
152             else:
153                 raise TypeError('tuple type string not closed with )')
154
155             rest_format = rest_format[1:]  # eat the )
156             return (None, rest_format, None)
157         else:
158             if not args or not isinstance(args[0], tuple):
159                 raise TypeError('expected tuple argument')
160
161             builder = GLib.VariantBuilder.new(variant_type_from_string('r'))
162             for i in range(len(args[0])):
163                 if format.startswith(')'):
164                     raise TypeError('too many arguments for tuple signature')
165
166                 (v, format, _) = self._create(format, args[0][i:])
167                 builder.add_value(v)
168             args = args[1:]
169             if not format.startswith(')'):
170                 raise TypeError('tuple type string not closed with )')
171
172             rest_format = format[1:]  # eat the )
173             return (builder.end(), rest_format, args)
174
175     def _create_dict(self, format, args):
176         """Handle the case where the outermost type of format is a dict."""
177
178         builder = None
179         if args is None or not args[0]:
180             # empty value: we need to call _create() to parse the subtype,
181             # and specify the element type precisely
182             rest_format = self._create(format[2:], None)[1]
183             rest_format = self._create(rest_format, None)[1]
184             if not rest_format.startswith('}'):
185                 raise TypeError('dictionary type string not closed with }')
186             rest_format = rest_format[1:]  # eat the }
187             element_type = format[:len(format) - len(rest_format)]
188             builder = GLib.VariantBuilder.new(variant_type_from_string(element_type))
189         else:
190             builder = GLib.VariantBuilder.new(variant_type_from_string('a{?*}'))
191             for k, v in args[0].items():
192                 (key_v, rest_format, _) = self._create(format[2:], [k])
193                 (val_v, rest_format, _) = self._create(rest_format, [v])
194
195                 if not rest_format.startswith('}'):
196                     raise TypeError('dictionary type string not closed with }')
197                 rest_format = rest_format[1:]  # eat the }
198
199                 entry = GLib.VariantBuilder.new(variant_type_from_string('{?*}'))
200                 entry.add_value(key_v)
201                 entry.add_value(val_v)
202                 builder.add_value(entry.end())
203
204         if args is not None:
205             args = args[1:]
206         return (builder.end(), rest_format, args)
207
208     def _create_array(self, format, args):
209         """Handle the case where the outermost type of format is an array."""
210
211         builder = None
212         if args is None or not args[0]:
213             # empty value: we need to call _create() to parse the subtype,
214             # and specify the element type precisely
215             rest_format = self._create(format[1:], None)[1]
216             element_type = format[:len(format) - len(rest_format)]
217             builder = GLib.VariantBuilder.new(variant_type_from_string(element_type))
218         else:
219             builder = GLib.VariantBuilder.new(variant_type_from_string('a*'))
220             for i in range(len(args[0])):
221                 (v, rest_format, _) = self._create(format[1:], args[0][i:])
222                 builder.add_value(v)
223         if args is not None:
224             args = args[1:]
225         return (builder.end(), rest_format, args)
226
227
228 class Variant(GLib.Variant):
229     def __new__(cls, format_string, value):
230         """Create a GVariant from a native Python object.
231
232         format_string is a standard GVariant type signature, value is a Python
233         object whose structure has to match the signature.
234
235         Examples:
236           GLib.Variant('i', 1)
237           GLib.Variant('(is)', (1, 'hello'))
238           GLib.Variant('(asa{sv})', ([], {'foo': GLib.Variant('b', True),
239                                           'bar': GLib.Variant('i', 2)}))
240         """
241         creator = _VariantCreator()
242         (v, rest_format, _) = creator._create(format_string, [value])
243         if rest_format:
244             raise TypeError('invalid remaining format string: "%s"' % rest_format)
245         v.format_string = format_string
246         return v
247
248     @staticmethod
249     def new_tuple(*elements):
250         return GLib.Variant.new_tuple(elements)
251
252     def __del__(self):
253         try:
254             self.unref()
255         except ImportError:
256             # Calling unref will cause gi and gi.repository.GLib to be
257             # imported. However, if the program is exiting, then these
258             # modules have likely been removed from sys.modules and will
259             # raise an exception. Assume that's the case for ImportError
260             # and ignore the exception since everything will be cleaned
261             # up, anyways.
262             pass
263
264     def __str__(self):
265         return self.print_(True)
266
267     def __repr__(self):
268         if hasattr(self, 'format_string'):
269             f = self.format_string
270         else:
271             f = self.get_type_string()
272         return "GLib.Variant('%s', %s)" % (f, self.print_(False))
273
274     def __eq__(self, other):
275         try:
276             return self.equal(other)
277         except TypeError:
278             return False
279
280     def __ne__(self, other):
281         try:
282             return not self.equal(other)
283         except TypeError:
284             return True
285
286     def __hash__(self):
287         # We're not using just hash(self.unpack()) because otherwise we'll have
288         # hash collisions between the same content in different variant types,
289         # which will cause a performance issue in set/dict/etc.
290         return hash((self.get_type_string(), self.unpack()))
291
292     def unpack(self):
293         """Decompose a GVariant into a native Python object."""
294
295         LEAF_ACCESSORS = {
296             'b': self.get_boolean,
297             'y': self.get_byte,
298             'n': self.get_int16,
299             'q': self.get_uint16,
300             'i': self.get_int32,
301             'u': self.get_uint32,
302             'x': self.get_int64,
303             't': self.get_uint64,
304             'h': self.get_handle,
305             'd': self.get_double,
306             's': self.get_string,
307             'o': self.get_string,  # object path
308             'g': self.get_string,  # signature
309         }
310
311         # simple values
312         la = LEAF_ACCESSORS.get(self.get_type_string())
313         if la:
314             return la()
315
316         # tuple
317         if self.get_type_string().startswith('('):
318             res = [self.get_child_value(i).unpack()
319                    for i in range(self.n_children())]
320             return tuple(res)
321
322         # dictionary
323         if self.get_type_string().startswith('a{'):
324             res = {}
325             for i in range(self.n_children()):
326                 v = self.get_child_value(i)
327                 res[v.get_child_value(0).unpack()] = v.get_child_value(1).unpack()
328             return res
329
330         # array
331         if self.get_type_string().startswith('a'):
332             return [self.get_child_value(i).unpack()
333                     for i in range(self.n_children())]
334
335         # variant (just unbox transparently)
336         if self.get_type_string().startswith('v'):
337             return self.get_variant().unpack()
338
339         # maybe
340         if self.get_type_string().startswith('m'):
341             m = self.get_maybe()
342             return m.unpack() if m else None
343
344         raise NotImplementedError('unsupported GVariant type ' + self.get_type_string())
345
346     @classmethod
347     def split_signature(klass, signature):
348         """Return a list of the element signatures of the topmost signature tuple.
349
350         If the signature is not a tuple, it returns one element with the entire
351         signature. If the signature is an empty tuple, the result is [].
352
353         This is useful for e. g. iterating over method parameters which are
354         passed as a single Variant.
355         """
356         if signature == '()':
357             return []
358
359         if not signature.startswith('('):
360             return [signature]
361
362         result = []
363         head = ''
364         tail = signature[1:-1]  # eat the surrounding ()
365         while tail:
366             c = tail[0]
367             head += c
368             tail = tail[1:]
369
370             if c in ('m', 'a'):
371                 # prefixes, keep collecting
372                 continue
373             if c in ('(', '{'):
374                 # consume until corresponding )/}
375                 level = 1
376                 up = c
377                 if up == '(':
378                     down = ')'
379                 else:
380                     down = '}'
381                 while level > 0:
382                     c = tail[0]
383                     head += c
384                     tail = tail[1:]
385                     if c == up:
386                         level += 1
387                     elif c == down:
388                         level -= 1
389
390             # otherwise we have a simple type
391             result.append(head)
392             head = ''
393
394         return result
395
396     #
397     # Pythonic iterators
398     #
399
400     def __len__(self):
401         if self.get_type_string() in ['s', 'o', 'g']:
402             return len(self.get_string())
403         # Array, dict, tuple
404         if self.get_type_string().startswith('a') or self.get_type_string().startswith('('):
405             return self.n_children()
406         raise TypeError('GVariant type %s does not have a length' % self.get_type_string())
407
408     def __getitem__(self, key):
409         # dict
410         if self.get_type_string().startswith('a{'):
411             try:
412                 val = self.lookup_value(key, variant_type_from_string('*'))
413                 if val is None:
414                     raise KeyError(key)
415                 return val.unpack()
416             except TypeError:
417                 # lookup_value() only works for string keys, which is certainly
418                 # the common case; we have to do painful iteration for other
419                 # key types
420                 for i in range(self.n_children()):
421                     v = self.get_child_value(i)
422                     if v.get_child_value(0).unpack() == key:
423                         return v.get_child_value(1).unpack()
424                 raise KeyError(key)
425
426         # array/tuple
427         if self.get_type_string().startswith('a') or self.get_type_string().startswith('('):
428             key = int(key)
429             if key < 0:
430                 key = self.n_children() + key
431             if key < 0 or key >= self.n_children():
432                 raise IndexError('list index out of range')
433             return self.get_child_value(key).unpack()
434
435         # string
436         if self.get_type_string() in ['s', 'o', 'g']:
437             return self.get_string().__getitem__(key)
438
439         raise TypeError('GVariant type %s is not a container' % self.get_type_string())
440
441     #
442     # Pythonic bool operations
443     #
444
445     def __nonzero__(self):
446         return self.__bool__()
447
448     def __bool__(self):
449         if self.get_type_string() in ['y', 'n', 'q', 'i', 'u', 'x', 't', 'h', 'd']:
450             return self.unpack() != 0
451         if self.get_type_string() in ['b']:
452             return self.get_boolean()
453         if self.get_type_string() in ['s', 'o', 'g']:
454             return len(self.get_string()) != 0
455         # Array, dict, tuple
456         if self.get_type_string().startswith('a') or self.get_type_string().startswith('('):
457             return self.n_children() != 0
458         if self.get_type_string() in ['v']:
459             # unpack works recursively, hence bool also works recursively
460             return bool(self.unpack())
461         # Everything else is True
462         return True
463
464     def keys(self):
465         if not self.get_type_string().startswith('a{'):
466             return TypeError, 'GVariant type %s is not a dictionary' % self.get_type_string()
467
468         res = []
469         for i in range(self.n_children()):
470             v = self.get_child_value(i)
471             res.append(v.get_child_value(0).unpack())
472         return res
473
474
475 def get_string(self):
476     value, length = GLib.Variant.get_string(self)
477     return value
478
479
480 setattr(Variant, 'get_string', get_string)
481
482 __all__.append('Variant')
483
484
485 def markup_escape_text(text, length=-1):
486     if isinstance(text, bytes):
487         return GLib.markup_escape_text(text.decode('UTF-8'), length)
488     else:
489         return GLib.markup_escape_text(text, length)
490
491
492 __all__.append('markup_escape_text')
493
494
495 # backwards compatible names from old static bindings
496 for n in ['DESKTOP', 'DOCUMENTS', 'DOWNLOAD', 'MUSIC', 'PICTURES',
497           'PUBLIC_SHARE', 'TEMPLATES', 'VIDEOS']:
498     attr = 'USER_DIRECTORY_' + n
499     deprecated_attr("GLib", attr, "GLib.UserDirectory.DIRECTORY_" + n)
500     globals()[attr] = getattr(GLib.UserDirectory, 'DIRECTORY_' + n)
501     __all__.append(attr)
502
503 for n in ['ERR', 'HUP', 'IN', 'NVAL', 'OUT', 'PRI']:
504     globals()['IO_' + n] = getattr(GLib.IOCondition, n)
505     __all__.append('IO_' + n)
506
507 for n in ['APPEND', 'GET_MASK', 'IS_READABLE', 'IS_SEEKABLE',
508           'MASK', 'NONBLOCK', 'SET_MASK']:
509     attr = 'IO_FLAG_' + n
510     deprecated_attr("GLib", attr, "GLib.IOFlags." + n)
511     globals()[attr] = getattr(GLib.IOFlags, n)
512     __all__.append(attr)
513
514 # spelling for the win
515 IO_FLAG_IS_WRITEABLE = GLib.IOFlags.IS_WRITABLE
516 deprecated_attr("GLib", "IO_FLAG_IS_WRITEABLE", "GLib.IOFlags.IS_WRITABLE")
517 __all__.append('IO_FLAG_IS_WRITEABLE')
518
519 for n in ['AGAIN', 'EOF', 'ERROR', 'NORMAL']:
520     attr = 'IO_STATUS_' + n
521     globals()[attr] = getattr(GLib.IOStatus, n)
522     deprecated_attr("GLib", attr, "GLib.IOStatus." + n)
523     __all__.append(attr)
524
525 for n in ['CHILD_INHERITS_STDIN', 'DO_NOT_REAP_CHILD', 'FILE_AND_ARGV_ZERO',
526           'LEAVE_DESCRIPTORS_OPEN', 'SEARCH_PATH', 'STDERR_TO_DEV_NULL',
527           'STDOUT_TO_DEV_NULL']:
528     attr = 'SPAWN_' + n
529     globals()[attr] = getattr(GLib.SpawnFlags, n)
530     deprecated_attr("GLib", attr, "GLib.SpawnFlags." + n)
531     __all__.append(attr)
532
533 for n in ['HIDDEN', 'IN_MAIN', 'REVERSE', 'NO_ARG', 'FILENAME', 'OPTIONAL_ARG',
534           'NOALIAS']:
535     attr = 'OPTION_FLAG_' + n
536     globals()[attr] = getattr(GLib.OptionFlags, n)
537     deprecated_attr("GLib", attr, "GLib.OptionFlags." + n)
538     __all__.append(attr)
539
540 for n in ['UNKNOWN_OPTION', 'BAD_VALUE', 'FAILED']:
541     attr = 'OPTION_ERROR_' + n
542     deprecated_attr("GLib", attr, "GLib.OptionError." + n)
543     globals()[attr] = getattr(GLib.OptionError, n)
544     __all__.append(attr)
545
546
547 # these are not currently exported in GLib gir, presumably because they are
548 # platform dependent; so get them from our static bindings
549 for name in ['G_MINFLOAT', 'G_MAXFLOAT', 'G_MINDOUBLE', 'G_MAXDOUBLE',
550              'G_MINSHORT', 'G_MAXSHORT', 'G_MAXUSHORT', 'G_MININT', 'G_MAXINT',
551              'G_MAXUINT', 'G_MINLONG', 'G_MAXLONG', 'G_MAXULONG', 'G_MAXSIZE',
552              'G_MINSSIZE', 'G_MAXSSIZE', 'G_MINOFFSET', 'G_MAXOFFSET']:
553     attr = name.split("_", 1)[-1]
554     globals()[attr] = getattr(_gi, name)
555     __all__.append(attr)
556
557
558 class MainLoop(GLib.MainLoop):
559     # Backwards compatible constructor API
560     def __new__(cls, context=None):
561         return GLib.MainLoop.new(context, False)
562
563     # Retain classic pygobject behaviour of quitting main loops on SIGINT
564     def __init__(self, context=None):
565         def _handler(loop):
566             loop.quit()
567             loop._quit_by_sigint = True
568             # We handle signal deletion in __del__, return True so GLib
569             # doesn't do the deletion for us.
570             return True
571
572         if sys.platform != 'win32':
573             # compatibility shim, keep around until we depend on glib 2.36
574             if hasattr(GLib, 'unix_signal_add'):
575                 fn = GLib.unix_signal_add
576             else:
577                 fn = GLib.unix_signal_add_full
578             self._signal_source = fn(GLib.PRIORITY_DEFAULT, signal.SIGINT, _handler, self)
579
580     def __del__(self):
581         if hasattr(self, '_signal_source'):
582             GLib.source_remove(self._signal_source)
583
584     def run(self):
585         super(MainLoop, self).run()
586         if hasattr(self, '_quit_by_sigint'):
587             # caught by _main_loop_sigint_handler()
588             raise KeyboardInterrupt
589
590
591 MainLoop = override(MainLoop)
592 __all__.append('MainLoop')
593
594
595 class MainContext(GLib.MainContext):
596     # Backwards compatible API with default value
597     def iteration(self, may_block=True):
598         return super(MainContext, self).iteration(may_block)
599
600
601 MainContext = override(MainContext)
602 __all__.append('MainContext')
603
604
605 class Source(GLib.Source):
606     def __new__(cls, *args, **kwargs):
607         # use our custom pyg_source_new() here as g_source_new() is not
608         # bindable
609         source = source_new()
610         source.__class__ = cls
611         setattr(source, '__pygi_custom_source', True)
612         return source
613
614     def __init__(self, *args, **kwargs):
615         return super(Source, self).__init__()
616
617     def set_callback(self, fn, user_data=None):
618         if hasattr(self, '__pygi_custom_source'):
619             # use our custom pyg_source_set_callback() if for a GSource object
620             # with custom functions
621             source_set_callback(self, fn, user_data)
622         else:
623             # otherwise, for Idle and Timeout, use the standard method
624             super(Source, self).set_callback(fn, user_data)
625
626     def get_current_time(self):
627         return GLib.get_real_time() * 0.000001
628
629     get_current_time = deprecated(get_current_time,
630                                   'GLib.Source.get_time() or GLib.get_real_time()')
631
632     # as get/set_priority are introspected, we can't use the static
633     # property(get_priority, ..) here
634     def __get_priority(self):
635         return self.get_priority()
636
637     def __set_priority(self, value):
638         self.set_priority(value)
639
640     priority = property(__get_priority, __set_priority)
641
642     def __get_can_recurse(self):
643         return self.get_can_recurse()
644
645     def __set_can_recurse(self, value):
646         self.set_can_recurse(value)
647
648     can_recurse = property(__get_can_recurse, __set_can_recurse)
649
650
651 Source = override(Source)
652 __all__.append('Source')
653
654
655 class Idle(Source):
656     def __new__(cls, priority=GLib.PRIORITY_DEFAULT):
657         source = GLib.idle_source_new()
658         source.__class__ = cls
659         return source
660
661     def __init__(self, priority=GLib.PRIORITY_DEFAULT):
662         super(Source, self).__init__()
663         if priority != GLib.PRIORITY_DEFAULT:
664             self.set_priority(priority)
665
666
667 __all__.append('Idle')
668
669
670 class Timeout(Source):
671     def __new__(cls, interval=0, priority=GLib.PRIORITY_DEFAULT):
672         source = GLib.timeout_source_new(interval)
673         source.__class__ = cls
674         return source
675
676     def __init__(self, interval=0, priority=GLib.PRIORITY_DEFAULT):
677         if priority != GLib.PRIORITY_DEFAULT:
678             self.set_priority(priority)
679
680
681 __all__.append('Timeout')
682
683
684 # backwards compatible API
685 def idle_add(function, *user_data, **kwargs):
686     priority = kwargs.get('priority', GLib.PRIORITY_DEFAULT_IDLE)
687     return GLib.idle_add(priority, function, *user_data)
688
689
690 __all__.append('idle_add')
691
692
693 def timeout_add(interval, function, *user_data, **kwargs):
694     priority = kwargs.get('priority', GLib.PRIORITY_DEFAULT)
695     return GLib.timeout_add(priority, interval, function, *user_data)
696
697
698 __all__.append('timeout_add')
699
700
701 def timeout_add_seconds(interval, function, *user_data, **kwargs):
702     priority = kwargs.get('priority', GLib.PRIORITY_DEFAULT)
703     return GLib.timeout_add_seconds(priority, interval, function, *user_data)
704
705
706 __all__.append('timeout_add_seconds')
707
708
709 # The GI GLib API uses g_io_add_watch_full renamed to g_io_add_watch with
710 # a signature of (channel, priority, condition, func, user_data).
711 # Prior to PyGObject 3.8, this function was statically bound with an API closer to the
712 # non-full version with a signature of: (fd, condition, func, *user_data)
713 # We need to support this until we are okay with breaking API in a way which is
714 # not backwards compatible.
715 #
716 # This needs to take into account several historical APIs:
717 # - calling with an fd as first argument
718 # - calling with a Python file object as first argument (we keep this one as
719 #   it's really convenient and does not change the number of arguments)
720 # - calling without a priority as second argument
721 def _io_add_watch_get_args(channel, priority_, condition, *cb_and_user_data, **kwargs):
722     if not isinstance(priority_, int) or isinstance(priority_, GLib.IOCondition):
723         warnings.warn('Calling io_add_watch without priority as second argument is deprecated',
724                       PyGIDeprecationWarning)
725         # shift the arguments around
726         user_data = cb_and_user_data
727         callback = condition
728         condition = priority_
729         if not callable(callback):
730             raise TypeError('third argument must be callable')
731
732         # backwards compatibility: Call with priority kwarg
733         if 'priority' in kwargs:
734             warnings.warn('Calling io_add_watch with priority keyword argument is deprecated, put it as second positional argument',
735                           PyGIDeprecationWarning)
736             priority_ = kwargs['priority']
737         else:
738             priority_ = GLib.PRIORITY_DEFAULT
739     else:
740         if len(cb_and_user_data) < 1 or not callable(cb_and_user_data[0]):
741             raise TypeError('expecting callback as fourth argument')
742         callback = cb_and_user_data[0]
743         user_data = cb_and_user_data[1:]
744
745     # backwards compatibility: Allow calling with fd
746     if isinstance(channel, int):
747         func_fdtransform = lambda _, cond, *data: callback(channel, cond, *data)
748         real_channel = GLib.IOChannel.unix_new(channel)
749     elif isinstance(channel, socket.socket) and sys.platform == 'win32':
750         func_fdtransform = lambda _, cond, *data: callback(channel, cond, *data)
751         real_channel = GLib.IOChannel.win32_new_socket(channel.fileno())
752     elif hasattr(channel, 'fileno'):
753         # backwards compatibility: Allow calling with Python file
754         func_fdtransform = lambda _, cond, *data: callback(channel, cond, *data)
755         real_channel = GLib.IOChannel.unix_new(channel.fileno())
756     else:
757         assert isinstance(channel, GLib.IOChannel)
758         func_fdtransform = callback
759         real_channel = channel
760
761     return real_channel, priority_, condition, func_fdtransform, user_data
762
763
764 __all__.append('_io_add_watch_get_args')
765
766
767 def io_add_watch(*args, **kwargs):
768     """io_add_watch(channel, priority, condition, func, *user_data) -> event_source_id"""
769     channel, priority, condition, func, user_data = _io_add_watch_get_args(*args, **kwargs)
770     return GLib.io_add_watch(channel, priority, condition, func, *user_data)
771
772
773 __all__.append('io_add_watch')
774
775
776 # backwards compatible API
777 class IOChannel(GLib.IOChannel):
778     def __new__(cls, filedes=None, filename=None, mode=None, hwnd=None):
779         if filedes is not None:
780             return GLib.IOChannel.unix_new(filedes)
781         if filename is not None:
782             return GLib.IOChannel.new_file(filename, mode or 'r')
783         if hwnd is not None:
784             return GLib.IOChannel.win32_new_fd(hwnd)
785         raise TypeError('either a valid file descriptor, file name, or window handle must be supplied')
786
787     def __init__(self, *args, **kwargs):
788         return super(IOChannel, self).__init__()
789
790     def read(self, max_count=-1):
791         return io_channel_read(self, max_count)
792
793     def readline(self, size_hint=-1):
794         # note, size_hint is just to maintain backwards compatible API; the
795         # old static binding did not actually use it
796         (status, buf, length, terminator_pos) = self.read_line()
797         if buf is None:
798             return ''
799         return buf
800
801     def readlines(self, size_hint=-1):
802         # note, size_hint is just to maintain backwards compatible API;
803         # the old static binding did not actually use it
804         lines = []
805         status = GLib.IOStatus.NORMAL
806         while status == GLib.IOStatus.NORMAL:
807             (status, buf, length, terminator_pos) = self.read_line()
808             # note, this appends an empty line after EOF; this is
809             # bug-compatible with the old static bindings
810             if buf is None:
811                 buf = ''
812             lines.append(buf)
813         return lines
814
815     def write(self, buf, buflen=-1):
816         if not isinstance(buf, bytes):
817             buf = buf.encode('UTF-8')
818         if buflen == -1:
819             buflen = len(buf)
820         (status, written) = self.write_chars(buf, buflen)
821         return written
822
823     def writelines(self, lines):
824         for line in lines:
825             self.write(line)
826
827     _whence_map = {0: GLib.SeekType.SET, 1: GLib.SeekType.CUR, 2: GLib.SeekType.END}
828
829     def seek(self, offset, whence=0):
830         try:
831             w = self._whence_map[whence]
832         except KeyError:
833             raise ValueError("invalid 'whence' value")
834         return self.seek_position(offset, w)
835
836     def add_watch(self, condition, callback, *user_data, **kwargs):
837         priority = kwargs.get('priority', GLib.PRIORITY_DEFAULT)
838         return io_add_watch(self, priority, condition, callback, *user_data)
839
840     add_watch = deprecated(add_watch, 'GLib.io_add_watch()')
841
842     def __iter__(self):
843         return self
844
845     def __next__(self):
846         (status, buf, length, terminator_pos) = self.read_line()
847         if status == GLib.IOStatus.NORMAL:
848             return buf
849         raise StopIteration
850
851     # Python 2.x compatibility
852     next = __next__
853
854
855 IOChannel = override(IOChannel)
856 __all__.append('IOChannel')
857
858
859 class PollFD(GLib.PollFD):
860     def __new__(cls, fd, events):
861         pollfd = GLib.PollFD()
862         pollfd.__class__ = cls
863         return pollfd
864
865     def __init__(self, fd, events):
866         self.fd = fd
867         self.events = events
868
869
870 PollFD = override(PollFD)
871 __all__.append('PollFD')
872
873
874 # The GI GLib API uses g_child_watch_add_full renamed to g_child_watch_add with
875 # a signature of (priority, pid, callback, data).
876 # Prior to PyGObject 3.8, this function was statically bound with an API closer to the
877 # non-full version with a signature of: (pid, callback, data=None, priority=GLib.PRIORITY_DEFAULT)
878 # We need to support this until we are okay with breaking API in a way which is
879 # not backwards compatible.
880 def _child_watch_add_get_args(priority_or_pid, pid_or_callback, *args, **kwargs):
881     user_data = []
882
883     if callable(pid_or_callback):
884         warnings.warn('Calling child_watch_add without priority as first argument is deprecated',
885                       PyGIDeprecationWarning)
886         pid = priority_or_pid
887         callback = pid_or_callback
888         if len(args) == 0:
889             priority = kwargs.get('priority', GLib.PRIORITY_DEFAULT)
890         elif len(args) == 1:
891             user_data = args
892             priority = kwargs.get('priority', GLib.PRIORITY_DEFAULT)
893         elif len(args) == 2:
894             user_data = [args[0]]
895             priority = args[1]
896         else:
897             raise TypeError('expected at most 4 positional arguments')
898     else:
899         priority = priority_or_pid
900         pid = pid_or_callback
901         if 'function' in kwargs:
902             callback = kwargs['function']
903             user_data = args
904         elif len(args) > 0 and callable(args[0]):
905             callback = args[0]
906             user_data = args[1:]
907         else:
908             raise TypeError('expected callback as third argument')
909
910     if 'data' in kwargs:
911         if user_data:
912             raise TypeError('got multiple values for "data" argument')
913         user_data = [kwargs['data']]
914
915     return priority, pid, callback, user_data
916
917
918 # we need this to be accessible for unit testing
919 __all__.append('_child_watch_add_get_args')
920
921
922 def child_watch_add(*args, **kwargs):
923     """child_watch_add(priority, pid, function, *data)"""
924     priority, pid, function, data = _child_watch_add_get_args(*args, **kwargs)
925     return GLib.child_watch_add(priority, pid, function, *data)
926
927
928 __all__.append('child_watch_add')
929
930
931 def get_current_time():
932     return GLib.get_real_time() * 0.000001
933
934
935 get_current_time = deprecated(get_current_time, 'GLib.get_real_time()')
936
937 __all__.append('get_current_time')
938
939
940 # backwards compatible API with default argument, and ignoring bytes_read
941 # output argument
942 def filename_from_utf8(utf8string, len=-1):
943     return GLib.filename_from_utf8(utf8string, len)[0]
944
945
946 __all__.append('filename_from_utf8')
947
948
949 # backwards compatible API for renamed function
950 if not hasattr(GLib, 'unix_signal_add_full'):
951     def add_full_compat(*args):
952         warnings.warn('GLib.unix_signal_add_full() was renamed to GLib.unix_signal_add()',
953                       PyGIDeprecationWarning)
954         return GLib.unix_signal_add(*args)
955
956     GLib.unix_signal_add_full = add_full_compat
957
958
959 # obsolete constants for backwards compatibility
960 glib_version = (GLib.MAJOR_VERSION, GLib.MINOR_VERSION, GLib.MICRO_VERSION)
961 __all__.append('glib_version')
962 deprecated_attr("GLib", "glib_version",
963                 "(GLib.MAJOR_VERSION, GLib.MINOR_VERSION, GLib.MICRO_VERSION)")
964
965 pyglib_version = version_info
966 __all__.append('pyglib_version')
967 deprecated_attr("GLib", "pyglib_version", "gi.version_info")