diff --git a/tests/tools/test_daemon_pool.py b/tests/tools/test_daemon_pool.py index 0bab785095..66064bcaad 100644 --- a/tests/tools/test_daemon_pool.py +++ b/tests/tools/test_daemon_pool.py @@ -94,26 +94,63 @@ def test_submit_propagates_caller_contextvars(): pool.shutdown(wait=True) -def test_workers_support_worker_context_without_legacy_initializer_attrs(monkeypatch): - """Python 3.14 creates a worker context instead of storing initializer fields.""" - seen_contexts = [] +def _capture_worker_args(monkeypatch, pool): + """Swap the stdlib worker for one that records its args and resolves one item. - def worker_with_context(_executor_ref, worker_context, work_queue): - seen_contexts.append(worker_context) - work_queue.get().run() + The stdlib ``_worker`` signature differs between interpreters, so the fake + accepts anything and completes the work item's future directly — the test + then runs on 3.11 and 3.14 alike and asserts only on the arg shape chosen. + """ + seen = [] + def fake_worker(*args): + seen.append(args) + pool._work_queue.get().future.set_result("done") + + monkeypatch.setattr(daemon_pool, "_worker", fake_worker) + return seen + + +def test_worker_gets_context_when_executor_builds_worker_contexts(monkeypatch): + """3.14+ shape (#58596, #111813): the executor exposes ``_create_worker_context`` + and no ``_initializer``/``_initargs``; the worker must receive + ``(executor_ref, ctx, work_queue)`` — reading the legacy fields raised + ``AttributeError`` on every pool spawn.""" pool = DaemonThreadPoolExecutor(max_workers=1) - monkeypatch.setattr(daemon_pool, "_worker", worker_with_context) - monkeypatch.setattr( - pool, "_create_worker_context", lambda: "worker-context", raising=False - ) - del pool._initializer - del pool._initargs + monkeypatch.setattr(pool, "_create_worker_context", lambda: "worker-context", raising=False) + monkeypatch.delattr(pool, "_initializer", raising=False) + monkeypatch.delattr(pool, "_initargs", raising=False) + seen = _capture_worker_args(monkeypatch, pool) try: - assert pool.submit(lambda: "done").result(timeout=10) == "done" - assert seen_contexts == ["worker-context"] + assert pool.submit(lambda: None).result(timeout=10) == "done" finally: pool.shutdown(wait=True) + ((executor_ref, ctx, work_queue),) = seen + assert executor_ref() is pool + assert ctx == "worker-context" + assert work_queue is pool._work_queue + + +def test_worker_gets_initializer_when_executor_stores_initializer_fields(monkeypatch): + """3.11–3.13 shape: no ``_create_worker_context``; the worker must receive + ``(executor_ref, work_queue, initializer, initargs)``.""" + + def init(*_): + return None + + pool = DaemonThreadPoolExecutor(max_workers=1) + monkeypatch.delattr(pool, "_create_worker_context", raising=False) + monkeypatch.setattr(pool, "_initializer", init, raising=False) + monkeypatch.setattr(pool, "_initargs", (1, 2), raising=False) + seen = _capture_worker_args(monkeypatch, pool) + try: + assert pool.submit(lambda: None).result(timeout=10) == "done" + finally: + pool.shutdown(wait=True) + ((executor_ref, work_queue, initializer, initargs),) = seen + assert executor_ref() is pool + assert work_queue is pool._work_queue + assert (initializer, initargs) == (init, (1, 2)) def _repo_root():