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 ):
@@ -1145,13 +1147,54 @@ def test_leaking_fds_on_error(self):
1145
1147
# 1024 times (each call leaked two fds).
1146
1148
for i in range (1024 ):
1147
1149
with self .assertRaises (OSError ) as c :
1148
- subprocess .Popen ([ 'nonexisting_i_hope' ] ,
1150
+ subprocess .Popen (NONEXISTING_CMD ,
1149
1151
stdout = subprocess .PIPE ,
1150
1152
stderr = subprocess .PIPE )
1151
1153
# ignore errors that indicate the command was not found
1152
1154
if c .exception .errno not in (errno .ENOENT , errno .EACCES ):
1153
1155
raise c .exception
1154
1156
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
+
1155
1198
@unittest .skipIf (threading is None , "threading required" )
1156
1199
def test_double_close_on_error (self ):
1157
1200
# Issue #18851
@@ -1164,7 +1207,7 @@ def open_fds():
1164
1207
t .start ()
1165
1208
try :
1166
1209
with self .assertRaises (EnvironmentError ):
1167
- subprocess .Popen ([ 'nonexisting_i_hope' ] ,
1210
+ subprocess .Popen (NONEXISTING_CMD ,
1168
1211
stdin = subprocess .PIPE ,
1169
1212
stdout = subprocess .PIPE ,
1170
1213
stderr = subprocess .PIPE )
@@ -2430,7 +2473,7 @@ def test_leak_fast_process_del_killed(self):
2430
2473
# should trigger the wait() of p
2431
2474
time .sleep (0.2 )
2432
2475
with self .assertRaises (OSError ) as c :
2433
- with subprocess .Popen ([ 'nonexisting_i_hope' ] ,
2476
+ with subprocess .Popen (NONEXISTING_CMD ,
2434
2477
stdout = subprocess .PIPE ,
2435
2478
stderr = subprocess .PIPE ) as proc :
2436
2479
pass
@@ -2876,7 +2919,7 @@ def test_communicate_stdin(self):
2876
2919
2877
2920
def test_invalid_args (self ):
2878
2921
with self .assertRaises ((FileNotFoundError , PermissionError )) as c :
2879
- with subprocess .Popen ([ 'nonexisting_i_hope' ] ,
2922
+ with subprocess .Popen (NONEXISTING_CMD ,
2880
2923
stdout = subprocess .PIPE ,
2881
2924
stderr = subprocess .PIPE ) as proc :
2882
2925
pass
0 commit comments