blob: 6759351f13abceab600b21297022aea1abe70291 [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
32else:
Steve Dower4e02f8f82019-01-25 14:59:12 -080033 WINEXE = getattr(sys, 'frozen', False)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010034 WINSERVICE = sys.executable.lower().endswith("pythonservice.exe")
35
36if WINSERVICE:
37 _python_exe = os.path.join(sys.exec_prefix, 'python.exe')
38else:
39 _python_exe = sys.executable
40
41def set_executable(exe):
42 global _python_exe
43 _python_exe = exe
44
45def get_executable():
46 return _python_exe
47
48#
49#
50#
51
52def is_forking(argv):
53 '''
54 Return whether commandline indicates we are forking
55 '''
56 if len(argv) >= 2 and argv[1] == '--multiprocessing-fork':
57 return True
58 else:
59 return False
60
61
62def freeze_support():
63 '''
64 Run code for process object if this in not the main process
65 '''
66 if is_forking(sys.argv):
Richard Oudkerka40675a2014-03-23 11:54:15 +000067 kwds = {}
68 for arg in sys.argv[2:]:
69 name, value = arg.split('=')
70 if value == 'None':
71 kwds[name] = None
72 else:
73 kwds[name] = int(value)
74 spawn_main(**kwds)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010075 sys.exit()
76
77
Richard Oudkerk7d2d43c2013-08-22 11:38:57 +010078def get_command_line(**kwds):
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010079 '''
80 Returns prefix of command line used for spawning a child process
81 '''
82 if getattr(sys, 'frozen', False):
Richard Oudkerk99d8dd22014-03-23 18:44:11 +000083 return ([sys.executable, '--multiprocessing-fork'] +
84 ['%s=%r' % item for item in kwds.items()])
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010085 else:
Richard Oudkerk7d2d43c2013-08-22 11:38:57 +010086 prog = 'from multiprocessing.spawn import spawn_main; spawn_main(%s)'
87 prog %= ', '.join('%s=%r' % item for item in kwds.items())
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010088 opts = util._args_from_interpreter_flags()
89 return [_python_exe] + opts + ['-c', prog, '--multiprocessing-fork']
90
91
Richard Oudkerk7d2d43c2013-08-22 11:38:57 +010092def spawn_main(pipe_handle, parent_pid=None, tracker_fd=None):
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010093 '''
Berker Peksag16fb6742016-01-07 19:02:55 +020094 Run code specified by data received over pipe
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010095 '''
Allen W. Smith, Ph.Dbd73e722017-08-29 17:52:18 -050096 assert is_forking(sys.argv), "Not forking"
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010097 if sys.platform == 'win32':
98 import msvcrt
Victor Stinner2cc9d212018-06-27 11:40:24 +020099 import _winapi
100
101 if parent_pid is not None:
102 source_process = _winapi.OpenProcess(
103 _winapi.PROCESS_DUP_HANDLE, False, parent_pid)
104 else:
105 source_process = None
Victor Stinner0aab8662018-06-27 15:18:39 +0200106 try:
107 new_handle = reduction.duplicate(pipe_handle,
108 source_process=source_process)
109 finally:
110 if source_process is not None:
111 _winapi.CloseHandle(source_process)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100112 fd = msvcrt.open_osfhandle(new_handle, os.O_RDONLY)
113 else:
Richard Oudkerk7d2d43c2013-08-22 11:38:57 +0100114 from . import semaphore_tracker
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100115 semaphore_tracker._semaphore_tracker._fd = tracker_fd
Richard Oudkerk7d2d43c2013-08-22 11:38:57 +0100116 fd = pipe_handle
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100117 exitcode = _main(fd)
118 sys.exit(exitcode)
119
120
121def _main(fd):
122 with os.fdopen(fd, 'rb', closefd=True) as from_parent:
123 process.current_process()._inheriting = True
124 try:
Davin Potts54586472016-09-09 18:03:10 -0500125 preparation_data = reduction.pickle.load(from_parent)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100126 prepare(preparation_data)
Davin Potts54586472016-09-09 18:03:10 -0500127 self = reduction.pickle.load(from_parent)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100128 finally:
129 del process.current_process()._inheriting
130 return self._bootstrap()
131
132
133def _check_not_importing_main():
134 if getattr(process.current_process(), '_inheriting', False):
135 raise RuntimeError('''
136 An attempt has been made to start a new process before the
137 current process has finished its bootstrapping phase.
138
139 This probably means that you are not using fork to start your
140 child processes and you have forgotten to use the proper idiom
141 in the main module:
142
143 if __name__ == '__main__':
144 freeze_support()
145 ...
146
147 The "freeze_support()" line can be omitted if the program
148 is not going to be frozen to produce an executable.''')
149
150
151def get_preparation_data(name):
152 '''
153 Return info about parent needed by child to unpickle process object
154 '''
155 _check_not_importing_main()
156 d = dict(
157 log_to_stderr=util._log_to_stderr,
158 authkey=process.current_process().authkey,
159 )
160
161 if util._logger is not None:
162 d['log_level'] = util._logger.getEffectiveLevel()
163
164 sys_path=sys.path.copy()
165 try:
166 i = sys_path.index('')
167 except ValueError:
168 pass
169 else:
170 sys_path[i] = process.ORIGINAL_DIR
171
172 d.update(
173 name=name,
174 sys_path=sys_path,
175 sys_argv=sys.argv,
176 orig_dir=process.ORIGINAL_DIR,
177 dir=os.getcwd(),
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100178 start_method=get_start_method(),
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100179 )
180
Nick Coghlan9a767352013-12-17 22:17:26 +1000181 # Figure out whether to initialise main in the subprocess as a module
182 # or through direct execution (or to leave it alone entirely)
183 main_module = sys.modules['__main__']
184 main_mod_name = getattr(main_module.__spec__, "name", None)
185 if main_mod_name is not None:
186 d['init_main_from_name'] = main_mod_name
187 elif sys.platform != 'win32' or (not WINEXE and not WINSERVICE):
188 main_path = getattr(main_module, '__file__', None)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100189 if main_path is not None:
190 if (not os.path.isabs(main_path) and
191 process.ORIGINAL_DIR is not None):
192 main_path = os.path.join(process.ORIGINAL_DIR, main_path)
Nick Coghlan9a767352013-12-17 22:17:26 +1000193 d['init_main_from_path'] = os.path.normpath(main_path)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100194
195 return d
196
197#
198# Prepare current process
199#
200
201old_main_modules = []
202
203def prepare(data):
204 '''
205 Try to get current process ready to unpickle process object
206 '''
207 if 'name' in data:
208 process.current_process().name = data['name']
209
210 if 'authkey' in data:
211 process.current_process().authkey = data['authkey']
212
213 if 'log_to_stderr' in data and data['log_to_stderr']:
214 util.log_to_stderr()
215
216 if 'log_level' in data:
217 util.get_logger().setLevel(data['log_level'])
218
219 if 'sys_path' in data:
220 sys.path = data['sys_path']
221
222 if 'sys_argv' in data:
223 sys.argv = data['sys_argv']
224
225 if 'dir' in data:
226 os.chdir(data['dir'])
227
228 if 'orig_dir' in data:
229 process.ORIGINAL_DIR = data['orig_dir']
230
231 if 'start_method' in data:
Antoine Pitroucd2a2012016-12-10 17:13:16 +0100232 set_start_method(data['start_method'], force=True)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100233
Nick Coghlan9a767352013-12-17 22:17:26 +1000234 if 'init_main_from_name' in data:
235 _fixup_main_from_name(data['init_main_from_name'])
236 elif 'init_main_from_path' in data:
237 _fixup_main_from_path(data['init_main_from_path'])
238
239# Multiprocessing module helpers to fix up the main module in
240# spawned subprocesses
241def _fixup_main_from_name(mod_name):
242 # __main__.py files for packages, directories, zip archives, etc, run
243 # their "main only" code unconditionally, so we don't even try to
244 # populate anything in __main__, nor do we make any changes to
245 # __main__ attributes
246 current_main = sys.modules['__main__']
247 if mod_name == "__main__" or mod_name.endswith(".__main__"):
248 return
249
250 # If this process was forked, __main__ may already be populated
251 if getattr(current_main.__spec__, "name", None) == mod_name:
252 return
253
254 # Otherwise, __main__ may contain some non-main code where we need to
255 # support unpickling it properly. We rerun it as __mp_main__ and make
256 # the normal __main__ an alias to that
257 old_main_modules.append(current_main)
258 main_module = types.ModuleType("__mp_main__")
259 main_content = runpy.run_module(mod_name,
260 run_name="__mp_main__",
261 alter_sys=True)
262 main_module.__dict__.update(main_content)
263 sys.modules['__main__'] = sys.modules['__mp_main__'] = main_module
264
265
266def _fixup_main_from_path(main_path):
267 # If this process was forked, __main__ may already be populated
268 current_main = sys.modules['__main__']
269
270 # Unfortunately, the main ipython launch script historically had no
271 # "if __name__ == '__main__'" guard, so we work around that
272 # by treating it like a __main__.py file
273 # See https://github.com/ipython/ipython/issues/4698
274 main_name = os.path.splitext(os.path.basename(main_path))[0]
275 if main_name == 'ipython':
276 return
277
278 # Otherwise, if __file__ already has the setting we expect,
279 # there's nothing more to do
280 if getattr(current_main, '__file__', None) == main_path:
281 return
282
283 # If the parent process has sent a path through rather than a module
284 # name we assume it is an executable script that may contain
285 # non-main code that needs to be executed
286 old_main_modules.append(current_main)
287 main_module = types.ModuleType("__mp_main__")
288 main_content = runpy.run_path(main_path,
289 run_name="__mp_main__")
290 main_module.__dict__.update(main_content)
291 sys.modules['__main__'] = sys.modules['__mp_main__'] = main_module
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100292
293
294def import_main_path(main_path):
295 '''
296 Set sys.modules['__main__'] to module at main_path
297 '''
Nick Coghlan9a767352013-12-17 22:17:26 +1000298 _fixup_main_from_path(main_path)