@@ -128,12 +128,13 @@ def stdout(self, value):
128128 import _winapi
129129 class STARTUPINFO :
130130 def __init__ (self , * , dwFlags = 0 , hStdInput = None , hStdOutput = None ,
131- hStdError = None , wShowWindow = 0 ):
131+ hStdError = None , wShowWindow = 0 , lpAttributeList = None ):
132132 self .dwFlags = dwFlags
133133 self .hStdInput = hStdInput
134134 self .hStdOutput = hStdOutput
135135 self .hStdError = hStdError
136136 self .wShowWindow = wShowWindow
137+ self .lpAttributeList = lpAttributeList or {"handle_list" : []}
137138else :
138139 import _posixsubprocess
139140 import select
@@ -577,9 +578,6 @@ def getoutput(cmd):
577578 return getstatusoutput (cmd )[1 ]
578579
579580
580- _PLATFORM_DEFAULT_CLOSE_FDS = object ()
581-
582-
583581class Popen (object ):
584582 """ Execute a child program in a new process.
585583
@@ -630,7 +628,7 @@ class Popen(object):
630628
631629 def __init__ (self , args , bufsize = - 1 , executable = None ,
632630 stdin = None , stdout = None , stderr = None ,
633- preexec_fn = None , close_fds = _PLATFORM_DEFAULT_CLOSE_FDS ,
631+ preexec_fn = None , close_fds = True ,
634632 shell = False , cwd = None , env = None , universal_newlines = None ,
635633 startupinfo = None , creationflags = 0 ,
636634 restore_signals = True , start_new_session = False ,
@@ -655,21 +653,8 @@ def __init__(self, args, bufsize=-1, executable=None,
655653 if preexec_fn is not None :
656654 raise ValueError ("preexec_fn is not supported on Windows "
657655 "platforms" )
658- any_stdio_set = (stdin is not None or stdout is not None or
659- stderr is not None )
660- if close_fds is _PLATFORM_DEFAULT_CLOSE_FDS :
661- if any_stdio_set :
662- close_fds = False
663- else :
664- close_fds = True
665- elif close_fds and any_stdio_set :
666- raise ValueError (
667- "close_fds is not supported on Windows platforms"
668- " if you redirect stdin/stdout/stderr" )
669656 else :
670657 # POSIX
671- if close_fds is _PLATFORM_DEFAULT_CLOSE_FDS :
672- close_fds = True
673658 if pass_fds and not close_fds :
674659 warnings .warn ("pass_fds overriding close_fds." , RuntimeWarning )
675660 close_fds = True
@@ -1019,6 +1004,19 @@ def _make_inheritable(self, handle):
10191004 return Handle (h )
10201005
10211006
1007+ def _filter_handle_list (self , handle_list ):
1008+ """Filter out console handles that can't be used
1009+ in lpAttributeList["handle_list"] and make sure the list
1010+ isn't empty. This also removes duplicate handles."""
1011+ # An handle with it's lowest two bits set might be a special console
1012+ # handle that if passed in lpAttributeList["handle_list"], will
1013+ # cause it to fail.
1014+ return list ({handle for handle in handle_list
1015+ if handle & 0x3 != 0x3
1016+ or _winapi .GetFileType (handle ) !=
1017+ _winapi .FILE_TYPE_CHAR })
1018+
1019+
10221020 def _execute_child (self , args , executable , preexec_fn , close_fds ,
10231021 pass_fds , cwd , env ,
10241022 startupinfo , creationflags , shell ,
@@ -1036,12 +1034,41 @@ def _execute_child(self, args, executable, preexec_fn, close_fds,
10361034 # Process startup details
10371035 if startupinfo is None :
10381036 startupinfo = STARTUPINFO ()
1039- if - 1 not in (p2cread , c2pwrite , errwrite ):
1037+
1038+ use_std_handles = - 1 not in (p2cread , c2pwrite , errwrite )
1039+ if use_std_handles :
10401040 startupinfo .dwFlags |= _winapi .STARTF_USESTDHANDLES
10411041 startupinfo .hStdInput = p2cread
10421042 startupinfo .hStdOutput = c2pwrite
10431043 startupinfo .hStdError = errwrite
10441044
1045+ attribute_list = startupinfo .lpAttributeList
1046+ have_handle_list = bool (attribute_list and
1047+ "handle_list" in attribute_list and
1048+ attribute_list ["handle_list" ])
1049+
1050+ # If we were given an handle_list or need to create one
1051+ if have_handle_list or (use_std_handles and close_fds ):
1052+ if attribute_list is None :
1053+ attribute_list = startupinfo .lpAttributeList = {}
1054+ handle_list = attribute_list ["handle_list" ] = \
1055+ list (attribute_list .get ("handle_list" , []))
1056+
1057+ if use_std_handles :
1058+ handle_list += [int (p2cread ), int (c2pwrite ), int (errwrite )]
1059+
1060+ handle_list [:] = self ._filter_handle_list (handle_list )
1061+
1062+ if handle_list :
1063+ if not close_fds :
1064+ warnings .warn ("startupinfo.lpAttributeList['handle_list'] "
1065+ "overriding close_fds" , RuntimeWarning )
1066+
1067+ # When using the handle_list we always request to inherit
1068+ # handles but the only handles that will be inherited are
1069+ # the ones in the handle_list
1070+ close_fds = False
1071+
10451072 if shell :
10461073 startupinfo .dwFlags |= _winapi .STARTF_USESHOWWINDOW
10471074 startupinfo .wShowWindow = _winapi .SW_HIDE
0 commit comments