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