-
Notifications
You must be signed in to change notification settings - Fork 48.1k
fix: DaemonThreadPoolExecutor compat with Python 3.14 #69311
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,19 +26,39 @@ | |
|
|
||
| from __future__ import annotations | ||
|
|
||
| import sys | ||
| import threading | ||
| import weakref | ||
| from concurrent.futures import ThreadPoolExecutor | ||
| from concurrent.futures.thread import _worker | ||
|
|
||
| __all__ = ["DaemonThreadPoolExecutor"] | ||
|
|
||
| # Python 3.14 replaced ThreadPoolExecutor._initializer/_initargs with a | ||
| # prepare_context() → (_create_worker_context, _resolve_work_item_task) | ||
| # pattern, and changed _worker's signature from | ||
| # _worker(executor_ref, work_queue, initializer, initargs) # 3.8–3.13 | ||
| # to | ||
| # _worker(executor_ref, ctx, work_queue) # 3.14+ | ||
| # The override below must branch on the running interpreter version. | ||
| _PY314 = sys.version_info >= (3, 14) | ||
|
|
||
|
|
||
| class DaemonThreadPoolExecutor(ThreadPoolExecutor): | ||
| """ThreadPoolExecutor variant whose workers do not block process exit.""" | ||
|
|
||
| def __init__(self, *args, **kwargs): | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The new 3.14 branch does not read either backfilled attribute, and the in-tree daemon-pool users only construct or submit through this executor. Please remove this compatibility backfill unless there is a concrete external consumer to preserve; it is unrelated to the WorkerContext repair. |
||
| super().__init__(*args, **kwargs) | ||
| # Belt-and-suspenders: on 3.14+ the stdlib __init__ no longer sets | ||
| # _initializer/_initargs. Ensure they exist so any external code | ||
| # that reads them (or an older code path) doesn't AttributeError. | ||
| if not hasattr(self, "_initializer"): | ||
| self._initializer = None | ||
| if not hasattr(self, "_initargs"): | ||
| self._initargs = () | ||
|
|
||
| def _adjust_thread_count(self) -> None: | ||
| # Mirrors CPython's implementation (3.8–3.13) with two changes: | ||
| # Mirrors CPython's implementation with two changes: | ||
| # daemon=True and no _threads_queues registration. | ||
| if self._idle_semaphore.acquire(timeout=0): | ||
| return | ||
|
|
@@ -49,16 +69,30 @@ def weakref_cb(_, q=self._work_queue): | |
| num_threads = len(self._threads) | ||
| if num_threads < self._max_workers: | ||
| thread_name = "%s_%d" % (self._thread_name_prefix or self, num_threads) | ||
| t = threading.Thread( | ||
| name=thread_name, | ||
| target=_worker, | ||
| args=( | ||
| weakref.ref(self, weakref_cb), | ||
| self._work_queue, | ||
| self._initializer, | ||
| self._initargs, | ||
| ), | ||
| daemon=True, | ||
| ) | ||
| if _PY314: | ||
| # Python 3.14+: _worker(executor_ref, ctx, work_queue) | ||
| t = threading.Thread( | ||
| name=thread_name, | ||
| target=_worker, | ||
| args=( | ||
| weakref.ref(self, weakref_cb), | ||
| self._create_worker_context(), | ||
| self._work_queue, | ||
| ), | ||
| daemon=True, | ||
| ) | ||
| else: | ||
| # Python 3.8–3.13: _worker(executor_ref, work_queue, initializer, initargs) | ||
| t = threading.Thread( | ||
| name=thread_name, | ||
| target=_worker, | ||
| args=( | ||
| weakref.ref(self, weakref_cb), | ||
| self._work_queue, | ||
| self._initializer, | ||
| self._initargs, | ||
| ), | ||
| daemon=True, | ||
| ) | ||
| t.start() | ||
| self._threads.add(t) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please gate this on the interface actually required, e.g.
hasattr(self, "_create_worker_context"), rather thansys.version_info. This override depends on CPython private executor internals; the presence of that factory directly determines whether the three-argument WorkerContext tuple is valid.