(root)/
Python-3.11.7/
Lib/
multiprocessing/
popen_spawn_win32.py
       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  # Exit code used by Popen.terminate()
      18  TERMINATE = 0x10000
      19  WINEXE = (sys.platform == 'win32' and getattr(sys, 'frozen', False))
      20  WINSERVICE = sys.executable.lower().endswith("pythonservice.exe")
      21  
      22  
      23  def _path_eq(p1, p2):
      24      return p1 == p2 or os.path.normcase(p1) == os.path.normcase(p2)
      25  
      26  WINENV = 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 ESC[4;38;5;81mPopen(ESC[4;38;5;149mobject):
      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 duplicated by the child process
      49          # -- see spawn_main() in spawn.py.
      50          #
      51          # bpo-33929: Previously, the read end of pipe was "stolen" by the child
      52          # process, but it leaked a handle if the child process had been
      53          # terminated before it could steal the handle from the parent process.
      54          rhandle, whandle = _winapi.CreatePipe(None, 0)
      55          wfd = msvcrt.open_osfhandle(whandle, 0)
      56          cmd = spawn.get_command_line(parent_pid=os.getpid(),
      57                                       pipe_handle=rhandle)
      58  
      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              cmd[0] = python_exe = sys._base_executable
      65              env = os.environ.copy()
      66              env["__PYVENV_LAUNCHER__"] = sys.executable
      67          else:
      68              env = None
      69  
      70          cmd = ' '.join('"%s"' % x for x in cmd)
      71  
      72          with open(wfd, 'wb', closefd=True) as to_child:
      73              # start process
      74              try:
      75                  hp, ht, pid, tid = _winapi.CreateProcess(
      76                      python_exe, cmd,
      77                      None, None, False, 0, env, None, None)
      78                  _winapi.CloseHandle(ht)
      79              except:
      80                  _winapi.CloseHandle(rhandle)
      81                  raise
      82  
      83              # set attributes of self
      84              self.pid = pid
      85              self.returncode = None
      86              self._handle = hp
      87              self.sentinel = int(hp)
      88              self.finalizer = util.Finalize(self, _close_handles,
      89                                             (self.sentinel, int(rhandle)))
      90  
      91              # send information to child
      92              set_spawning_popen(self)
      93              try:
      94                  reduction.dump(prep_data, to_child)
      95                  reduction.dump(process_obj, to_child)
      96              finally:
      97                  set_spawning_popen(None)
      98  
      99      def duplicate_for_child(self, handle):
     100          assert self is get_spawning_popen()
     101          return reduction.duplicate(handle, self.sentinel)
     102  
     103      def wait(self, timeout=None):
     104          if self.returncode is None:
     105              if timeout is None:
     106                  msecs = _winapi.INFINITE
     107              else:
     108                  msecs = max(0, int(timeout * 1000 + 0.5))
     109  
     110              res = _winapi.WaitForSingleObject(int(self._handle), msecs)
     111              if res == _winapi.WAIT_OBJECT_0:
     112                  code = _winapi.GetExitCodeProcess(self._handle)
     113                  if code == TERMINATE:
     114                      code = -signal.SIGTERM
     115                  self.returncode = code
     116  
     117          return self.returncode
     118  
     119      def poll(self):
     120          return self.wait(timeout=0)
     121  
     122      def terminate(self):
     123          if self.returncode is None:
     124              try:
     125                  _winapi.TerminateProcess(int(self._handle), TERMINATE)
     126              except PermissionError:
     127                  # ERROR_ACCESS_DENIED (winerror 5) is received when the
     128                  # process already died.
     129                  code = _winapi.GetExitCodeProcess(int(self._handle))
     130                  if code == _winapi.STILL_ACTIVE:
     131                      raise
     132                  self.returncode = code
     133              else:
     134                  self.returncode = -signal.SIGTERM
     135  
     136      kill = terminate
     137  
     138      def close(self):
     139          self.finalizer()