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

Python code coverage for Lib/multiprocessing/process.py

#countcontent
1n/a#
2n/a# Module providing the `Process` class which emulates `threading.Thread`
3n/a#
4n/a# multiprocessing/process.py
5n/a#
6n/a# Copyright (c) 2006-2008, R Oudkerk
7n/a# Licensed to PSF under a Contributor Agreement.
8n/a#
9n/a
10n/a__all__ = ['BaseProcess', 'current_process', 'active_children']
11n/a
12n/a#
13n/a# Imports
14n/a#
15n/a
16n/aimport os
17n/aimport sys
18n/aimport signal
19n/aimport itertools
20n/afrom _weakrefset import WeakSet
21n/a
22n/a#
23n/a#
24n/a#
25n/a
26n/atry:
27n/a ORIGINAL_DIR = os.path.abspath(os.getcwd())
28n/aexcept OSError:
29n/a ORIGINAL_DIR = None
30n/a
31n/a#
32n/a# Public functions
33n/a#
34n/a
35n/adef current_process():
36n/a '''
37n/a Return process object representing the current process
38n/a '''
39n/a return _current_process
40n/a
41n/adef active_children():
42n/a '''
43n/a Return list of process objects corresponding to live child processes
44n/a '''
45n/a _cleanup()
46n/a return list(_children)
47n/a
48n/a#
49n/a#
50n/a#
51n/a
52n/adef _cleanup():
53n/a # check for processes which have finished
54n/a for p in list(_children):
55n/a if p._popen.poll() is not None:
56n/a _children.discard(p)
57n/a
58n/a#
59n/a# The `Process` class
60n/a#
61n/a
62n/aclass BaseProcess(object):
63n/a '''
64n/a Process objects represent activity that is run in a separate process
65n/a
66n/a The class is analogous to `threading.Thread`
67n/a '''
68n/a def _Popen(self):
69n/a raise NotImplementedError
70n/a
71n/a def __init__(self, group=None, target=None, name=None, args=(), kwargs={},
72n/a *, daemon=None):
73n/a assert group is None, 'group argument must be None for now'
74n/a count = next(_process_counter)
75n/a self._identity = _current_process._identity + (count,)
76n/a self._config = _current_process._config.copy()
77n/a self._parent_pid = os.getpid()
78n/a self._popen = None
79n/a self._target = target
80n/a self._args = tuple(args)
81n/a self._kwargs = dict(kwargs)
82n/a self._name = name or type(self).__name__ + '-' + \
83n/a ':'.join(str(i) for i in self._identity)
84n/a if daemon is not None:
85n/a self.daemon = daemon
86n/a _dangling.add(self)
87n/a
88n/a def run(self):
89n/a '''
90n/a Method to be run in sub-process; can be overridden in sub-class
91n/a '''
92n/a if self._target:
93n/a self._target(*self._args, **self._kwargs)
94n/a
95n/a def start(self):
96n/a '''
97n/a Start child process
98n/a '''
99n/a assert self._popen is None, 'cannot start a process twice'
100n/a assert self._parent_pid == os.getpid(), \
101n/a 'can only start a process object created by current process'
102n/a assert not _current_process._config.get('daemon'), \
103n/a 'daemonic processes are not allowed to have children'
104n/a _cleanup()
105n/a self._popen = self._Popen(self)
106n/a self._sentinel = self._popen.sentinel
107n/a _children.add(self)
108n/a
109n/a def terminate(self):
110n/a '''
111n/a Terminate process; sends SIGTERM signal or uses TerminateProcess()
112n/a '''
113n/a self._popen.terminate()
114n/a
115n/a def join(self, timeout=None):
116n/a '''
117n/a Wait until child process terminates
118n/a '''
119n/a assert self._parent_pid == os.getpid(), 'can only join a child process'
120n/a assert self._popen is not None, 'can only join a started process'
121n/a res = self._popen.wait(timeout)
122n/a if res is not None:
123n/a _children.discard(self)
124n/a
125n/a def is_alive(self):
126n/a '''
127n/a Return whether process is alive
128n/a '''
129n/a if self is _current_process:
130n/a return True
131n/a assert self._parent_pid == os.getpid(), 'can only test a child process'
132n/a if self._popen is None:
133n/a return False
134n/a self._popen.poll()
135n/a return self._popen.returncode is None
136n/a
137n/a @property
138n/a def name(self):
139n/a return self._name
140n/a
141n/a @name.setter
142n/a def name(self, name):
143n/a assert isinstance(name, str), 'name must be a string'
144n/a self._name = name
145n/a
146n/a @property
147n/a def daemon(self):
148n/a '''
149n/a Return whether process is a daemon
150n/a '''
151n/a return self._config.get('daemon', False)
152n/a
153n/a @daemon.setter
154n/a def daemon(self, daemonic):
155n/a '''
156n/a Set whether process is a daemon
157n/a '''
158n/a assert self._popen is None, 'process has already started'
159n/a self._config['daemon'] = daemonic
160n/a
161n/a @property
162n/a def authkey(self):
163n/a return self._config['authkey']
164n/a
165n/a @authkey.setter
166n/a def authkey(self, authkey):
167n/a '''
168n/a Set authorization key of process
169n/a '''
170n/a self._config['authkey'] = AuthenticationString(authkey)
171n/a
172n/a @property
173n/a def exitcode(self):
174n/a '''
175n/a Return exit code of process or `None` if it has yet to stop
176n/a '''
177n/a if self._popen is None:
178n/a return self._popen
179n/a return self._popen.poll()
180n/a
181n/a @property
182n/a def ident(self):
183n/a '''
184n/a Return identifier (PID) of process or `None` if it has yet to start
185n/a '''
186n/a if self is _current_process:
187n/a return os.getpid()
188n/a else:
189n/a return self._popen and self._popen.pid
190n/a
191n/a pid = ident
192n/a
193n/a @property
194n/a def sentinel(self):
195n/a '''
196n/a Return a file descriptor (Unix) or handle (Windows) suitable for
197n/a waiting for process termination.
198n/a '''
199n/a try:
200n/a return self._sentinel
201n/a except AttributeError:
202n/a raise ValueError("process not started")
203n/a
204n/a def __repr__(self):
205n/a if self is _current_process:
206n/a status = 'started'
207n/a elif self._parent_pid != os.getpid():
208n/a status = 'unknown'
209n/a elif self._popen is None:
210n/a status = 'initial'
211n/a else:
212n/a if self._popen.poll() is not None:
213n/a status = self.exitcode
214n/a else:
215n/a status = 'started'
216n/a
217n/a if type(status) is int:
218n/a if status == 0:
219n/a status = 'stopped'
220n/a else:
221n/a status = 'stopped[%s]' % _exitcode_to_name.get(status, status)
222n/a
223n/a return '<%s(%s, %s%s)>' % (type(self).__name__, self._name,
224n/a status, self.daemon and ' daemon' or '')
225n/a
226n/a ##
227n/a
228n/a def _bootstrap(self):
229n/a from . import util, context
230n/a global _current_process, _process_counter, _children
231n/a
232n/a try:
233n/a if self._start_method is not None:
234n/a context._force_start_method(self._start_method)
235n/a _process_counter = itertools.count(1)
236n/a _children = set()
237n/a util._close_stdin()
238n/a old_process = _current_process
239n/a _current_process = self
240n/a try:
241n/a util._finalizer_registry.clear()
242n/a util._run_after_forkers()
243n/a finally:
244n/a # delay finalization of the old process object until after
245n/a # _run_after_forkers() is executed
246n/a del old_process
247n/a util.info('child process calling self.run()')
248n/a try:
249n/a self.run()
250n/a exitcode = 0
251n/a finally:
252n/a util._exit_function()
253n/a except SystemExit as e:
254n/a if not e.args:
255n/a exitcode = 1
256n/a elif isinstance(e.args[0], int):
257n/a exitcode = e.args[0]
258n/a else:
259n/a sys.stderr.write(str(e.args[0]) + '\n')
260n/a exitcode = 1
261n/a except:
262n/a exitcode = 1
263n/a import traceback
264n/a sys.stderr.write('Process %s:\n' % self.name)
265n/a traceback.print_exc()
266n/a finally:
267n/a util.info('process exiting with exitcode %d' % exitcode)
268n/a sys.stdout.flush()
269n/a sys.stderr.flush()
270n/a
271n/a return exitcode
272n/a
273n/a#
274n/a# We subclass bytes to avoid accidental transmission of auth keys over network
275n/a#
276n/a
277n/aclass AuthenticationString(bytes):
278n/a def __reduce__(self):
279n/a from .context import get_spawning_popen
280n/a if get_spawning_popen() is None:
281n/a raise TypeError(
282n/a 'Pickling an AuthenticationString object is '
283n/a 'disallowed for security reasons'
284n/a )
285n/a return AuthenticationString, (bytes(self),)
286n/a
287n/a#
288n/a# Create object representing the main process
289n/a#
290n/a
291n/aclass _MainProcess(BaseProcess):
292n/a
293n/a def __init__(self):
294n/a self._identity = ()
295n/a self._name = 'MainProcess'
296n/a self._parent_pid = None
297n/a self._popen = None
298n/a self._config = {'authkey': AuthenticationString(os.urandom(32)),
299n/a 'semprefix': '/mp'}
300n/a # Note that some versions of FreeBSD only allow named
301n/a # semaphores to have names of up to 14 characters. Therefore
302n/a # we choose a short prefix.
303n/a #
304n/a # On MacOSX in a sandbox it may be necessary to use a
305n/a # different prefix -- see #19478.
306n/a #
307n/a # Everything in self._config will be inherited by descendant
308n/a # processes.
309n/a
310n/a
311n/a_current_process = _MainProcess()
312n/a_process_counter = itertools.count(1)
313n/a_children = set()
314n/adel _MainProcess
315n/a
316n/a#
317n/a# Give names to some return codes
318n/a#
319n/a
320n/a_exitcode_to_name = {}
321n/a
322n/afor name, signum in list(signal.__dict__.items()):
323n/a if name[:3]=='SIG' and '_' not in name:
324n/a _exitcode_to_name[-signum] = name
325n/a
326n/a# For debug and leak testing
327n/a_dangling = WeakSet()