blob: c8297f3134cb8f901edc8c07b71d727bbecb30f5 [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
12import pickle
13import sys
Nick Coghlan9a767352013-12-17 22:17:26 +100014import runpy
15import types
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010016
Richard Oudkerkb1694cf2013-10-16 16:41:56 +010017from . import get_start_method, set_start_method
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010018from . import process
19from . 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):
67 main()
68 sys.exit()
69
70
Richard Oudkerk7d2d43c2013-08-22 11:38:57 +010071def get_command_line(**kwds):
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010072 '''
73 Returns prefix of command line used for spawning a child process
74 '''
75 if getattr(sys, 'frozen', False):
76 return [sys.executable, '--multiprocessing-fork']
77 else:
Richard Oudkerk7d2d43c2013-08-22 11:38:57 +010078 prog = 'from multiprocessing.spawn import spawn_main; spawn_main(%s)'
79 prog %= ', '.join('%s=%r' % item for item in kwds.items())
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010080 opts = util._args_from_interpreter_flags()
81 return [_python_exe] + opts + ['-c', prog, '--multiprocessing-fork']
82
83
Richard Oudkerk7d2d43c2013-08-22 11:38:57 +010084def spawn_main(pipe_handle, parent_pid=None, tracker_fd=None):
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010085 '''
86 Run code specifed by data received over pipe
87 '''
88 assert is_forking(sys.argv)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010089 if sys.platform == 'win32':
90 import msvcrt
91 from .reduction import steal_handle
Richard Oudkerk7d2d43c2013-08-22 11:38:57 +010092 new_handle = steal_handle(parent_pid, pipe_handle)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010093 fd = msvcrt.open_osfhandle(new_handle, os.O_RDONLY)
94 else:
Richard Oudkerk7d2d43c2013-08-22 11:38:57 +010095 from . import semaphore_tracker
Richard Oudkerkb1694cf2013-10-16 16:41:56 +010096 semaphore_tracker._semaphore_tracker._fd = tracker_fd
Richard Oudkerk7d2d43c2013-08-22 11:38:57 +010097 fd = pipe_handle
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010098 exitcode = _main(fd)
99 sys.exit(exitcode)
100
101
102def _main(fd):
103 with os.fdopen(fd, 'rb', closefd=True) as from_parent:
104 process.current_process()._inheriting = True
105 try:
106 preparation_data = pickle.load(from_parent)
107 prepare(preparation_data)
108 self = pickle.load(from_parent)
109 finally:
110 del process.current_process()._inheriting
111 return self._bootstrap()
112
113
114def _check_not_importing_main():
115 if getattr(process.current_process(), '_inheriting', False):
116 raise RuntimeError('''
117 An attempt has been made to start a new process before the
118 current process has finished its bootstrapping phase.
119
120 This probably means that you are not using fork to start your
121 child processes and you have forgotten to use the proper idiom
122 in the main module:
123
124 if __name__ == '__main__':
125 freeze_support()
126 ...
127
128 The "freeze_support()" line can be omitted if the program
129 is not going to be frozen to produce an executable.''')
130
131
132def get_preparation_data(name):
133 '''
134 Return info about parent needed by child to unpickle process object
135 '''
136 _check_not_importing_main()
137 d = dict(
138 log_to_stderr=util._log_to_stderr,
139 authkey=process.current_process().authkey,
140 )
141
142 if util._logger is not None:
143 d['log_level'] = util._logger.getEffectiveLevel()
144
145 sys_path=sys.path.copy()
146 try:
147 i = sys_path.index('')
148 except ValueError:
149 pass
150 else:
151 sys_path[i] = process.ORIGINAL_DIR
152
153 d.update(
154 name=name,
155 sys_path=sys_path,
156 sys_argv=sys.argv,
157 orig_dir=process.ORIGINAL_DIR,
158 dir=os.getcwd(),
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100159 start_method=get_start_method(),
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100160 )
161
Nick Coghlan9a767352013-12-17 22:17:26 +1000162 # Figure out whether to initialise main in the subprocess as a module
163 # or through direct execution (or to leave it alone entirely)
164 main_module = sys.modules['__main__']
165 main_mod_name = getattr(main_module.__spec__, "name", None)
166 if main_mod_name is not None:
167 d['init_main_from_name'] = main_mod_name
168 elif sys.platform != 'win32' or (not WINEXE and not WINSERVICE):
169 main_path = getattr(main_module, '__file__', None)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100170 if main_path is not None:
171 if (not os.path.isabs(main_path) and
172 process.ORIGINAL_DIR is not None):
173 main_path = os.path.join(process.ORIGINAL_DIR, main_path)
Nick Coghlan9a767352013-12-17 22:17:26 +1000174 d['init_main_from_path'] = os.path.normpath(main_path)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100175
176 return d
177
178#
179# Prepare current process
180#
181
182old_main_modules = []
183
184def prepare(data):
185 '''
186 Try to get current process ready to unpickle process object
187 '''
188 if 'name' in data:
189 process.current_process().name = data['name']
190
191 if 'authkey' in data:
192 process.current_process().authkey = data['authkey']
193
194 if 'log_to_stderr' in data and data['log_to_stderr']:
195 util.log_to_stderr()
196
197 if 'log_level' in data:
198 util.get_logger().setLevel(data['log_level'])
199
200 if 'sys_path' in data:
201 sys.path = data['sys_path']
202
203 if 'sys_argv' in data:
204 sys.argv = data['sys_argv']
205
206 if 'dir' in data:
207 os.chdir(data['dir'])
208
209 if 'orig_dir' in data:
210 process.ORIGINAL_DIR = data['orig_dir']
211
212 if 'start_method' in data:
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100213 set_start_method(data['start_method'])
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100214
Nick Coghlan9a767352013-12-17 22:17:26 +1000215 if 'init_main_from_name' in data:
216 _fixup_main_from_name(data['init_main_from_name'])
217 elif 'init_main_from_path' in data:
218 _fixup_main_from_path(data['init_main_from_path'])
219
220# Multiprocessing module helpers to fix up the main module in
221# spawned subprocesses
222def _fixup_main_from_name(mod_name):
223 # __main__.py files for packages, directories, zip archives, etc, run
224 # their "main only" code unconditionally, so we don't even try to
225 # populate anything in __main__, nor do we make any changes to
226 # __main__ attributes
227 current_main = sys.modules['__main__']
228 if mod_name == "__main__" or mod_name.endswith(".__main__"):
229 return
230
231 # If this process was forked, __main__ may already be populated
232 if getattr(current_main.__spec__, "name", None) == mod_name:
233 return
234
235 # Otherwise, __main__ may contain some non-main code where we need to
236 # support unpickling it properly. We rerun it as __mp_main__ and make
237 # the normal __main__ an alias to that
238 old_main_modules.append(current_main)
239 main_module = types.ModuleType("__mp_main__")
240 main_content = runpy.run_module(mod_name,
241 run_name="__mp_main__",
242 alter_sys=True)
243 main_module.__dict__.update(main_content)
244 sys.modules['__main__'] = sys.modules['__mp_main__'] = main_module
245
246
247def _fixup_main_from_path(main_path):
248 # If this process was forked, __main__ may already be populated
249 current_main = sys.modules['__main__']
250
251 # Unfortunately, the main ipython launch script historically had no
252 # "if __name__ == '__main__'" guard, so we work around that
253 # by treating it like a __main__.py file
254 # See https://github.com/ipython/ipython/issues/4698
255 main_name = os.path.splitext(os.path.basename(main_path))[0]
256 if main_name == 'ipython':
257 return
258
259 # Otherwise, if __file__ already has the setting we expect,
260 # there's nothing more to do
261 if getattr(current_main, '__file__', None) == main_path:
262 return
263
264 # If the parent process has sent a path through rather than a module
265 # name we assume it is an executable script that may contain
266 # non-main code that needs to be executed
267 old_main_modules.append(current_main)
268 main_module = types.ModuleType("__mp_main__")
269 main_content = runpy.run_path(main_path,
270 run_name="__mp_main__")
271 main_module.__dict__.update(main_content)
272 sys.modules['__main__'] = sys.modules['__mp_main__'] = main_module
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100273
274
275def import_main_path(main_path):
276 '''
277 Set sys.modules['__main__'] to module at main_path
278 '''
Nick Coghlan9a767352013-12-17 22:17:26 +1000279 _fixup_main_from_path(main_path)