diff --git a/stdlib/@tests/stubtest_allowlists/common.txt b/stdlib/@tests/stubtest_allowlists/common.txt index b6efe09bf..55c69d50f 100644 --- a/stdlib/@tests/stubtest_allowlists/common.txt +++ b/stdlib/@tests/stubtest_allowlists/common.txt @@ -312,6 +312,8 @@ collections.abc.* # Types are re-exported from _collections_abc, so errors shou _?ctypes.Array.raw # exists but stubtest can't see it; only available if _CT == c_char _?ctypes.Array._type_ # _type_ is abstract, https://github.com/python/typeshed/pull/6361 _?ctypes.Array._length_ # _length_ is abstract, https://github.com/python/typeshed/pull/6361 +_?ctypes.Structure.__getattr__ # doesn't exist, but makes things easy if we pretend it does +_?ctypes.Union.__getattr__ # doesn't exist, but makes things easy if we pretend it does # runtime is *args, **kwargs due to a wrapper # we have more accurate signatures in the stubs diff --git a/stdlib/_ctypes.pyi b/stdlib/_ctypes.pyi index f4d26d053..a1df1751b 100644 --- a/stdlib/_ctypes.pyi +++ b/stdlib/_ctypes.pyi @@ -1,9 +1,10 @@ +import _typeshed import sys from _typeshed import ReadableBuffer, WriteableBuffer from abc import abstractmethod from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence from ctypes import CDLL, ArgumentError as ArgumentError, c_void_p -from typing import Any, ClassVar, Generic, TypeVar, overload +from typing import Any, ClassVar, Generic, TypeVar, final, overload, type_check_only from typing_extensions import Self, TypeAlias if sys.version_info >= (3, 9): @@ -47,46 +48,73 @@ if sys.platform == "win32": def LoadLibrary(name: str, load_flags: int = 0, /) -> int: ... def FreeLibrary(handle: int, /) -> None: ... -class _CDataMeta(type): - # By default mypy complains about the following two methods, because strictly speaking cls - # might not be a Type[_CT]. However this can never actually happen, because the only class that - # uses _CDataMeta as its metaclass is _CData. So it's safe to ignore the errors here. - def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] - def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] +if sys.version_info >= (3, 13): + # This class is not exposed. It calls itself _ctypes.CType_Type. + @type_check_only + class _CType_Type(type): + # By default mypy complains about the following two methods, because strictly speaking cls + # might not be a Type[_CT]. However this doesn't happen because this is only a + # metaclass for subclasses of _CData. + def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] -class _CData(metaclass=_CDataMeta): + _CTypeBaseType = _CType_Type + +else: + _CTypeBaseType = type + +# This class is not exposed. +@type_check_only +class _CData: _b_base_: int _b_needsfree_: bool _objects: Mapping[Any, int] | None - # At runtime the following classmethods are available only on classes, not - # on instances. This can't be reflected properly in the type system: - # - # Structure.from_buffer(...) # valid at runtime - # Structure(...).from_buffer(...) # invalid at runtime - # - @classmethod - def from_buffer(cls, source: WriteableBuffer, offset: int = ...) -> Self: ... - @classmethod - def from_buffer_copy(cls, source: ReadableBuffer, offset: int = ...) -> Self: ... - @classmethod - def from_address(cls, address: int) -> Self: ... - @classmethod - def from_param(cls, value: Any, /) -> Self | _CArgObject: ... - @classmethod - def in_dll(cls, library: CDLL, name: str) -> Self: ... def __buffer__(self, flags: int, /) -> memoryview: ... - def __release_buffer__(self, buffer: memoryview, /) -> None: ... + def __ctypes_from_outparam__(self, /) -> Self: ... -class _SimpleCData(_CData, Generic[_T]): +# this is a union of all the subclasses of _CData, which is useful because of +# the methods that are present on each of those subclasses which are not present +# on _CData itself. +_CDataType: TypeAlias = _SimpleCData[Any] | _Pointer[Any] | CFuncPtr | Union | Structure | Array[Any] + +# This class is not exposed. It calls itself _ctypes.PyCSimpleType. +@type_check_only +class _PyCSimpleType(_CTypeBaseType): + def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ... + def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ... + def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ... + if sys.version_info < (3, 13): + # Inherited from CType_Type starting on 3.13 + def __mul__(self: type[_CT], value: int, /) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + def __rmul__(self: type[_CT], value: int, /) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + +class _SimpleCData(_CData, Generic[_T], metaclass=_PyCSimpleType): value: _T # The TypeVar can be unsolved here, # but we can't use overloads without creating many, many mypy false-positive errors def __init__(self, value: _T = ...) -> None: ... # pyright: ignore[reportInvalidTypeVarUse] + def __ctypes_from_outparam__(self, /) -> _T: ... # type: ignore[override] class _CanCastTo(_CData): ... class _PointerLike(_CanCastTo): ... -class _Pointer(_PointerLike, _CData, Generic[_CT]): +# This type is not exposed. It calls itself _ctypes.PyCPointerType. +@type_check_only +class _PyCPointerType(_CTypeBaseType): + def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ... + def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ... + def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ... + def set_type(self, type: Any, /) -> None: ... + if sys.version_info < (3, 13): + # Inherited from CType_Type starting on 3.13 + def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + +class _Pointer(_PointerLike, _CData, Generic[_CT], metaclass=_PyCPointerType): _type_: type[_CT] contents: _CT @overload @@ -105,16 +133,32 @@ def POINTER(type: None, /) -> type[c_void_p]: ... def POINTER(type: type[_CT], /) -> type[_Pointer[_CT]]: ... def pointer(obj: _CT, /) -> _Pointer[_CT]: ... +# This class is not exposed. It calls itself _ctypes.CArgObject. +@final +@type_check_only class _CArgObject: ... -def byref(obj: _CData, offset: int = ...) -> _CArgObject: ... +def byref(obj: _CData | _CDataType, offset: int = ...) -> _CArgObject: ... -_ECT: TypeAlias = Callable[[_CData | None, CFuncPtr, tuple[_CData, ...]], _CData] +_ECT: TypeAlias = Callable[[_CData | _CDataType | None, CFuncPtr, tuple[_CData | _CDataType, ...]], _CDataType] _PF: TypeAlias = tuple[int] | tuple[int, str | None] | tuple[int, str | None, Any] -class CFuncPtr(_PointerLike, _CData): - restype: type[_CData] | Callable[[int], Any] | None - argtypes: Sequence[type[_CData]] +# This class is not exposed. It calls itself _ctypes.PyCFuncPtrType. +@type_check_only +class _PyCFuncPtrType(_CTypeBaseType): + def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ... + def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ... + def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ... + if sys.version_info < (3, 13): + # Inherited from CType_Type starting on 3.13 + def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + +class CFuncPtr(_PointerLike, _CData, metaclass=_PyCFuncPtrType): + restype: type[_CDataType] | Callable[[int], Any] | None + argtypes: Sequence[type[_CDataType]] errcheck: _ECT # Abstract attribute that must be defined on subclasses _flags_: ClassVar[int] @@ -129,7 +173,7 @@ class CFuncPtr(_PointerLike, _CData): if sys.platform == "win32": @overload def __init__( - self, vtbl_index: int, name: str, paramflags: tuple[_PF, ...] | None = ..., iid: _CData | None = ..., / + self, vtbl_index: int, name: str, paramflags: tuple[_PF, ...] | None = ..., iid: _CData | _CDataType | None = ..., / ) -> None: ... def __call__(self, *args: Any, **kwargs: Any) -> Any: ... @@ -137,30 +181,95 @@ class CFuncPtr(_PointerLike, _CData): _GetT = TypeVar("_GetT") _SetT = TypeVar("_SetT") +# This class is not exposed. It calls itself _ctypes.CField. +@final +@type_check_only class _CField(Generic[_CT, _GetT, _SetT]): offset: int size: int - @overload - def __get__(self, instance: None, owner: type[Any] | None, /) -> Self: ... - @overload - def __get__(self, instance: Any, owner: type[Any] | None, /) -> _GetT: ... + if sys.version_info >= (3, 10): + @overload + def __get__(self, instance: None, owner: type[Any] | None = None, /) -> Self: ... + @overload + def __get__(self, instance: Any, owner: type[Any] | None = None, /) -> _GetT: ... + else: + @overload + def __get__(self, instance: None, owner: type[Any] | None, /) -> Self: ... + @overload + def __get__(self, instance: Any, owner: type[Any] | None, /) -> _GetT: ... + def __set__(self, instance: Any, value: _SetT, /) -> None: ... -class _StructUnionMeta(_CDataMeta): - _fields_: Sequence[tuple[str, type[_CData]] | tuple[str, type[_CData], int]] - _pack_: int - _anonymous_: Sequence[str] +# This class is not exposed. It calls itself _ctypes.UnionType. +@type_check_only +class _UnionType(_CTypeBaseType): + def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ... + def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ... + def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ... + # At runtime, various attributes are created on a Union subclass based + # on its _fields_. This method doesn't exist, but represents those + # dynamically created attributes. def __getattr__(self, name: str) -> _CField[Any, Any, Any]: ... + if sys.version_info < (3, 13): + # Inherited from CType_Type starting on 3.13 + def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + +class Union(_CData, metaclass=_UnionType): + _fields_: ClassVar[Sequence[tuple[str, type[_CDataType]] | tuple[str, type[_CDataType], int]]] + _pack_: ClassVar[int] + _anonymous_: ClassVar[Sequence[str]] + if sys.version_info >= (3, 13): + _align_: ClassVar[int] -class _StructUnionBase(_CData, metaclass=_StructUnionMeta): def __init__(self, *args: Any, **kw: Any) -> None: ... def __getattr__(self, name: str) -> Any: ... def __setattr__(self, name: str, value: Any) -> None: ... -class Union(_StructUnionBase): ... -class Structure(_StructUnionBase): ... +# This class is not exposed. It calls itself _ctypes.PyCStructType. +@type_check_only +class _PyCStructType(_CTypeBaseType): + def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ... + def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ... + def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ... + # At runtime, various attributes are created on a Structure subclass based + # on its _fields_. This method doesn't exist, but represents those + # dynamically created attributes. + def __getattr__(self, name: str) -> _CField[Any, Any, Any]: ... + if sys.version_info < (3, 13): + # Inherited from CType_Type starting on 3.13 + def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] -class Array(_CData, Generic[_CT]): +class Structure(_CData, metaclass=_PyCStructType): + _fields_: ClassVar[Sequence[tuple[str, type[_CDataType]] | tuple[str, type[_CDataType], int]]] + _pack_: ClassVar[int] + _anonymous_: ClassVar[Sequence[str]] + if sys.version_info >= (3, 13): + _align_: ClassVar[int] + + def __init__(self, *args: Any, **kw: Any) -> None: ... + def __getattr__(self, name: str) -> Any: ... + def __setattr__(self, name: str, value: Any) -> None: ... + +# This class is not exposed. It calls itself _ctypes.PyCArrayType. +@type_check_only +class _PyCArrayType(_CTypeBaseType): + def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ... + def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ... + def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ... + if sys.version_info < (3, 13): + # Inherited from CType_Type starting on 3.13 + def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + +class Array(_CData, Generic[_CT], metaclass=_PyCArrayType): @property @abstractmethod def _length_(self) -> int: ... @@ -205,9 +314,9 @@ class Array(_CData, Generic[_CT]): if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... -def addressof(obj: _CData, /) -> int: ... -def alignment(obj_or_type: _CData | type[_CData], /) -> int: ... +def addressof(obj: _CData | _CDataType, /) -> int: ... +def alignment(obj_or_type: _CData | _CDataType | type[_CData | _CDataType], /) -> int: ... def get_errno() -> int: ... -def resize(obj: _CData, size: int, /) -> None: ... +def resize(obj: _CData | _CDataType, size: int, /) -> None: ... def set_errno(value: int, /) -> int: ... -def sizeof(obj_or_type: _CData | type[_CData], /) -> int: ... +def sizeof(obj_or_type: _CData | _CDataType | type[_CData | _CDataType], /) -> int: ... diff --git a/stdlib/ctypes/__init__.pyi b/stdlib/ctypes/__init__.pyi index 40a073d10..8e4505110 100644 --- a/stdlib/ctypes/__init__.pyi +++ b/stdlib/ctypes/__init__.pyi @@ -10,13 +10,11 @@ from _ctypes import ( _CanCastTo as _CanCastTo, _CArgObject as _CArgObject, _CData as _CData, - _CDataMeta as _CDataMeta, + _CDataType as _CDataType, _CField as _CField, _Pointer as _Pointer, _PointerLike as _PointerLike, _SimpleCData as _SimpleCData, - _StructUnionBase as _StructUnionBase, - _StructUnionMeta as _StructUnionMeta, addressof as addressof, alignment as alignment, byref as byref, @@ -28,7 +26,7 @@ from _ctypes import ( ) from ctypes._endian import BigEndianStructure as BigEndianStructure, LittleEndianStructure as LittleEndianStructure from typing import Any, ClassVar, Generic, TypeVar -from typing_extensions import TypeAlias +from typing_extensions import Self, TypeAlias if sys.platform == "win32": from _ctypes import FormatError as FormatError, get_last_error as get_last_error, set_last_error as set_last_error @@ -48,7 +46,7 @@ class ArgumentError(Exception): ... class CDLL: _func_flags_: ClassVar[int] - _func_restype_: ClassVar[_CData] + _func_restype_: ClassVar[_CDataType] _name: str _handle: int _FuncPtr: type[_FuncPointer] @@ -91,15 +89,21 @@ class _NamedFuncPointer(_FuncPointer): __name__: str def CFUNCTYPE( - restype: type[_CData] | None, *argtypes: type[_CData], use_errno: bool = ..., use_last_error: bool = ... + restype: type[_CData | _CDataType] | None, + *argtypes: type[_CData | _CDataType], + use_errno: bool = ..., + use_last_error: bool = ..., ) -> type[_FuncPointer]: ... if sys.platform == "win32": def WINFUNCTYPE( - restype: type[_CData] | None, *argtypes: type[_CData], use_errno: bool = ..., use_last_error: bool = ... + restype: type[_CData | _CDataType] | None, + *argtypes: type[_CData | _CDataType], + use_errno: bool = ..., + use_last_error: bool = ..., ) -> type[_FuncPointer]: ... -def PYFUNCTYPE(restype: type[_CData] | None, *argtypes: type[_CData]) -> type[_FuncPointer]: ... +def PYFUNCTYPE(restype: type[_CData | _CDataType] | None, *argtypes: type[_CData | _CDataType]) -> type[_FuncPointer]: ... # Any type that can be implicitly converted to c_void_p when passed as a C function argument. # (bytes is not included here, see below.) @@ -112,7 +116,7 @@ _CVoidConstPLike: TypeAlias = _CVoidPLike | bytes _CastT = TypeVar("_CastT", bound=_CanCastTo) -def cast(obj: _CData | _CArgObject | int, typ: type[_CastT]) -> _CastT: ... +def cast(obj: _CData | _CDataType | _CArgObject | int, typ: type[_CastT]) -> _CastT: ... def create_string_buffer(init: int | bytes, size: int | None = None) -> Array[c_char]: ... c_buffer = create_string_buffer @@ -140,6 +144,8 @@ class c_char(_SimpleCData[bytes]): class c_char_p(_PointerLike, _SimpleCData[bytes | None]): def __init__(self, value: int | bytes | None = ...) -> None: ... + @classmethod + def from_param(cls, value: Any, /) -> Self | _CArgObject: ... class c_double(_SimpleCData[float]): ... class c_longdouble(_SimpleCData[float]): ... # can be an alias for c_double @@ -155,7 +161,11 @@ class c_uint(_SimpleCData[int]): ... # can be an alias for c_ulong class c_ulong(_SimpleCData[int]): ... class c_ulonglong(_SimpleCData[int]): ... # can be an alias for c_ulong class c_ushort(_SimpleCData[int]): ... -class c_void_p(_PointerLike, _SimpleCData[int | None]): ... + +class c_void_p(_PointerLike, _SimpleCData[int | None]): + @classmethod + def from_param(cls, value: Any, /) -> Self | _CArgObject: ... + class c_wchar(_SimpleCData[str]): ... c_int8 = c_byte @@ -174,6 +184,8 @@ class c_uint64(_SimpleCData[int]): ... class c_wchar_p(_PointerLike, _SimpleCData[str | None]): def __init__(self, value: int | str | None = ...) -> None: ... + @classmethod + def from_param(cls, value: Any, /) -> Self | _CArgObject: ... class c_bool(_SimpleCData[bool]): def __init__(self, value: bool = ...) -> None: ... diff --git a/stdlib/ctypes/wintypes.pyi b/stdlib/ctypes/wintypes.pyi index 8847860f2..e938d8f22 100644 --- a/stdlib/ctypes/wintypes.pyi +++ b/stdlib/ctypes/wintypes.pyi @@ -1,7 +1,7 @@ +from _ctypes import _CArgObject, _CField from ctypes import ( Array, Structure, - _CField, _Pointer, _SimpleCData, c_byte, @@ -21,8 +21,8 @@ from ctypes import ( c_wchar, c_wchar_p, ) -from typing import TypeVar -from typing_extensions import TypeAlias +from typing import Any, TypeVar +from typing_extensions import Self, TypeAlias BYTE = c_byte WORD = c_ushort @@ -241,10 +241,16 @@ LPBYTE = PBYTE PBOOLEAN = PBYTE # LP_c_char -class PCHAR(_Pointer[CHAR]): ... +class PCHAR(_Pointer[CHAR]): + # this is inherited from ctypes.c_char_p, kind of. + @classmethod + def from_param(cls, value: Any, /) -> Self | _CArgObject: ... # LP_c_wchar -class PWCHAR(_Pointer[WCHAR]): ... +class PWCHAR(_Pointer[WCHAR]): + # inherited from ctypes.c_wchar_p, kind of + @classmethod + def from_param(cls, value: Any, /) -> Self | _CArgObject: ... # LP_c_void_p class PHANDLE(_Pointer[HANDLE]): ... diff --git a/stdlib/multiprocessing/context.pyi b/stdlib/multiprocessing/context.pyi index 9900f009a..03d1d2e5c 100644 --- a/stdlib/multiprocessing/context.pyi +++ b/stdlib/multiprocessing/context.pyi @@ -1,7 +1,8 @@ import ctypes import sys +from _ctypes import _CData from collections.abc import Callable, Iterable, Sequence -from ctypes import _CData, _SimpleCData, c_char +from ctypes import _SimpleCData, c_char from logging import Logger, _Level as _LoggingLevel from multiprocessing import popen_fork, popen_forkserver, popen_spawn_posix, popen_spawn_win32, queues, synchronize from multiprocessing.managers import SyncManager diff --git a/stdlib/multiprocessing/sharedctypes.pyi b/stdlib/multiprocessing/sharedctypes.pyi index 2b0498abc..5283445d8 100644 --- a/stdlib/multiprocessing/sharedctypes.pyi +++ b/stdlib/multiprocessing/sharedctypes.pyi @@ -1,6 +1,7 @@ import ctypes +from _ctypes import _CData from collections.abc import Callable, Iterable, Sequence -from ctypes import _CData, _SimpleCData, c_char +from ctypes import _SimpleCData, c_char from multiprocessing.context import BaseContext from multiprocessing.synchronize import _LockLike from types import TracebackType