def winpopen4(orig, cmd, env=None, newlines=False, bufsize=-1): """Same as util.popen4, but manually creates an input pipe with a larger than default buffer""" import msvcrt if sys.version_info[0] < 3: import _subprocess handles = _subprocess.CreatePipe(None, pipei_bufsize) rfd, wfd = [msvcrt.open_osfhandle(h, 0) for h in handles] else: import _winapi handles = _winapi.CreatePipe(None, pipei_bufsize) rfd, wfd = [msvcrt.open_osfhandle(h, 0) for h in handles] handles = [subprocess.Handle(h) for h in handles] handles[0].Detach() handles[1].Detach() p = subprocess.Popen( cmd, shell=True, bufsize=bufsize, close_fds=False, stdin=rfd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=newlines, env=env, ) p.stdin = util.fdopen(wfd, "wb", bufsize) return p.stdin, p.stdout, p.stderr, p
def _make_inheritable_handle(fd): """Return a duplicate of handle, which is inheritable""" h = _winapi.DuplicateHandle(_winapi.GetCurrentProcess(), msvcrt.get_osfhandle(fd), _winapi.GetCurrentProcess(), 0, 1, _winapi.DUPLICATE_SAME_ACCESS) return subprocess.Handle(h)
def make_inheritable_handle(fd): """Create inheritable duplicate of handle from file descriptor""" h = _winapi.DuplicateHandle(_winapi.GetCurrentProcess(), msvcrt.get_osfhandle(fd), _winapi.GetCurrentProcess(), 0, 1, _winapi.DUPLICATE_SAME_ACCESS) return subprocess.Handle(h)
def detect_fate_sharing_support_win32(): global win32_job, win32_AssignProcessToJobObject if win32_job is None and sys.platform == "win32": import ctypes try: from ctypes.wintypes import BOOL, DWORD, HANDLE, LPVOID, LPCWSTR kernel32 = ctypes.WinDLL("kernel32") kernel32.CreateJobObjectW.argtypes = (LPVOID, LPCWSTR) kernel32.CreateJobObjectW.restype = HANDLE sijo_argtypes = (HANDLE, ctypes.c_int, LPVOID, DWORD) kernel32.SetInformationJobObject.argtypes = sijo_argtypes kernel32.SetInformationJobObject.restype = BOOL kernel32.AssignProcessToJobObject.argtypes = (HANDLE, HANDLE) kernel32.AssignProcessToJobObject.restype = BOOL kernel32.IsDebuggerPresent.argtypes = () kernel32.IsDebuggerPresent.restype = BOOL except (AttributeError, TypeError, ImportError): kernel32 = None job = kernel32.CreateJobObjectW(None, None) if kernel32 else None job = subprocess.Handle(job) if job else job if job: from ctypes.wintypes import DWORD, LARGE_INTEGER, ULARGE_INTEGER class JOBOBJECT_BASIC_LIMIT_INFORMATION(ctypes.Structure): _fields_ = [ ("PerProcessUserTimeLimit", LARGE_INTEGER), ("PerJobUserTimeLimit", LARGE_INTEGER), ("LimitFlags", DWORD), ("MinimumWorkingSetSize", ctypes.c_size_t), ("MaximumWorkingSetSize", ctypes.c_size_t), ("ActiveProcessLimit", DWORD), ("Affinity", ctypes.c_size_t), ("PriorityClass", DWORD), ("SchedulingClass", DWORD), ] class IO_COUNTERS(ctypes.Structure): _fields_ = [ ("ReadOperationCount", ULARGE_INTEGER), ("WriteOperationCount", ULARGE_INTEGER), ("OtherOperationCount", ULARGE_INTEGER), ("ReadTransferCount", ULARGE_INTEGER), ("WriteTransferCount", ULARGE_INTEGER), ("OtherTransferCount", ULARGE_INTEGER), ] class JOBOBJECT_EXTENDED_LIMIT_INFORMATION(ctypes.Structure): _fields_ = [ ("BasicLimitInformation", JOBOBJECT_BASIC_LIMIT_INFORMATION), ("IoInfo", IO_COUNTERS), ("ProcessMemoryLimit", ctypes.c_size_t), ("JobMemoryLimit", ctypes.c_size_t), ("PeakProcessMemoryUsed", ctypes.c_size_t), ("PeakJobMemoryUsed", ctypes.c_size_t), ] debug = kernel32.IsDebuggerPresent() # Defined in <WinNT.h>; also available here: # https://docs.microsoft.com/en-us/windows/win32/api/jobapi2/nf-jobapi2-setinformationjobobject JobObjectExtendedLimitInformation = 9 JOB_OBJECT_LIMIT_BREAKAWAY_OK = 0x00000800 JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION = 0x00000400 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x00002000 buf = JOBOBJECT_EXTENDED_LIMIT_INFORMATION() buf.BasicLimitInformation.LimitFlags = ( (0 if debug else JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE) | JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION | JOB_OBJECT_LIMIT_BREAKAWAY_OK) infoclass = JobObjectExtendedLimitInformation if not kernel32.SetInformationJobObject( job, infoclass, ctypes.byref(buf), ctypes.sizeof(buf)): job = None win32_AssignProcessToJobObject = (kernel32.AssignProcessToJobObject if kernel32 is not None else False) win32_job = job if job else False return bool(win32_job)
def _execute_child(self, *args_tuple): if sys.hexversion < 0x02070600: # prior to 2.7.6 (args, executable, preexec_fn, close_fds, cwd, env, universal_newlines, input_startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite) = args_tuple to_close = None elif sys.hexversion > 0x03040000: # 3.4.0 and later (args, executable, preexec_fn, close_fds, pass_fds, cwd, env, input_startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, restore_signals, start_new_session) = args_tuple to_close = set() to_close.add(p2cread) if p2cwrite is not None: to_close.add(p2cwrite) to_close.add(c2pwrite) if c2pread is not None: to_close.add(c2pread) to_close.add(errwrite) if errread is not None: to_close.add(errread) else: # 2.7.6 and later (args, executable, preexec_fn, close_fds, cwd, env, universal_newlines, input_startupinfo, creationflags, shell, to_close, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite) = args_tuple if shell: return super(ProcThreadAttributeHandleListPopen, self)._execute_child(*args_tuple) close_fds = self.__really_close_fds if not isinstance(args, basestring): args = subprocess.list2cmdline(args) if _winprocess_ctypes.CAN_USE_EXTENDED_STARTUPINFO: attribute_list_data = () startupinfoex = _winprocess_ctypes.STARTUPINFOEX() startupinfo = startupinfoex.StartupInfo startupinfo.cb = _winprocess_ctypes.sizeof( _winprocess_ctypes.STARTUPINFOEX) startupinfo_argument = startupinfoex else: startupinfo = _winprocess_ctypes.STARTUPINFO() startupinfo_argument = startupinfo if input_startupinfo is not None: startupinfo.dwFlags = input_startupinfo.dwFlags startupinfo.hStdInput = input_startupinfo.hStdInput startupinfo.hStdOutput = input_startupinfo.hStdOutput startupinfo.hStdError = input_startupinfo.hStdError startupinfo.wShowWindow = input_startupinfo.wShowWindow inherit_handles = 0 if close_fds else 1 if None not in (p2cread, c2pwrite, errwrite): if close_fds: HandleArray = _winprocess_ctypes.HANDLE * 3 # PY2COMPAT new_int doesn't seem to know how to convert file handles to int. # So we use the "native_int" instead. handles_to_inherit = HandleArray(native_int(p2cread), native_int(c2pwrite), native_int(errwrite)) attribute_list_data = ( (_winprocess_ctypes.PROC_THREAD_ATTRIBUTE_HANDLE_LIST, handles_to_inherit), ) inherit_handles = 1 startupinfo.dwFlags |= _winprocess_ctypes.STARTF_USESTDHANDLES # PY2COMPAT new_int doesn't seem to know how to convert file handles to int. # So we use the "native_int" instead. startupinfo.hStdInput = native_int(p2cread) startupinfo.hStdOutput = native_int(c2pwrite) startupinfo.hStdError = native_int(errwrite) if _winprocess_ctypes.CAN_USE_EXTENDED_STARTUPINFO: attribute_list = _winprocess_ctypes.ProcThreadAttributeList( attribute_list_data) startupinfoex.lpAttributeList = attribute_list.value creationflags |= _winprocess_ctypes.EXTENDED_STARTUPINFO_PRESENT def _close_in_parent(fd): fd.Close() if to_close: to_close.remove(fd) # create the process try: hp, ht, pid, tid = _winprocess_ctypes.ExtendedCreateProcess( executable, args, None, None, # No special security inherit_handles, #Inherit handles creationflags, _winprocess_ctypes.EnvironmentBlock(env) if env else None, cwd, startupinfo_argument) finally: # Child is launched. Close the parent's copy of those pipe # handles that only the child should have open. You need # to make sure that no handles to the write end of the # output pipe are maintained in this process or else the # pipe will not close when the child process exits and the # ReadFile will hang. if p2cread is not None: _close_in_parent(p2cread) if c2pwrite is not None: _close_in_parent(c2pwrite) if errwrite is not None: _close_in_parent(errwrite) self._child_created = True self._handle = subprocess.Handle(hp) if hasattr( subprocess, 'Handle') else hp self._thread = ht self.pid = pid self.tid = tid ht.Close()