|
6 | 6 | import platform
|
7 | 7 | import signal
|
8 | 8 | import io
|
| 9 | +import itertools |
9 | 10 | import os
|
10 | 11 | import errno
|
11 | 12 | import tempfile
|
@@ -2134,6 +2135,55 @@ def test_swap_fds(self):
|
2134 | 2135 | self.check_swap_fds(2, 0, 1)
|
2135 | 2136 | self.check_swap_fds(2, 1, 0)
|
2136 | 2137 |
|
| 2138 | + def _check_swap_std_fds_with_one_closed(self, from_fds, to_fds): |
| 2139 | + saved_fds = self._save_fds(range(3)) |
| 2140 | + try: |
| 2141 | + for from_fd in from_fds: |
| 2142 | + with tempfile.TemporaryFile() as f: |
| 2143 | + os.dup2(f.fileno(), from_fd) |
| 2144 | + |
| 2145 | + fd_to_close = (set(range(3)) - set(from_fds)).pop() |
| 2146 | + os.close(fd_to_close) |
| 2147 | + |
| 2148 | + arg_names = ['stdin', 'stdout', 'stderr'] |
| 2149 | + kwargs = {} |
| 2150 | + for from_fd, to_fd in zip(from_fds, to_fds): |
| 2151 | + kwargs[arg_names[to_fd]] = from_fd |
| 2152 | + |
| 2153 | + code = textwrap.dedent(r''' |
| 2154 | + import os, sys |
| 2155 | + skipped_fd = int(sys.argv[1]) |
| 2156 | + for fd in range(3): |
| 2157 | + if fd != skipped_fd: |
| 2158 | + os.write(fd, str(fd).encode('ascii')) |
| 2159 | + ''') |
| 2160 | + |
| 2161 | + skipped_fd = (set(range(3)) - set(to_fds)).pop() |
| 2162 | + |
| 2163 | + rc = subprocess.call([sys.executable, '-c', code, str(skipped_fd)], |
| 2164 | + **kwargs) |
| 2165 | + self.assertEqual(rc, 0) |
| 2166 | + |
| 2167 | + for from_fd, to_fd in zip(from_fds, to_fds): |
| 2168 | + os.lseek(from_fd, 0, os.SEEK_SET) |
| 2169 | + read_bytes = os.read(from_fd, 1024) |
| 2170 | + read_fds = list(map(int, read_bytes.decode('ascii'))) |
| 2171 | + msg = textwrap.dedent(f""" |
| 2172 | + When testing {from_fds} to {to_fds} redirection, |
| 2173 | + parent descriptor {from_fd} got redirected |
| 2174 | + to descriptor(s) {read_fds} instead of descriptor {to_fd}. |
| 2175 | + """) |
| 2176 | + self.assertEqual([to_fd], read_fds, msg) |
| 2177 | + finally: |
| 2178 | + self._restore_fds(saved_fds) |
| 2179 | + |
| 2180 | + # Check that subprocess can remap std fds correctly even |
| 2181 | + # if one of them is closed (#32844). |
| 2182 | + def test_swap_std_fds_with_one_closed(self): |
| 2183 | + for from_fds in itertools.combinations(range(3), 2): |
| 2184 | + for to_fds in itertools.permutations(range(3), 2): |
| 2185 | + self._check_swap_std_fds_with_one_closed(from_fds, to_fds) |
| 2186 | + |
2137 | 2187 | def test_surrogates_error_message(self):
|
2138 | 2188 | def prepare():
|
2139 | 2189 | raise ValueError("surrogate:\uDCff")
|
|
0 commit comments