runtime/spawn_subprocess.py
changeset 2322 7ce4e5cf6339
child 2492 7dd551ac2fa0
equal deleted inserted replaced
2321:0a3103cd825d 2322:7ce4e5cf6339
       
     1 #!/usr/bin/env python
       
     2 # -*- coding: utf-8 -*-
       
     3 
       
     4 # subset of subprocess built-in module using posix_spawn rather than fork.
       
     5 
       
     6 import posix_spawn
       
     7 import os
       
     8 import signal
       
     9 
       
    10 PIPE = "42"
       
    11 
       
    12 class Popen(object):
       
    13     def __init__(self, args,stdin=None, stdout=None):
       
    14         self.returncode = None
       
    15         self.stdout = None
       
    16         self.stdin = None
       
    17         # TODO: stderr
       
    18         file_actions = posix_spawn.FileActions()
       
    19         if stdout is not None:
       
    20             # child's stdout, child 2 parent pipe
       
    21             c2pread, c2pwrite = os.pipe()
       
    22             # attach child's stdout to writing en of c2p pipe
       
    23             file_actions.add_dup2(c2pwrite, 1)
       
    24             # close other end
       
    25             file_actions.add_close(c2pread)
       
    26         if stdin is not None:
       
    27             # child's stdin, parent to child pipe
       
    28             p2cread, p2cwrite = os.pipe()
       
    29             # attach child's stdin to reading en of p2c pipe
       
    30             file_actions.add_dup2(p2cread, 0)
       
    31             # close other end
       
    32             file_actions.add_close(p2cwrite)
       
    33         self.pid = posix_spawn.posix_spawnp(args[0], args, file_actions=file_actions)
       
    34         if stdout is not None:
       
    35             self.stdout = os.fdopen(c2pread)
       
    36             os.close(c2pwrite)
       
    37         if stdin is not None:
       
    38             self.stdin = os.fdopen(p2cwrite, 'w')
       
    39             os.close(p2cread)
       
    40 
       
    41     def _wait(self):
       
    42         if self.returncode is None:
       
    43             self.returncode = os.waitpid(self.pid,0)[1]
       
    44 
       
    45     def communicate(self):
       
    46         if self.stdin is not None:
       
    47             self.stdin.close()
       
    48             self.stdin = None
       
    49         if self.stdout is not None:
       
    50             stdoutdata = self.stdout.read()
       
    51         else:
       
    52             stdoutdata = ""
       
    53         
       
    54         # TODO
       
    55         stderrdata = ""
       
    56 
       
    57         self._wait()
       
    58         if self.stdout is not None:
       
    59             self.stdout.close()
       
    60             self.stdout = None
       
    61         
       
    62         return (stdoutdata, stderrdata)
       
    63 
       
    64     def wait(self):
       
    65         if self.stdin is not None:
       
    66             self.stdin.close()
       
    67             self.stdin = None
       
    68         self._wait()
       
    69         if self.stdout is not None:
       
    70             self.stdout.close()
       
    71             self.stdout = None
       
    72         return self.returncode
       
    73 
       
    74     def poll(self):
       
    75         if self.returncode is None:
       
    76             pid, ret = os.waitpid(self.pid, os.WNOHANG)
       
    77             if (pid,ret) != (0,0):
       
    78                 self.returncode = ret
       
    79 
       
    80                 if self.stdin is not None:
       
    81                     self.stdin.close()
       
    82                     self.stdin = None
       
    83                 if self.stdout is not None:
       
    84                     self.stdout.close()
       
    85                     self.stdout = None
       
    86 
       
    87         return self.returncode
       
    88         
       
    89     def kill(self):
       
    90         os.kill(self.pid, signal.SIGKILL)
       
    91 
       
    92         if self.stdin is not None:
       
    93             self.stdin.close()
       
    94             self.stdin = None
       
    95         if self.stdout is not None:
       
    96             self.stdout.close()
       
    97             self.stdout = None
       
    98 
       
    99         
       
   100 
       
   101 def call(*args):
       
   102     cmd = []
       
   103     if isinstance(args[0], str):
       
   104         if len(args)==1:
       
   105             # oversimplified splitting of arguments,
       
   106             # TODO: care about use of simple and double quotes
       
   107             cmd = args[0].split()
       
   108         else:
       
   109             cmd = args
       
   110     elif isinstance(args[0], list) and len(args)==1:
       
   111         cmd = args[0]
       
   112     else:
       
   113         raise Exception("Wrong arguments passed to subprocess.call")
       
   114     pid = posix_spawn.posix_spawnp(cmd[0], cmd)
       
   115     return os.waitpid(pid,0)
       
   116 
       
   117 if __name__ == '__main__':
       
   118     # unit test
       
   119 
       
   120     p = Popen(["tr", "abc", "def"], stdin=PIPE, stdout=PIPE)
       
   121     p.stdin.write("blah")
       
   122     p.stdin.close()
       
   123     print p.stdout.read()
       
   124     p.wait()
       
   125 
       
   126     p = Popen(["tr", "abc", "def"], stdin=PIPE, stdout=PIPE)
       
   127     p.stdin.write("blah")
       
   128     print p.communicate()
       
   129 
       
   130     call("echo blah0")
       
   131     call(["echo", "blah1"])
       
   132     call("echo", "blah2")