run subprocesses in threads

This commit is contained in:
Tilman Kranz 2023-07-20 06:22:19 +02:00
parent b9416da9c8
commit 8aed22de69

View File

@ -1,5 +1,8 @@
#!/usr/bin/env python3
import re
import queue
import threading
import gi
import html
import logging
@ -12,18 +15,30 @@ import sys
gi.require_version('Gdk', '3.0')
gi.require_version('Gtk', '3.0')
try:
from gi.repository import Gtk
except ImportError:
logging.error("Could not import Gtk")
sys.exit(1)
try:
from gi.repository import Gio
except ImportError:
logging.error("Could not import Gio")
sys.exit(1)
# try:
# from gi.repository import GObject
# except ImportError:
# logging.error("Could not import GObject")
# sys.exit(1)
try:
from gi.repository import GLib
except ImportError:
logging.error("Could not import Gio")
sys.exit(1)
try:
from gi.repository import Gtk
except ImportError:
logging.error("Could not import Gtk")
sys.exit(1)
class UpdateWindow(Gtk.ApplicationWindow):
def clear(self):
@ -81,14 +96,17 @@ class UpdateWindow(Gtk.ApplicationWindow):
def insert(self, text, iter):
self.buffer.insert(iter, text + "\n")
def run(self, args, ignore_stderr=False, output_msg=None,
empty_msg=None, env={}):
def execute(self, args, ignore_stderr=False, output_msg=None,
empty_msg=None, env={}):
self.lock()
self.clear()
self.prepend_mesg(
"INFO",
"Running command \"%s\" ..." % " ".join(args))
thread = threading.Thread(target=self.run, args=(args, env,))
thread.start()
def run(self, args, env={}):
p = subprocess.Popen(
args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False,
env=dict(os.environ, **env))
@ -97,8 +115,6 @@ class UpdateWindow(Gtk.ApplicationWindow):
sel.register(p.stdout, selectors.EVENT_READ)
sel.register(p.stderr, selectors.EVENT_READ)
done = False
output = False
error = False
while not done:
for key, _ in sel.select():
@ -106,39 +122,17 @@ class UpdateWindow(Gtk.ApplicationWindow):
if data:
if key.fileobj is p.stdout:
logging.debug("STDOUT: " + data)
self.append_mesg("STDOUT", data)
output = True
elif not ignore_stderr:
logging.debug("STDERR: " + data)
self.append_mesg("STDERR", data)
self.stdout_queue.put(data)
else:
self.stderr_queue.put(data)
exit_code = p.poll()
if exit_code is not None:
if exit_code != 0:
self.append_mesg(
"ERROR",
"apt-get exit code: %d\n" % exit_code
)
error = True
self.stdout_queue.put("EXIT %d" % exit_code)
done = True
break
self.unlock()
if error:
return False
else:
if output:
if output_msg is not None:
self.prepend_mesg("INFO", output_msg)
elif empty_msg is not None:
self.append_mesg("INFO", empty_msg)
return True
def lock(self):
self.update_button.set_sensitive(False)
self.upgrade_button.set_sensitive(False)
@ -154,7 +148,7 @@ class UpdateWindow(Gtk.ApplicationWindow):
def upgrade(self):
args = ['/usr/bin/apt-get', '-yqq', 'full-upgrade']
env = {'DEBIAN_FRONTEND': 'noninteractive'}
self.run(
self.execute(
args,
env=env,
empty_msg="No package upgrades were performed.")
@ -165,14 +159,14 @@ class UpdateWindow(Gtk.ApplicationWindow):
def update(self):
args = ['/usr/bin/apt-get', '-y', 'update']
env = {'DEBIAN_FRONTEND': 'noninteractive'}
self.run(args, env=env)
self.execute(args, env=env)
def on_update(self, *args):
self.update()
def list(self):
args = ['/usr/bin/apt', '-qq', 'list', '--upgradable']
self.run(
self.execute(
args,
ignore_stderr=True,
output_msg="Found the following package upgrades:",
@ -184,11 +178,39 @@ class UpdateWindow(Gtk.ApplicationWindow):
def on_quit(self, *args):
self.application.quit()
def update_buffer(self):
try:
text = self.stdout_queue.get(block=False)
self.append_mesg("STDOUT", text)
match = re.fullmatch(r'EXIT (\d+)', text)
if match is not None:
exit_code = int(match.group(1))
if exit_code != 0:
self.append_mesg(
"ERROR",
"Command exited with code %d" % exit_code)
self.unlock()
except queue.Empty:
pass
try:
text = self.stderr_queue.get(block=False)
self.append_mesg("STDERR", text)
except queue.Empty:
pass
return True
def __init__(self, application):
super(UpdateWindow, self).__init__(
application=application,
title="Simple APT Update")
self.application = application
self.stdout_queue = queue.Queue()
self.stderr_queue = queue.Queue()
GLib.timeout_add(100, self.update_buffer)
self.init_ui()
def init_ui(self):
@ -282,8 +304,6 @@ class SimpleAptUpdate(Gtk.Application):
self.window.present()
self.window.show_all()
self.window.update()
self.window.list()
def main():