blob: 014782f616c823bae543909e3b17dad3dccc8cd0 [file] [log] [blame]
Victor Stinneraddaaaa2020-03-09 23:45:59 +01001"""
2Basic subprocess implementation for POSIX which only uses os functions. Only
3implement features required by setup.py to build C extension modules when
4subprocess is unavailable. setup.py is not used on Windows.
5"""
6import os
7
8
9# distutils.spawn used by distutils.command.build_ext
10# calls subprocess.Popen().wait()
11class Popen:
12 def __init__(self, cmd, env=None):
13 self._cmd = cmd
14 self._env = env
15 self.returncode = None
16
17 def wait(self):
18 pid = os.fork()
19 if pid == 0:
20 # Child process
21 try:
22 if self._env is not None:
23 os.execve(self._cmd[0], self._cmd, self._env)
24 else:
25 os.execv(self._cmd[0], self._cmd)
26 finally:
27 os._exit(1)
28 else:
29 # Parent process
Victor Stinner40bfdb12020-03-31 23:45:13 +020030 _, status = os.waitpid(pid, 0)
Victor Stinner65a796e2020-04-01 18:49:29 +020031 self.returncode = os.waitstatus_to_exitcode(status)
Victor Stinneraddaaaa2020-03-09 23:45:59 +010032
33 return self.returncode
34
35
36def _check_cmd(cmd):
37 # Use regex [a-zA-Z0-9./-]+: reject empty string, space, etc.
38 safe_chars = []
39 for first, last in (("a", "z"), ("A", "Z"), ("0", "9")):
40 for ch in range(ord(first), ord(last) + 1):
41 safe_chars.append(chr(ch))
42 safe_chars.append("./-")
43 safe_chars = ''.join(safe_chars)
44
45 if isinstance(cmd, (tuple, list)):
46 check_strs = cmd
47 elif isinstance(cmd, str):
48 check_strs = [cmd]
49 else:
50 return False
51
52 for arg in check_strs:
53 if not isinstance(arg, str):
54 return False
55 if not arg:
56 # reject empty string
57 return False
58 for ch in arg:
59 if ch not in safe_chars:
60 return False
61
62 return True
63
64
65# _aix_support used by distutil.util calls subprocess.check_output()
66def check_output(cmd, **kwargs):
67 if kwargs:
68 raise NotImplementedError(repr(kwargs))
69
70 if not _check_cmd(cmd):
71 raise ValueError(f"unsupported command: {cmd!r}")
72
73 tmp_filename = "check_output.tmp"
74 if not isinstance(cmd, str):
75 cmd = " ".join(cmd)
76 cmd = f"{cmd} >{tmp_filename}"
77
78 try:
79 # system() spawns a shell
80 status = os.system(cmd)
Victor Stinner65a796e2020-04-01 18:49:29 +020081 exitcode = os.waitstatus_to_exitcode(status)
Victor Stinner40bfdb12020-03-31 23:45:13 +020082 if exitcode:
83 raise ValueError(f"Command {cmd!r} returned non-zero "
84 f"exit status {exitcode!r}")
Victor Stinneraddaaaa2020-03-09 23:45:59 +010085
86 try:
87 with open(tmp_filename, "rb") as fp:
88 stdout = fp.read()
89 except FileNotFoundError:
90 stdout = b''
91 finally:
92 try:
93 os.unlink(tmp_filename)
94 except OSError:
95 pass
96
97 return stdout