Richard Oudkerk | 84ed9a6 | 2013-08-14 15:35:41 +0100 | [diff] [blame] | 1 | import os |
| 2 | import msvcrt |
| 3 | import signal |
| 4 | import sys |
| 5 | import _winapi |
| 6 | |
Davin Potts | 5458647 | 2016-09-09 18:03:10 -0500 | [diff] [blame] | 7 | from .context import reduction, get_spawning_popen, set_spawning_popen |
Richard Oudkerk | 84ed9a6 | 2013-08-14 15:35:41 +0100 | [diff] [blame] | 8 | from . import spawn |
Richard Oudkerk | 84ed9a6 | 2013-08-14 15:35:41 +0100 | [diff] [blame] | 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 | |
Victor Stinner | 2cc9d21 | 2018-06-27 11:40:24 +0200 | [diff] [blame] | 21 | |
Steve Dower | a8474d0 | 2019-02-03 23:19:38 -0800 | [diff] [blame] | 22 | def _path_eq(p1, p2): |
| 23 | return p1 == p2 or os.path.normcase(p1) == os.path.normcase(p2) |
| 24 | |
Steve Dower | 323e743 | 2019-06-29 14:28:59 -0700 | [diff] [blame] | 25 | WINENV = not _path_eq(sys.executable, sys._base_executable) |
Steve Dower | a8474d0 | 2019-02-03 23:19:38 -0800 | [diff] [blame] | 26 | |
| 27 | |
Victor Stinner | 2cc9d21 | 2018-06-27 11:40:24 +0200 | [diff] [blame] | 28 | def _close_handles(*handles): |
| 29 | for handle in handles: |
| 30 | _winapi.CloseHandle(handle) |
| 31 | |
| 32 | |
Richard Oudkerk | 84ed9a6 | 2013-08-14 15:35:41 +0100 | [diff] [blame] | 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 Popen(object): |
| 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) |
Richard Oudkerk | 84ed9a6 | 2013-08-14 15:35:41 +0100 | [diff] [blame] | 46 | |
Victor Stinner | 2cc9d21 | 2018-06-27 11:40:24 +0200 | [diff] [blame] | 47 | # read end of pipe will be duplicated by the child process |
Richard Oudkerk | 84ed9a6 | 2013-08-14 15:35:41 +0100 | [diff] [blame] | 48 | # -- see spawn_main() in spawn.py. |
Victor Stinner | 2cc9d21 | 2018-06-27 11:40:24 +0200 | [diff] [blame] | 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. |
Richard Oudkerk | 84ed9a6 | 2013-08-14 15:35:41 +0100 | [diff] [blame] | 53 | rhandle, whandle = _winapi.CreatePipe(None, 0) |
| 54 | wfd = msvcrt.open_osfhandle(whandle, 0) |
Richard Oudkerk | 7d2d43c | 2013-08-22 11:38:57 +0100 | [diff] [blame] | 55 | cmd = spawn.get_command_line(parent_pid=os.getpid(), |
| 56 | pipe_handle=rhandle) |
| 57 | cmd = ' '.join('"%s"' % x for x in cmd) |
Richard Oudkerk | 84ed9a6 | 2013-08-14 15:35:41 +0100 | [diff] [blame] | 58 | |
Steve Dower | a8474d0 | 2019-02-03 23:19:38 -0800 | [diff] [blame] | 59 | python_exe = spawn.get_executable() |
| 60 | |
| 61 | # bpo-35797: When running in a venv, we bypass the redirect |
| 62 | # executor and launch our base Python. |
| 63 | if WINENV and _path_eq(python_exe, sys.executable): |
| 64 | python_exe = sys._base_executable |
| 65 | env = os.environ.copy() |
| 66 | env["__PYVENV_LAUNCHER__"] = sys.executable |
| 67 | else: |
| 68 | env = None |
| 69 | |
Richard Oudkerk | 84ed9a6 | 2013-08-14 15:35:41 +0100 | [diff] [blame] | 70 | with open(wfd, 'wb', closefd=True) as to_child: |
| 71 | # start process |
| 72 | try: |
| 73 | hp, ht, pid, tid = _winapi.CreateProcess( |
Steve Dower | a8474d0 | 2019-02-03 23:19:38 -0800 | [diff] [blame] | 74 | python_exe, cmd, |
Miss Islington (bot) | 436b429 | 2019-09-13 09:59:11 -0700 | [diff] [blame] | 75 | None, None, False, 0, env, None, None) |
Richard Oudkerk | 84ed9a6 | 2013-08-14 15:35:41 +0100 | [diff] [blame] | 76 | _winapi.CloseHandle(ht) |
| 77 | except: |
| 78 | _winapi.CloseHandle(rhandle) |
| 79 | raise |
| 80 | |
| 81 | # set attributes of self |
| 82 | self.pid = pid |
| 83 | self.returncode = None |
| 84 | self._handle = hp |
| 85 | self.sentinel = int(hp) |
Victor Stinner | 2cc9d21 | 2018-06-27 11:40:24 +0200 | [diff] [blame] | 86 | self.finalizer = util.Finalize(self, _close_handles, |
| 87 | (self.sentinel, int(rhandle))) |
Richard Oudkerk | 84ed9a6 | 2013-08-14 15:35:41 +0100 | [diff] [blame] | 88 | |
| 89 | # send information to child |
Davin Potts | 5458647 | 2016-09-09 18:03:10 -0500 | [diff] [blame] | 90 | set_spawning_popen(self) |
Richard Oudkerk | 84ed9a6 | 2013-08-14 15:35:41 +0100 | [diff] [blame] | 91 | try: |
| 92 | reduction.dump(prep_data, to_child) |
| 93 | reduction.dump(process_obj, to_child) |
| 94 | finally: |
Davin Potts | 5458647 | 2016-09-09 18:03:10 -0500 | [diff] [blame] | 95 | set_spawning_popen(None) |
Richard Oudkerk | 84ed9a6 | 2013-08-14 15:35:41 +0100 | [diff] [blame] | 96 | |
| 97 | def duplicate_for_child(self, handle): |
Davin Potts | 5458647 | 2016-09-09 18:03:10 -0500 | [diff] [blame] | 98 | assert self is get_spawning_popen() |
Richard Oudkerk | 84ed9a6 | 2013-08-14 15:35:41 +0100 | [diff] [blame] | 99 | return reduction.duplicate(handle, self.sentinel) |
| 100 | |
| 101 | def wait(self, timeout=None): |
| 102 | if self.returncode is None: |
| 103 | if timeout is None: |
| 104 | msecs = _winapi.INFINITE |
| 105 | else: |
| 106 | msecs = max(0, int(timeout * 1000 + 0.5)) |
| 107 | |
| 108 | res = _winapi.WaitForSingleObject(int(self._handle), msecs) |
| 109 | if res == _winapi.WAIT_OBJECT_0: |
| 110 | code = _winapi.GetExitCodeProcess(self._handle) |
| 111 | if code == TERMINATE: |
| 112 | code = -signal.SIGTERM |
| 113 | self.returncode = code |
| 114 | |
| 115 | return self.returncode |
| 116 | |
| 117 | def poll(self): |
| 118 | return self.wait(timeout=0) |
| 119 | |
| 120 | def terminate(self): |
| 121 | if self.returncode is None: |
| 122 | try: |
| 123 | _winapi.TerminateProcess(int(self._handle), TERMINATE) |
| 124 | except OSError: |
| 125 | if self.wait(timeout=1.0) is None: |
| 126 | raise |
Antoine Pitrou | 13e96cc | 2017-06-24 19:22:23 +0200 | [diff] [blame] | 127 | |
Vitor Pereira | ba75af7 | 2017-07-18 16:34:23 +0100 | [diff] [blame] | 128 | kill = terminate |
| 129 | |
Antoine Pitrou | 13e96cc | 2017-06-24 19:22:23 +0200 | [diff] [blame] | 130 | def close(self): |
| 131 | self.finalizer() |