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