Imported Upstream version 3.19.91
[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 # 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
26 from ..module import get_introspection_module
27 from .._gi import (variant_type_from_string, source_new,
28                    source_set_callback, io_channel_read)
29 from ..overrides import override, deprecated, deprecated_attr
30 from gi import PyGIDeprecationWarning, version_info
31
32 GLib = get_introspection_module('GLib')
33
34 __all__ = []
35
36 from gi import _option as option
37 option  # pyflakes
38 __all__.append('option')
39
40
41 # Types and functions still needed from static bindings
42 from gi._gi import _glib
43 from gi._gi import _gobject
44 from gi._error import GError
45
46 Error = GError
47 OptionContext = _glib.OptionContext
48 OptionGroup = _glib.OptionGroup
49 Pid = _glib.Pid
50 spawn_async = _glib.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         self.unref()
254
255     def __str__(self):
256         return self.print_(True)
257
258     def __repr__(self):
259         if hasattr(self, 'format_string'):
260             f = self.format_string
261         else:
262             f = self.get_type_string()
263         return "GLib.Variant('%s', %s)" % (f, self.print_(False))
264
265     def __eq__(self, other):
266         try:
267             return self.equal(other)
268         except TypeError:
269             return False
270
271     def __ne__(self, other):
272         try:
273             return not self.equal(other)
274         except TypeError:
275             return True
276
277     def __hash__(self):
278         # We're not using just hash(self.unpack()) because otherwise we'll have
279         # hash collisions between the same content in different variant types,
280         # which will cause a performance issue in set/dict/etc.
281         return hash((self.get_type_string(), self.unpack()))
282
283     def unpack(self):
284         """Decompose a GVariant into a native Python object."""
285
286         LEAF_ACCESSORS = {
287             'b': self.get_boolean,
288             'y': self.get_byte,
289             'n': self.get_int16,
290             'q': self.get_uint16,
291             'i': self.get_int32,
292             'u': self.get_uint32,
293             'x': self.get_int64,
294             't': self.get_uint64,
295             'h': self.get_handle,
296             'd': self.get_double,
297             's': self.get_string,
298             'o': self.get_string,  # object path
299             'g': self.get_string,  # signature
300         }
301
302         # simple values
303         la = LEAF_ACCESSORS.get(self.get_type_string())
304         if la:
305             return la()
306
307         # tuple
308         if self.get_type_string().startswith('('):
309             res = [self.get_child_value(i).unpack()
310                    for i in range(self.n_children())]
311             return tuple(res)
312
313         # dictionary
314         if self.get_type_string().startswith('a{'):
315             res = {}
316             for i in range(self.n_children()):
317                 v = self.get_child_value(i)
318                 res[v.get_child_value(0).unpack()] = v.get_child_value(1).unpack()
319             return res
320
321         # array
322         if self.get_type_string().startswith('a'):
323             return [self.get_child_value(i).unpack()
324                     for i in range(self.n_children())]
325
326         # variant (just unbox transparently)
327         if self.get_type_string().startswith('v'):
328             return self.get_variant().unpack()
329
330         # maybe
331         if self.get_type_string().startswith('m'):
332             m = self.get_maybe()
333             return m.unpack() if m else None
334
335         raise NotImplementedError('unsupported GVariant type ' + self.get_type_string())
336
337     @classmethod
338     def split_signature(klass, signature):
339         """Return a list of the element signatures of the topmost signature tuple.
340
341         If the signature is not a tuple, it returns one element with the entire
342         signature. If the signature is an empty tuple, the result is [].
343
344         This is useful for e. g. iterating over method parameters which are
345         passed as a single Variant.
346         """
347         if signature == '()':
348             return []
349
350         if not signature.startswith('('):
351             return [signature]
352
353         result = []
354         head = ''
355         tail = signature[1:-1]  # eat the surrounding ()
356         while tail:
357             c = tail[0]
358             head += c
359             tail = tail[1:]
360
361             if c in ('m', 'a'):
362                 # prefixes, keep collecting
363                 continue
364             if c in ('(', '{'):
365                 # consume until corresponding )/}
366                 level = 1
367                 up = c
368                 if up == '(':
369                     down = ')'
370                 else:
371                     down = '}'
372                 while level > 0:
373                     c = tail[0]
374                     head += c
375                     tail = tail[1:]
376                     if c == up:
377                         level += 1
378                     elif c == down:
379                         level -= 1
380
381             # otherwise we have a simple type
382             result.append(head)
383             head = ''
384
385         return result
386
387     #
388     # Pythonic iterators
389     #
390
391     def __len__(self):
392         if self.get_type_string() in ['s', 'o', 'g']:
393             return len(self.get_string())
394         # Array, dict, tuple
395         if self.get_type_string().startswith('a') or self.get_type_string().startswith('('):
396             return self.n_children()
397         raise TypeError('GVariant type %s does not have a length' % self.get_type_string())
398
399     def __getitem__(self, key):
400         # dict
401         if self.get_type_string().startswith('a{'):
402             try:
403                 val = self.lookup_value(key, variant_type_from_string('*'))
404                 if val is None:
405                     raise KeyError(key)
406                 return val.unpack()
407             except TypeError:
408                 # lookup_value() only works for string keys, which is certainly
409                 # the common case; we have to do painful iteration for other
410                 # key types
411                 for i in range(self.n_children()):
412                     v = self.get_child_value(i)
413                     if v.get_child_value(0).unpack() == key:
414                         return v.get_child_value(1).unpack()
415                 raise KeyError(key)
416
417         # array/tuple
418         if self.get_type_string().startswith('a') or self.get_type_string().startswith('('):
419             key = int(key)
420             if key < 0:
421                 key = self.n_children() + key
422             if key < 0 or key >= self.n_children():
423                 raise IndexError('list index out of range')
424             return self.get_child_value(key).unpack()
425
426         # string
427         if self.get_type_string() in ['s', 'o', 'g']:
428             return self.get_string().__getitem__(key)
429
430         raise TypeError('GVariant type %s is not a container' % self.get_type_string())
431
432     #
433     # Pythonic bool operations
434     #
435
436     def __nonzero__(self):
437         return self.__bool__()
438
439     def __bool__(self):
440         if self.get_type_string() in ['y', 'n', 'q', 'i', 'u', 'x', 't', 'h', 'd']:
441             return self.unpack() != 0
442         if self.get_type_string() in ['b']:
443             return self.get_boolean()
444         if self.get_type_string() in ['s', 'o', 'g']:
445             return len(self.get_string()) != 0
446         # Array, dict, tuple
447         if self.get_type_string().startswith('a') or self.get_type_string().startswith('('):
448             return self.n_children() != 0
449         if self.get_type_string() in ['v']:
450             # unpack works recursively, hence bool also works recursively
451             return bool(self.unpack())
452         # Everything else is True
453         return True
454
455     def keys(self):
456         if not self.get_type_string().startswith('a{'):
457             return TypeError, 'GVariant type %s is not a dictionary' % self.get_type_string()
458
459         res = []
460         for i in range(self.n_children()):
461             v = self.get_child_value(i)
462             res.append(v.get_child_value(0).unpack())
463         return res
464
465
466 def get_string(self):
467     value, length = GLib.Variant.get_string(self)
468     return value
469
470 setattr(Variant, 'get_string', get_string)
471
472 __all__.append('Variant')
473
474
475 def markup_escape_text(text, length=-1):
476     if isinstance(text, bytes):
477         return GLib.markup_escape_text(text.decode('UTF-8'), length)
478     else:
479         return GLib.markup_escape_text(text, length)
480 __all__.append('markup_escape_text')
481
482
483 # backwards compatible names from old static bindings
484 for n in ['DESKTOP', 'DOCUMENTS', 'DOWNLOAD', 'MUSIC', 'PICTURES',
485           'PUBLIC_SHARE', 'TEMPLATES', 'VIDEOS']:
486     attr = 'USER_DIRECTORY_' + n
487     deprecated_attr("GLib", attr, "GLib.UserDirectory.DIRECTORY_" + n)
488     globals()[attr] = getattr(GLib.UserDirectory, 'DIRECTORY_' + n)
489     __all__.append(attr)
490
491 for n in ['ERR', 'HUP', 'IN', 'NVAL', 'OUT', 'PRI']:
492     globals()['IO_' + n] = getattr(GLib.IOCondition, n)
493     __all__.append('IO_' + n)
494
495 for n in ['APPEND', 'GET_MASK', 'IS_READABLE', 'IS_SEEKABLE',
496           'MASK', 'NONBLOCK', 'SET_MASK']:
497     attr = 'IO_FLAG_' + n
498     deprecated_attr("GLib", attr, "GLib.IOFlags." + n)
499     globals()[attr] = getattr(GLib.IOFlags, n)
500     __all__.append(attr)
501
502 # spelling for the win
503 IO_FLAG_IS_WRITEABLE = GLib.IOFlags.IS_WRITABLE
504 deprecated_attr("GLib", "IO_FLAG_IS_WRITEABLE", "GLib.IOFlags.IS_WRITABLE")
505 __all__.append('IO_FLAG_IS_WRITEABLE')
506
507 for n in ['AGAIN', 'EOF', 'ERROR', 'NORMAL']:
508     attr = 'IO_STATUS_' + n
509     globals()[attr] = getattr(GLib.IOStatus, n)
510     deprecated_attr("GLib", attr, "GLib.IOStatus." + n)
511     __all__.append(attr)
512
513 for n in ['CHILD_INHERITS_STDIN', 'DO_NOT_REAP_CHILD', 'FILE_AND_ARGV_ZERO',
514           'LEAVE_DESCRIPTORS_OPEN', 'SEARCH_PATH', 'STDERR_TO_DEV_NULL',
515           'STDOUT_TO_DEV_NULL']:
516     attr = 'SPAWN_' + n
517     globals()[attr] = getattr(GLib.SpawnFlags, n)
518     deprecated_attr("GLib", attr, "GLib.SpawnFlags." + n)
519     __all__.append(attr)
520
521 for n in ['HIDDEN', 'IN_MAIN', 'REVERSE', 'NO_ARG', 'FILENAME', 'OPTIONAL_ARG',
522           'NOALIAS']:
523     attr = 'OPTION_FLAG_' + n
524     globals()[attr] = getattr(GLib.OptionFlags, n)
525     deprecated_attr("GLib", attr, "GLib.OptionFlags." + n)
526     __all__.append(attr)
527
528 for n in ['UNKNOWN_OPTION', 'BAD_VALUE', 'FAILED']:
529     attr = 'OPTION_ERROR_' + n
530     deprecated_attr("GLib", attr, "GLib.OptionError." + n)
531     globals()[attr] = getattr(GLib.OptionError, n)
532     __all__.append(attr)
533
534
535 # these are not currently exported in GLib gir, presumably because they are
536 # platform dependent; so get them from our static bindings
537 for name in ['G_MINFLOAT', 'G_MAXFLOAT', 'G_MINDOUBLE', 'G_MAXDOUBLE',
538              'G_MINSHORT', 'G_MAXSHORT', 'G_MAXUSHORT', 'G_MININT', 'G_MAXINT',
539              'G_MAXUINT', 'G_MINLONG', 'G_MAXLONG', 'G_MAXULONG', 'G_MAXSIZE',
540              'G_MINSSIZE', 'G_MAXSSIZE', 'G_MINOFFSET', 'G_MAXOFFSET']:
541     attr = name.split("_", 1)[-1]
542     globals()[attr] = getattr(_gobject, name)
543     __all__.append(attr)
544
545
546 class MainLoop(GLib.MainLoop):
547     # Backwards compatible constructor API
548     def __new__(cls, context=None):
549         return GLib.MainLoop.new(context, False)
550
551     # Retain classic pygobject behaviour of quitting main loops on SIGINT
552     def __init__(self, context=None):
553         def _handler(loop):
554             loop.quit()
555             loop._quit_by_sigint = True
556             # We handle signal deletion in __del__, return True so GLib
557             # doesn't do the deletion for us.
558             return True
559
560         if sys.platform != 'win32':
561             # compatibility shim, keep around until we depend on glib 2.36
562             if hasattr(GLib, 'unix_signal_add'):
563                 fn = GLib.unix_signal_add
564             else:
565                 fn = GLib.unix_signal_add_full
566             self._signal_source = fn(GLib.PRIORITY_DEFAULT, signal.SIGINT, _handler, self)
567
568     def __del__(self):
569         if hasattr(self, '_signal_source'):
570             GLib.source_remove(self._signal_source)
571
572     def run(self):
573         super(MainLoop, self).run()
574         if hasattr(self, '_quit_by_sigint'):
575             # caught by _main_loop_sigint_handler()
576             raise KeyboardInterrupt
577
578 MainLoop = override(MainLoop)
579 __all__.append('MainLoop')
580
581
582 class MainContext(GLib.MainContext):
583     # Backwards compatible API with default value
584     def iteration(self, may_block=True):
585         return super(MainContext, self).iteration(may_block)
586
587 MainContext = override(MainContext)
588 __all__.append('MainContext')
589
590
591 class Source(GLib.Source):
592     def __new__(cls, *args, **kwargs):
593         # use our custom pyg_source_new() here as g_source_new() is not
594         # bindable
595         source = source_new()
596         source.__class__ = cls
597         setattr(source, '__pygi_custom_source', True)
598         return source
599
600     def __init__(self, *args, **kwargs):
601         return super(Source, self).__init__()
602
603     def set_callback(self, fn, user_data=None):
604         if hasattr(self, '__pygi_custom_source'):
605             # use our custom pyg_source_set_callback() if for a GSource object
606             # with custom functions
607             source_set_callback(self, fn, user_data)
608         else:
609             # otherwise, for Idle and Timeout, use the standard method
610             super(Source, self).set_callback(fn, user_data)
611
612     def get_current_time(self):
613         return GLib.get_real_time() * 0.000001
614
615     get_current_time = deprecated(get_current_time,
616                                   'GLib.Source.get_time() or GLib.get_real_time()')
617
618     # as get/set_priority are introspected, we can't use the static
619     # property(get_priority, ..) here
620     def __get_priority(self):
621         return self.get_priority()
622
623     def __set_priority(self, value):
624         self.set_priority(value)
625
626     priority = property(__get_priority, __set_priority)
627
628     def __get_can_recurse(self):
629         return self.get_can_recurse()
630
631     def __set_can_recurse(self, value):
632         self.set_can_recurse(value)
633
634     can_recurse = property(__get_can_recurse, __set_can_recurse)
635
636 Source = override(Source)
637 __all__.append('Source')
638
639
640 class Idle(Source):
641     def __new__(cls, priority=GLib.PRIORITY_DEFAULT):
642         source = GLib.idle_source_new()
643         source.__class__ = cls
644         return source
645
646     def __init__(self, priority=GLib.PRIORITY_DEFAULT):
647         super(Source, self).__init__()
648         if priority != GLib.PRIORITY_DEFAULT:
649             self.set_priority(priority)
650
651 __all__.append('Idle')
652
653
654 class Timeout(Source):
655     def __new__(cls, interval=0, priority=GLib.PRIORITY_DEFAULT):
656         source = GLib.timeout_source_new(interval)
657         source.__class__ = cls
658         return source
659
660     def __init__(self, interval=0, priority=GLib.PRIORITY_DEFAULT):
661         if priority != GLib.PRIORITY_DEFAULT:
662             self.set_priority(priority)
663
664 __all__.append('Timeout')
665
666
667 # backwards compatible API
668 def idle_add(function, *user_data, **kwargs):
669     priority = kwargs.get('priority', GLib.PRIORITY_DEFAULT_IDLE)
670     return GLib.idle_add(priority, function, *user_data)
671
672 __all__.append('idle_add')
673
674
675 def timeout_add(interval, function, *user_data, **kwargs):
676     priority = kwargs.get('priority', GLib.PRIORITY_DEFAULT)
677     return GLib.timeout_add(priority, interval, function, *user_data)
678
679 __all__.append('timeout_add')
680
681
682 def timeout_add_seconds(interval, function, *user_data, **kwargs):
683     priority = kwargs.get('priority', GLib.PRIORITY_DEFAULT)
684     return GLib.timeout_add_seconds(priority, interval, function, *user_data)
685
686 __all__.append('timeout_add_seconds')
687
688
689 # The GI GLib API uses g_io_add_watch_full renamed to g_io_add_watch with
690 # a signature of (channel, priority, condition, func, user_data).
691 # Prior to PyGObject 3.8, this function was statically bound with an API closer to the
692 # non-full version with a signature of: (fd, condition, func, *user_data)
693 # We need to support this until we are okay with breaking API in a way which is
694 # not backwards compatible.
695 #
696 # This needs to take into account several historical APIs:
697 # - calling with an fd as first argument
698 # - calling with a Python file object as first argument (we keep this one as
699 #   it's really convenient and does not change the number of arguments)
700 # - calling without a priority as second argument
701 def _io_add_watch_get_args(channel, priority_, condition, *cb_and_user_data, **kwargs):
702     if not isinstance(priority_, int) or isinstance(priority_, GLib.IOCondition):
703         warnings.warn('Calling io_add_watch without priority as second argument is deprecated',
704                       PyGIDeprecationWarning)
705         # shift the arguments around
706         user_data = cb_and_user_data
707         callback = condition
708         condition = priority_
709         if not callable(callback):
710             raise TypeError('third argument must be callable')
711
712         # backwards compatibility: Call with priority kwarg
713         if 'priority' in kwargs:
714             warnings.warn('Calling io_add_watch with priority keyword argument is deprecated, put it as second positional argument',
715                           PyGIDeprecationWarning)
716             priority_ = kwargs['priority']
717         else:
718             priority_ = GLib.PRIORITY_DEFAULT
719     else:
720         if len(cb_and_user_data) < 1 or not callable(cb_and_user_data[0]):
721             raise TypeError('expecting callback as fourth argument')
722         callback = cb_and_user_data[0]
723         user_data = cb_and_user_data[1:]
724
725     # backwards compatibility: Allow calling with fd
726     if isinstance(channel, int):
727         func_fdtransform = lambda _, cond, *data: callback(channel, cond, *data)
728         real_channel = GLib.IOChannel.unix_new(channel)
729     elif hasattr(channel, 'fileno'):
730         # backwards compatibility: Allow calling with Python file
731         func_fdtransform = lambda _, cond, *data: callback(channel, cond, *data)
732         real_channel = GLib.IOChannel.unix_new(channel.fileno())
733     else:
734         assert isinstance(channel, GLib.IOChannel)
735         func_fdtransform = callback
736         real_channel = channel
737
738     return real_channel, priority_, condition, func_fdtransform, user_data
739
740 __all__.append('_io_add_watch_get_args')
741
742
743 def io_add_watch(*args, **kwargs):
744     """io_add_watch(channel, priority, condition, func, *user_data) -> event_source_id"""
745     channel, priority, condition, func, user_data = _io_add_watch_get_args(*args, **kwargs)
746     return GLib.io_add_watch(channel, priority, condition, func, *user_data)
747
748 __all__.append('io_add_watch')
749
750
751 # backwards compatible API
752 class IOChannel(GLib.IOChannel):
753     def __new__(cls, filedes=None, filename=None, mode=None, hwnd=None):
754         if filedes is not None:
755             return GLib.IOChannel.unix_new(filedes)
756         if filename is not None:
757             return GLib.IOChannel.new_file(filename, mode or 'r')
758         if hwnd is not None:
759             return GLib.IOChannel.win32_new_fd(hwnd)
760         raise TypeError('either a valid file descriptor, file name, or window handle must be supplied')
761
762     def __init__(self, *args, **kwargs):
763         return super(IOChannel, self).__init__()
764
765     def read(self, max_count=-1):
766         return io_channel_read(self, max_count)
767
768     def readline(self, size_hint=-1):
769         # note, size_hint is just to maintain backwards compatible API; the
770         # old static binding did not actually use it
771         (status, buf, length, terminator_pos) = self.read_line()
772         if buf is None:
773             return ''
774         return buf
775
776     def readlines(self, size_hint=-1):
777         # note, size_hint is just to maintain backwards compatible API;
778         # the old static binding did not actually use it
779         lines = []
780         status = GLib.IOStatus.NORMAL
781         while status == GLib.IOStatus.NORMAL:
782             (status, buf, length, terminator_pos) = self.read_line()
783             # note, this appends an empty line after EOF; this is
784             # bug-compatible with the old static bindings
785             if buf is None:
786                 buf = ''
787             lines.append(buf)
788         return lines
789
790     def write(self, buf, buflen=-1):
791         if not isinstance(buf, bytes):
792             buf = buf.encode('UTF-8')
793         if buflen == -1:
794             buflen = len(buf)
795         (status, written) = self.write_chars(buf, buflen)
796         return written
797
798     def writelines(self, lines):
799         for line in lines:
800             self.write(line)
801
802     _whence_map = {0: GLib.SeekType.SET, 1: GLib.SeekType.CUR, 2: GLib.SeekType.END}
803
804     def seek(self, offset, whence=0):
805         try:
806             w = self._whence_map[whence]
807         except KeyError:
808             raise ValueError("invalid 'whence' value")
809         return self.seek_position(offset, w)
810
811     def add_watch(self, condition, callback, *user_data, **kwargs):
812         priority = kwargs.get('priority', GLib.PRIORITY_DEFAULT)
813         return io_add_watch(self, priority, condition, callback, *user_data)
814
815     add_watch = deprecated(add_watch, 'GLib.io_add_watch()')
816
817     def __iter__(self):
818         return self
819
820     def __next__(self):
821         (status, buf, length, terminator_pos) = self.read_line()
822         if status == GLib.IOStatus.NORMAL:
823             return buf
824         raise StopIteration
825
826     # Python 2.x compatibility
827     next = __next__
828
829 IOChannel = override(IOChannel)
830 __all__.append('IOChannel')
831
832
833 class PollFD(GLib.PollFD):
834     def __new__(cls, fd, events):
835         pollfd = GLib.PollFD()
836         pollfd.__class__ = cls
837         return pollfd
838
839     def __init__(self, fd, events):
840         self.fd = fd
841         self.events = events
842
843 PollFD = override(PollFD)
844 __all__.append('PollFD')
845
846
847 # The GI GLib API uses g_child_watch_add_full renamed to g_child_watch_add with
848 # a signature of (priority, pid, callback, data).
849 # Prior to PyGObject 3.8, this function was statically bound with an API closer to the
850 # non-full version with a signature of: (pid, callback, data=None, priority=GLib.PRIORITY_DEFAULT)
851 # We need to support this until we are okay with breaking API in a way which is
852 # not backwards compatible.
853 def _child_watch_add_get_args(priority_or_pid, pid_or_callback, *args, **kwargs):
854     user_data = []
855
856     if callable(pid_or_callback):
857         warnings.warn('Calling child_watch_add without priority as first argument is deprecated',
858                       PyGIDeprecationWarning)
859         pid = priority_or_pid
860         callback = pid_or_callback
861         if len(args) == 0:
862             priority = kwargs.get('priority', GLib.PRIORITY_DEFAULT)
863         elif len(args) == 1:
864             user_data = args
865             priority = kwargs.get('priority', GLib.PRIORITY_DEFAULT)
866         elif len(args) == 2:
867             user_data = [args[0]]
868             priority = args[1]
869         else:
870             raise TypeError('expected at most 4 positional arguments')
871     else:
872         priority = priority_or_pid
873         pid = pid_or_callback
874         if 'function' in kwargs:
875             callback = kwargs['function']
876             user_data = args
877         elif len(args) > 0 and callable(args[0]):
878             callback = args[0]
879             user_data = args[1:]
880         else:
881             raise TypeError('expected callback as third argument')
882
883     if 'data' in kwargs:
884         if user_data:
885             raise TypeError('got multiple values for "data" argument')
886         user_data = [kwargs['data']]
887
888     return priority, pid, callback, user_data
889
890 # we need this to be accessible for unit testing
891 __all__.append('_child_watch_add_get_args')
892
893
894 def child_watch_add(*args, **kwargs):
895     """child_watch_add(priority, pid, function, *data)"""
896     priority, pid, function, data = _child_watch_add_get_args(*args, **kwargs)
897     return GLib.child_watch_add(priority, pid, function, *data)
898
899 __all__.append('child_watch_add')
900
901
902 def get_current_time():
903     return GLib.get_real_time() * 0.000001
904
905 get_current_time = deprecated(get_current_time, 'GLib.get_real_time()')
906
907 __all__.append('get_current_time')
908
909
910 # backwards compatible API with default argument, and ignoring bytes_read
911 # output argument
912 def filename_from_utf8(utf8string, len=-1):
913     return GLib.filename_from_utf8(utf8string, len)[0]
914
915 __all__.append('filename_from_utf8')
916
917
918 # backwards compatible API for renamed function
919 if not hasattr(GLib, 'unix_signal_add_full'):
920     def add_full_compat(*args):
921         warnings.warn('GLib.unix_signal_add_full() was renamed to GLib.unix_signal_add()',
922                       PyGIDeprecationWarning)
923         return GLib.unix_signal_add(*args)
924
925     GLib.unix_signal_add_full = add_full_compat
926
927
928 # obsolete constants for backwards compatibility
929 glib_version = (GLib.MAJOR_VERSION, GLib.MINOR_VERSION, GLib.MICRO_VERSION)
930 __all__.append('glib_version')
931 deprecated_attr("GLib", "glib_version",
932                 "(GLib.MAJOR_VERSION, GLib.MINOR_VERSION, GLib.MICRO_VERSION)")
933
934 pyglib_version = version_info
935 __all__.append('pyglib_version')
936 deprecated_attr("GLib", "pyglib_version", "gi.version_info")