| 43 | |
| 44 | |
| 45 | class ExecutionContext(object): |
| 46 | def __init__(self, cmd, cwd, env, stop_signal, is_server, report): |
| 47 | self._log = multiprocessing.get_logger() |
| 48 | self.cmd = cmd |
| 49 | self.cwd = cwd |
| 50 | self.env = env |
| 51 | self.stop_signal = stop_signal |
| 52 | self.is_server = is_server |
| 53 | self.report = report |
| 54 | self.expired = False |
| 55 | self.killed = False |
| 56 | self.proc = None |
| 57 | |
| 58 | def _popen_args(self): |
| 59 | args = { |
| 60 | 'cwd': self.cwd, |
| 61 | 'env': self.env, |
| 62 | 'stdout': self.report.out, |
| 63 | 'stderr': subprocess.STDOUT, |
| 64 | } |
| 65 | # make sure child processes doesn't remain after killing |
| 66 | if platform.system() == 'Windows': |
| 67 | DETACHED_PROCESS = 0x00000008 |
| 68 | args.update(creationflags=DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP) |
| 69 | else: |
| 70 | args.update(preexec_fn=os.setsid) |
| 71 | return args |
| 72 | |
| 73 | def start(self): |
| 74 | joined = ' '.join(self.cmd) |
| 75 | self._log.debug('COMMAND: %s', joined) |
| 76 | self._log.debug('WORKDIR: %s', self.cwd) |
| 77 | self._log.debug('LOGFILE: %s', self.report.logpath) |
| 78 | self.report.begin() |
| 79 | self.proc = subprocess.Popen(self.cmd, **self._popen_args()) |
| 80 | self._log.debug(' PID: %d', self.proc.pid) |
| 81 | self._log.debug(' PGID: %d', os.getpgid(self.proc.pid)) |
| 82 | return self._scoped() |
| 83 | |
| 84 | @contextlib.contextmanager |
| 85 | def _scoped(self): |
| 86 | yield self |
| 87 | if self.is_server: |
| 88 | # the server is supposed to run until we stop it |
| 89 | if self.returncode is not None: |
| 90 | self.report.died() |
| 91 | else: |
| 92 | if self.stop_signal != SIGNONE: |
| 93 | if self.sigwait(self.stop_signal): |
| 94 | self.report.end(self.returncode) |
| 95 | else: |
| 96 | self.report.killed() |
| 97 | else: |
| 98 | self.sigwait(SIGKILL) |
| 99 | else: |
| 100 | # the client is supposed to exit normally |
| 101 | if self.returncode is not None: |
| 102 | self.report.end(self.returncode) |