Issue #6064: Add a `daemon` keyword argument to the threading.Thread
and multiprocessing.Process constructors in order to override the default behaviour of inheriting the daemonic property from the current thread/process.
This commit is contained in:
parent
4bc685752f
commit
0bd4deba38
|
@ -297,7 +297,7 @@ The :mod:`multiprocessing` package mostly replicates the API of the
|
||||||
:class:`Process` and exceptions
|
:class:`Process` and exceptions
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. class:: Process([group[, target[, name[, args[, kwargs]]]]])
|
.. class:: Process([group[, target[, name[, args[, kwargs]]]]], *, daemon=None)
|
||||||
|
|
||||||
Process objects represent activity that is run in a separate process. The
|
Process objects represent activity that is run in a separate process. The
|
||||||
:class:`Process` class has equivalents of all the methods of
|
:class:`Process` class has equivalents of all the methods of
|
||||||
|
@ -312,13 +312,19 @@ The :mod:`multiprocessing` package mostly replicates the API of the
|
||||||
:sub:`1`,N\ :sub:`2`,...,N\ :sub:`k` is a sequence of integers whose length
|
:sub:`1`,N\ :sub:`2`,...,N\ :sub:`k` is a sequence of integers whose length
|
||||||
is determined by the *generation* of the process. *args* is the argument
|
is determined by the *generation* of the process. *args* is the argument
|
||||||
tuple for the target invocation. *kwargs* is a dictionary of keyword
|
tuple for the target invocation. *kwargs* is a dictionary of keyword
|
||||||
arguments for the target invocation. By default, no arguments are passed to
|
arguments for the target invocation. If provided, the keyword-only *daemon* argument
|
||||||
*target*.
|
sets the process :attr:`daemon` flag to ``True`` or ``False``. If ``None``
|
||||||
|
(the default), this flag will be inherited from the creating process.
|
||||||
|
|
||||||
|
By default, no arguments are passed to *target*.
|
||||||
|
|
||||||
If a subclass overrides the constructor, it must make sure it invokes the
|
If a subclass overrides the constructor, it must make sure it invokes the
|
||||||
base class constructor (:meth:`Process.__init__`) before doing anything else
|
base class constructor (:meth:`Process.__init__`) before doing anything else
|
||||||
to the process.
|
to the process.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.3
|
||||||
|
Added the *daemon* argument.
|
||||||
|
|
||||||
.. method:: run()
|
.. method:: run()
|
||||||
|
|
||||||
Method representing the process's activity.
|
Method representing the process's activity.
|
||||||
|
|
|
@ -241,7 +241,7 @@ changed through the :attr:`name` attribute.
|
||||||
A thread can be flagged as a "daemon thread". The significance of this flag is
|
A thread can be flagged as a "daemon thread". The significance of this flag is
|
||||||
that the entire Python program exits when only daemon threads are left. The
|
that the entire Python program exits when only daemon threads are left. The
|
||||||
initial value is inherited from the creating thread. The flag can be set
|
initial value is inherited from the creating thread. The flag can be set
|
||||||
through the :attr:`daemon` property.
|
through the :attr:`daemon` property or the *daemon* constructor argument.
|
||||||
|
|
||||||
There is a "main thread" object; this corresponds to the initial thread of
|
There is a "main thread" object; this corresponds to the initial thread of
|
||||||
control in the Python program. It is not a daemon thread.
|
control in the Python program. It is not a daemon thread.
|
||||||
|
@ -254,7 +254,8 @@ daemonic, and cannot be :meth:`join`\ ed. They are never deleted, since it is
|
||||||
impossible to detect the termination of alien threads.
|
impossible to detect the termination of alien threads.
|
||||||
|
|
||||||
|
|
||||||
.. class:: Thread(group=None, target=None, name=None, args=(), kwargs={})
|
.. class:: Thread(group=None, target=None, name=None, args=(), kwargs={},
|
||||||
|
verbose=None, *, daemon=None)
|
||||||
|
|
||||||
This constructor should always be called with keyword arguments. Arguments
|
This constructor should always be called with keyword arguments. Arguments
|
||||||
are:
|
are:
|
||||||
|
@ -273,10 +274,19 @@ impossible to detect the termination of alien threads.
|
||||||
*kwargs* is a dictionary of keyword arguments for the target invocation.
|
*kwargs* is a dictionary of keyword arguments for the target invocation.
|
||||||
Defaults to ``{}``.
|
Defaults to ``{}``.
|
||||||
|
|
||||||
|
*verbose* is a flag used for debugging messages.
|
||||||
|
|
||||||
|
If not ``None``, *daemon* explicitly sets whether the thread is daemonic.
|
||||||
|
If ``None`` (the default), the daemonic property is inherited from the
|
||||||
|
current thread.
|
||||||
|
|
||||||
If the subclass overrides the constructor, it must make sure to invoke the
|
If the subclass overrides the constructor, it must make sure to invoke the
|
||||||
base class constructor (``Thread.__init__()``) before doing anything else to
|
base class constructor (``Thread.__init__()``) before doing anything else to
|
||||||
the thread.
|
the thread.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.3
|
||||||
|
Added the *daemon* argument.
|
||||||
|
|
||||||
.. method:: start()
|
.. method:: start()
|
||||||
|
|
||||||
Start the thread's activity.
|
Start the thread's activity.
|
||||||
|
|
|
@ -91,12 +91,16 @@ class Process(object):
|
||||||
'''
|
'''
|
||||||
_Popen = None
|
_Popen = None
|
||||||
|
|
||||||
def __init__(self, group=None, target=None, name=None, args=(), kwargs={}):
|
def __init__(self, group=None, target=None, name=None, args=(), kwargs={},
|
||||||
|
*, daemon=None):
|
||||||
assert group is None, 'group argument must be None for now'
|
assert group is None, 'group argument must be None for now'
|
||||||
count = next(_current_process._counter)
|
count = next(_current_process._counter)
|
||||||
self._identity = _current_process._identity + (count,)
|
self._identity = _current_process._identity + (count,)
|
||||||
self._authkey = _current_process._authkey
|
self._authkey = _current_process._authkey
|
||||||
self._daemonic = _current_process._daemonic
|
if daemon is not None:
|
||||||
|
self._daemonic = daemon
|
||||||
|
else:
|
||||||
|
self._daemonic = _current_process._daemonic
|
||||||
self._tempdir = _current_process._tempdir
|
self._tempdir = _current_process._tempdir
|
||||||
self._parent_pid = os.getpid()
|
self._parent_pid = os.getpid()
|
||||||
self._popen = None
|
self._popen = None
|
||||||
|
|
|
@ -163,6 +163,18 @@ class _TestProcess(BaseTestCase):
|
||||||
self.assertEqual(current.ident, os.getpid())
|
self.assertEqual(current.ident, os.getpid())
|
||||||
self.assertEqual(current.exitcode, None)
|
self.assertEqual(current.exitcode, None)
|
||||||
|
|
||||||
|
def test_daemon_argument(self):
|
||||||
|
if self.TYPE == "threads":
|
||||||
|
return
|
||||||
|
|
||||||
|
# By default uses the current process's daemon flag.
|
||||||
|
proc0 = self.Process(target=self._test)
|
||||||
|
self.assertEquals(proc0.daemon, self.current_process().daemon)
|
||||||
|
proc1 = self.Process(target=self._test, daemon=True)
|
||||||
|
self.assertTrue(proc1.daemon)
|
||||||
|
proc2 = self.Process(target=self._test, daemon=False)
|
||||||
|
self.assertFalse(proc2.daemon)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _test(cls, q, *args, **kwds):
|
def _test(cls, q, *args, **kwds):
|
||||||
current = cls.current_process()
|
current = cls.current_process()
|
||||||
|
|
|
@ -427,6 +427,14 @@ class ThreadTests(BaseTestCase):
|
||||||
t.daemon = True
|
t.daemon = True
|
||||||
self.assertTrue('daemon' in repr(t))
|
self.assertTrue('daemon' in repr(t))
|
||||||
|
|
||||||
|
def test_deamon_param(self):
|
||||||
|
t = threading.Thread()
|
||||||
|
self.assertFalse(t.daemon)
|
||||||
|
t = threading.Thread(daemon=False)
|
||||||
|
self.assertFalse(t.daemon)
|
||||||
|
t = threading.Thread(daemon=True)
|
||||||
|
self.assertTrue(t.daemon)
|
||||||
|
|
||||||
|
|
||||||
class ThreadJoinOnShutdown(BaseTestCase):
|
class ThreadJoinOnShutdown(BaseTestCase):
|
||||||
|
|
||||||
|
|
|
@ -622,7 +622,7 @@ class Thread(_Verbose):
|
||||||
#XXX __exc_clear = _sys.exc_clear
|
#XXX __exc_clear = _sys.exc_clear
|
||||||
|
|
||||||
def __init__(self, group=None, target=None, name=None,
|
def __init__(self, group=None, target=None, name=None,
|
||||||
args=(), kwargs=None, verbose=None):
|
args=(), kwargs=None, verbose=None, *, daemon=None):
|
||||||
assert group is None, "group argument must be None for now"
|
assert group is None, "group argument must be None for now"
|
||||||
_Verbose.__init__(self, verbose)
|
_Verbose.__init__(self, verbose)
|
||||||
if kwargs is None:
|
if kwargs is None:
|
||||||
|
@ -631,7 +631,10 @@ class Thread(_Verbose):
|
||||||
self._name = str(name or _newname())
|
self._name = str(name or _newname())
|
||||||
self._args = args
|
self._args = args
|
||||||
self._kwargs = kwargs
|
self._kwargs = kwargs
|
||||||
self._daemonic = self._set_daemon()
|
if daemon is not None:
|
||||||
|
self._daemonic = daemon
|
||||||
|
else:
|
||||||
|
self._daemonic = current_thread().daemon
|
||||||
self._ident = None
|
self._ident = None
|
||||||
self._started = Event()
|
self._started = Event()
|
||||||
self._stopped = False
|
self._stopped = False
|
||||||
|
@ -648,10 +651,6 @@ class Thread(_Verbose):
|
||||||
self._block.__init__()
|
self._block.__init__()
|
||||||
self._started._reset_internal_locks()
|
self._started._reset_internal_locks()
|
||||||
|
|
||||||
def _set_daemon(self):
|
|
||||||
# Overridden in _MainThread and _DummyThread
|
|
||||||
return current_thread().daemon
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
assert self._initialized, "Thread.__init__() was not called"
|
assert self._initialized, "Thread.__init__() was not called"
|
||||||
status = "initial"
|
status = "initial"
|
||||||
|
@ -948,15 +947,12 @@ class _Timer(Thread):
|
||||||
class _MainThread(Thread):
|
class _MainThread(Thread):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
Thread.__init__(self, name="MainThread")
|
Thread.__init__(self, name="MainThread", daemon=False)
|
||||||
self._started.set()
|
self._started.set()
|
||||||
self._set_ident()
|
self._set_ident()
|
||||||
with _active_limbo_lock:
|
with _active_limbo_lock:
|
||||||
_active[self._ident] = self
|
_active[self._ident] = self
|
||||||
|
|
||||||
def _set_daemon(self):
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _exitfunc(self):
|
def _exitfunc(self):
|
||||||
self._stop()
|
self._stop()
|
||||||
t = _pickSomeNonDaemonThread()
|
t = _pickSomeNonDaemonThread()
|
||||||
|
@ -988,7 +984,7 @@ def _pickSomeNonDaemonThread():
|
||||||
class _DummyThread(Thread):
|
class _DummyThread(Thread):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
Thread.__init__(self, name=_newname("Dummy-%d"))
|
Thread.__init__(self, name=_newname("Dummy-%d"), daemon=True)
|
||||||
|
|
||||||
# Thread._block consumes an OS-level locking primitive, which
|
# Thread._block consumes an OS-level locking primitive, which
|
||||||
# can never be used by a _DummyThread. Since a _DummyThread
|
# can never be used by a _DummyThread. Since a _DummyThread
|
||||||
|
@ -1000,9 +996,6 @@ class _DummyThread(Thread):
|
||||||
with _active_limbo_lock:
|
with _active_limbo_lock:
|
||||||
_active[self._ident] = self
|
_active[self._ident] = self
|
||||||
|
|
||||||
def _set_daemon(self):
|
|
||||||
return True
|
|
||||||
|
|
||||||
def join(self, timeout=None):
|
def join(self, timeout=None):
|
||||||
assert False, "cannot join a dummy thread"
|
assert False, "cannot join a dummy thread"
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,11 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #6064: Add a ``daemon`` keyword argument to the threading.Thread
|
||||||
|
and multiprocessing.Process constructors in order to override the
|
||||||
|
default behaviour of inheriting the daemonic property from the current
|
||||||
|
thread/process.
|
||||||
|
|
||||||
- Issue #10956: Buffered I/O classes retry reading or writing after a signal
|
- Issue #10956: Buffered I/O classes retry reading or writing after a signal
|
||||||
has arrived and the handler returned successfully.
|
has arrived and the handler returned successfully.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue