blob: 364b53f499c4514a6b4a61e039c339321f5f0a7f [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
14
Richard Oudkerkb1694cf2013-10-16 16:41:56 +010015from . import get_start_method, set_start_method
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010016from . import process
17from . import util
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010018
19__all__ = ['_main', 'freeze_support', 'set_executable', 'get_executable',
20 'get_preparation_data', 'get_command_line', 'import_main_path']
21
22#
23# _python_exe is the assumed path to the python executable.
24# People embedding Python want to modify it.
25#
26
27if sys.platform != 'win32':
28 WINEXE = False
29 WINSERVICE = False
30else:
31 WINEXE = (sys.platform == 'win32' and getattr(sys, 'frozen', False))
32 WINSERVICE = sys.executable.lower().endswith("pythonservice.exe")
33
34if WINSERVICE:
35 _python_exe = os.path.join(sys.exec_prefix, 'python.exe')
36else:
37 _python_exe = sys.executable
38
39def set_executable(exe):
40 global _python_exe
41 _python_exe = exe
42
43def get_executable():
44 return _python_exe
45
46#
47#
48#
49
50def is_forking(argv):
51 '''
52 Return whether commandline indicates we are forking
53 '''
54 if len(argv) >= 2 and argv[1] == '--multiprocessing-fork':
55 return True
56 else:
57 return False
58
59
60def freeze_support():
61 '''
62 Run code for process object if this in not the main process
63 '''
64 if is_forking(sys.argv):
65 main()
66 sys.exit()
67
68
Richard Oudkerk7d2d43c2013-08-22 11:38:57 +010069def get_command_line(**kwds):
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010070 '''
71 Returns prefix of command line used for spawning a child process
72 '''
73 if getattr(sys, 'frozen', False):
74 return [sys.executable, '--multiprocessing-fork']
75 else:
Richard Oudkerk7d2d43c2013-08-22 11:38:57 +010076 prog = 'from multiprocessing.spawn import spawn_main; spawn_main(%s)'
77 prog %= ', '.join('%s=%r' % item for item in kwds.items())
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010078 opts = util._args_from_interpreter_flags()
79 return [_python_exe] + opts + ['-c', prog, '--multiprocessing-fork']
80
81
Richard Oudkerk7d2d43c2013-08-22 11:38:57 +010082def spawn_main(pipe_handle, parent_pid=None, tracker_fd=None):
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010083 '''
84 Run code specifed by data received over pipe
85 '''
86 assert is_forking(sys.argv)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010087 if sys.platform == 'win32':
88 import msvcrt
89 from .reduction import steal_handle
Richard Oudkerk7d2d43c2013-08-22 11:38:57 +010090 new_handle = steal_handle(parent_pid, pipe_handle)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010091 fd = msvcrt.open_osfhandle(new_handle, os.O_RDONLY)
92 else:
Richard Oudkerk7d2d43c2013-08-22 11:38:57 +010093 from . import semaphore_tracker
Richard Oudkerkb1694cf2013-10-16 16:41:56 +010094 semaphore_tracker._semaphore_tracker._fd = tracker_fd
Richard Oudkerk7d2d43c2013-08-22 11:38:57 +010095 fd = pipe_handle
Richard Oudkerk84ed9a62013-08-14 15:35:41 +010096 exitcode = _main(fd)
97 sys.exit(exitcode)
98
99
100def _main(fd):
101 with os.fdopen(fd, 'rb', closefd=True) as from_parent:
102 process.current_process()._inheriting = True
103 try:
104 preparation_data = pickle.load(from_parent)
105 prepare(preparation_data)
106 self = pickle.load(from_parent)
107 finally:
108 del process.current_process()._inheriting
109 return self._bootstrap()
110
111
112def _check_not_importing_main():
113 if getattr(process.current_process(), '_inheriting', False):
114 raise RuntimeError('''
115 An attempt has been made to start a new process before the
116 current process has finished its bootstrapping phase.
117
118 This probably means that you are not using fork to start your
119 child processes and you have forgotten to use the proper idiom
120 in the main module:
121
122 if __name__ == '__main__':
123 freeze_support()
124 ...
125
126 The "freeze_support()" line can be omitted if the program
127 is not going to be frozen to produce an executable.''')
128
129
130def get_preparation_data(name):
131 '''
132 Return info about parent needed by child to unpickle process object
133 '''
134 _check_not_importing_main()
135 d = dict(
136 log_to_stderr=util._log_to_stderr,
137 authkey=process.current_process().authkey,
138 )
139
140 if util._logger is not None:
141 d['log_level'] = util._logger.getEffectiveLevel()
142
143 sys_path=sys.path.copy()
144 try:
145 i = sys_path.index('')
146 except ValueError:
147 pass
148 else:
149 sys_path[i] = process.ORIGINAL_DIR
150
151 d.update(
152 name=name,
153 sys_path=sys_path,
154 sys_argv=sys.argv,
155 orig_dir=process.ORIGINAL_DIR,
156 dir=os.getcwd(),
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100157 start_method=get_start_method(),
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100158 )
159
160 if sys.platform != 'win32' or (not WINEXE and not WINSERVICE):
161 main_path = getattr(sys.modules['__main__'], '__file__', None)
162 if not main_path and sys.argv[0] not in ('', '-c'):
163 main_path = sys.argv[0]
164 if main_path is not None:
165 if (not os.path.isabs(main_path) and
166 process.ORIGINAL_DIR is not None):
167 main_path = os.path.join(process.ORIGINAL_DIR, main_path)
168 d['main_path'] = os.path.normpath(main_path)
169
170 return d
171
172#
173# Prepare current process
174#
175
176old_main_modules = []
177
178def prepare(data):
179 '''
180 Try to get current process ready to unpickle process object
181 '''
182 if 'name' in data:
183 process.current_process().name = data['name']
184
185 if 'authkey' in data:
186 process.current_process().authkey = data['authkey']
187
188 if 'log_to_stderr' in data and data['log_to_stderr']:
189 util.log_to_stderr()
190
191 if 'log_level' in data:
192 util.get_logger().setLevel(data['log_level'])
193
194 if 'sys_path' in data:
195 sys.path = data['sys_path']
196
197 if 'sys_argv' in data:
198 sys.argv = data['sys_argv']
199
200 if 'dir' in data:
201 os.chdir(data['dir'])
202
203 if 'orig_dir' in data:
204 process.ORIGINAL_DIR = data['orig_dir']
205
206 if 'start_method' in data:
Richard Oudkerkb1694cf2013-10-16 16:41:56 +0100207 set_start_method(data['start_method'])
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100208
209 if 'main_path' in data:
210 import_main_path(data['main_path'])
211
212
213def import_main_path(main_path):
214 '''
215 Set sys.modules['__main__'] to module at main_path
216 '''
217 # XXX (ncoghlan): The following code makes several bogus
218 # assumptions regarding the relationship between __file__
219 # and a module's real name. See PEP 302 and issue #10845
220 if getattr(sys.modules['__main__'], '__file__', None) == main_path:
221 return
222
223 main_name = os.path.splitext(os.path.basename(main_path))[0]
224 if main_name == '__init__':
225 main_name = os.path.basename(os.path.dirname(main_path))
226
227 if main_name == '__main__':
228 main_module = sys.modules['__main__']
229 main_module.__file__ = main_path
230 elif main_name != 'ipython':
231 # Main modules not actually called __main__.py may
232 # contain additional code that should still be executed
233 import importlib
234 import types
235
236 if main_path is None:
237 dirs = None
238 elif os.path.basename(main_path).startswith('__init__.py'):
239 dirs = [os.path.dirname(os.path.dirname(main_path))]
240 else:
241 dirs = [os.path.dirname(main_path)]
242
243 assert main_name not in sys.modules, main_name
244 sys.modules.pop('__mp_main__', None)
245 # We should not try to load __main__
246 # since that would execute 'if __name__ == "__main__"'
247 # clauses, potentially causing a psuedo fork bomb.
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100248 main_module = types.ModuleType(main_name)
Eric Snowb523f842013-11-22 09:05:39 -0700249 # XXX Use a target of main_module?
250 spec = importlib.find_spec(main_name, path=dirs)
251 methods = importlib._bootstrap._SpecMethods(spec)
252 methods.init_module_attrs(main_module)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100253 main_module.__name__ = '__mp_main__'
Eric Snowb523f842013-11-22 09:05:39 -0700254 code = spec.loader.get_code(main_name)
Richard Oudkerk84ed9a62013-08-14 15:35:41 +0100255 exec(code, main_module.__dict__)
256
257 old_main_modules.append(sys.modules['__main__'])
258 sys.modules['__main__'] = sys.modules['__mp_main__'] = main_module