.. 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;
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
--------
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()
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()
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)