|
49 | 49 | else: |
50 | 50 | SETBINARY = '' |
51 | 51 |
|
| 52 | +NONEXISTING_CMD = ('nonexisting_i_hope',) |
| 53 | + |
52 | 54 |
|
53 | 55 | class BaseTestCase(unittest.TestCase): |
54 | 56 | def setUp(self): |
@@ -1120,10 +1122,11 @@ def _test_bufsize_equal_one(self, line, expected, universal_newlines): |
1120 | 1122 | p.stdin.write(line) # expect that it flushes the line in text mode |
1121 | 1123 | os.close(p.stdin.fileno()) # close it without flushing the buffer |
1122 | 1124 | read_line = p.stdout.readline() |
1123 | | - try: |
1124 | | - p.stdin.close() |
1125 | | - except OSError: |
1126 | | - pass |
| 1125 | + with support.SuppressCrashReport(): |
| 1126 | + try: |
| 1127 | + p.stdin.close() |
| 1128 | + except OSError: |
| 1129 | + pass |
1127 | 1130 | p.stdin = None |
1128 | 1131 | self.assertEqual(p.returncode, 0) |
1129 | 1132 | self.assertEqual(read_line, expected) |
@@ -1148,13 +1151,54 @@ def test_leaking_fds_on_error(self): |
1148 | 1151 | # 1024 times (each call leaked two fds). |
1149 | 1152 | for i in range(1024): |
1150 | 1153 | with self.assertRaises(OSError) as c: |
1151 | | - subprocess.Popen(['nonexisting_i_hope'], |
| 1154 | + subprocess.Popen(NONEXISTING_CMD, |
1152 | 1155 | stdout=subprocess.PIPE, |
1153 | 1156 | stderr=subprocess.PIPE) |
1154 | 1157 | # ignore errors that indicate the command was not found |
1155 | 1158 | if c.exception.errno not in (errno.ENOENT, errno.EACCES): |
1156 | 1159 | raise c.exception |
1157 | 1160 |
|
| 1161 | + def test_nonexisting_with_pipes(self): |
| 1162 | + # bpo-30121: Popen with pipes must close properly pipes on error. |
| 1163 | + # Previously, os.close() was called with a Windows handle which is not |
| 1164 | + # a valid file descriptor. |
| 1165 | + # |
| 1166 | + # Run the test in a subprocess to control how the CRT reports errors |
| 1167 | + # and to get stderr content. |
| 1168 | + try: |
| 1169 | + import msvcrt |
| 1170 | + msvcrt.CrtSetReportMode |
| 1171 | + except (AttributeError, ImportError): |
| 1172 | + self.skipTest("need msvcrt.CrtSetReportMode") |
| 1173 | + |
| 1174 | + code = textwrap.dedent(f""" |
| 1175 | + import msvcrt |
| 1176 | + import subprocess |
| 1177 | +
|
| 1178 | + cmd = {NONEXISTING_CMD!r} |
| 1179 | +
|
| 1180 | + for report_type in [msvcrt.CRT_WARN, |
| 1181 | + msvcrt.CRT_ERROR, |
| 1182 | + msvcrt.CRT_ASSERT]: |
| 1183 | + msvcrt.CrtSetReportMode(report_type, msvcrt.CRTDBG_MODE_FILE) |
| 1184 | + msvcrt.CrtSetReportFile(report_type, msvcrt.CRTDBG_FILE_STDERR) |
| 1185 | +
|
| 1186 | + try: |
| 1187 | + subprocess.Popen([cmd], |
| 1188 | + stdout=subprocess.PIPE, |
| 1189 | + stderr=subprocess.PIPE) |
| 1190 | + except OSError: |
| 1191 | + pass |
| 1192 | + """) |
| 1193 | + cmd = [sys.executable, "-c", code] |
| 1194 | + proc = subprocess.Popen(cmd, |
| 1195 | + stderr=subprocess.PIPE, |
| 1196 | + universal_newlines=True) |
| 1197 | + with proc: |
| 1198 | + stderr = proc.communicate()[1] |
| 1199 | + self.assertEqual(stderr, "") |
| 1200 | + self.assertEqual(proc.returncode, 0) |
| 1201 | + |
1158 | 1202 | @unittest.skipIf(threading is None, "threading required") |
1159 | 1203 | def test_double_close_on_error(self): |
1160 | 1204 | # Issue #18851 |
@@ -1167,7 +1211,7 @@ def open_fds(): |
1167 | 1211 | t.start() |
1168 | 1212 | try: |
1169 | 1213 | with self.assertRaises(EnvironmentError): |
1170 | | - subprocess.Popen(['nonexisting_i_hope'], |
| 1214 | + subprocess.Popen(NONEXISTING_CMD, |
1171 | 1215 | stdin=subprocess.PIPE, |
1172 | 1216 | stdout=subprocess.PIPE, |
1173 | 1217 | stderr=subprocess.PIPE) |
@@ -2433,7 +2477,7 @@ def test_leak_fast_process_del_killed(self): |
2433 | 2477 | # should trigger the wait() of p |
2434 | 2478 | time.sleep(0.2) |
2435 | 2479 | with self.assertRaises(OSError) as c: |
2436 | | - with subprocess.Popen(['nonexisting_i_hope'], |
| 2480 | + with subprocess.Popen(NONEXISTING_CMD, |
2437 | 2481 | stdout=subprocess.PIPE, |
2438 | 2482 | stderr=subprocess.PIPE) as proc: |
2439 | 2483 | pass |
@@ -2863,7 +2907,7 @@ def test_communicate_stdin(self): |
2863 | 2907 |
|
2864 | 2908 | def test_invalid_args(self): |
2865 | 2909 | with self.assertRaises((FileNotFoundError, PermissionError)) as c: |
2866 | | - with subprocess.Popen(['nonexisting_i_hope'], |
| 2910 | + with subprocess.Popen(NONEXISTING_CMD, |
2867 | 2911 | stdout=subprocess.PIPE, |
2868 | 2912 | stderr=subprocess.PIPE) as proc: |
2869 | 2913 | pass |
|