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:
Antoine Pitrou 2011-02-25 22:07:43 +00:00
parent 4bc685752f
commit 0bd4deba38
7 changed files with 59 additions and 21 deletions

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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()

View File

@ -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):

View File

@ -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"

View File

@ -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.