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.

98 lines
2.8 KiB

import os
import re
import subprocess
from functools import partial
from multiprocessing.dummy import Pool
import sys
from gooey.gui.pubsub import pub
from gooey.gui.util.casting import safe_float
from gooey.gui.util.functional import unit, bind
from gooey.gui.util.taskkill import taskkill
class ProcessController(object):
def __init__(self, progress_regex, progress_expr):
self._process = None
self.progress_regex = progress_regex
self.progress_expr = progress_expr
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():
taskkill(self._process.pid)
def running(self):
return self._process and self.poll() is None
def run(self, command):
env = os.environ.copy()
env["GOOEY"] = "1"
try:
self._process = subprocess.Popen(
command.encode(sys.getfilesystemencoding()),
bufsize=1, stdout=subprocess.PIPE, stdin=subprocess.PIPE,
stderr=subprocess.STDOUT, shell=True, env=env)
except:
self._process = subprocess.Popen(
command,
bufsize=1, stdout=subprocess.PIPE, stdin=subprocess.PIPE,
stderr=subprocess.STDOUT, shell=True, env=env)
Pool(1).apply_async(self._forward_stdout, (self._process,))
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
pub.send_message('console_update', msg=line)
pub.send_message('progress_update', progress=self._extract_progress(line))
pub.send_message('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())
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