From edd53eaf3b1e3e6eaa18ec32d8437c04dd529e5d Mon Sep 17 00:00:00 2001 From: Romain Vavassori Date: Fri, 21 Aug 2026 14:20:22 +0200 Subject: [PATCH 1/2] gh-156131: threading: add run() and run_daemon() to start threads easily --- Doc/library/threading.rst | 11 +++++++++++ Lib/test/test_threading.py | 15 +++++++++++++++ Lib/threading.py | 16 +++++++++++++++- ...026-08-21-12-14-00.gh-issue-156131.KElflR.rst | 2 ++ 4 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2026-08-21-12-14-00.gh-issue-156131.KElflR.rst diff --git a/Doc/library/threading.rst b/Doc/library/threading.rst index 5d9a7b6314b1668..22d2f95f6910ba0 100644 --- a/Doc/library/threading.rst +++ b/Doc/library/threading.rst @@ -220,6 +220,17 @@ This module defines the following functions: .. versionadded:: 3.4 +.. function:: run(func, /, *args, **kwargs) + run_daemon(func, /, *args, **kwargs) + + Run `func(*args, **kwargs)` in a thread and return the corresponding + :class:`Thread` object. The thread is started automatically. + + With :func:`run_daemon` the thread is set as daemonic. + + .. versionadded:: 3.16 + + .. function:: settrace(func) .. index:: single: trace function diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 96b43936be92cda..a6ac6dda7987079 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -1519,6 +1519,21 @@ def run_in_bg(): self.assertEqual(err, b"") self.assertEqual(out.strip(), b"Exiting...") + def test_run(self): + def func(x, y): + z.append(x + y) + + z = [] + thread = threading.run(func, 5, y=3) + thread.join() + self.assertEqual(z, [8]) + + z = [] + thread = threading.run_daemon(func, 8, y=4) + thread.join() + self.assertEqual(z, [12]) + self.assertEqual(thread.daemon, True) + class ThreadJoinOnShutdown(BaseTestCase): def _run_and_join(self, script): diff --git a/Lib/threading.py b/Lib/threading.py index abac31e25886fae..ad04c00dd42177c 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -30,7 +30,8 @@ 'setprofile', 'settrace', 'local', 'stack_size', 'excepthook', 'ExceptHookArgs', 'gettrace', 'getprofile', 'serialize_iterator', 'synchronized_iterator', 'concurrent_tee', - 'setprofile_all_threads','settrace_all_threads'] + 'setprofile_all_threads','settrace_all_threads', + 'run', 'run_daemon'] # Rename some stuff so "from threading import *" is safe _start_joinable_thread = _thread.start_joinable_thread @@ -1664,6 +1665,19 @@ def enumerate(): with _active_limbo_lock: return list(_active.values()) + list(_limbo.values()) +def run(func, /, *args, **kwargs): + """Return a running Thread object of func(*args, **kwargs).""" + thread = Thread(target=func, name=func.__name__, args=args, kwargs=kwargs) + thread.start() + return thread + +def run_daemon(func, /, *args, **kwargs): + """Return a running daemonic Thread object of func(*args, **kwargs).""" + thread = Thread(target=func, name=func.__name__, args=args, kwargs=kwargs) + thread.daemon = True + thread.start() + return thread + _threading_atexits = [] _SHUTTING_DOWN = False diff --git a/Misc/NEWS.d/next/Library/2026-08-21-12-14-00.gh-issue-156131.KElflR.rst b/Misc/NEWS.d/next/Library/2026-08-21-12-14-00.gh-issue-156131.KElflR.rst new file mode 100644 index 000000000000000..29bd39f962915ea --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-08-21-12-14-00.gh-issue-156131.KElflR.rst @@ -0,0 +1,2 @@ +Add :func:`threading.run` and :func:`threading.run_daemon` functions as +a convenient way to start threads. From 4735ce76e9ad26b254b9e37a73be16bb139138d9 Mon Sep 17 00:00:00 2001 From: Romain Vavassori Date: Fri, 21 Aug 2026 17:40:13 +0200 Subject: [PATCH 2/2] gh-156131: add suggested modifications --- Doc/library/threading.rst | 22 +++++++++++++++++++--- Doc/whatsnew/3.16.rst | 8 ++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/Doc/library/threading.rst b/Doc/library/threading.rst index 22d2f95f6910ba0..d3ed6ccdb0d64e2 100644 --- a/Doc/library/threading.rst +++ b/Doc/library/threading.rst @@ -223,12 +223,28 @@ This module defines the following functions: .. function:: run(func, /, *args, **kwargs) run_daemon(func, /, *args, **kwargs) - Run `func(*args, **kwargs)` in a thread and return the corresponding + Run ``func(*args, **kwargs)`` in a thread and return the corresponding :class:`Thread` object. The thread is started automatically. - With :func:`run_daemon` the thread is set as daemonic. + With :func:`run_daemon`, the thread is set as daemonic. - .. versionadded:: 3.16 + Example: + + .. code-block:: python + + import threading, urllib + + def fetch(url, data=None): + response = urllib.request.urlopen(url, data) + # further processing... + + t1 = threading.run(fetch, 'https://example.com/') + t2 = threading.run(fetch, 'https://example.com/post', data=payload) + + t1.join() + t2.join() + + .. versionadded:: next .. function:: settrace(func) diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst index 063755e1eadcb53..14e871735d02365 100644 --- a/Doc/whatsnew/3.16.rst +++ b/Doc/whatsnew/3.16.rst @@ -482,6 +482,14 @@ symtable (Contributed by Serhiy Storchaka in :gh:`153844`.) +threading +--------- + +* Add :func:`threading.run` and :func:`threading.run_daemon` + as a convenient way to start threads. + (Contributed by Romain Vavassori in :gh:`156131`.) + + tkinter -------