@@ -872,37 +872,6 @@ def __init__(self, args, bufsize=-1, executable=None,
872
872
'and universal_newlines are supplied but '
873
873
'different. Pass one or the other.' )
874
874
875
- # Input and output objects. The general principle is like
876
- # this:
877
- #
878
- # Parent Child
879
- # ------ -----
880
- # p2cwrite ---stdin---> p2cread
881
- # c2pread <--stdout--- c2pwrite
882
- # errread <--stderr--- errwrite
883
- #
884
- # On POSIX, the child objects are file descriptors. On
885
- # Windows, these are Windows file handles. The parent objects
886
- # are file descriptors on both platforms. The parent objects
887
- # are -1 when not using PIPEs. The child objects are -1
888
- # when not redirecting.
889
-
890
- (p2cread , p2cwrite ,
891
- c2pread , c2pwrite ,
892
- errread , errwrite ) = self ._get_handles (stdin , stdout , stderr )
893
-
894
- # We wrap OS handles *before* launching the child, otherwise a
895
- # quickly terminating child could make our fds unwrappable
896
- # (see #8458).
897
-
898
- if _mswindows :
899
- if p2cwrite != - 1 :
900
- p2cwrite = msvcrt .open_osfhandle (p2cwrite .Detach (), 0 )
901
- if c2pread != - 1 :
902
- c2pread = msvcrt .open_osfhandle (c2pread .Detach (), 0 )
903
- if errread != - 1 :
904
- errread = msvcrt .open_osfhandle (errread .Detach (), 0 )
905
-
906
875
self .text_mode = encoding or errors or text or universal_newlines
907
876
if self .text_mode and encoding is None :
908
877
self .encoding = encoding = _text_encoding ()
@@ -1003,6 +972,39 @@ def __init__(self, args, bufsize=-1, executable=None,
1003
972
if uid < 0 :
1004
973
raise ValueError (f"User ID cannot be negative, got { uid } " )
1005
974
975
+ # Input and output objects. The general principle is like
976
+ # this:
977
+ #
978
+ # Parent Child
979
+ # ------ -----
980
+ # p2cwrite ---stdin---> p2cread
981
+ # c2pread <--stdout--- c2pwrite
982
+ # errread <--stderr--- errwrite
983
+ #
984
+ # On POSIX, the child objects are file descriptors. On
985
+ # Windows, these are Windows file handles. The parent objects
986
+ # are file descriptors on both platforms. The parent objects
987
+ # are -1 when not using PIPEs. The child objects are -1
988
+ # when not redirecting.
989
+
990
+ (p2cread , p2cwrite ,
991
+ c2pread , c2pwrite ,
992
+ errread , errwrite ) = self ._get_handles (stdin , stdout , stderr )
993
+
994
+ # From here on, raising exceptions may cause file descriptor leakage
995
+
996
+ # We wrap OS handles *before* launching the child, otherwise a
997
+ # quickly terminating child could make our fds unwrappable
998
+ # (see #8458).
999
+
1000
+ if _mswindows :
1001
+ if p2cwrite != - 1 :
1002
+ p2cwrite = msvcrt .open_osfhandle (p2cwrite .Detach (), 0 )
1003
+ if c2pread != - 1 :
1004
+ c2pread = msvcrt .open_osfhandle (c2pread .Detach (), 0 )
1005
+ if errread != - 1 :
1006
+ errread = msvcrt .open_osfhandle (errread .Detach (), 0 )
1007
+
1006
1008
try :
1007
1009
if p2cwrite != - 1 :
1008
1010
self .stdin = io .open (p2cwrite , 'wb' , bufsize )
@@ -1306,6 +1308,26 @@ def _close_pipe_fds(self,
1306
1308
# Prevent a double close of these handles/fds from __init__ on error.
1307
1309
self ._closed_child_pipe_fds = True
1308
1310
1311
+ @contextlib .contextmanager
1312
+ def _on_error_fd_closer (self ):
1313
+ """Helper to ensure file descriptors opened in _get_handles are closed"""
1314
+ to_close = []
1315
+ try :
1316
+ yield to_close
1317
+ except :
1318
+ if hasattr (self , '_devnull' ):
1319
+ to_close .append (self ._devnull )
1320
+ del self ._devnull
1321
+ for fd in to_close :
1322
+ try :
1323
+ if _mswindows and isinstance (fd , Handle ):
1324
+ fd .Close ()
1325
+ else :
1326
+ os .close (fd )
1327
+ except OSError :
1328
+ pass
1329
+ raise
1330
+
1309
1331
if _mswindows :
1310
1332
#
1311
1333
# Windows methods
@@ -1321,61 +1343,68 @@ def _get_handles(self, stdin, stdout, stderr):
1321
1343
c2pread , c2pwrite = - 1 , - 1
1322
1344
errread , errwrite = - 1 , - 1
1323
1345
1324
- if stdin is None :
1325
- p2cread = _winapi .GetStdHandle (_winapi .STD_INPUT_HANDLE )
1326
- if p2cread is None :
1327
- p2cread , _ = _winapi .CreatePipe (None , 0 )
1328
- p2cread = Handle (p2cread )
1329
- _winapi .CloseHandle (_ )
1330
- elif stdin == PIPE :
1331
- p2cread , p2cwrite = _winapi .CreatePipe (None , 0 )
1332
- p2cread , p2cwrite = Handle (p2cread ), Handle (p2cwrite )
1333
- elif stdin == DEVNULL :
1334
- p2cread = msvcrt .get_osfhandle (self ._get_devnull ())
1335
- elif isinstance (stdin , int ):
1336
- p2cread = msvcrt .get_osfhandle (stdin )
1337
- else :
1338
- # Assuming file-like object
1339
- p2cread = msvcrt .get_osfhandle (stdin .fileno ())
1340
- p2cread = self ._make_inheritable (p2cread )
1341
-
1342
- if stdout is None :
1343
- c2pwrite = _winapi .GetStdHandle (_winapi .STD_OUTPUT_HANDLE )
1344
- if c2pwrite is None :
1345
- _ , c2pwrite = _winapi .CreatePipe (None , 0 )
1346
- c2pwrite = Handle (c2pwrite )
1347
- _winapi .CloseHandle (_ )
1348
- elif stdout == PIPE :
1349
- c2pread , c2pwrite = _winapi .CreatePipe (None , 0 )
1350
- c2pread , c2pwrite = Handle (c2pread ), Handle (c2pwrite )
1351
- elif stdout == DEVNULL :
1352
- c2pwrite = msvcrt .get_osfhandle (self ._get_devnull ())
1353
- elif isinstance (stdout , int ):
1354
- c2pwrite = msvcrt .get_osfhandle (stdout )
1355
- else :
1356
- # Assuming file-like object
1357
- c2pwrite = msvcrt .get_osfhandle (stdout .fileno ())
1358
- c2pwrite = self ._make_inheritable (c2pwrite )
1359
-
1360
- if stderr is None :
1361
- errwrite = _winapi .GetStdHandle (_winapi .STD_ERROR_HANDLE )
1362
- if errwrite is None :
1363
- _ , errwrite = _winapi .CreatePipe (None , 0 )
1364
- errwrite = Handle (errwrite )
1365
- _winapi .CloseHandle (_ )
1366
- elif stderr == PIPE :
1367
- errread , errwrite = _winapi .CreatePipe (None , 0 )
1368
- errread , errwrite = Handle (errread ), Handle (errwrite )
1369
- elif stderr == STDOUT :
1370
- errwrite = c2pwrite
1371
- elif stderr == DEVNULL :
1372
- errwrite = msvcrt .get_osfhandle (self ._get_devnull ())
1373
- elif isinstance (stderr , int ):
1374
- errwrite = msvcrt .get_osfhandle (stderr )
1375
- else :
1376
- # Assuming file-like object
1377
- errwrite = msvcrt .get_osfhandle (stderr .fileno ())
1378
- errwrite = self ._make_inheritable (errwrite )
1346
+ with self ._on_error_fd_closer () as err_close_fds :
1347
+ if stdin is None :
1348
+ p2cread = _winapi .GetStdHandle (_winapi .STD_INPUT_HANDLE )
1349
+ if p2cread is None :
1350
+ p2cread , _ = _winapi .CreatePipe (None , 0 )
1351
+ p2cread = Handle (p2cread )
1352
+ err_close_fds .append (p2cread )
1353
+ _winapi .CloseHandle (_ )
1354
+ elif stdin == PIPE :
1355
+ p2cread , p2cwrite = _winapi .CreatePipe (None , 0 )
1356
+ p2cread , p2cwrite = Handle (p2cread ), Handle (p2cwrite )
1357
+ err_close_fds .extend ((p2cread , p2cwrite ))
1358
+ elif stdin == DEVNULL :
1359
+ p2cread = msvcrt .get_osfhandle (self ._get_devnull ())
1360
+ elif isinstance (stdin , int ):
1361
+ p2cread = msvcrt .get_osfhandle (stdin )
1362
+ else :
1363
+ # Assuming file-like object
1364
+ p2cread = msvcrt .get_osfhandle (stdin .fileno ())
1365
+ p2cread = self ._make_inheritable (p2cread )
1366
+
1367
+ if stdout is None :
1368
+ c2pwrite = _winapi .GetStdHandle (_winapi .STD_OUTPUT_HANDLE )
1369
+ if c2pwrite is None :
1370
+ _ , c2pwrite = _winapi .CreatePipe (None , 0 )
1371
+ c2pwrite = Handle (c2pwrite )
1372
+ err_close_fds .append (c2pwrite )
1373
+ _winapi .CloseHandle (_ )
1374
+ elif stdout == PIPE :
1375
+ c2pread , c2pwrite = _winapi .CreatePipe (None , 0 )
1376
+ c2pread , c2pwrite = Handle (c2pread ), Handle (c2pwrite )
1377
+ err_close_fds .extend ((c2pread , c2pwrite ))
1378
+ elif stdout == DEVNULL :
1379
+ c2pwrite = msvcrt .get_osfhandle (self ._get_devnull ())
1380
+ elif isinstance (stdout , int ):
1381
+ c2pwrite = msvcrt .get_osfhandle (stdout )
1382
+ else :
1383
+ # Assuming file-like object
1384
+ c2pwrite = msvcrt .get_osfhandle (stdout .fileno ())
1385
+ c2pwrite = self ._make_inheritable (c2pwrite )
1386
+
1387
+ if stderr is None :
1388
+ errwrite = _winapi .GetStdHandle (_winapi .STD_ERROR_HANDLE )
1389
+ if errwrite is None :
1390
+ _ , errwrite = _winapi .CreatePipe (None , 0 )
1391
+ errwrite = Handle (errwrite )
1392
+ err_close_fds .append (errwrite )
1393
+ _winapi .CloseHandle (_ )
1394
+ elif stderr == PIPE :
1395
+ errread , errwrite = _winapi .CreatePipe (None , 0 )
1396
+ errread , errwrite = Handle (errread ), Handle (errwrite )
1397
+ err_close_fds .extend ((errread , errwrite ))
1398
+ elif stderr == STDOUT :
1399
+ errwrite = c2pwrite
1400
+ elif stderr == DEVNULL :
1401
+ errwrite = msvcrt .get_osfhandle (self ._get_devnull ())
1402
+ elif isinstance (stderr , int ):
1403
+ errwrite = msvcrt .get_osfhandle (stderr )
1404
+ else :
1405
+ # Assuming file-like object
1406
+ errwrite = msvcrt .get_osfhandle (stderr .fileno ())
1407
+ errwrite = self ._make_inheritable (errwrite )
1379
1408
1380
1409
return (p2cread , p2cwrite ,
1381
1410
c2pread , c2pwrite ,
@@ -1662,52 +1691,56 @@ def _get_handles(self, stdin, stdout, stderr):
1662
1691
c2pread , c2pwrite = - 1 , - 1
1663
1692
errread , errwrite = - 1 , - 1
1664
1693
1665
- if stdin is None :
1666
- pass
1667
- elif stdin == PIPE :
1668
- p2cread , p2cwrite = os .pipe ()
1669
- if self .pipesize > 0 and hasattr (fcntl , "F_SETPIPE_SZ" ):
1670
- fcntl .fcntl (p2cwrite , fcntl .F_SETPIPE_SZ , self .pipesize )
1671
- elif stdin == DEVNULL :
1672
- p2cread = self ._get_devnull ()
1673
- elif isinstance (stdin , int ):
1674
- p2cread = stdin
1675
- else :
1676
- # Assuming file-like object
1677
- p2cread = stdin .fileno ()
1694
+ with self ._on_error_fd_closer () as err_close_fds :
1695
+ if stdin is None :
1696
+ pass
1697
+ elif stdin == PIPE :
1698
+ p2cread , p2cwrite = os .pipe ()
1699
+ err_close_fds .extend ((p2cread , p2cwrite ))
1700
+ if self .pipesize > 0 and hasattr (fcntl , "F_SETPIPE_SZ" ):
1701
+ fcntl .fcntl (p2cwrite , fcntl .F_SETPIPE_SZ , self .pipesize )
1702
+ elif stdin == DEVNULL :
1703
+ p2cread = self ._get_devnull ()
1704
+ elif isinstance (stdin , int ):
1705
+ p2cread = stdin
1706
+ else :
1707
+ # Assuming file-like object
1708
+ p2cread = stdin .fileno ()
1678
1709
1679
- if stdout is None :
1680
- pass
1681
- elif stdout == PIPE :
1682
- c2pread , c2pwrite = os .pipe ()
1683
- if self .pipesize > 0 and hasattr (fcntl , "F_SETPIPE_SZ" ):
1684
- fcntl .fcntl (c2pwrite , fcntl .F_SETPIPE_SZ , self .pipesize )
1685
- elif stdout == DEVNULL :
1686
- c2pwrite = self ._get_devnull ()
1687
- elif isinstance (stdout , int ):
1688
- c2pwrite = stdout
1689
- else :
1690
- # Assuming file-like object
1691
- c2pwrite = stdout .fileno ()
1710
+ if stdout is None :
1711
+ pass
1712
+ elif stdout == PIPE :
1713
+ c2pread , c2pwrite = os .pipe ()
1714
+ err_close_fds .extend ((c2pread , c2pwrite ))
1715
+ if self .pipesize > 0 and hasattr (fcntl , "F_SETPIPE_SZ" ):
1716
+ fcntl .fcntl (c2pwrite , fcntl .F_SETPIPE_SZ , self .pipesize )
1717
+ elif stdout == DEVNULL :
1718
+ c2pwrite = self ._get_devnull ()
1719
+ elif isinstance (stdout , int ):
1720
+ c2pwrite = stdout
1721
+ else :
1722
+ # Assuming file-like object
1723
+ c2pwrite = stdout .fileno ()
1692
1724
1693
- if stderr is None :
1694
- pass
1695
- elif stderr == PIPE :
1696
- errread , errwrite = os .pipe ()
1697
- if self .pipesize > 0 and hasattr (fcntl , "F_SETPIPE_SZ" ):
1698
- fcntl .fcntl (errwrite , fcntl .F_SETPIPE_SZ , self .pipesize )
1699
- elif stderr == STDOUT :
1700
- if c2pwrite != - 1 :
1701
- errwrite = c2pwrite
1702
- else : # child's stdout is not set, use parent's stdout
1703
- errwrite = sys .__stdout__ .fileno ()
1704
- elif stderr == DEVNULL :
1705
- errwrite = self ._get_devnull ()
1706
- elif isinstance (stderr , int ):
1707
- errwrite = stderr
1708
- else :
1709
- # Assuming file-like object
1710
- errwrite = stderr .fileno ()
1725
+ if stderr is None :
1726
+ pass
1727
+ elif stderr == PIPE :
1728
+ errread , errwrite = os .pipe ()
1729
+ err_close_fds .extend ((errread , errwrite ))
1730
+ if self .pipesize > 0 and hasattr (fcntl , "F_SETPIPE_SZ" ):
1731
+ fcntl .fcntl (errwrite , fcntl .F_SETPIPE_SZ , self .pipesize )
1732
+ elif stderr == STDOUT :
1733
+ if c2pwrite != - 1 :
1734
+ errwrite = c2pwrite
1735
+ else : # child's stdout is not set, use parent's stdout
1736
+ errwrite = sys .__stdout__ .fileno ()
1737
+ elif stderr == DEVNULL :
1738
+ errwrite = self ._get_devnull ()
1739
+ elif isinstance (stderr , int ):
1740
+ errwrite = stderr
1741
+ else :
1742
+ # Assuming file-like object
1743
+ errwrite = stderr .fileno ()
1711
1744
1712
1745
return (p2cread , p2cwrite ,
1713
1746
c2pread , c2pwrite ,
0 commit comments