def test_fds(self): with open("/dev/null") as devnull: proc = supervise_api.Process(["sh", "-c", "sleep inf"], fds={0: devnull}) proc.kill() proc.wait() proc.close()
def test_fds_unopened(self): with open("/dev/null") as devnull: proc = supervise_api.Process(["sh", "-c", "sleep inf"], fds={ devnull.fileno(): 0, 42: devnull }) proc.kill() proc.wait() proc.close()
def just_run(self, args): r, w = os.pipe() try: proc = supervise_api.Process(args, fds={w: w}) except: os.close(r) os.close(w) raise os.close(w) proc.kill() self.assertEqual(proc.wait().killed_with(), signal.SIGKILL) proc.close() # we should get eof because the process should be dead data = os.read(r, 4096) self.assertEqual(len(data), 0) os.close(r)
def test_fds_unopened_noleaks(self): with tempfile.TemporaryFile() as temp: with open("/dev/null") as devnull: proc = supervise_api.Process( ["sh", "-c", "ls -1 /proc/$$/fd; true"], fds={ devnull.fileno(): 0, 42: devnull, 1: temp }) sent_fds = self.open_fds.union(set([devnull.fileno(), 42, 1])) proc.wait() proc.close() temp.seek(0) received_fds = parse_fd_set(temp.read()) self.assertTrue(received_fds.issubset(sent_fds))
def multifork(self, cmd): # using % here to avoid needing to escape {} cmd = "{ echo started; %s; }" % (cmd) started_length = len("started\n") * 5 args = [ "sh", "-c", "{ %s & %s & } & { %s & %s & } & { { { %s & } & } &} &" % (cmd, cmd, cmd, cmd, cmd) ] started, stdout = os.pipe() try: exited, w = os.pipe() except: os.close(exited) os.close(w) raise try: proc = supervise_api.Process(args, fds={w: w, 1: stdout}) except: os.close(started) os.close(stdout) os.close(exited) os.close(w) raise os.close(stdout) os.close(w) more_length = started_length while more_length > 0: more_length -= len(os.read(started, more_length)) proc.kill() proc.wait() proc.close() # we should get eof because the process should be dead data = os.read(exited, 4096) self.assertEqual(len(data), 0) os.close(started) os.close(exited)
def sh(cmd: List[str], timeout: Optional[float], env: Dict[str, str] = None) -> int: """Note that env is a set of _updates_ to the environment, not the complete environment! """ # http://catern.com/posts/fork.html with supervise_api.Process(cmd, env=(env or {})) as proc: start_time = time.time() if timeout is None: return proc.wait() else: while time.time() - start_time <= timeout: try: time.sleep(0.1) except RuntimeError: subprocess.run(["git", "worktree", "prune"]) raise returncode = proc.poll() if returncode is not None: return returncode log.error("BUILD TIMED OUT!") return -1 # indicate timeout
def test_flags_no_cloexec(self): proc = supervise_api.Process(["sh", "-c", "sleep inf"], flags=0) inheritable = os.get_inheritable(proc.fileno()) proc.close() self.assertTrue(inheritable)
def test_flags_yes_cloexec(self): proc = supervise_api.Process(["sh", "-c", "sleep inf"], flags=os.O_CLOEXEC) inheritable = os.get_inheritable(proc.fileno()) proc.close() self.assertFalse(inheritable)
def test_fds_closed_fileno(self): with open("/dev/null") as devnull: fdnum = devnull.fileno() # devnull is closed now with self.assertRaises(ValueError): supervise_api.Process(["sh", "-c", "sleep inf"], fds={0: fdnum})
def test_fds_closed(self): with open("/dev/null") as devnull: pass # devnull is closed now with self.assertRaises(ValueError): supervise_api.Process(["sh", "-c", "sleep inf"], fds={0: devnull})
def test_executable_not_found(self): with self.assertRaises(OSError) as cm: supervise_api.Process( ["supervise_api_nonexistent_executable_aosije"]) self.assertEqual(cm.exception.errno, errno.ENOENT)