1 import io
2 import os
3
4 from .context import reduction, set_spawning_popen
5 from . import popen_fork
6 from . import spawn
7 from . import util
8
9 __all__ = ['Popen']
10
11
12 #
13 # Wrapper for an fd used while launching a process
14 #
15
16 class ESC[4;38;5;81m_DupFd(ESC[4;38;5;149mobject):
17 def __init__(self, fd):
18 self.fd = fd
19 def detach(self):
20 return self.fd
21
22 #
23 # Start child process using a fresh interpreter
24 #
25
26 class ESC[4;38;5;81mPopen(ESC[4;38;5;149mpopen_forkESC[4;38;5;149m.ESC[4;38;5;149mPopen):
27 method = 'spawn'
28 DupFd = _DupFd
29
30 def __init__(self, process_obj):
31 self._fds = []
32 super().__init__(process_obj)
33
34 def duplicate_for_child(self, fd):
35 self._fds.append(fd)
36 return fd
37
38 def _launch(self, process_obj):
39 from . import resource_tracker
40 tracker_fd = resource_tracker.getfd()
41 self._fds.append(tracker_fd)
42 prep_data = spawn.get_preparation_data(process_obj._name)
43 fp = io.BytesIO()
44 set_spawning_popen(self)
45 try:
46 reduction.dump(prep_data, fp)
47 reduction.dump(process_obj, fp)
48 finally:
49 set_spawning_popen(None)
50
51 parent_r = child_w = child_r = parent_w = None
52 try:
53 parent_r, child_w = os.pipe()
54 child_r, parent_w = os.pipe()
55 cmd = spawn.get_command_line(tracker_fd=tracker_fd,
56 pipe_handle=child_r)
57 self._fds.extend([child_r, child_w])
58 self.pid = util.spawnv_passfds(spawn.get_executable(),
59 cmd, self._fds)
60 self.sentinel = parent_r
61 with open(parent_w, 'wb', closefd=False) as f:
62 f.write(fp.getbuffer())
63 finally:
64 fds_to_close = []
65 for fd in (parent_r, parent_w):
66 if fd is not None:
67 fds_to_close.append(fd)
68 self.finalizer = util.Finalize(self, util.close_fds, fds_to_close)
69
70 for fd in (child_r, child_w):
71 if fd is not None:
72 os.close(fd)