From 0a5c28e9a7558cda3485462801bb365e7a125d86 Mon Sep 17 00:00:00 2001 From: donBarbos Date: Mon, 4 Aug 2025 19:45:57 +0400 Subject: [PATCH 1/6] [concurrent.futures.interpreter] Update to 3.14 * Add `do_call` function * Add `interp`, `results` attributes to `WorkerContext` * Remove `ExecutionFailed` * Remove `shared` argument from methods --- stdlib/@tests/stubtest_allowlists/py314.txt | 8 --- stdlib/concurrent/futures/interpreter.pyi | 58 +++++++-------------- 2 files changed, 19 insertions(+), 47 deletions(-) diff --git a/stdlib/@tests/stubtest_allowlists/py314.txt b/stdlib/@tests/stubtest_allowlists/py314.txt index d5ed8af95f86..180846df3832 100644 --- a/stdlib/@tests/stubtest_allowlists/py314.txt +++ b/stdlib/@tests/stubtest_allowlists/py314.txt @@ -2,14 +2,6 @@ # TODO: New errors in Python 3.14 that need to be fixed or moved below # ==================================================================== -concurrent.futures.InterpreterPoolExecutor.__init__ -concurrent.futures.InterpreterPoolExecutor.prepare_context -concurrent.futures.interpreter.ExecutionFailed -concurrent.futures.interpreter.InterpreterPoolExecutor.__init__ -concurrent.futures.interpreter.InterpreterPoolExecutor.prepare_context -concurrent.futures.interpreter.WorkerContext.__init__ -concurrent.futures.interpreter.WorkerContext.prepare -concurrent.futures.interpreter.do_call multiprocessing.managers.BaseListProxy.clear multiprocessing.managers.BaseListProxy.copy multiprocessing.managers.DictProxy.__ior__ diff --git a/stdlib/concurrent/futures/interpreter.pyi b/stdlib/concurrent/futures/interpreter.pyi index 9c1078983d8c..f5fbfa3856b6 100644 --- a/stdlib/concurrent/futures/interpreter.pyi +++ b/stdlib/concurrent/futures/interpreter.pyi @@ -1,10 +1,14 @@ import sys -from collections.abc import Callable, Mapping +from collections.abc import Callable from concurrent.futures import ThreadPoolExecutor +from queue import Queue from typing import Literal, Protocol, overload, type_check_only from typing_extensions import ParamSpec, Self, TypeAlias, TypeVar, TypeVarTuple, Unpack _Task: TypeAlias = tuple[bytes, Literal["function", "script"]] +_Ts = TypeVarTuple("_Ts") +_P = ParamSpec("_P") +_R = TypeVar("_R") @type_check_only class _TaskFunc(Protocol): @@ -13,62 +17,41 @@ class _TaskFunc(Protocol): @overload def __call__(self, fn: str) -> tuple[bytes, Literal["script"]]: ... -_Ts = TypeVarTuple("_Ts") -_P = ParamSpec("_P") -_R = TypeVar("_R") - -# A `type.simplenamespace` with `__name__` attribute. -@type_check_only -class _HasName(Protocol): - __name__: str - -# `_interpreters.exec` technically gives us a simple namespace. -@type_check_only -class _ExcInfo(Protocol): - formatted: str - msg: str - type: _HasName - if sys.version_info >= (3, 14): from concurrent.futures.thread import BrokenThreadPool, WorkerContext as ThreadWorkerContext + from concurrent.interpreters import Interpreter - from _interpreters import InterpreterError - - class ExecutionFailed(InterpreterError): - def __init__(self, excinfo: _ExcInfo) -> None: ... # type: ignore[override] + def do_call(results: Queue, func: Callable[_P, _R], args: tuple[_P.args], kwargs: dict[str, _P.kwargs]) -> _R: ... class WorkerContext(ThreadWorkerContext): - # Parent class doesn't have `shared` argument, - @overload # type: ignore[override] + interp: Interpreter | None + results: Queue | None + @overload @classmethod def prepare( - cls, initializer: Callable[[Unpack[_Ts]], object], initargs: tuple[Unpack[_Ts]], shared: Mapping[str, object] + cls, initializer: Callable[[Unpack[_Ts]], object], initargs: tuple[Unpack[_Ts]] ) -> tuple[Callable[[], Self], _TaskFunc]: ... - @overload # type: ignore[override] + @overload @classmethod - def prepare( - cls, initializer: Callable[[], object], initargs: tuple[()], shared: Mapping[str, object] - ) -> tuple[Callable[[], Self], _TaskFunc]: ... - def __init__( - self, initdata: tuple[bytes, Literal["function", "script"]], shared: Mapping[str, object] | None = None - ) -> None: ... # type: ignore[override] + def prepare(cls, initializer: Callable[[], object], initargs: tuple[()]) -> tuple[Callable[[], Self], _TaskFunc]: ... + def __init__(self, initdata: _Task) -> None: ... def __del__(self) -> None: ... - def run(self, task: _Task) -> None: ... # type: ignore[override] + def run(self, task: _Task) -> None: ... # type: ignore[override] class BrokenInterpreterPool(BrokenThreadPool): ... class InterpreterPoolExecutor(ThreadPoolExecutor): BROKEN: type[BrokenInterpreterPool] - @overload # type: ignore[override] + @overload @classmethod def prepare_context( - cls, initializer: Callable[[], object], initargs: tuple[()], shared: Mapping[str, object] + cls, initializer: Callable[[], object], initargs: tuple[()] ) -> tuple[Callable[[], WorkerContext], _TaskFunc]: ... - @overload # type: ignore[override] + @overload @classmethod def prepare_context( - cls, initializer: Callable[[Unpack[_Ts]], object], initargs: tuple[Unpack[_Ts]], shared: Mapping[str, object] + cls, initializer: Callable[[Unpack[_Ts]], object], initargs: tuple[Unpack[_Ts]] ) -> tuple[Callable[[], WorkerContext], _TaskFunc]: ... @overload def __init__( @@ -77,7 +60,6 @@ if sys.version_info >= (3, 14): thread_name_prefix: str = "", initializer: Callable[[], object] | None = None, initargs: tuple[()] = (), - shared: Mapping[str, object] | None = None, ) -> None: ... @overload def __init__( @@ -87,7 +69,6 @@ if sys.version_info >= (3, 14): *, initializer: Callable[[Unpack[_Ts]], object], initargs: tuple[Unpack[_Ts]], - shared: Mapping[str, object] | None = None, ) -> None: ... @overload def __init__( @@ -96,5 +77,4 @@ if sys.version_info >= (3, 14): thread_name_prefix: str, initializer: Callable[[Unpack[_Ts]], object], initargs: tuple[Unpack[_Ts]], - shared: Mapping[str, object] | None = None, ) -> None: ... From 582040f7add7094d770fdd089bbdbf7be0935a80 Mon Sep 17 00:00:00 2001 From: donBarbos Date: Mon, 4 Aug 2025 19:54:55 +0400 Subject: [PATCH 2/6] Back `type: ignore[override]` comments --- stdlib/concurrent/futures/interpreter.pyi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/concurrent/futures/interpreter.pyi b/stdlib/concurrent/futures/interpreter.pyi index f5fbfa3856b6..3943a6e19c9f 100644 --- a/stdlib/concurrent/futures/interpreter.pyi +++ b/stdlib/concurrent/futures/interpreter.pyi @@ -21,12 +21,12 @@ if sys.version_info >= (3, 14): from concurrent.futures.thread import BrokenThreadPool, WorkerContext as ThreadWorkerContext from concurrent.interpreters import Interpreter - def do_call(results: Queue, func: Callable[_P, _R], args: tuple[_P.args], kwargs: dict[str, _P.kwargs]) -> _R: ... + def do_call(results: Queue, func: Callable[_P, _R], args: _P.args, kwargs: _P.kwargs) -> _R: ... class WorkerContext(ThreadWorkerContext): interp: Interpreter | None results: Queue | None - @overload + @overload # type: ignore[override] @classmethod def prepare( cls, initializer: Callable[[Unpack[_Ts]], object], initargs: tuple[Unpack[_Ts]] @@ -43,7 +43,7 @@ if sys.version_info >= (3, 14): class InterpreterPoolExecutor(ThreadPoolExecutor): BROKEN: type[BrokenInterpreterPool] - @overload + @overload # type: ignore[override] @classmethod def prepare_context( cls, initializer: Callable[[], object], initargs: tuple[()] From 13469a551fd5febb967b47afdbc81a2a67d4986c Mon Sep 17 00:00:00 2001 From: donBarbos Date: Mon, 4 Aug 2025 20:05:47 +0400 Subject: [PATCH 3/6] Use tuple and dict --- stdlib/concurrent/futures/interpreter.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/concurrent/futures/interpreter.pyi b/stdlib/concurrent/futures/interpreter.pyi index 3943a6e19c9f..543509bca964 100644 --- a/stdlib/concurrent/futures/interpreter.pyi +++ b/stdlib/concurrent/futures/interpreter.pyi @@ -21,7 +21,7 @@ if sys.version_info >= (3, 14): from concurrent.futures.thread import BrokenThreadPool, WorkerContext as ThreadWorkerContext from concurrent.interpreters import Interpreter - def do_call(results: Queue, func: Callable[_P, _R], args: _P.args, kwargs: _P.kwargs) -> _R: ... + def do_call(results: Queue, func: Callable[..., _R], args: tuple[object, ...], kwargs: dict[str, object]) -> _R: ... class WorkerContext(ThreadWorkerContext): interp: Interpreter | None From 9ce1d8ce9851fd36884119b99cdfbee89601975d Mon Sep 17 00:00:00 2001 From: donBarbos Date: Mon, 4 Aug 2025 20:12:03 +0400 Subject: [PATCH 4/6] Add types to generic Queue --- stdlib/concurrent/futures/interpreter.pyi | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/stdlib/concurrent/futures/interpreter.pyi b/stdlib/concurrent/futures/interpreter.pyi index 543509bca964..8457a90d4b5d 100644 --- a/stdlib/concurrent/futures/interpreter.pyi +++ b/stdlib/concurrent/futures/interpreter.pyi @@ -21,11 +21,13 @@ if sys.version_info >= (3, 14): from concurrent.futures.thread import BrokenThreadPool, WorkerContext as ThreadWorkerContext from concurrent.interpreters import Interpreter - def do_call(results: Queue, func: Callable[..., _R], args: tuple[object, ...], kwargs: dict[str, object]) -> _R: ... + def do_call( + results: Queue[BaseException | None], func: Callable[..., _R], args: tuple[object, ...], kwargs: dict[str, object] + ) -> _R: ... class WorkerContext(ThreadWorkerContext): interp: Interpreter | None - results: Queue | None + results: Queue[BaseException | None] | None @overload # type: ignore[override] @classmethod def prepare( From e0f1cf0704f5456c3af0878b139e92ebb8803fac Mon Sep 17 00:00:00 2001 From: donBarbos Date: Mon, 4 Aug 2025 20:18:13 +0400 Subject: [PATCH 5/6] Remove shared argument from tests --- stdlib/@tests/test_cases/check_concurrent_futures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/@tests/test_cases/check_concurrent_futures.py b/stdlib/@tests/test_cases/check_concurrent_futures.py index 82cf03fb5c49..51a94b09d386 100644 --- a/stdlib/@tests/test_cases/check_concurrent_futures.py +++ b/stdlib/@tests/test_cases/check_concurrent_futures.py @@ -47,7 +47,7 @@ def check_interpreter_pool_executor() -> None: with InterpreterPoolExecutor(initializer=_initializer, initargs=("x",)): # type: ignore ... - context = InterpreterPoolExecutor.prepare_context(initializer=_initializer, initargs=(1,), shared={}) + context = InterpreterPoolExecutor.prepare_context(initializer=_initializer, initargs=(1,)) worker_context = context[0]() assert_type(worker_context, concurrent.futures.interpreter.WorkerContext) resolve_task = context[1] From 8ce945f7cec2c26d87683ba0107d62e23749de3d Mon Sep 17 00:00:00 2001 From: donBarbos Date: Mon, 4 Aug 2025 21:55:30 +0400 Subject: [PATCH 6/6] Accept suggestions --- stdlib/_interpreters.pyi | 4 ++-- stdlib/concurrent/futures/interpreter.pyi | 11 ++++------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/stdlib/_interpreters.pyi b/stdlib/_interpreters.pyi index 54fc0e39d239..f89a24e7d85c 100644 --- a/stdlib/_interpreters.pyi +++ b/stdlib/_interpreters.pyi @@ -34,8 +34,8 @@ def exec( def call( id: SupportsIndex, callable: Callable[..., _R], - args: tuple[object, ...] | None = None, - kwargs: dict[str, object] | None = None, + args: tuple[Any, ...] | None = None, + kwargs: dict[str, Any] | None = None, *, restrict: bool = False, ) -> tuple[_R, types.SimpleNamespace]: ... diff --git a/stdlib/concurrent/futures/interpreter.pyi b/stdlib/concurrent/futures/interpreter.pyi index 8457a90d4b5d..e101022babcb 100644 --- a/stdlib/concurrent/futures/interpreter.pyi +++ b/stdlib/concurrent/futures/interpreter.pyi @@ -1,8 +1,7 @@ import sys from collections.abc import Callable from concurrent.futures import ThreadPoolExecutor -from queue import Queue -from typing import Literal, Protocol, overload, type_check_only +from typing import Any, Literal, Protocol, overload, type_check_only from typing_extensions import ParamSpec, Self, TypeAlias, TypeVar, TypeVarTuple, Unpack _Task: TypeAlias = tuple[bytes, Literal["function", "script"]] @@ -19,15 +18,13 @@ class _TaskFunc(Protocol): if sys.version_info >= (3, 14): from concurrent.futures.thread import BrokenThreadPool, WorkerContext as ThreadWorkerContext - from concurrent.interpreters import Interpreter + from concurrent.interpreters import Interpreter, Queue - def do_call( - results: Queue[BaseException | None], func: Callable[..., _R], args: tuple[object, ...], kwargs: dict[str, object] - ) -> _R: ... + def do_call(results: Queue, func: Callable[..., _R], args: tuple[Any, ...], kwargs: dict[str, Any]) -> _R: ... class WorkerContext(ThreadWorkerContext): interp: Interpreter | None - results: Queue[BaseException | None] | None + results: Queue | None @overload # type: ignore[override] @classmethod def prepare(