Browse Source
bpo-39763: Add _bootsubprocess to build Python on AIX (GH-18872)
bpo-39763: Add _bootsubprocess to build Python on AIX (GH-18872)
Add _bootsubprocess module to bootstrap Python: subprocess implementation which only uses the os module. On AIX, distutils.util uses _aix_support which calls subprocess.check_output(), before the _posixsubprocess module is built. Implement check_output() with os.system() in _bootsubprocess.pull/18892/head
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 107 additions and 41 deletions
-
102Lib/_bootsubprocess.py
-
46setup.py
@ -0,0 +1,102 @@ |
|||
""" |
|||
Basic subprocess implementation for POSIX which only uses os functions. Only |
|||
implement features required by setup.py to build C extension modules when |
|||
subprocess is unavailable. setup.py is not used on Windows. |
|||
""" |
|||
import os |
|||
|
|||
|
|||
# distutils.spawn used by distutils.command.build_ext |
|||
# calls subprocess.Popen().wait() |
|||
class Popen: |
|||
def __init__(self, cmd, env=None): |
|||
self._cmd = cmd |
|||
self._env = env |
|||
self.returncode = None |
|||
|
|||
def wait(self): |
|||
pid = os.fork() |
|||
if pid == 0: |
|||
# Child process |
|||
try: |
|||
if self._env is not None: |
|||
os.execve(self._cmd[0], self._cmd, self._env) |
|||
else: |
|||
os.execv(self._cmd[0], self._cmd) |
|||
finally: |
|||
os._exit(1) |
|||
else: |
|||
# Parent process |
|||
pid, status = os.waitpid(pid, 0) |
|||
if os.WIFSIGNALED(status): |
|||
self.returncode = -os.WTERMSIG(status) |
|||
elif os.WIFEXITED(status): |
|||
self.returncode = os.WEXITSTATUS(status) |
|||
elif os.WIFSTOPPED(status): |
|||
self.returncode = -os.WSTOPSIG(status) |
|||
else: |
|||
raise Exception(f"unknown child process exit status: {status!r}") |
|||
|
|||
return self.returncode |
|||
|
|||
|
|||
def _check_cmd(cmd): |
|||
# Use regex [a-zA-Z0-9./-]+: reject empty string, space, etc. |
|||
safe_chars = [] |
|||
for first, last in (("a", "z"), ("A", "Z"), ("0", "9")): |
|||
for ch in range(ord(first), ord(last) + 1): |
|||
safe_chars.append(chr(ch)) |
|||
safe_chars.append("./-") |
|||
safe_chars = ''.join(safe_chars) |
|||
|
|||
if isinstance(cmd, (tuple, list)): |
|||
check_strs = cmd |
|||
elif isinstance(cmd, str): |
|||
check_strs = [cmd] |
|||
else: |
|||
return False |
|||
|
|||
for arg in check_strs: |
|||
if not isinstance(arg, str): |
|||
return False |
|||
if not arg: |
|||
# reject empty string |
|||
return False |
|||
for ch in arg: |
|||
if ch not in safe_chars: |
|||
return False |
|||
|
|||
return True |
|||
|
|||
|
|||
# _aix_support used by distutil.util calls subprocess.check_output() |
|||
def check_output(cmd, **kwargs): |
|||
if kwargs: |
|||
raise NotImplementedError(repr(kwargs)) |
|||
|
|||
if not _check_cmd(cmd): |
|||
raise ValueError(f"unsupported command: {cmd!r}") |
|||
|
|||
tmp_filename = "check_output.tmp" |
|||
if not isinstance(cmd, str): |
|||
cmd = " ".join(cmd) |
|||
cmd = f"{cmd} >{tmp_filename}" |
|||
|
|||
try: |
|||
# system() spawns a shell |
|||
status = os.system(cmd) |
|||
if status: |
|||
raise ValueError(f"Command {cmd!r} failed with status {status!r}") |
|||
|
|||
try: |
|||
with open(tmp_filename, "rb") as fp: |
|||
stdout = fp.read() |
|||
except FileNotFoundError: |
|||
stdout = b'' |
|||
finally: |
|||
try: |
|||
os.unlink(tmp_filename) |
|||
except OSError: |
|||
pass |
|||
|
|||
return stdout |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue