summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVictor Stinner <vstinner@python.org>2020-09-23 21:21:19 (GMT)
committerGitHub <noreply@github.com>2020-09-23 21:21:19 (GMT)
commit98c16c991d6e70a48f4280a7cd464d807bdd9f2b (patch)
treefce606ea267df285b5c1149fea97340389701b8b
parent2e4dd336e5b50fd30947fdecb605ddcd71f7f6f5 (diff)
downloadcpython-98c16c991d6e70a48f4280a7cd464d807bdd9f2b.zip
cpython-98c16c991d6e70a48f4280a7cd464d807bdd9f2b.tar.gz
cpython-98c16c991d6e70a48f4280a7cd464d807bdd9f2b.tar.bz2
bpo-41833: threading.Thread now uses the target name (GH-22357)
-rw-r--r--Doc/library/threading.rst9
-rw-r--r--Lib/test/test_threading.py34
-rw-r--r--Lib/threading.py20
-rw-r--r--Misc/NEWS.d/next/Library/2020-09-22-13-51-14.bpo-41833.6HVDjT.rst2
4 files changed, 55 insertions, 10 deletions
diff --git a/Doc/library/threading.rst b/Doc/library/threading.rst
index 7fcf93d..7eb12fe 100644
--- a/Doc/library/threading.rst
+++ b/Doc/library/threading.rst
@@ -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.
diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py
index d02d3b3..2f0f3ae 100644
--- a/Lib/test/test_threading.py
+++ b/Lib/test/test_threading.py
@@ -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
diff --git a/Lib/threading.py b/Lib/threading.py
index ab29db7..06c77f7 100644
--- a/Lib/threading.py
+++ b/Lib/threading.py
@@ -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
index 0000000..abb3a07
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2020-09-22-13-51-14.bpo-41833.6HVDjT.rst
@@ -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.