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