Edouard@2322: #!/usr/bin/env python Edouard@2322: # -*- coding: utf-8 -*- Edouard@2322: Edouard@2322: # subset of subprocess built-in module using posix_spawn rather than fork. Edouard@2322: andrej@2537: from __future__ import print_function edouard@2492: from __future__ import absolute_import Edouard@2322: import os Edouard@2322: import signal edouard@2492: import posix_spawn Edouard@2322: Edouard@2322: PIPE = "42" Edouard@2322: edouard@2492: Edouard@2322: class Popen(object): edouard@2492: def __init__(self, args, stdin=None, stdout=None): Edouard@2322: self.returncode = None Edouard@2322: self.stdout = None Edouard@2322: self.stdin = None Edouard@2322: # TODO: stderr Edouard@2322: file_actions = posix_spawn.FileActions() Edouard@2322: if stdout is not None: Edouard@2322: # child's stdout, child 2 parent pipe Edouard@2322: c2pread, c2pwrite = os.pipe() Edouard@2322: # attach child's stdout to writing en of c2p pipe Edouard@2322: file_actions.add_dup2(c2pwrite, 1) Edouard@2322: # close other end Edouard@2322: file_actions.add_close(c2pread) Edouard@2322: if stdin is not None: Edouard@2322: # child's stdin, parent to child pipe Edouard@2322: p2cread, p2cwrite = os.pipe() Edouard@2322: # attach child's stdin to reading en of p2c pipe Edouard@2322: file_actions.add_dup2(p2cread, 0) Edouard@2322: # close other end Edouard@2322: file_actions.add_close(p2cwrite) Edouard@2322: self.pid = posix_spawn.posix_spawnp(args[0], args, file_actions=file_actions) Edouard@2322: if stdout is not None: Edouard@2322: self.stdout = os.fdopen(c2pread) Edouard@2322: os.close(c2pwrite) Edouard@2322: if stdin is not None: Edouard@2322: self.stdin = os.fdopen(p2cwrite, 'w') Edouard@2322: os.close(p2cread) Edouard@2322: Edouard@2322: def _wait(self): Edouard@2322: if self.returncode is None: edouard@2492: self.returncode = os.waitpid(self.pid, 0)[1] Edouard@2322: Edouard@2322: def communicate(self): Edouard@2322: if self.stdin is not None: Edouard@2322: self.stdin.close() Edouard@2322: self.stdin = None Edouard@2322: if self.stdout is not None: Edouard@2322: stdoutdata = self.stdout.read() Edouard@2322: else: Edouard@2322: stdoutdata = "" edouard@2492: Edouard@2322: # TODO Edouard@2322: stderrdata = "" Edouard@2322: Edouard@2322: self._wait() Edouard@2322: if self.stdout is not None: Edouard@2322: self.stdout.close() Edouard@2322: self.stdout = None edouard@2492: Edouard@2322: return (stdoutdata, stderrdata) Edouard@2322: Edouard@2322: def wait(self): Edouard@2322: if self.stdin is not None: Edouard@2322: self.stdin.close() Edouard@2322: self.stdin = None Edouard@2322: self._wait() Edouard@2322: if self.stdout is not None: Edouard@2322: self.stdout.close() Edouard@2322: self.stdout = None Edouard@2322: return self.returncode Edouard@2322: Edouard@2322: def poll(self): Edouard@2322: if self.returncode is None: Edouard@2322: pid, ret = os.waitpid(self.pid, os.WNOHANG) edouard@2492: if (pid, ret) != (0, 0): Edouard@2322: self.returncode = ret Edouard@2322: Edouard@2322: if self.stdin is not None: Edouard@2322: self.stdin.close() Edouard@2322: self.stdin = None Edouard@2322: if self.stdout is not None: Edouard@2322: self.stdout.close() Edouard@2322: self.stdout = None Edouard@2322: Edouard@2322: return self.returncode edouard@2492: Edouard@2322: def kill(self): Edouard@2322: os.kill(self.pid, signal.SIGKILL) Edouard@2322: Edouard@2322: if self.stdin is not None: Edouard@2322: self.stdin.close() Edouard@2322: self.stdin = None Edouard@2322: if self.stdout is not None: Edouard@2322: self.stdout.close() Edouard@2322: self.stdout = None Edouard@2322: Edouard@2322: Edouard@2322: def call(*args): Edouard@2322: cmd = [] Edouard@2322: if isinstance(args[0], str): edouard@2492: if len(args) == 1: Edouard@2322: # oversimplified splitting of arguments, Edouard@2322: # TODO: care about use of simple and double quotes Edouard@2322: cmd = args[0].split() Edouard@2322: else: Edouard@2322: cmd = args edouard@2492: elif isinstance(args[0], list) and len(args) == 1: Edouard@2322: cmd = args[0] Edouard@2322: else: Edouard@2322: raise Exception("Wrong arguments passed to subprocess.call") Edouard@2322: pid = posix_spawn.posix_spawnp(cmd[0], cmd) edouard@2492: return os.waitpid(pid, 0) edouard@2492: Edouard@2322: Edouard@2322: if __name__ == '__main__': Edouard@2322: # unit test Edouard@2322: Edouard@2322: p = Popen(["tr", "abc", "def"], stdin=PIPE, stdout=PIPE) Edouard@2322: p.stdin.write("blah") Edouard@2322: p.stdin.close() edouard@2492: print(p.stdout.read()) Edouard@2322: p.wait() Edouard@2322: Edouard@2322: p = Popen(["tr", "abc", "def"], stdin=PIPE, stdout=PIPE) Edouard@2322: p.stdin.write("blah") edouard@2492: print(p.communicate()) Edouard@2322: Edouard@2322: call("echo blah0") Edouard@2322: call(["echo", "blah1"]) Edouard@2322: call("echo", "blah2")