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

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()),
bufsize=1, stdout=subprocess.PIPE, stdin=subprocess.PIPE,
stderr=subprocess.STDOUT, shell=self.shell_execution, env=env)
except:
self._process = subprocess.Popen(
command,
bufsize=1, 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