|
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
|
@@ -2108,6 +2109,55 @@ def test_swap_fds(self):
|
2108 | 2109 | self.check_swap_fds(2, 0, 1)
|
2109 | 2110 | self.check_swap_fds(2, 1, 0)
|
2110 | 2111 |
|
| 2112 | + def _check_swap_std_fds_with_one_closed(self, from_fds, to_fds): |
| 2113 | + saved_fds = self._save_fds(range(3)) |
| 2114 | + try: |
| 2115 | + for from_fd in from_fds: |
| 2116 | + with tempfile.TemporaryFile() as f: |
| 2117 | + os.dup2(f.fileno(), from_fd) |
| 2118 | + |
| 2119 | + fd_to_close = (set(range(3)) - set(from_fds)).pop() |
| 2120 | + os.close(fd_to_close) |
| 2121 | + |
| 2122 | + arg_names = ['stdin', 'stdout', 'stderr'] |
| 2123 | + kwargs = {} |
| 2124 | + for from_fd, to_fd in zip(from_fds, to_fds): |
| 2125 | + kwargs[arg_names[to_fd]] = from_fd |
| 2126 | + |
| 2127 | + code = textwrap.dedent(r''' |
| 2128 | + import os, sys |
| 2129 | + skipped_fd = int(sys.argv[1]) |
| 2130 | + for fd in range(3): |
| 2131 | + if fd != skipped_fd: |
| 2132 | + os.write(fd, str(fd).encode('ascii')) |
| 2133 | + ''') |
| 2134 | + |
| 2135 | + skipped_fd = (set(range(3)) - set(to_fds)).pop() |
| 2136 | + |
| 2137 | + rc = subprocess.call([sys.executable, '-c', code, str(skipped_fd)], |
| 2138 | + **kwargs) |
| 2139 | + self.assertEqual(rc, 0) |
| 2140 | + |
| 2141 | + for from_fd, to_fd in zip(from_fds, to_fds): |
| 2142 | + os.lseek(from_fd, 0, os.SEEK_SET) |
| 2143 | + read_bytes = os.read(from_fd, 1024) |
| 2144 | + read_fds = list(map(int, read_bytes.decode('ascii'))) |
| 2145 | + msg = textwrap.dedent(f""" |
| 2146 | + When testing {from_fds} to {to_fds} redirection, |
| 2147 | + parent descriptor {from_fd} got redirected |
| 2148 | + to descriptor(s) {read_fds} instead of descriptor {to_fd}. |
| 2149 | + """) |
| 2150 | + self.assertEqual([to_fd], read_fds, msg) |
| 2151 | + finally: |
| 2152 | + self._restore_fds(saved_fds) |
| 2153 | + |
| 2154 | + # Check that subprocess can remap std fds correctly even |
| 2155 | + # if one of them is closed (#32844). |
| 2156 | + def test_swap_std_fds_with_one_closed(self): |
| 2157 | + for from_fds in itertools.combinations(range(3), 2): |
| 2158 | + for to_fds in itertools.permutations(range(3), 2): |
| 2159 | + self._check_swap_std_fds_with_one_closed(from_fds, to_fds) |
| 2160 | + |
2111 | 2161 | def test_surrogates_error_message(self):
|
2112 | 2162 | def prepare():
|
2113 | 2163 | raise ValueError("surrogate:\uDCff")
|
|
0 commit comments