Spawn a subprocess using os.posix_spawn() with stdin, stdout, and stderr piped. Returns an object compatible with subprocess.Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE). Under gevent+uWSGI, fork() hangs indefinitely in gevent's _before_fork atfork handler regardless of whethe
(cmd)
| 117 | |
| 118 | |
| 119 | def posix_spawn_proc(cmd): |
| 120 | """ |
| 121 | Spawn a subprocess using os.posix_spawn() with stdin, stdout, and stderr piped. |
| 122 | |
| 123 | Returns an object compatible with subprocess.Popen(cmd, stdin=PIPE, |
| 124 | stdout=PIPE, stderr=PIPE). Under gevent+uWSGI, fork() hangs indefinitely |
| 125 | in gevent's _before_fork atfork handler regardless of whether it is called |
| 126 | from a hub greenlet or a threadpool thread. os.posix_spawn() is explicitly |
| 127 | defined by POSIX to not call pthread_atfork handlers. |
| 128 | """ |
| 129 | import os |
| 130 | import shutil |
| 131 | import signal |
| 132 | import subprocess |
| 133 | import time |
| 134 | |
| 135 | stdin_r, stdin_w = os.pipe() # child reads stdin (FD 0) from here |
| 136 | stdout_r, stdout_w = os.pipe() # child writes stdout (FD 1) here; parent reads |
| 137 | stderr_r, stderr_w = os.pipe() # child writes stderr (FD 2) here; parent reads |
| 138 | |
| 139 | stdin_w_ok = stdout_r_ok = stderr_r_ok = False |
| 140 | try: |
| 141 | executable = shutil.which(cmd[0]) or cmd[0] |
| 142 | child_pid = os.posix_spawn( |
| 143 | executable, cmd, os.environ, |
| 144 | file_actions=[ |
| 145 | (os.POSIX_SPAWN_DUP2, stdin_r, 0), |
| 146 | (os.POSIX_SPAWN_DUP2, stdout_w, 1), |
| 147 | (os.POSIX_SPAWN_DUP2, stderr_w, 2), |
| 148 | (os.POSIX_SPAWN_CLOSE, stdin_r), |
| 149 | (os.POSIX_SPAWN_CLOSE, stdout_w), |
| 150 | (os.POSIX_SPAWN_CLOSE, stderr_w), |
| 151 | ], |
| 152 | ) |
| 153 | |
| 154 | import fcntl |
| 155 | fcntl.fcntl(stdin_w, fcntl.F_SETFL, fcntl.fcntl(stdin_w, fcntl.F_GETFL) | os.O_NONBLOCK) |
| 156 | stdin_file = os.fdopen(stdin_w, 'wb', buffering=0) |
| 157 | stdin_w_ok = True |
| 158 | stdout_file = os.fdopen(stdout_r, 'rb', buffering=0) |
| 159 | stdout_r_ok = True |
| 160 | stderr_file = os.fdopen(stderr_r, 'rb', buffering=0) |
| 161 | stderr_r_ok = True |
| 162 | |
| 163 | class _Proc: |
| 164 | stdin = stdin_file |
| 165 | stdout = stdout_file |
| 166 | stderr = stderr_file |
| 167 | |
| 168 | def __init__(self): |
| 169 | self.pid = child_pid |
| 170 | self.returncode = None |
| 171 | |
| 172 | def _reap(self, status): |
| 173 | if os.WIFEXITED(status): |
| 174 | self.returncode = os.WEXITSTATUS(status) |
| 175 | elif os.WIFSIGNALED(status): |
| 176 | self.returncode = -os.WTERMSIG(status) |
no test coverage detected