1 import os 2 import msvcrt 3 import signal 4 import sys 5 import _winapi 6 7 from .context import reduction, get_spawning_popen, set_spawning_popen 8 from . import spawn 9 from . import util 10 11 __all__ = ['Popen'] 12 13 # 14 # 15 # 16 17 TERMINATE = 0x10000 18 WINEXE = (sys.platform == 'win32' and getattr(sys, 'frozen', False)) 19 WINSERVICE = sys.executable.lower().endswith("pythonservice.exe") 20 21 22 def _path_eq(p1, p2): 23 return p1 == p2 or os.path.normcase(p1) == os.path.normcase(p2) 24 25 WINENV = (hasattr(sys, '_base_executable') and 26 not _path_eq(sys.executable, sys._base_executable)) 27 28 29 def _close_handles(*handles): 30 for handle in handles: 31 _winapi.CloseHandle(handle) 32 33 34 # 35 # We define a Popen class similar to the one from subprocess, but 36 # whose constructor takes a process object as its argument. 37 # 38 39 class Popen(object): 40 ''' 41 Start a subprocess to run the code of a process object 42 ''' 43 method = 'spawn' 44 45 def __init__(self, process_obj): 46 prep_data = spawn.get_preparation_data(process_obj._name) 47 48 # read end of pipe will be "stolen" by the child process 49 # -- see spawn_main() in spawn.py. 50 rhandle, whandle = _winapi.CreatePipe(None, 0) 51 wfd = msvcrt.open_osfhandle(whandle, 0) 52 cmd = spawn.get_command_line(parent_pid=os.getpid(), 53 pipe_handle=rhandle) 54 cmd = ' '.join('"%s"' % x for x in cmd) 55 56 python_exe = spawn.get_executable() 57 58 # bpo-35797: When running in a venv, we bypass the redirect 59 # executor and launch our base Python. 60 if WINENV and _path_eq(python_exe, sys.executable): 61 python_exe = sys._base_executable 62 env = os.environ.copy() 63 env["__PYVENV_LAUNCHER__"] = sys.executable 64 else: 65 env = None 66 67 with open(wfd, 'wb', closefd=True) as to_child: 68 # start process 69 try: 70 hp, ht, pid, tid = _winapi.CreateProcess( 71 python_exe, cmd, 72 env, None, False, 0, None, None, None) 73 _winapi.CloseHandle(ht) 74 except: 75 _winapi.CloseHandle(rhandle) 76 raise 77 78 # set attributes of self 79 self.pid = pid 80 self.returncode = None 81 self._handle = hp 82 self.sentinel = int(hp) 83 self.finalizer = util.Finalize(self, _winapi.CloseHandle, (self.sentinel,)) 84 85 # send information to child 86 set_spawning_popen(self) 87 try: 88 reduction.dump(prep_data, to_child) 89 reduction.dump(process_obj, to_child) 90 finally: 91 set_spawning_popen(None) 92 93 def duplicate_for_child(self, handle): 94 assert self is get_spawning_popen() 95 return reduction.duplicate(handle, self.sentinel) 96 97 def wait(self, timeout=None): 98 if self.returncode is None: 99 if timeout is None: 100 msecs = _winapi.INFINITE 101 else: 102 msecs = max(0, int(timeout * 1000 + 0.5)) 103 104 res = _winapi.WaitForSingleObject(int(self._handle), msecs) 105 if res == _winapi.WAIT_OBJECT_0: 106 code = _winapi.GetExitCodeProcess(self._handle) 107 if code == TERMINATE: 108 code = -signal.SIGTERM 109 self.returncode = code 110 111 return self.returncode 112 113 def poll(self): 114 return self.wait(timeout=0) 115 116 def terminate(self): 117 if self.returncode is None: 118 try: 119 _winapi.TerminateProcess(int(self._handle), TERMINATE) 120 except OSError: 121 if self.wait(timeout=1.0) is None: 122 raise 123 124 kill = terminate 125 126 def close(self): 127 self.finalizer() 128