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