Skip to content

Commit 98c16c9

Browse files
authored
bpo-41833: threading.Thread now uses the target name (GH-22357)
1 parent 2e4dd33 commit 98c16c9

File tree

4 files changed

+55
-10
lines changed

4 files changed

+55
-10
lines changed

Doc/library/threading.rst

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -264,8 +264,10 @@ since it is impossible to detect the termination of alien threads.
264264
*target* is the callable object to be invoked by the :meth:`run` method.
265265
Defaults to ``None``, meaning nothing is called.
266266

267-
*name* is the thread name. By default, a unique name is constructed of the
268-
form "Thread-*N*" where *N* is a small decimal number.
267+
*name* is the thread name. By default, a unique name is constructed
268+
of the form "Thread-*N*" where *N* is a small decimal number,
269+
or "Thread-*N* (target)" where "target" is ``target.__name__`` if the
270+
*target* argument is specified.
269271

270272
*args* is the argument tuple for the target invocation. Defaults to ``()``.
271273

@@ -280,6 +282,9 @@ since it is impossible to detect the termination of alien threads.
280282
base class constructor (``Thread.__init__()``) before doing anything else to
281283
the thread.
282284

285+
.. versionchanged:: 3.10
286+
Use the *target* name if *name* argument is omitted.
287+
283288
.. versionchanged:: 3.3
284289
Added the *daemon* argument.
285290

Lib/test/test_threading.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import signal
2121
import textwrap
2222

23+
from unittest import mock
2324
from test import lock_tests
2425
from test import support
2526

@@ -86,6 +87,33 @@ def tearDown(self):
8687

8788
class ThreadTests(BaseTestCase):
8889

90+
@cpython_only
91+
def test_name(self):
92+
def func(): pass
93+
94+
thread = threading.Thread(name="myname1")
95+
self.assertEqual(thread.name, "myname1")
96+
97+
# Convert int name to str
98+
thread = threading.Thread(name=123)
99+
self.assertEqual(thread.name, "123")
100+
101+
# target name is ignored if name is specified
102+
thread = threading.Thread(target=func, name="myname2")
103+
self.assertEqual(thread.name, "myname2")
104+
105+
with mock.patch.object(threading, '_counter', return_value=2):
106+
thread = threading.Thread(name="")
107+
self.assertEqual(thread.name, "Thread-2")
108+
109+
with mock.patch.object(threading, '_counter', return_value=3):
110+
thread = threading.Thread()
111+
self.assertEqual(thread.name, "Thread-3")
112+
113+
with mock.patch.object(threading, '_counter', return_value=5):
114+
thread = threading.Thread(target=func)
115+
self.assertEqual(thread.name, "Thread-5 (func)")
116+
89117
# Create a bunch of threads, let each do some work, wait until all are
90118
# done.
91119
def test_various_ops(self):
@@ -531,7 +559,7 @@ def test_main_thread_after_fork_from_nonmain_thread(self):
531559
import os, threading, sys
532560
from test import support
533561
534-
def f():
562+
def func():
535563
pid = os.fork()
536564
if pid == 0:
537565
main = threading.main_thread()
@@ -544,14 +572,14 @@ def f():
544572
else:
545573
support.wait_process(pid, exitcode=0)
546574
547-
th = threading.Thread(target=f)
575+
th = threading.Thread(target=func)
548576
th.start()
549577
th.join()
550578
"""
551579
_, out, err = assert_python_ok("-c", code)
552580
data = out.decode().replace('\r', '')
553581
self.assertEqual(err, b"")
554-
self.assertEqual(data, "Thread-1\nTrue\nTrue\n")
582+
self.assertEqual(data, "Thread-1 (func)\nTrue\nTrue\n")
555583

556584
def test_main_thread_during_shutdown(self):
557585
# bpo-31516: current_thread() should still point to the main thread

Lib/threading.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -745,10 +745,9 @@ class BrokenBarrierError(RuntimeError):
745745

746746

747747
# Helper to generate new thread names
748-
_counter = _count().__next__
749-
_counter() # Consume 0 so first non-main thread has id 1.
750-
def _newname(template="Thread-%d"):
751-
return template % _counter()
748+
_counter = _count(1).__next__
749+
def _newname(name_template):
750+
return name_template % _counter()
752751

753752
# Active thread administration
754753
_active_limbo_lock = _allocate_lock()
@@ -800,8 +799,19 @@ class is implemented.
800799
assert group is None, "group argument must be None for now"
801800
if kwargs is None:
802801
kwargs = {}
802+
if name:
803+
name = str(name)
804+
else:
805+
name = _newname("Thread-%d")
806+
if target is not None:
807+
try:
808+
target_name = target.__name__
809+
name += f" ({target_name})"
810+
except AttributeError:
811+
pass
812+
803813
self._target = target
804-
self._name = str(name or _newname())
814+
self._name = name
805815
self._args = args
806816
self._kwargs = kwargs
807817
if daemon is not None:
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
The :class:`threading.Thread` constructor now uses the target name if the
2+
*target* argument is specified but the *name* argument is omitted.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy