]> granicus.if.org Git - python/commitdiff
Issue #6064: Add a `daemon` keyword argument to the threading.Thread
authorAntoine Pitrou <solipsis@pitrou.net>
Fri, 25 Feb 2011 22:07:43 +0000 (22:07 +0000)
committerAntoine Pitrou <solipsis@pitrou.net>
Fri, 25 Feb 2011 22:07:43 +0000 (22:07 +0000)
and multiprocessing.Process constructors in order to override the
default behaviour of inheriting the daemonic property from the current
thread/process.

Doc/library/multiprocessing.rst
Doc/library/threading.rst
Lib/multiprocessing/process.py
Lib/test/test_multiprocessing.py
Lib/test/test_threading.py
Lib/threading.py
Misc/NEWS

index cc40a2a4c04bf18faa9be4a7c30a426e47d156e2..9bdbf549ee884f14aa5150e7d91374fe5a18b8a4 100644 (file)
@@ -297,7 +297,7 @@ The :mod:`multiprocessing` package mostly replicates the API of the
 :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
    :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
    is determined by the *generation* of the process.  *args* is the argument
    tuple for the target invocation.  *kwargs* is a dictionary of keyword
-   arguments for the target invocation.  By default, no arguments are passed to
-   *target*.
+   arguments for the target invocation.  If provided, the keyword-only *daemon* argument
+   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
    base class constructor (:meth:`Process.__init__`) before doing anything else
    to the process.
 
+   .. versionchanged:: 3.3
+      Added the *daemon* argument.
+
    .. method:: run()
 
       Method representing the process's activity.
index 5f1b9bfb065cd67fd001bf763e2d3d01b7272bc2..7d192b5f097a97867b716a91004c7b5cca2f03bf 100644 (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
 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
-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
 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.
 
 
-.. 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
    are:
@@ -273,10 +274,19 @@ impossible to detect the termination of alien threads.
    *kwargs* is a dictionary of keyword arguments for the target invocation.
    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
    base class constructor (``Thread.__init__()``) before doing anything else to
    the thread.
 
+   .. versionchanged:: 3.3
+      Added the *daemon* argument.
+
    .. method:: start()
 
       Start the thread's activity.
index b56a061079a1a16040297c878bf04bb72709d40f..3fb9ff600fa33f8dbc40b36a8842d86efa8e0f97 100644 (file)
@@ -91,12 +91,16 @@ class Process(object):
     '''
     _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'
         count = next(_current_process._counter)
         self._identity = _current_process._identity + (count,)
         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._parent_pid = os.getpid()
         self._popen = None
index 465a83102e31f5aaed8997678b967d115756e856..d154fce98aa9e4642f12d54a54bec5b3346292a1 100644 (file)
@@ -163,6 +163,18 @@ class _TestProcess(BaseTestCase):
         self.assertEqual(current.ident, os.getpid())
         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
     def _test(cls, q, *args, **kwds):
         current = cls.current_process()
index ecbbdbf56f683305f34d4605b860215af8422973..13a428d463d6389dfa994f7b53016ede51f28deb 100644 (file)
@@ -427,6 +427,14 @@ class ThreadTests(BaseTestCase):
         t.daemon = True
         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):
 
index fd93f7433354b2f87dd1964e6e56ba20589873d7..cb09afaa6635d81d1ecb2ba718104eb418f8701e 100644 (file)
@@ -622,7 +622,7 @@ class Thread(_Verbose):
     #XXX __exc_clear = _sys.exc_clear
 
     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"
         _Verbose.__init__(self, verbose)
         if kwargs is None:
@@ -631,7 +631,10 @@ class Thread(_Verbose):
         self._name = str(name or _newname())
         self._args = args
         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._started = Event()
         self._stopped = False
@@ -648,10 +651,6 @@ class Thread(_Verbose):
             self._block.__init__()
         self._started._reset_internal_locks()
 
-    def _set_daemon(self):
-        # Overridden in _MainThread and _DummyThread
-        return current_thread().daemon
-
     def __repr__(self):
         assert self._initialized, "Thread.__init__() was not called"
         status = "initial"
@@ -948,15 +947,12 @@ class _Timer(Thread):
 class _MainThread(Thread):
 
     def __init__(self):
-        Thread.__init__(self, name="MainThread")
+        Thread.__init__(self, name="MainThread", daemon=False)
         self._started.set()
         self._set_ident()
         with _active_limbo_lock:
             _active[self._ident] = self
 
-    def _set_daemon(self):
-        return False
-
     def _exitfunc(self):
         self._stop()
         t = _pickSomeNonDaemonThread()
@@ -988,7 +984,7 @@ def _pickSomeNonDaemonThread():
 class _DummyThread(Thread):
 
     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
         # can never be used by a _DummyThread.  Since a _DummyThread
@@ -1000,9 +996,6 @@ class _DummyThread(Thread):
         with _active_limbo_lock:
             _active[self._ident] = self
 
-    def _set_daemon(self):
-        return True
-
     def join(self, timeout=None):
         assert False, "cannot join a dummy thread"
 
index 99ccbd3e6b1d0fa9c30ff8ca273536e7d6c76d09..89a8bdbceb44302e23182fb798993661ac519786 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -35,6 +35,11 @@ Core and Builtins
 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
   has arrived and the handler returned successfully.