mirror of https://github.com/chriskiehl/Gooey.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
109 lines
3.8 KiB
109 lines
3.8 KiB
import os
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
from functools import partial
|
|
from threading import Thread
|
|
|
|
from gooey.gui import events
|
|
from gooey.gui.pubsub import pub
|
|
from gooey.gui.util.casting import safe_float
|
|
from gooey.gui.util.taskkill import taskkill
|
|
from gooey.util.functional import unit, bind
|
|
|
|
|
|
class ProcessController(object):
|
|
def __init__(self, progress_regex, progress_expr, hide_progress_msg,
|
|
encoding, shell=True):
|
|
self._process = None
|
|
self.progress_regex = progress_regex
|
|
self.progress_expr = progress_expr
|
|
self.hide_progress_msg = hide_progress_msg
|
|
self.encoding = encoding
|
|
self.wasForcefullyStopped = False
|
|
self.shell_execution = shell
|
|
|
|
def was_success(self):
|
|
self._process.communicate()
|
|
return self._process.returncode == 0
|
|
|
|
def poll(self):
|
|
if not self._process:
|
|
raise Exception('Not started!')
|
|
self._process.poll()
|
|
|
|
def stop(self):
|
|
if self.running():
|
|
self.wasForcefullyStopped = True
|
|
taskkill(self._process.pid)
|
|
|
|
def running(self):
|
|
return self._process and self.poll() is None
|
|
|
|
def run(self, command):
|
|
self.wasForcefullyStopped = False
|
|
env = os.environ.copy()
|
|
env["GOOEY"] = "1"
|
|
env["PYTHONIOENCODING"] = self.encoding
|
|
try:
|
|
self._process = subprocess.Popen(
|
|
command.encode(sys.getfilesystemencoding()),
|
|
stdout=subprocess.PIPE, stdin=subprocess.PIPE,
|
|
stderr=subprocess.STDOUT, shell=self.shell_execution, env=env)
|
|
except:
|
|
self._process = subprocess.Popen(
|
|
command,
|
|
stdout=subprocess.PIPE, stdin=subprocess.PIPE,
|
|
stderr = subprocess.STDOUT, shell = self.shell_execution, env=env)
|
|
|
|
t = Thread(target=self._forward_stdout, args=(self._process,))
|
|
t.start()
|
|
|
|
def _forward_stdout(self, process):
|
|
'''
|
|
Reads the stdout of `process` and forwards lines and progress
|
|
to any interested subscribers
|
|
'''
|
|
while True:
|
|
line = process.stdout.readline()
|
|
if not line:
|
|
break
|
|
_progress = self._extract_progress(line)
|
|
pub.send_message(events.PROGRESS_UPDATE, progress=_progress)
|
|
if _progress is None or self.hide_progress_msg is False:
|
|
pub.send_message(events.CONSOLE_UPDATE,
|
|
msg=line.decode(self.encoding))
|
|
pub.send_message(events.EXECUTION_COMPLETE)
|
|
|
|
def _extract_progress(self, text):
|
|
'''
|
|
Finds progress information in the text using the
|
|
user-supplied regex and calculation instructions
|
|
'''
|
|
# monad-ish dispatch to avoid the if/else soup
|
|
find = partial(re.search, string=text.strip().decode(self.encoding))
|
|
regex = unit(self.progress_regex)
|
|
match = bind(regex, find)
|
|
result = bind(match, self._calculate_progress)
|
|
return result
|
|
|
|
def _calculate_progress(self, match):
|
|
'''
|
|
Calculates the final progress value found by the regex
|
|
'''
|
|
if not self.progress_expr:
|
|
return safe_float(match.group(1))
|
|
else:
|
|
return self._eval_progress(match)
|
|
|
|
def _eval_progress(self, match):
|
|
'''
|
|
Runs the user-supplied progress calculation rule
|
|
'''
|
|
_locals = {k: safe_float(v) for k, v in match.groupdict().items()}
|
|
if "x" not in _locals:
|
|
_locals["x"] = [safe_float(x) for x in match.groups()]
|
|
try:
|
|
return int(eval(self.progress_expr, {}, _locals))
|
|
except:
|
|
return None
|