ยปCore Development>Code coverage>Lib/multiprocessing/spawn.py

Python code coverage for Lib/multiprocessing/spawn.py

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