|
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") |