160 lines
5.4 KiB
Python
160 lines
5.4 KiB
Python
# Wrappers for GLib functions.
|
|
#
|
|
# Use these only if there is no other abstraction above the low level glib
|
|
# functions.
|
|
#
|
|
# Copyright (C) 2018 Red Hat, Inc.
|
|
#
|
|
# This copyrighted material is made available to anyone wishing to use,
|
|
# modify, copy, or redistribute it subject to the terms and conditions of
|
|
# the GNU General Public License v.2, or (at your option) any later version.
|
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
# ANY WARRANTY expressed or implied, including the implied warranties of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
|
# Public License for more details. You should have received a copy of the
|
|
# GNU General Public License along with this program; if not, write to the
|
|
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
|
|
# source code or documentation are not subject to the GNU General Public
|
|
# License and may only be used or replicated with the express permission of
|
|
# Red Hat, Inc.
|
|
#
|
|
# Author(s): Jiri Konecny <jkonecny@redhat.com>
|
|
#
|
|
|
|
import gi
|
|
gi.require_version("GLib", "2.0")
|
|
gi.require_version("Gio", "2.0")
|
|
|
|
from gi.repository.GLib import markup_escape_text, format_size_full, \
|
|
timeout_add_seconds, timeout_add, idle_add, \
|
|
io_add_watch, child_watch_add, \
|
|
source_remove, timeout_source_new, \
|
|
spawn_close_pid, spawn_async_with_pipes, \
|
|
MainLoop, MainContext, \
|
|
GError, Variant, VariantType, Bytes, \
|
|
IOCondition, IOChannel, SpawnFlags, \
|
|
MAXUINT
|
|
from gi.repository.Gio import Cancellable
|
|
|
|
from pyanaconda.anaconda_loggers import get_module_logger
|
|
log = get_module_logger(__name__)
|
|
|
|
|
|
__all__ = ["create_main_loop", "create_new_context",
|
|
"markup_escape_text", "format_size_full",
|
|
"timeout_add_seconds", "timeout_add", "idle_add",
|
|
"io_add_watch", "child_watch_add",
|
|
"source_remove", "timeout_source_new",
|
|
"spawn_close_pid", "spawn_async_with_pipes",
|
|
"GError", "Variant", "VariantType", "Bytes",
|
|
"IOCondition", "IOChannel", "SpawnFlags",
|
|
"MAXUINT", "Cancellable"]
|
|
|
|
|
|
def create_main_loop(main_context=None):
|
|
"""Create GLib main loop.
|
|
|
|
:param main_context: main context to be used for the loop
|
|
:type main_context: GLib.MainContext
|
|
:returns: GLib.MainLoop instance.
|
|
"""
|
|
return MainLoop(main_context)
|
|
|
|
|
|
def create_new_context():
|
|
"""Create GLib context.
|
|
|
|
:returns: GLib.MainContext."""
|
|
return MainContext.new()
|
|
|
|
|
|
class GLibCallResult():
|
|
"""Result of GLib async finish callback."""
|
|
def __init__(self):
|
|
self.received_data = None
|
|
self.error_message = ""
|
|
self.timeout = False
|
|
|
|
@property
|
|
def succeeded(self):
|
|
"""The async call has succeeded."""
|
|
return not self.failed
|
|
|
|
@property
|
|
def failed(self):
|
|
"""The async call has failed."""
|
|
return bool(self.error_message) or self.timeout
|
|
|
|
|
|
def sync_call_glib(context, async_call, async_call_finish, timeout, *call_args):
|
|
"""Call GLib asynchronous method synchronously with timeout.
|
|
|
|
:param context: context for the new loop in which the method will be called
|
|
:type context: GMainContext
|
|
:param async_call: asynchronous GLib method to be called
|
|
:type async_call: GLib method
|
|
:param async_call_finish: finish method of the asynchronous call
|
|
:type async_call_finish: GLib method
|
|
:param timeout: timeout for the loop in seconds (0 == no timeout)
|
|
:type timeout: int
|
|
|
|
*call_args should hold all positional arguments preceding the cancellable argument
|
|
"""
|
|
|
|
info = async_call.get_symbol()
|
|
result = GLibCallResult()
|
|
|
|
loop = create_main_loop(context)
|
|
callbacks = [loop.quit]
|
|
|
|
def _stop_loop():
|
|
log.debug("sync_call_glib[%s]: quit", info)
|
|
while callbacks:
|
|
callback = callbacks.pop()
|
|
callback()
|
|
|
|
def _cancellable_cb():
|
|
log.debug("sync_call_glib[%s]: cancelled", info)
|
|
|
|
cancellable = Cancellable()
|
|
cancellable_id = cancellable.connect(_cancellable_cb)
|
|
callbacks.append(lambda: cancellable.disconnect(cancellable_id))
|
|
|
|
def _timeout_cb(user_data):
|
|
log.debug("sync_call_glib[%s]: timeout", info)
|
|
result.timeout = True
|
|
cancellable.cancel()
|
|
return False
|
|
|
|
timeout_source = timeout_source_new(int(timeout * 1000))
|
|
timeout_source.set_callback(_timeout_cb)
|
|
timeout_source.attach(context)
|
|
callbacks.append(timeout_source.destroy)
|
|
|
|
def _finish_cb(source_object, async_result):
|
|
log.debug("sync_call_glib[%s]: call %s",
|
|
info,
|
|
async_call_finish.get_symbol())
|
|
try:
|
|
result.received_data = async_call_finish(async_result)
|
|
except Exception as e: # pylint: disable=broad-except
|
|
result.error_message = str(e)
|
|
finally:
|
|
_stop_loop()
|
|
|
|
context.push_thread_default()
|
|
|
|
log.debug("sync_call_glib[%s]: call", info)
|
|
try:
|
|
async_call(
|
|
*call_args,
|
|
cancellable=cancellable,
|
|
callback=_finish_cb
|
|
)
|
|
loop.run()
|
|
finally:
|
|
_stop_loop()
|
|
context.pop_thread_default()
|
|
|
|
return result
|