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 = not _path_eq(sys.executable, sys._base_executable)
26
27
28 def _close_handles(*handles):
29 for handle in handles:
30 _winapi.CloseHandle(handle)
31
32
33 #
34 # We define a Popen class similar to the one from subprocess, but
35 # whose constructor takes a process object as its argument.
36 #
37
38 class ESC[4;38;5;81mPopen(ESC[4;38;5;149mobject):
39 '''
40 Start a subprocess to run the code of a process object
41 '''
42 method = 'spawn'
43
44 def __init__(self, process_obj):
45 prep_data = spawn.get_preparation_data(process_obj._name)
46
47 # read end of pipe will be duplicated by the child process
48 # -- see spawn_main() in spawn.py.
49 #
50 # bpo-33929: Previously, the read end of pipe was "stolen" by the child
51 # process, but it leaked a handle if the child process had been
52 # terminated before it could steal the handle from the parent process.
53 rhandle, whandle = _winapi.CreatePipe(None, 0)
54 wfd = msvcrt.open_osfhandle(whandle, 0)
55 cmd = spawn.get_command_line(parent_pid=os.getpid(),
56 pipe_handle=rhandle)
57
58 python_exe = spawn.get_executable()
59
60 # bpo-35797: When running in a venv, we bypass the redirect
61 # executor and launch our base Python.
62 if WINENV and _path_eq(python_exe, sys.executable):
63 cmd[0] = python_exe = sys._base_executable
64 env = os.environ.copy()
65 env["__PYVENV_LAUNCHER__"] = sys.executable
66 else:
67 env = None
68
69 cmd = ' '.join('"%s"' % x for x in cmd)
70
71 with open(wfd, 'wb', closefd=True) as to_child:
72 # start process
73 try:
74 hp, ht, pid, tid = _winapi.CreateProcess(
75 python_exe, cmd,
76 None, None, False, 0, env, None, None)
77 _winapi.CloseHandle(ht)
78 except:
79 _winapi.CloseHandle(rhandle)
80 raise
81
82 # set attributes of self
83 self.pid = pid
84 self.returncode = None
85 self._handle = hp
86 self.sentinel = int(hp)
87 self.finalizer = util.Finalize(self, _close_handles,
88 (self.sentinel, int(rhandle)))
89
90 # send information to child
91 set_spawning_popen(self)
92 try:
93 reduction.dump(prep_data, to_child)
94 reduction.dump(process_obj, to_child)
95 finally:
96 set_spawning_popen(None)
97
98 def duplicate_for_child(self, handle):
99 assert self is get_spawning_popen()
100 return reduction.duplicate(handle, self.sentinel)
101
102 def wait(self, timeout=None):
103 if self.returncode is None:
104 if timeout is None:
105 msecs = _winapi.INFINITE
106 else:
107 msecs = max(0, int(timeout * 1000 + 0.5))
108
109 res = _winapi.WaitForSingleObject(int(self._handle), msecs)
110 if res == _winapi.WAIT_OBJECT_0:
111 code = _winapi.GetExitCodeProcess(self._handle)
112 if code == TERMINATE:
113 code = -signal.SIGTERM
114 self.returncode = code
115
116 return self.returncode
117
118 def poll(self):
119 return self.wait(timeout=0)
120
121 def terminate(self):
122 if self.returncode is None:
123 try:
124 _winapi.TerminateProcess(int(self._handle), TERMINATE)
125 except OSError:
126 if self.wait(timeout=1.0) is None:
127 raise
128
129 kill = terminate
130
131 def close(self):
132 self.finalizer()