]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-41833: threading.Thread now uses the target name (GH-22357)
authorVictor Stinner <vstinner@python.org>
Wed, 23 Sep 2020 21:21:19 +0000 (23:21 +0200)
committerGitHub <noreply@github.com>
Wed, 23 Sep 2020 21:21:19 +0000 (23:21 +0200)
Doc/library/threading.rst
Lib/test/test_threading.py
Lib/threading.py
Misc/NEWS.d/next/Library/2020-09-22-13-51-14.bpo-41833.6HVDjT.rst [new file with mode: 0644]

index 7fcf93d74610ebae58c2f855ffcd5561af1e59b7..7eb12fe116bd2da6dd642a44375f1258bca25417 100644 (file)
@@ -264,8 +264,10 @@ since it is impossible to detect the termination of alien threads.
    *target* is the callable object to be invoked by the :meth:`run` method.
    Defaults to ``None``, meaning nothing is called.
 
-   *name* is the thread name.  By default, a unique name is constructed of the
-   form "Thread-*N*" where *N* is a small decimal number.
+   *name* is the thread name. By default, a unique name is constructed
+   of the form "Thread-*N*" where *N* is a small decimal number,
+   or "Thread-*N* (target)" where "target" is ``target.__name__`` if the
+   *target* argument is specified.
 
    *args* is the argument tuple for the target invocation.  Defaults to ``()``.
 
@@ -280,6 +282,9 @@ since it is impossible to detect the termination of alien threads.
    base class constructor (``Thread.__init__()``) before doing anything else to
    the thread.
 
+   .. versionchanged:: 3.10
+      Use the *target* name if *name* argument is omitted.
+
    .. versionchanged:: 3.3
       Added the *daemon* argument.
 
index d02d3b346f9cc8e28bed16690c567c91a8c7fc86..2f0f3ae0946a579b5e4f0caa974514ffbb3346a3 100644 (file)
@@ -20,6 +20,7 @@ import subprocess
 import signal
 import textwrap
 
+from unittest import mock
 from test import lock_tests
 from test import support
 
@@ -86,6 +87,33 @@ class BaseTestCase(unittest.TestCase):
 
 class ThreadTests(BaseTestCase):
 
+    @cpython_only
+    def test_name(self):
+        def func(): pass
+
+        thread = threading.Thread(name="myname1")
+        self.assertEqual(thread.name, "myname1")
+
+        # Convert int name to str
+        thread = threading.Thread(name=123)
+        self.assertEqual(thread.name, "123")
+
+        # target name is ignored if name is specified
+        thread = threading.Thread(target=func, name="myname2")
+        self.assertEqual(thread.name, "myname2")
+
+        with mock.patch.object(threading, '_counter', return_value=2):
+            thread = threading.Thread(name="")
+            self.assertEqual(thread.name, "Thread-2")
+
+        with mock.patch.object(threading, '_counter', return_value=3):
+            thread = threading.Thread()
+            self.assertEqual(thread.name, "Thread-3")
+
+        with mock.patch.object(threading, '_counter', return_value=5):
+            thread = threading.Thread(target=func)
+            self.assertEqual(thread.name, "Thread-5 (func)")
+
     # Create a bunch of threads, let each do some work, wait until all are
     # done.
     def test_various_ops(self):
@@ -531,7 +559,7 @@ class ThreadTests(BaseTestCase):
             import os, threading, sys
             from test import support
 
-            def f():
+            def func():
                 pid = os.fork()
                 if pid == 0:
                     main = threading.main_thread()
@@ -544,14 +572,14 @@ class ThreadTests(BaseTestCase):
                 else:
                     support.wait_process(pid, exitcode=0)
 
-            th = threading.Thread(target=f)
+            th = threading.Thread(target=func)
             th.start()
             th.join()
         """
         _, out, err = assert_python_ok("-c", code)
         data = out.decode().replace('\r', '')
         self.assertEqual(err, b"")
-        self.assertEqual(data, "Thread-1\nTrue\nTrue\n")
+        self.assertEqual(data, "Thread-1 (func)\nTrue\nTrue\n")
 
     def test_main_thread_during_shutdown(self):
         # bpo-31516: current_thread() should still point to the main thread
index ab29db77a747a2d0c8a9cb736e990283f94179f3..06c77f70fe74f59e186ddf751a795336f82fef70 100644 (file)
@@ -745,10 +745,9 @@ class BrokenBarrierError(RuntimeError):
 
 
 # Helper to generate new thread names
-_counter = _count().__next__
-_counter() # Consume 0 so first non-main thread has id 1.
-def _newname(template="Thread-%d"):
-    return template % _counter()
+_counter = _count(1).__next__
+def _newname(name_template):
+    return name_template % _counter()
 
 # Active thread administration
 _active_limbo_lock = _allocate_lock()
@@ -800,8 +799,19 @@ class Thread:
         assert group is None, "group argument must be None for now"
         if kwargs is None:
             kwargs = {}
+        if name:
+            name = str(name)
+        else:
+            name = _newname("Thread-%d")
+            if target is not None:
+                try:
+                    target_name = target.__name__
+                    name += f" ({target_name})"
+                except AttributeError:
+                    pass
+
         self._target = target
-        self._name = str(name or _newname())
+        self._name = name
         self._args = args
         self._kwargs = kwargs
         if daemon is not None:
diff --git a/Misc/NEWS.d/next/Library/2020-09-22-13-51-14.bpo-41833.6HVDjT.rst b/Misc/NEWS.d/next/Library/2020-09-22-13-51-14.bpo-41833.6HVDjT.rst
new file mode 100644 (file)
index 0000000..abb3a07
--- /dev/null
@@ -0,0 +1,2 @@
+The :class:`threading.Thread` constructor now uses the target name if the
+*target* argument is specified but the *name* argument is omitted.