From 75158fb9e87249faafe7d14088fdb045d4845d12 Mon Sep 17 00:00:00 2001 From: Kumar Anirudha Date: Tue, 6 Oct 2026 10:31:19 +0530 Subject: [PATCH] [multiprocessing] Fix return types of `Value` and `Array` The stubs did not match what CPython returns: - With `lock=False`, `Value` and `Array` return the raw ctypes object. `BaseContext.Value` claimed `Synchronized[_CT]` instead of `_CT`, and both `Array` stubs returned something other than `ctypes.Array[_CT]`. - `sharedctypes.Value` returned `SynchronizedBase` for simple C types and typecode strings, hiding the `value` attribute that the runtime `Synchronized` wrapper provides. - `BaseContext.Value` returned `Synchronized[_CT]` for other ctypes such as structures. These are wrapped in a `SynchronizedBase` subclass that has no `value` attribute. Make both modules match the runtime and each other. Fixes #10319 --- .../multiprocessing/check_ctypes.py | 24 ++++++++++++++++--- stdlib/multiprocessing/context.pyi | 10 ++++---- stdlib/multiprocessing/sharedctypes.pyi | 8 +++++-- 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/stdlib/@tests/test_cases/multiprocessing/check_ctypes.py b/stdlib/@tests/test_cases/multiprocessing/check_ctypes.py index 42da0a66fa60..e301c4126484 100644 --- a/stdlib/@tests/test_cases/multiprocessing/check_ctypes.py +++ b/stdlib/@tests/test_cases/multiprocessing/check_ctypes.py @@ -1,10 +1,17 @@ from __future__ import annotations -from ctypes import c_char, c_float, c_int -from multiprocessing import Array, Value -from multiprocessing.sharedctypes import Synchronized, SynchronizedArray, SynchronizedString +import ctypes +from ctypes import Structure, c_char, c_float, c_int +from multiprocessing import Array, Value, sharedctypes +from multiprocessing.sharedctypes import Synchronized, SynchronizedArray, SynchronizedBase, SynchronizedString +from typing import Any from typing_extensions import assert_type + +class Point(Structure): + _fields_ = [("x", c_int), ("y", c_int)] + + string = Array(c_char, 12) assert_type(string, SynchronizedString) assert_type(string.value, bytes) @@ -17,3 +24,14 @@ field = Value(c_float, 0.0) assert_type(field, Synchronized[float]) field.value = 1.2 + +assert_type(Value("i", 0), Synchronized[Any]) +assert_type(Value(c_int, 0, lock=False), c_int) +assert_type(Value(Point), SynchronizedBase[Point]) +assert_type(Array(c_int, 3, lock=False), ctypes.Array[c_int]) + +assert_type(sharedctypes.Value(c_int, 0), Synchronized[int]) +assert_type(sharedctypes.Value("i", 0), Synchronized[Any]) +assert_type(sharedctypes.Value(c_int, 0, lock=False), c_int) +assert_type(sharedctypes.Value(Point), SynchronizedBase[Point]) +assert_type(sharedctypes.Array(c_int, 3, lock=False), ctypes.Array[c_int]) diff --git a/stdlib/multiprocessing/context.pyi b/stdlib/multiprocessing/context.pyi index 13fd967515d0..d9a6c82c8059 100644 --- a/stdlib/multiprocessing/context.pyi +++ b/stdlib/multiprocessing/context.pyi @@ -8,7 +8,7 @@ from multiprocessing import popen_fork, popen_forkserver, popen_spawn_posix, pop from multiprocessing.managers import SyncManager from multiprocessing.pool import Pool as _Pool from multiprocessing.process import BaseProcess -from multiprocessing.sharedctypes import Synchronized, SynchronizedArray, SynchronizedString +from multiprocessing.sharedctypes import Synchronized, SynchronizedArray, SynchronizedBase, SynchronizedString from typing import Any, ClassVar, Literal, TypeAlias, TypeVar, overload if sys.platform != "win32": @@ -90,9 +90,9 @@ class BaseContext: self, typecode_or_type: type[_SimpleCData[_T]], *args: Any, lock: Literal[True] | _LockLike = True ) -> Synchronized[_T]: ... @overload - def Value(self, typecode_or_type: type[_CT], *args: Any, lock: Literal[False]) -> Synchronized[_CT]: ... + def Value(self, typecode_or_type: type[_CT], *args: Any, lock: Literal[False]) -> _CT: ... @overload - def Value(self, typecode_or_type: type[_CT], *args: Any, lock: Literal[True] | _LockLike = True) -> Synchronized[_CT]: ... + def Value(self, typecode_or_type: type[_CT], *args: Any, lock: Literal[True] | _LockLike = True) -> SynchronizedBase[_CT]: ... @overload def Value(self, typecode_or_type: str, *args: Any, lock: Literal[True] | _LockLike = True) -> Synchronized[Any]: ... @overload @@ -100,8 +100,8 @@ class BaseContext: @overload def Array( - self, typecode_or_type: type[_SimpleCData[_T]], size_or_initializer: int | Sequence[Any], *, lock: Literal[False] - ) -> SynchronizedArray[_T]: ... + self, typecode_or_type: type[_CT], size_or_initializer: int | Sequence[Any], *, lock: Literal[False] + ) -> ctypes.Array[_CT]: ... @overload def Array( self, typecode_or_type: type[c_char], size_or_initializer: int | Sequence[Any], *, lock: Literal[True] | _LockLike = True diff --git a/stdlib/multiprocessing/sharedctypes.pyi b/stdlib/multiprocessing/sharedctypes.pyi index 693fd2f70139..db492a351e1f 100644 --- a/stdlib/multiprocessing/sharedctypes.pyi +++ b/stdlib/multiprocessing/sharedctypes.pyi @@ -25,13 +25,17 @@ def RawArray(typecode_or_type: str, size_or_initializer: int | Sequence[Any]) -> @overload def Value(typecode_or_type: type[_CT], *args: Any, lock: Literal[False], ctx: BaseContext | None = None) -> _CT: ... @overload +def Value( + typecode_or_type: type[_SimpleCData[_T]], *args: Any, lock: Literal[True] | _LockLike = True, ctx: BaseContext | None = None +) -> Synchronized[_T]: ... +@overload def Value( typecode_or_type: type[_CT], *args: Any, lock: Literal[True] | _LockLike = True, ctx: BaseContext | None = None ) -> SynchronizedBase[_CT]: ... @overload def Value( typecode_or_type: str, *args: Any, lock: Literal[True] | _LockLike = True, ctx: BaseContext | None = None -) -> SynchronizedBase[Any]: ... +) -> Synchronized[Any]: ... @overload def Value( typecode_or_type: str | type[_CData], *args: Any, lock: bool | _LockLike = True, ctx: BaseContext | None = None @@ -40,7 +44,7 @@ def Value( @overload def Array( typecode_or_type: type[_CT], size_or_initializer: int | Sequence[Any], *, lock: Literal[False], ctx: BaseContext | None = None -) -> _CT: ... +) -> ctypes.Array[_CT]: ... @overload def Array( typecode_or_type: type[c_char],