blob: 860fa4ceb5ced51d021c93b4078eb3eb1fe63a52 [file] [log] [blame]
Richard Oudkerk84ed9a62013-08-14 15:35:41 +01001#
2# Code used to start processes when using the spawn or forkserver
3# start methods.
4#
5# multiprocessing/spawn.py
6#
7# Copyright (c) 2006-2008, R Oudkerk
8# Licensed to PSF under a Contributor Agreement.
9#
10
11import os
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010012import sys
Nick Coghlan9a767352013-12-17 22:17:26 +100013import runpy
14import types
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010015
Richard Oudkerkb1694cf2013-10-16 16:41:56 +010016from . import get_start_method, set_start_method
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010017from . import process
Davin Potts54586472016-09-09 18:03:10 -050018from .context import reduction
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010019from . import util
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010020
21__all__ = ['_main', 'freeze_support', 'set_executable', 'get_executable',
22 'get_preparation_data', 'get_command_line', 'import_main_path']
23
24#
25# _python_exe is the assumed path to the python executable.
26# People embedding Python want to modify it.
27#
28
29if sys.platform != 'win32':
30 WINEXE = False
31 WINSERVICE = False
Steve Dower4e02f8f82019-01-25 14:59:12 -080032 _WINENV = False
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010033else:
Steve Dower4e02f8f82019-01-25 14:59:12 -080034 WINEXE = getattr(sys, 'frozen', False)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010035 WINSERVICE = sys.executable.lower().endswith("pythonservice.exe")
Steve Dower4e02f8f82019-01-25 14:59:12 -080036 _WINENV = '__PYVENV_LAUNCHER__' in os.environ
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010037
38if WINSERVICE:
39 _python_exe = os.path.join(sys.exec_prefix, 'python.exe')
Steve Dower4e02f8f82019-01-25 14:59:12 -080040elif _WINENV:
41 # bpo-35797: When running in a venv, we need to bypass the redirect
42 # executor and launch our base Python.
43 import _winapi
44 _python_exe = _winapi.GetModuleFileName(0)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010045else:
46 _python_exe = sys.executable
47
48def set_executable(exe):
49 global _python_exe
50 _python_exe = exe
51
52def get_executable():
53 return _python_exe
54
55#
56#
57#
58
59def is_forking(argv):
60 '''
61 Return whether commandline indicates we are forking
62 '''
63 if len(argv) >= 2 and argv[1] == '--multiprocessing-fork':
64 return True
65 else:
66 return False
67
68
69def freeze_support():
70 '''
71 Run code for process object if this in not the main process
72 '''
73 if is_forking(sys.argv):
Richard Oudkerka40675a2014-03-23 11:54:15 +000074 kwds = {}
75 for arg in sys.argv[2:]:
76 name, value = arg.split('=')
77 if value == 'None':
78 kwds[name] = None
79 else:
80 kwds[name] = int(value)
81 spawn_main(**kwds)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010082 sys.exit()
83
84
Richard Oudkerk7d2d43c2013-08-22 11:38:57 +010085def get_command_line(**kwds):
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010086 '''
87 Returns prefix of command line used for spawning a child process
88 '''
89 if getattr(sys, 'frozen', False):
Richard Oudkerk99d8dd22014-03-23 18:44:11 +000090 return ([sys.executable, '--multiprocessing-fork'] +
91 ['%s=%r' % item for item in kwds.items()])
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010092 else:
Richard Oudkerk7d2d43c2013-08-22 11:38:57 +010093 prog = 'from multiprocessing.spawn import spawn_main; spawn_main(%s)'
94 prog %= ', '.join('%s=%r' % item for item in kwds.items())
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010095 opts = util._args_from_interpreter_flags()
96 return [_python_exe] + opts + ['-c', prog, '--multiprocessing-fork']
97
98
Richard Oudkerk7d2d43c2013-08-22 11:38:57 +010099def spawn_main(pipe_handle, parent_pid=None, tracker_fd=None):
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100100 '''
Berker Peksag16fb6742016-01-07 19:02:55 +0200101 Run code specified by data received over pipe
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100102 '''
Allen W. Smith, Ph.Dbd73e722017-08-29 17:52:18 -0500103 assert is_forking(sys.argv), "Not forking"
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100104 if sys.platform == 'win32':
105 import msvcrt
Victor Stinner2cc9d212018-06-27 11:40:24 +0200106 import _winapi
107
108 if parent_pid is not None:
109 source_process = _winapi.OpenProcess(
110 _winapi.PROCESS_DUP_HANDLE, False, parent_pid)
111 else:
112 source_process = None
Victor Stinner0aab8662018-06-27 15:18:39 +0200113 try:
114 new_handle = reduction.duplicate(pipe_handle,
115 source_process=source_process)
116 finally:
117 if source_process is not None:
118 _winapi.CloseHandle(source_process)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100119 fd = msvcrt.open_osfhandle(new_handle, os.O_RDONLY)
120 else:
Richard Oudkerk7d2d43c2013-08-22 11:38:57 +0100121 from . import semaphore_tracker
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100122 semaphore_tracker._semaphore_tracker._fd = tracker_fd
Richard Oudkerk7d2d43c2013-08-22 11:38:57 +0100123 fd = pipe_handle
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100124 exitcode = _main(fd)
125 sys.exit(exitcode)
126
127
128def _main(fd):
129 with os.fdopen(fd, 'rb', closefd=True) as from_parent:
130 process.current_process()._inheriting = True
131 try:
Davin Potts54586472016-09-09 18:03:10 -0500132 preparation_data = reduction.pickle.load(from_parent)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100133 prepare(preparation_data)
Davin Potts54586472016-09-09 18:03:10 -0500134 self = reduction.pickle.load(from_parent)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100135 finally:
136 del process.current_process()._inheriting
137 return self._bootstrap()
138
139
140def _check_not_importing_main():
141 if getattr(process.current_process(), '_inheriting', False):
142 raise RuntimeError('''
143 An attempt has been made to start a new process before the
144 current process has finished its bootstrapping phase.
145
146 This probably means that you are not using fork to start your
147 child processes and you have forgotten to use the proper idiom
148 in the main module:
149
150 if __name__ == '__main__':
151 freeze_support()
152 ...
153
154 The "freeze_support()" line can be omitted if the program
155 is not going to be frozen to produce an executable.''')
156
157
158def get_preparation_data(name):
159 '''
160 Return info about parent needed by child to unpickle process object
161 '''
162 _check_not_importing_main()
163 d = dict(
164 log_to_stderr=util._log_to_stderr,
165 authkey=process.current_process().authkey,
166 )
167
168 if util._logger is not None:
169 d['log_level'] = util._logger.getEffectiveLevel()
170
171 sys_path=sys.path.copy()
172 try:
173 i = sys_path.index('')
174 except ValueError:
175 pass
176 else:
177 sys_path[i] = process.ORIGINAL_DIR
178
179 d.update(
180 name=name,
181 sys_path=sys_path,
182 sys_argv=sys.argv,
183 orig_dir=process.ORIGINAL_DIR,
184 dir=os.getcwd(),
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100185 start_method=get_start_method(),
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100186 )
187
Nick Coghlan9a767352013-12-17 22:17:26 +1000188 # Figure out whether to initialise main in the subprocess as a module
189 # or through direct execution (or to leave it alone entirely)
190 main_module = sys.modules['__main__']
191 main_mod_name = getattr(main_module.__spec__, "name", None)
192 if main_mod_name is not None:
193 d['init_main_from_name'] = main_mod_name
194 elif sys.platform != 'win32' or (not WINEXE and not WINSERVICE):
195 main_path = getattr(main_module, '__file__', None)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100196 if main_path is not None:
197 if (not os.path.isabs(main_path) and
198 process.ORIGINAL_DIR is not None):
199 main_path = os.path.join(process.ORIGINAL_DIR, main_path)
Nick Coghlan9a767352013-12-17 22:17:26 +1000200 d['init_main_from_path'] = os.path.normpath(main_path)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100201
202 return d
203
204#
205# Prepare current process
206#
207
208old_main_modules = []
209
210def prepare(data):
211 '''
212 Try to get current process ready to unpickle process object
213 '''
214 if 'name' in data:
215 process.current_process().name = data['name']
216
217 if 'authkey' in data:
218 process.current_process().authkey = data['authkey']
219
220 if 'log_to_stderr' in data and data['log_to_stderr']:
221 util.log_to_stderr()
222
223 if 'log_level' in data:
224 util.get_logger().setLevel(data['log_level'])
225
226 if 'sys_path' in data:
227 sys.path = data['sys_path']
228
229 if 'sys_argv' in data:
230 sys.argv = data['sys_argv']
231
232 if 'dir' in data:
233 os.chdir(data['dir'])
234
235 if 'orig_dir' in data:
236 process.ORIGINAL_DIR = data['orig_dir']
237
238 if 'start_method' in data:
Antoine Pitroucd2a2012016-12-10 17:13:16 +0100239 set_start_method(data['start_method'], force=True)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100240
Nick Coghlan9a767352013-12-17 22:17:26 +1000241 if 'init_main_from_name' in data:
242 _fixup_main_from_name(data['init_main_from_name'])
243 elif 'init_main_from_path' in data:
244 _fixup_main_from_path(data['init_main_from_path'])
245
246# Multiprocessing module helpers to fix up the main module in
247# spawned subprocesses
248def _fixup_main_from_name(mod_name):
249 # __main__.py files for packages, directories, zip archives, etc, run
250 # their "main only" code unconditionally, so we don't even try to
251 # populate anything in __main__, nor do we make any changes to
252 # __main__ attributes
253 current_main = sys.modules['__main__']
254 if mod_name == "__main__" or mod_name.endswith(".__main__"):
255 return
256
257 # If this process was forked, __main__ may already be populated
258 if getattr(current_main.__spec__, "name", None) == mod_name:
259 return
260
261 # Otherwise, __main__ may contain some non-main code where we need to
262 # support unpickling it properly. We rerun it as __mp_main__ and make
263 # the normal __main__ an alias to that
264 old_main_modules.append(current_main)
265 main_module = types.ModuleType("__mp_main__")
266 main_content = runpy.run_module(mod_name,
267 run_name="__mp_main__",
268 alter_sys=True)
269 main_module.__dict__.update(main_content)
270 sys.modules['__main__'] = sys.modules['__mp_main__'] = main_module
271
272
273def _fixup_main_from_path(main_path):
274 # If this process was forked, __main__ may already be populated
275 current_main = sys.modules['__main__']
276
277 # Unfortunately, the main ipython launch script historically had no
278 # "if __name__ == '__main__'" guard, so we work around that
279 # by treating it like a __main__.py file
280 # See https://github.com/ipython/ipython/issues/4698
281 main_name = os.path.splitext(os.path.basename(main_path))[0]
282 if main_name == 'ipython':
283 return
284
285 # Otherwise, if __file__ already has the setting we expect,
286 # there's nothing more to do
287 if getattr(current_main, '__file__', None) == main_path:
288 return
289
290 # If the parent process has sent a path through rather than a module
291 # name we assume it is an executable script that may contain
292 # non-main code that needs to be executed
293 old_main_modules.append(current_main)
294 main_module = types.ModuleType("__mp_main__")
295 main_content = runpy.run_path(main_path,
296 run_name="__mp_main__")
297 main_module.__dict__.update(main_content)
298 sys.modules['__main__'] = sys.modules['__mp_main__'] = main_module
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100299
300
301def import_main_path(main_path):
302 '''
303 Set sys.modules['__main__'] to module at main_path
304 '''
Nick Coghlan9a767352013-12-17 22:17:26 +1000305 _fixup_main_from_path(main_path)