blob: 2de4cb7f6378be5bf0936ceacbcf69933d1ef60e [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:
33 WINEXE = (sys.platform == 'win32' and getattr(sys, 'frozen', False))
34 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
106 new_handle = reduction.duplicate(pipe_handle,
107 source_process=source_process)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100108 fd = msvcrt.open_osfhandle(new_handle, os.O_RDONLY)
109 else:
Richard Oudkerk7d2d43c2013-08-22 11:38:57 +0100110 from . import semaphore_tracker
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100111 semaphore_tracker._semaphore_tracker._fd = tracker_fd
Richard Oudkerk7d2d43c2013-08-22 11:38:57 +0100112 fd = pipe_handle
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100113 exitcode = _main(fd)
114 sys.exit(exitcode)
115
116
117def _main(fd):
118 with os.fdopen(fd, 'rb', closefd=True) as from_parent:
119 process.current_process()._inheriting = True
120 try:
Davin Potts54586472016-09-09 18:03:10 -0500121 preparation_data = reduction.pickle.load(from_parent)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100122 prepare(preparation_data)
Davin Potts54586472016-09-09 18:03:10 -0500123 self = reduction.pickle.load(from_parent)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100124 finally:
125 del process.current_process()._inheriting
126 return self._bootstrap()
127
128
129def _check_not_importing_main():
130 if getattr(process.current_process(), '_inheriting', False):
131 raise RuntimeError('''
132 An attempt has been made to start a new process before the
133 current process has finished its bootstrapping phase.
134
135 This probably means that you are not using fork to start your
136 child processes and you have forgotten to use the proper idiom
137 in the main module:
138
139 if __name__ == '__main__':
140 freeze_support()
141 ...
142
143 The "freeze_support()" line can be omitted if the program
144 is not going to be frozen to produce an executable.''')
145
146
147def get_preparation_data(name):
148 '''
149 Return info about parent needed by child to unpickle process object
150 '''
151 _check_not_importing_main()
152 d = dict(
153 log_to_stderr=util._log_to_stderr,
154 authkey=process.current_process().authkey,
155 )
156
157 if util._logger is not None:
158 d['log_level'] = util._logger.getEffectiveLevel()
159
160 sys_path=sys.path.copy()
161 try:
162 i = sys_path.index('')
163 except ValueError:
164 pass
165 else:
166 sys_path[i] = process.ORIGINAL_DIR
167
168 d.update(
169 name=name,
170 sys_path=sys_path,
171 sys_argv=sys.argv,
172 orig_dir=process.ORIGINAL_DIR,
173 dir=os.getcwd(),
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100174 start_method=get_start_method(),
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100175 )
176
Nick Coghlan9a767352013-12-17 22:17:26 +1000177 # Figure out whether to initialise main in the subprocess as a module
178 # or through direct execution (or to leave it alone entirely)
179 main_module = sys.modules['__main__']
180 main_mod_name = getattr(main_module.__spec__, "name", None)
181 if main_mod_name is not None:
182 d['init_main_from_name'] = main_mod_name
183 elif sys.platform != 'win32' or (not WINEXE and not WINSERVICE):
184 main_path = getattr(main_module, '__file__', None)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100185 if main_path is not None:
186 if (not os.path.isabs(main_path) and
187 process.ORIGINAL_DIR is not None):
188 main_path = os.path.join(process.ORIGINAL_DIR, main_path)
Nick Coghlan9a767352013-12-17 22:17:26 +1000189 d['init_main_from_path'] = os.path.normpath(main_path)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100190
191 return d
192
193#
194# Prepare current process
195#
196
197old_main_modules = []
198
199def prepare(data):
200 '''
201 Try to get current process ready to unpickle process object
202 '''
203 if 'name' in data:
204 process.current_process().name = data['name']
205
206 if 'authkey' in data:
207 process.current_process().authkey = data['authkey']
208
209 if 'log_to_stderr' in data and data['log_to_stderr']:
210 util.log_to_stderr()
211
212 if 'log_level' in data:
213 util.get_logger().setLevel(data['log_level'])
214
215 if 'sys_path' in data:
216 sys.path = data['sys_path']
217
218 if 'sys_argv' in data:
219 sys.argv = data['sys_argv']
220
221 if 'dir' in data:
222 os.chdir(data['dir'])
223
224 if 'orig_dir' in data:
225 process.ORIGINAL_DIR = data['orig_dir']
226
227 if 'start_method' in data:
Antoine Pitroucd2a2012016-12-10 17:13:16 +0100228 set_start_method(data['start_method'], force=True)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100229
Nick Coghlan9a767352013-12-17 22:17:26 +1000230 if 'init_main_from_name' in data:
231 _fixup_main_from_name(data['init_main_from_name'])
232 elif 'init_main_from_path' in data:
233 _fixup_main_from_path(data['init_main_from_path'])
234
235# Multiprocessing module helpers to fix up the main module in
236# spawned subprocesses
237def _fixup_main_from_name(mod_name):
238 # __main__.py files for packages, directories, zip archives, etc, run
239 # their "main only" code unconditionally, so we don't even try to
240 # populate anything in __main__, nor do we make any changes to
241 # __main__ attributes
242 current_main = sys.modules['__main__']
243 if mod_name == "__main__" or mod_name.endswith(".__main__"):
244 return
245
246 # If this process was forked, __main__ may already be populated
247 if getattr(current_main.__spec__, "name", None) == mod_name:
248 return
249
250 # Otherwise, __main__ may contain some non-main code where we need to
251 # support unpickling it properly. We rerun it as __mp_main__ and make
252 # the normal __main__ an alias to that
253 old_main_modules.append(current_main)
254 main_module = types.ModuleType("__mp_main__")
255 main_content = runpy.run_module(mod_name,
256 run_name="__mp_main__",
257 alter_sys=True)
258 main_module.__dict__.update(main_content)
259 sys.modules['__main__'] = sys.modules['__mp_main__'] = main_module
260
261
262def _fixup_main_from_path(main_path):
263 # If this process was forked, __main__ may already be populated
264 current_main = sys.modules['__main__']
265
266 # Unfortunately, the main ipython launch script historically had no
267 # "if __name__ == '__main__'" guard, so we work around that
268 # by treating it like a __main__.py file
269 # See https://github.com/ipython/ipython/issues/4698
270 main_name = os.path.splitext(os.path.basename(main_path))[0]
271 if main_name == 'ipython':
272 return
273
274 # Otherwise, if __file__ already has the setting we expect,
275 # there's nothing more to do
276 if getattr(current_main, '__file__', None) == main_path:
277 return
278
279 # If the parent process has sent a path through rather than a module
280 # name we assume it is an executable script that may contain
281 # non-main code that needs to be executed
282 old_main_modules.append(current_main)
283 main_module = types.ModuleType("__mp_main__")
284 main_content = runpy.run_path(main_path,
285 run_name="__mp_main__")
286 main_module.__dict__.update(main_content)
287 sys.modules['__main__'] = sys.modules['__mp_main__'] = main_module
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100288
289
290def import_main_path(main_path):
291 '''
292 Set sys.modules['__main__'] to module at main_path
293 '''
Nick Coghlan9a767352013-12-17 22:17:26 +1000294 _fixup_main_from_path(main_path)