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.

88 lines
2.6 KiB

  1. import os
  2. import re
  3. import subprocess
  4. from functools import partial
  5. from multiprocessing.dummy import Pool
  6. from gooey.gui.pubsub import pub
  7. from gooey.gui.util.casting import safe_float
  8. from gooey.gui.util.functional import unit, bind
  9. from gooey.gui.util.taskkill import taskkill
  10. class ProcessController(object):
  11. def __init__(self, progress_regex, progress_expr):
  12. self._process = None
  13. self.progress_regex = progress_regex
  14. self.progress_expr = progress_expr
  15. def was_success(self):
  16. self._process.communicate()
  17. return self._process.returncode == 0
  18. def poll(self):
  19. if not self._process:
  20. raise Exception('Not started!')
  21. self._process.poll()
  22. def stop(self):
  23. if self.running():
  24. taskkill(self._process.pid)
  25. def running(self):
  26. return self._process and self.poll() is None
  27. def run(self, command):
  28. env = os.environ.copy()
  29. env["GOOEY"] = "1"
  30. self._process = subprocess.Popen(command, bufsize=1, stdout=subprocess.PIPE,
  31. stderr=subprocess.STDOUT, shell=True, env=env)
  32. Pool(1).apply_async(self._forward_stdout, (self._process,))
  33. def _forward_stdout(self, process):
  34. '''
  35. Reads the stdout of `process` and forwards lines and progress
  36. to any interested subscribers
  37. '''
  38. while True:
  39. line = process.stdout.readline()
  40. if not line:
  41. break
  42. pub.send_message('console_update', msg=line)
  43. pub.send_message('progress_update', progress=self._extract_progress(line))
  44. pub.send_message('execution_complete')
  45. def _extract_progress(self, text):
  46. '''
  47. Finds progress information in the text using the
  48. user-supplied regex and calculation instructions
  49. '''
  50. # monad-ish dispatch to avoid the if/else soup
  51. find = partial(re.search, string=text.strip())
  52. regex = unit(self.progress_regex)
  53. match = bind(regex, find)
  54. result = bind(match, self._calculate_progress)
  55. return result
  56. def _calculate_progress(self, match):
  57. '''
  58. Calculates the final progress value found by the regex
  59. '''
  60. if not self.progress_expr:
  61. return safe_float(match.group(1))
  62. else:
  63. return self._eval_progress(match)
  64. def _eval_progress(self, match):
  65. '''
  66. Runs the user-supplied progress calculation rule
  67. '''
  68. _locals = {k: safe_float(v) for k, v in match.groupdict().items()}
  69. if "x" not in _locals:
  70. _locals["x"] = [safe_float(x) for x in match.groups()]
  71. try:
  72. return int(eval(self.progress_expr, {}, _locals))
  73. except:
  74. return None