exit code used by popen terminate we define a popen class similar to the one from subprocess but whose constructor takes a process object as its argument start a subprocess to run the code of a process object read end of pipe will be duplicated by the child process see spawnmain in spawn py bpo33929 previously the read end of pipe was stolen by the child process but it leaked a handle if the child process had been terminated before it could steal the handle from the parent process bpo35797 when running in a venv we bypass the redirect executor and launch our base python start process set attributes of self send information to child erroraccessdenied winerror 5 is received when the process already died exit code used by popen terminate we define a popen class similar to the one from subprocess but whose constructor takes a process object as its argument start a subprocess to run the code of a process object read end of pipe will be duplicated by the child process see spawn_main in spawn py bpo 33929 previously the read end of pipe was stolen by the child process but it leaked a handle if the child process had been terminated before it could steal the handle from the parent process bpo 35797 when running in a venv we bypass the redirect executor and launch our base python start process set attributes of self send information to child error_access_denied winerror 5 is received when the process already died
import os
import msvcrt
import signal
import sys
import _winapi

from .context import reduction, get_spawning_popen, set_spawning_popen
from . import spawn
from . import util

__all__ = ['Popen']






TERMINATE = 0x10000
WINEXE = (sys.platform == 'win32' and getattr(sys, 'frozen', False))
WINSERVICE = sys.executable.lower().endswith("pythonservice.exe")


def _path_eq(p1, p2):
    return p1 == p2 or os.path.normcase(p1) == os.path.normcase(p2)

WINENV = not _path_eq(sys.executable, sys._base_executable)


def _close_handles(*handles):
    for handle in handles:
        _winapi.CloseHandle(handle)







class Popen(object):
    
    method = 'spawn'

    def __init__(self, process_obj):
        prep_data = spawn.get_preparation_data(process_obj._name)

        
        
        
        
        
        
        rhandle, whandle = _winapi.CreatePipe(None, 0)
        wfd = msvcrt.open_osfhandle(whandle, 0)
        cmd = spawn.get_command_line(parent_pid=os.getpid(),
                                     pipe_handle=rhandle)

        python_exe = spawn.get_executable()

        
        
        if WINENV and _path_eq(python_exe, sys.executable):
            cmd[0] = python_exe = sys._base_executable
            env = os.environ.copy()
            env["__PYVENV_LAUNCHER__"] = sys.executable
        else:
            env = None

        cmd = ' '.join('"%s"' % x for x in cmd)

        with open(wfd, 'wb', closefd=True) as to_child:
            
            try:
                hp, ht, pid, tid = _winapi.CreateProcess(
                    python_exe, cmd,
                    None, None, False, 0, env, None, None)
                _winapi.CloseHandle(ht)
            except:
                _winapi.CloseHandle(rhandle)
                raise

            
            self.pid = pid
            self.returncode = None
            self._handle = hp
            self.sentinel = int(hp)
            self.finalizer = util.Finalize(self, _close_handles,
                                           (self.sentinel, int(rhandle)))

            
            set_spawning_popen(self)
            try:
                reduction.dump(prep_data, to_child)
                reduction.dump(process_obj, to_child)
            finally:
                set_spawning_popen(None)

    def duplicate_for_child(self, handle):
        assert self is get_spawning_popen()
        return reduction.duplicate(handle, self.sentinel)

    def wait(self, timeout=None):
        if self.returncode is None:
            if timeout is None:
                msecs = _winapi.INFINITE
            else:
                msecs = max(0, int(timeout * 1000 + 0.5))

            res = _winapi.WaitForSingleObject(int(self._handle), msecs)
            if res == _winapi.WAIT_OBJECT_0:
                code = _winapi.GetExitCodeProcess(self._handle)
                if code == TERMINATE:
                    code = -signal.SIGTERM
                self.returncode = code

        return self.returncode

    def poll(self):
        return self.wait(timeout=0)

    def terminate(self):
        if self.returncode is None:
            try:
                _winapi.TerminateProcess(int(self._handle), TERMINATE)
            except PermissionError:
                
                
                code = _winapi.GetExitCodeProcess(int(self._handle))
                if code == _winapi.STILL_ACTIVE:
                    raise
                self.returncode = code
            else:
                self.returncode = -signal.SIGTERM

    kill = terminate

    def close(self):
        self.finalizer()