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