mirror of
https://github.com/python/cpython.git
synced 2025-08-04 17:08:35 +00:00
gh-131913: multiprocessing: add interrupt for POSIX (GH-132453)
* multiprocessing: interrupt Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
This commit is contained in:
parent
862fd89036
commit
77605fa3bb
6 changed files with 54 additions and 2 deletions
|
@ -670,6 +670,25 @@ The :mod:`multiprocessing` package mostly replicates the API of the
|
|||
|
||||
.. versionadded:: 3.3
|
||||
|
||||
.. method:: interrupt()
|
||||
|
||||
Terminate the process. Works on POSIX using the :py:const:`~signal.SIGINT` signal.
|
||||
Behavior on Windows is undefined.
|
||||
|
||||
By default, this terminates the child process by raising :exc:`KeyboardInterrupt`.
|
||||
This behavior can be altered by setting the respective signal handler in the child
|
||||
process :func:`signal.signal` for :py:const:`~signal.SIGINT`.
|
||||
|
||||
Note: if the child process catches and discards :exc:`KeyboardInterrupt`, the
|
||||
process will not be terminated.
|
||||
|
||||
Note: the default behavior will also set :attr:`exitcode` to ``1`` as if an
|
||||
uncaught exception was raised in the child process. To have a different
|
||||
:attr:`exitcode` you may simply catch :exc:`KeyboardInterrupt` and call
|
||||
``exit(your_code)``.
|
||||
|
||||
.. versionadded:: next
|
||||
|
||||
.. method:: terminate()
|
||||
|
||||
Terminate the process. On POSIX this is done using the :py:const:`~signal.SIGTERM` signal;
|
||||
|
|
|
@ -972,6 +972,10 @@ multiprocessing
|
|||
The :func:`set` in :func:`multiprocessing.Manager` method is now available.
|
||||
(Contributed by Mingyu Park in :gh:`129949`.)
|
||||
|
||||
* Add :func:`multiprocessing.Process.interrupt` which terminates the child
|
||||
process by sending :py:const:`~signal.SIGINT`. This enables "finally" clauses
|
||||
and printing stack trace for the terminated process.
|
||||
(Contributed by Artem Pulkin in :gh:`131913`.)
|
||||
|
||||
operator
|
||||
--------
|
||||
|
|
|
@ -54,6 +54,9 @@ class Popen(object):
|
|||
if self.wait(timeout=0.1) is None:
|
||||
raise
|
||||
|
||||
def interrupt(self):
|
||||
self._send_signal(signal.SIGINT)
|
||||
|
||||
def terminate(self):
|
||||
self._send_signal(signal.SIGTERM)
|
||||
|
||||
|
|
|
@ -125,6 +125,13 @@ class BaseProcess(object):
|
|||
del self._target, self._args, self._kwargs
|
||||
_children.add(self)
|
||||
|
||||
def interrupt(self):
|
||||
'''
|
||||
Terminate process; sends SIGINT signal
|
||||
'''
|
||||
self._check_closed()
|
||||
self._popen.interrupt()
|
||||
|
||||
def terminate(self):
|
||||
'''
|
||||
Terminate process; sends SIGTERM signal or uses TerminateProcess()
|
||||
|
|
|
@ -512,15 +512,20 @@ class _TestProcess(BaseTestCase):
|
|||
def _sleep_some(cls):
|
||||
time.sleep(100)
|
||||
|
||||
@classmethod
|
||||
def _sleep_no_int_handler(cls):
|
||||
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||
cls._sleep_some()
|
||||
|
||||
@classmethod
|
||||
def _test_sleep(cls, delay):
|
||||
time.sleep(delay)
|
||||
|
||||
def _kill_process(self, meth):
|
||||
def _kill_process(self, meth, target=None):
|
||||
if self.TYPE == 'threads':
|
||||
self.skipTest('test not appropriate for {}'.format(self.TYPE))
|
||||
|
||||
p = self.Process(target=self._sleep_some)
|
||||
p = self.Process(target=target or self._sleep_some)
|
||||
p.daemon = True
|
||||
p.start()
|
||||
|
||||
|
@ -567,6 +572,19 @@ class _TestProcess(BaseTestCase):
|
|||
|
||||
return p.exitcode
|
||||
|
||||
@unittest.skipIf(os.name == 'nt', "POSIX only")
|
||||
def test_interrupt(self):
|
||||
exitcode = self._kill_process(multiprocessing.Process.interrupt)
|
||||
self.assertEqual(exitcode, 1)
|
||||
# exit code 1 is hard-coded for uncaught exceptions
|
||||
# (KeyboardInterrupt in this case)
|
||||
# in multiprocessing.BaseProcess._bootstrap
|
||||
|
||||
@unittest.skipIf(os.name == 'nt', "POSIX only")
|
||||
def test_interrupt_no_handler(self):
|
||||
exitcode = self._kill_process(multiprocessing.Process.interrupt, target=self._sleep_no_int_handler)
|
||||
self.assertEqual(exitcode, -signal.SIGINT)
|
||||
|
||||
def test_terminate(self):
|
||||
exitcode = self._kill_process(multiprocessing.Process.terminate)
|
||||
self.assertEqual(exitcode, -signal.SIGTERM)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Add a shortcut function :func:`multiprocessing.Process.interrupt` alongside the existing :func:`multiprocessing.Process.terminate` and :func:`multiprocessing.Process.kill` for an improved control over child process termination.
|
Loading…
Add table
Add a link
Reference in a new issue