annotate CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/multiprocessing/spawn.py @ 69:33d812a61356

planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author jpayne
date Tue, 18 Mar 2025 17:55:14 -0400
parents
children
rev   line source
jpayne@69 1 #
jpayne@69 2 # Code used to start processes when using the spawn or forkserver
jpayne@69 3 # start methods.
jpayne@69 4 #
jpayne@69 5 # multiprocessing/spawn.py
jpayne@69 6 #
jpayne@69 7 # Copyright (c) 2006-2008, R Oudkerk
jpayne@69 8 # Licensed to PSF under a Contributor Agreement.
jpayne@69 9 #
jpayne@69 10
jpayne@69 11 import os
jpayne@69 12 import sys
jpayne@69 13 import runpy
jpayne@69 14 import types
jpayne@69 15
jpayne@69 16 from . import get_start_method, set_start_method
jpayne@69 17 from . import process
jpayne@69 18 from .context import reduction
jpayne@69 19 from . import util
jpayne@69 20
jpayne@69 21 __all__ = ['_main', 'freeze_support', 'set_executable', 'get_executable',
jpayne@69 22 'get_preparation_data', 'get_command_line', 'import_main_path']
jpayne@69 23
jpayne@69 24 #
jpayne@69 25 # _python_exe is the assumed path to the python executable.
jpayne@69 26 # People embedding Python want to modify it.
jpayne@69 27 #
jpayne@69 28
jpayne@69 29 if sys.platform != 'win32':
jpayne@69 30 WINEXE = False
jpayne@69 31 WINSERVICE = False
jpayne@69 32 else:
jpayne@69 33 WINEXE = getattr(sys, 'frozen', False)
jpayne@69 34 WINSERVICE = sys.executable.lower().endswith("pythonservice.exe")
jpayne@69 35
jpayne@69 36 if WINSERVICE:
jpayne@69 37 _python_exe = os.path.join(sys.exec_prefix, 'python.exe')
jpayne@69 38 else:
jpayne@69 39 _python_exe = sys._base_executable
jpayne@69 40
jpayne@69 41 def set_executable(exe):
jpayne@69 42 global _python_exe
jpayne@69 43 _python_exe = exe
jpayne@69 44
jpayne@69 45 def get_executable():
jpayne@69 46 return _python_exe
jpayne@69 47
jpayne@69 48 #
jpayne@69 49 #
jpayne@69 50 #
jpayne@69 51
jpayne@69 52 def is_forking(argv):
jpayne@69 53 '''
jpayne@69 54 Return whether commandline indicates we are forking
jpayne@69 55 '''
jpayne@69 56 if len(argv) >= 2 and argv[1] == '--multiprocessing-fork':
jpayne@69 57 return True
jpayne@69 58 else:
jpayne@69 59 return False
jpayne@69 60
jpayne@69 61
jpayne@69 62 def freeze_support():
jpayne@69 63 '''
jpayne@69 64 Run code for process object if this in not the main process
jpayne@69 65 '''
jpayne@69 66 if is_forking(sys.argv):
jpayne@69 67 kwds = {}
jpayne@69 68 for arg in sys.argv[2:]:
jpayne@69 69 name, value = arg.split('=')
jpayne@69 70 if value == 'None':
jpayne@69 71 kwds[name] = None
jpayne@69 72 else:
jpayne@69 73 kwds[name] = int(value)
jpayne@69 74 spawn_main(**kwds)
jpayne@69 75 sys.exit()
jpayne@69 76
jpayne@69 77
jpayne@69 78 def get_command_line(**kwds):
jpayne@69 79 '''
jpayne@69 80 Returns prefix of command line used for spawning a child process
jpayne@69 81 '''
jpayne@69 82 if getattr(sys, 'frozen', False):
jpayne@69 83 return ([sys.executable, '--multiprocessing-fork'] +
jpayne@69 84 ['%s=%r' % item for item in kwds.items()])
jpayne@69 85 else:
jpayne@69 86 prog = 'from multiprocessing.spawn import spawn_main; spawn_main(%s)'
jpayne@69 87 prog %= ', '.join('%s=%r' % item for item in kwds.items())
jpayne@69 88 opts = util._args_from_interpreter_flags()
jpayne@69 89 return [_python_exe] + opts + ['-c', prog, '--multiprocessing-fork']
jpayne@69 90
jpayne@69 91
jpayne@69 92 def spawn_main(pipe_handle, parent_pid=None, tracker_fd=None):
jpayne@69 93 '''
jpayne@69 94 Run code specified by data received over pipe
jpayne@69 95 '''
jpayne@69 96 assert is_forking(sys.argv), "Not forking"
jpayne@69 97 if sys.platform == 'win32':
jpayne@69 98 import msvcrt
jpayne@69 99 import _winapi
jpayne@69 100
jpayne@69 101 if parent_pid is not None:
jpayne@69 102 source_process = _winapi.OpenProcess(
jpayne@69 103 _winapi.SYNCHRONIZE | _winapi.PROCESS_DUP_HANDLE,
jpayne@69 104 False, parent_pid)
jpayne@69 105 else:
jpayne@69 106 source_process = None
jpayne@69 107 new_handle = reduction.duplicate(pipe_handle,
jpayne@69 108 source_process=source_process)
jpayne@69 109 fd = msvcrt.open_osfhandle(new_handle, os.O_RDONLY)
jpayne@69 110 parent_sentinel = source_process
jpayne@69 111 else:
jpayne@69 112 from . import resource_tracker
jpayne@69 113 resource_tracker._resource_tracker._fd = tracker_fd
jpayne@69 114 fd = pipe_handle
jpayne@69 115 parent_sentinel = os.dup(pipe_handle)
jpayne@69 116 exitcode = _main(fd, parent_sentinel)
jpayne@69 117 sys.exit(exitcode)
jpayne@69 118
jpayne@69 119
jpayne@69 120 def _main(fd, parent_sentinel):
jpayne@69 121 with os.fdopen(fd, 'rb', closefd=True) as from_parent:
jpayne@69 122 process.current_process()._inheriting = True
jpayne@69 123 try:
jpayne@69 124 preparation_data = reduction.pickle.load(from_parent)
jpayne@69 125 prepare(preparation_data)
jpayne@69 126 self = reduction.pickle.load(from_parent)
jpayne@69 127 finally:
jpayne@69 128 del process.current_process()._inheriting
jpayne@69 129 return self._bootstrap(parent_sentinel)
jpayne@69 130
jpayne@69 131
jpayne@69 132 def _check_not_importing_main():
jpayne@69 133 if getattr(process.current_process(), '_inheriting', False):
jpayne@69 134 raise RuntimeError('''
jpayne@69 135 An attempt has been made to start a new process before the
jpayne@69 136 current process has finished its bootstrapping phase.
jpayne@69 137
jpayne@69 138 This probably means that you are not using fork to start your
jpayne@69 139 child processes and you have forgotten to use the proper idiom
jpayne@69 140 in the main module:
jpayne@69 141
jpayne@69 142 if __name__ == '__main__':
jpayne@69 143 freeze_support()
jpayne@69 144 ...
jpayne@69 145
jpayne@69 146 The "freeze_support()" line can be omitted if the program
jpayne@69 147 is not going to be frozen to produce an executable.''')
jpayne@69 148
jpayne@69 149
jpayne@69 150 def get_preparation_data(name):
jpayne@69 151 '''
jpayne@69 152 Return info about parent needed by child to unpickle process object
jpayne@69 153 '''
jpayne@69 154 _check_not_importing_main()
jpayne@69 155 d = dict(
jpayne@69 156 log_to_stderr=util._log_to_stderr,
jpayne@69 157 authkey=process.current_process().authkey,
jpayne@69 158 )
jpayne@69 159
jpayne@69 160 if util._logger is not None:
jpayne@69 161 d['log_level'] = util._logger.getEffectiveLevel()
jpayne@69 162
jpayne@69 163 sys_path=sys.path.copy()
jpayne@69 164 try:
jpayne@69 165 i = sys_path.index('')
jpayne@69 166 except ValueError:
jpayne@69 167 pass
jpayne@69 168 else:
jpayne@69 169 sys_path[i] = process.ORIGINAL_DIR
jpayne@69 170
jpayne@69 171 d.update(
jpayne@69 172 name=name,
jpayne@69 173 sys_path=sys_path,
jpayne@69 174 sys_argv=sys.argv,
jpayne@69 175 orig_dir=process.ORIGINAL_DIR,
jpayne@69 176 dir=os.getcwd(),
jpayne@69 177 start_method=get_start_method(),
jpayne@69 178 )
jpayne@69 179
jpayne@69 180 # Figure out whether to initialise main in the subprocess as a module
jpayne@69 181 # or through direct execution (or to leave it alone entirely)
jpayne@69 182 main_module = sys.modules['__main__']
jpayne@69 183 main_mod_name = getattr(main_module.__spec__, "name", None)
jpayne@69 184 if main_mod_name is not None:
jpayne@69 185 d['init_main_from_name'] = main_mod_name
jpayne@69 186 elif sys.platform != 'win32' or (not WINEXE and not WINSERVICE):
jpayne@69 187 main_path = getattr(main_module, '__file__', None)
jpayne@69 188 if main_path is not None:
jpayne@69 189 if (not os.path.isabs(main_path) and
jpayne@69 190 process.ORIGINAL_DIR is not None):
jpayne@69 191 main_path = os.path.join(process.ORIGINAL_DIR, main_path)
jpayne@69 192 d['init_main_from_path'] = os.path.normpath(main_path)
jpayne@69 193
jpayne@69 194 return d
jpayne@69 195
jpayne@69 196 #
jpayne@69 197 # Prepare current process
jpayne@69 198 #
jpayne@69 199
jpayne@69 200 old_main_modules = []
jpayne@69 201
jpayne@69 202 def prepare(data):
jpayne@69 203 '''
jpayne@69 204 Try to get current process ready to unpickle process object
jpayne@69 205 '''
jpayne@69 206 if 'name' in data:
jpayne@69 207 process.current_process().name = data['name']
jpayne@69 208
jpayne@69 209 if 'authkey' in data:
jpayne@69 210 process.current_process().authkey = data['authkey']
jpayne@69 211
jpayne@69 212 if 'log_to_stderr' in data and data['log_to_stderr']:
jpayne@69 213 util.log_to_stderr()
jpayne@69 214
jpayne@69 215 if 'log_level' in data:
jpayne@69 216 util.get_logger().setLevel(data['log_level'])
jpayne@69 217
jpayne@69 218 if 'sys_path' in data:
jpayne@69 219 sys.path = data['sys_path']
jpayne@69 220
jpayne@69 221 if 'sys_argv' in data:
jpayne@69 222 sys.argv = data['sys_argv']
jpayne@69 223
jpayne@69 224 if 'dir' in data:
jpayne@69 225 os.chdir(data['dir'])
jpayne@69 226
jpayne@69 227 if 'orig_dir' in data:
jpayne@69 228 process.ORIGINAL_DIR = data['orig_dir']
jpayne@69 229
jpayne@69 230 if 'start_method' in data:
jpayne@69 231 set_start_method(data['start_method'], force=True)
jpayne@69 232
jpayne@69 233 if 'init_main_from_name' in data:
jpayne@69 234 _fixup_main_from_name(data['init_main_from_name'])
jpayne@69 235 elif 'init_main_from_path' in data:
jpayne@69 236 _fixup_main_from_path(data['init_main_from_path'])
jpayne@69 237
jpayne@69 238 # Multiprocessing module helpers to fix up the main module in
jpayne@69 239 # spawned subprocesses
jpayne@69 240 def _fixup_main_from_name(mod_name):
jpayne@69 241 # __main__.py files for packages, directories, zip archives, etc, run
jpayne@69 242 # their "main only" code unconditionally, so we don't even try to
jpayne@69 243 # populate anything in __main__, nor do we make any changes to
jpayne@69 244 # __main__ attributes
jpayne@69 245 current_main = sys.modules['__main__']
jpayne@69 246 if mod_name == "__main__" or mod_name.endswith(".__main__"):
jpayne@69 247 return
jpayne@69 248
jpayne@69 249 # If this process was forked, __main__ may already be populated
jpayne@69 250 if getattr(current_main.__spec__, "name", None) == mod_name:
jpayne@69 251 return
jpayne@69 252
jpayne@69 253 # Otherwise, __main__ may contain some non-main code where we need to
jpayne@69 254 # support unpickling it properly. We rerun it as __mp_main__ and make
jpayne@69 255 # the normal __main__ an alias to that
jpayne@69 256 old_main_modules.append(current_main)
jpayne@69 257 main_module = types.ModuleType("__mp_main__")
jpayne@69 258 main_content = runpy.run_module(mod_name,
jpayne@69 259 run_name="__mp_main__",
jpayne@69 260 alter_sys=True)
jpayne@69 261 main_module.__dict__.update(main_content)
jpayne@69 262 sys.modules['__main__'] = sys.modules['__mp_main__'] = main_module
jpayne@69 263
jpayne@69 264
jpayne@69 265 def _fixup_main_from_path(main_path):
jpayne@69 266 # If this process was forked, __main__ may already be populated
jpayne@69 267 current_main = sys.modules['__main__']
jpayne@69 268
jpayne@69 269 # Unfortunately, the main ipython launch script historically had no
jpayne@69 270 # "if __name__ == '__main__'" guard, so we work around that
jpayne@69 271 # by treating it like a __main__.py file
jpayne@69 272 # See https://github.com/ipython/ipython/issues/4698
jpayne@69 273 main_name = os.path.splitext(os.path.basename(main_path))[0]
jpayne@69 274 if main_name == 'ipython':
jpayne@69 275 return
jpayne@69 276
jpayne@69 277 # Otherwise, if __file__ already has the setting we expect,
jpayne@69 278 # there's nothing more to do
jpayne@69 279 if getattr(current_main, '__file__', None) == main_path:
jpayne@69 280 return
jpayne@69 281
jpayne@69 282 # If the parent process has sent a path through rather than a module
jpayne@69 283 # name we assume it is an executable script that may contain
jpayne@69 284 # non-main code that needs to be executed
jpayne@69 285 old_main_modules.append(current_main)
jpayne@69 286 main_module = types.ModuleType("__mp_main__")
jpayne@69 287 main_content = runpy.run_path(main_path,
jpayne@69 288 run_name="__mp_main__")
jpayne@69 289 main_module.__dict__.update(main_content)
jpayne@69 290 sys.modules['__main__'] = sys.modules['__mp_main__'] = main_module
jpayne@69 291
jpayne@69 292
jpayne@69 293 def import_main_path(main_path):
jpayne@69 294 '''
jpayne@69 295 Set sys.modules['__main__'] to module at main_path
jpayne@69 296 '''
jpayne@69 297 _fixup_main_from_path(main_path)