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: 
kinsamanka@3750: 
kinsamanka@3750: 
edouard@3821: import os, sys
Edouard@2322: import signal
Edouard@3293: import shlex
edouard@2492: import posix_spawn
Edouard@2322: 
Edouard@2322: PIPE = "42"
Edouard@2322: 
edouard@3821: fsencoding = sys.getfilesystemencoding()
edouard@2492: 
Edouard@2322: class Popen(object):
Edouard@3860:     def __init__(self, args, stdin=None, stdout=None, stderr=None):
Edouard@2322:         self.returncode = None
Edouard@2322:         self.stdout = None
Edouard@3860:         self.stderr = None
Edouard@2322:         self.stdin = None
Edouard@2322:         file_actions = posix_spawn.FileActions()
Edouard@2322:         if stdout is not None:
Edouard@2322:             # child's stdout, child 2 parent pipe
Edouard@3860:             c1pread, c1pwrite = os.pipe()
Edouard@3860:             # attach child's stdout to writing en of c1p pipe
Edouard@3860:             file_actions.add_dup2(c1pwrite, 1)
Edouard@3860:             # close other end
Edouard@3860:             file_actions.add_close(c1pread)
Edouard@3860:         if stderr is not None:
Edouard@3860:             # child's stderr, child 2 parent pipe
Edouard@2322:             c2pread, c2pwrite = os.pipe()
Edouard@3860:             # attach child's stderr to writing en of c2p pipe
Edouard@3860:             file_actions.add_dup2(c2pwrite, 2)
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@3821:         args = [s.encode(fsencoding) for s in args if type(s)==str]
Edouard@2322:         self.pid = posix_spawn.posix_spawnp(args[0], args, file_actions=file_actions)
Edouard@2322:         if stdout is not None:
Edouard@3860:             self.stdout = os.fdopen(c1pread)
Edouard@3860:             os.close(c1pwrite)
Edouard@3860:         if stderr is not None:
Edouard@3860:             self.stderr = 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@3860: 
Edouard@2322:         if self.stdout is not None:
Edouard@2322:             stdoutdata = self.stdout.read()
Edouard@2322:         else:
Edouard@2322:             stdoutdata = ""
edouard@2492: 
Edouard@3860:         if self.stderr is not None:
Edouard@3860:             stderrdata = self.stderr.read()
Edouard@3860:         else:
Edouard@3860:             stderrdata = ""
Edouard@2322: 
Edouard@2322:         self._wait()
Edouard@3860: 
Edouard@2322:         if self.stdout is not None:
Edouard@2322:             self.stdout.close()
Edouard@2322:             self.stdout = None
edouard@2492: 
Edouard@3860:         if self.stderr is not None:
Edouard@3860:             self.stderr.close()
Edouard@3860:             self.stderr = None
Edouard@3860: 
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@3860: 
Edouard@2322:         self._wait()
Edouard@3860: 
Edouard@2322:         if self.stdout is not None:
Edouard@2322:             self.stdout.close()
Edouard@2322:             self.stdout = None
Edouard@3860: 
Edouard@3860:         if self.stderr is not None:
Edouard@3860:             self.stderr.close()
Edouard@3860:             self.stderr = None
Edouard@3860: 
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@3860: 
Edouard@2322:                 if self.stdout is not None:
Edouard@2322:                     self.stdout.close()
Edouard@2322:                     self.stdout = None
Edouard@2322: 
Edouard@3860:                 if self.stderr is not None:
Edouard@3860:                     self.stderr.close()
Edouard@3860:                     self.stderr = None
Edouard@3860: 
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@3860: 
Edouard@2322:         if self.stdout is not None:
Edouard@2322:             self.stdout.close()
Edouard@2322:             self.stdout = None
Edouard@2322: 
Edouard@3860:         if self.stderr is not None:
Edouard@3860:             self.stderr.close()
Edouard@3860:             self.stderr = None
Edouard@3860: 
Edouard@2322: 
Edouard@2322: def call(*args):
Edouard@2322:     cmd = []
Edouard@2322:     if isinstance(args[0], str):
edouard@2492:         if len(args) == 1:
Edouard@3293:             # splitting of arguments that cares about 
Edouard@3293:             # use of simple and double quotes
Edouard@3293:             cmd = shlex.split(args[0])
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")