text: 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
  
  