A ThreadPoolExecutor whose worker threads are daemon threads. Why: - In this repo, we run synchronous network calls in `run_in_executor`. - When the outer coroutine times out/cancels, the underlying thread keeps running. - Non-daemon worker threads then block process shutdown (
| 76 | |
| 77 | |
| 78 | class DaemonThreadPoolExecutor(ThreadPoolExecutor): |
| 79 | """ |
| 80 | A ThreadPoolExecutor whose worker threads are daemon threads. |
| 81 | |
| 82 | Why: |
| 83 | - In this repo, we run synchronous network calls in `run_in_executor`. |
| 84 | - When the outer coroutine times out/cancels, the underlying thread keeps running. |
| 85 | - Non-daemon worker threads then block process shutdown (Python waits for them). |
| 86 | |
| 87 | Using daemon threads ensures a finished CLI process can exit cleanly even if |
| 88 | some background executor work is still blocked in I/O. |
| 89 | """ |
| 90 | |
| 91 | def _adjust_thread_count(self) -> None: # pragma: no cover |
| 92 | # Based on CPython ThreadPoolExecutor._adjust_thread_count, but mark |
| 93 | # threads as daemon before starting. |
| 94 | if self._idle_semaphore.acquire(timeout=0): |
| 95 | return |
| 96 | |
| 97 | def weakref_cb(_, q=self._work_queue): |
| 98 | q.put(None) |
| 99 | |
| 100 | num_threads = len(self._threads) |
| 101 | if num_threads < self._max_workers: |
| 102 | thread_name = '%s_%d' % (self._thread_name_prefix |
| 103 | or self, num_threads) |
| 104 | # Import internal helpers from stdlib to keep behavior consistent. |
| 105 | from concurrent.futures.thread import _worker, _threads_queues # type: ignore |
| 106 | |
| 107 | t = threading.Thread( |
| 108 | name=thread_name, |
| 109 | target=_worker, |
| 110 | args=(weakref.ref(self, weakref_cb), self._work_queue, |
| 111 | self._initializer, self._initargs), |
| 112 | ) |
| 113 | t.daemon = True |
| 114 | t.start() |
| 115 | self._threads.add(t) |
| 116 | _threads_queues[t] = self._work_queue |
no outgoing calls
no test coverage detected