Skip to content

Commit 9a83f65

Browse files
authored
Add test_subprocess.test_nonexisting_with_pipes() (#3133)
bpo-30121: Test the Popen failure when Popen was created with pipes. Create also NONEXISTING_CMD variable in test_subprocess.py.
1 parent 4cab2cd commit 9a83f65

File tree

1 file changed

+47
-4
lines changed

1 file changed

+47
-4
lines changed

Lib/test/test_subprocess.py

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949
else:
5050
SETBINARY = ''
5151

52+
NONEXISTING_CMD = ('nonexisting_i_hope',)
53+
5254

5355
class BaseTestCase(unittest.TestCase):
5456
def setUp(self):
@@ -1145,13 +1147,54 @@ def test_leaking_fds_on_error(self):
11451147
# 1024 times (each call leaked two fds).
11461148
for i in range(1024):
11471149
with self.assertRaises(OSError) as c:
1148-
subprocess.Popen(['nonexisting_i_hope'],
1150+
subprocess.Popen(NONEXISTING_CMD,
11491151
stdout=subprocess.PIPE,
11501152
stderr=subprocess.PIPE)
11511153
# ignore errors that indicate the command was not found
11521154
if c.exception.errno not in (errno.ENOENT, errno.EACCES):
11531155
raise c.exception
11541156

1157+
def test_nonexisting_with_pipes(self):
1158+
# bpo-30121: Popen with pipes must close properly pipes on error.
1159+
# Previously, os.close() was called with a Windows handle which is not
1160+
# a valid file descriptor.
1161+
#
1162+
# Run the test in a subprocess to control how the CRT reports errors
1163+
# and to get stderr content.
1164+
try:
1165+
import msvcrt
1166+
msvcrt.CrtSetReportMode
1167+
except (AttributeError, ImportError):
1168+
self.skipTest("need msvcrt.CrtSetReportMode")
1169+
1170+
code = textwrap.dedent(f"""
1171+
import msvcrt
1172+
import subprocess
1173+
1174+
cmd = {NONEXISTING_CMD!r}
1175+
1176+
for report_type in [msvcrt.CRT_WARN,
1177+
msvcrt.CRT_ERROR,
1178+
msvcrt.CRT_ASSERT]:
1179+
msvcrt.CrtSetReportMode(report_type, msvcrt.CRTDBG_MODE_FILE)
1180+
msvcrt.CrtSetReportFile(report_type, msvcrt.CRTDBG_FILE_STDERR)
1181+
1182+
try:
1183+
subprocess.Popen([cmd],
1184+
stdout=subprocess.PIPE,
1185+
stderr=subprocess.PIPE)
1186+
except OSError:
1187+
pass
1188+
""")
1189+
cmd = [sys.executable, "-c", code]
1190+
proc = subprocess.Popen(cmd,
1191+
stderr=subprocess.PIPE,
1192+
universal_newlines=True)
1193+
with proc:
1194+
stderr = proc.communicate()[1]
1195+
self.assertEqual(stderr, "")
1196+
self.assertEqual(proc.returncode, 0)
1197+
11551198
@unittest.skipIf(threading is None, "threading required")
11561199
def test_double_close_on_error(self):
11571200
# Issue #18851
@@ -1164,7 +1207,7 @@ def open_fds():
11641207
t.start()
11651208
try:
11661209
with self.assertRaises(EnvironmentError):
1167-
subprocess.Popen(['nonexisting_i_hope'],
1210+
subprocess.Popen(NONEXISTING_CMD,
11681211
stdin=subprocess.PIPE,
11691212
stdout=subprocess.PIPE,
11701213
stderr=subprocess.PIPE)
@@ -2430,7 +2473,7 @@ def test_leak_fast_process_del_killed(self):
24302473
# should trigger the wait() of p
24312474
time.sleep(0.2)
24322475
with self.assertRaises(OSError) as c:
2433-
with subprocess.Popen(['nonexisting_i_hope'],
2476+
with subprocess.Popen(NONEXISTING_CMD,
24342477
stdout=subprocess.PIPE,
24352478
stderr=subprocess.PIPE) as proc:
24362479
pass
@@ -2876,7 +2919,7 @@ def test_communicate_stdin(self):
28762919

28772920
def test_invalid_args(self):
28782921
with self.assertRaises((FileNotFoundError, PermissionError)) as c:
2879-
with subprocess.Popen(['nonexisting_i_hope'],
2922+
with subprocess.Popen(NONEXISTING_CMD,
28802923
stdout=subprocess.PIPE,
28812924
stderr=subprocess.PIPE) as proc:
28822925
pass

0 commit comments

Comments
 (0)