diff --git a/stdlib/@tests/stubtest_allowlists/common.txt b/stdlib/@tests/stubtest_allowlists/common.txt index 10a5cd00d..797dd0e45 100644 --- a/stdlib/@tests/stubtest_allowlists/common.txt +++ b/stdlib/@tests/stubtest_allowlists/common.txt @@ -14,9 +14,6 @@ asyncio.base_events.BaseEventLoop.subprocess_exec # BaseEventLoop adds several builtins.dict.get collections\.ChainMap\.fromkeys # https://github.com/python/mypy/issues/17023 contextlib._GeneratorContextManagerBase.__init__ # skipped in the stubs in favor of its child classes -ctypes.CDLL._FuncPtr # None at class level but initialized in __init__ to this value -ctypes.memmove # CFunctionType -ctypes.memset # CFunctionType http.client.HTTPConnection.response_class # the actual type at runtime is abc.ABCMeta importlib.abc.Loader.exec_module # See Lib/importlib/_abc.py. Might be defined for backwards compatibility importlib.abc.MetaPathFinder.find_spec # Not defined on the actual class, but expected to exist. @@ -319,9 +316,10 @@ csv.Dialect.skipinitialspace csv.DictReader.__init__ # runtime sig has *args but will error if more than 5 positional args are supplied csv.DictWriter.__init__ # runtime sig has *args but will error if more than 5 positional args are supplied -_?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.Array.raw # exists but stubtest can't see it; only available if _CT == c_char +ctypes.CDLL._FuncPtr # None at class level but initialized in __init__ to this value _?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 diff --git a/stdlib/ctypes/__init__.pyi b/stdlib/ctypes/__init__.pyi index a15dd3615..e0676aad5 100644 --- a/stdlib/ctypes/__init__.pyi +++ b/stdlib/ctypes/__init__.pyi @@ -25,7 +25,7 @@ from _ctypes import ( sizeof as sizeof, ) from ctypes._endian import BigEndianStructure as BigEndianStructure, LittleEndianStructure as LittleEndianStructure -from typing import Any, ClassVar, Generic, TypeVar +from typing import Any, ClassVar, Generic, TypeVar, type_check_only from typing_extensions import Self, TypeAlias, deprecated if sys.platform == "win32": @@ -45,12 +45,24 @@ DEFAULT_MODE: int class ArgumentError(Exception): ... +# defined within CDLL.__init__ +# Runtime name is ctypes.CDLL.__init__.._FuncPtr +@type_check_only +class _FuncPtr(_CFuncPtr): + _flags_: ClassVar[int] + _restype_: ClassVar[type[_CDataType]] + +# Not a real class; _FuncPtr with a __name__ set on it. +@type_check_only +class _NamedFuncPointer(_FuncPtr): + __name__: str + class CDLL: _func_flags_: ClassVar[int] _func_restype_: ClassVar[type[_CDataType]] _name: str _handle: int - _FuncPtr: type[_FuncPointer] + _FuncPtr: type[_FuncPtr] def __init__( self, name: str | None, @@ -84,27 +96,36 @@ if sys.platform == "win32": pydll: LibraryLoader[PyDLL] pythonapi: PyDLL -class _FuncPointer(_CFuncPtr): ... +# Class definition within CFUNCTYPE / WINFUNCTYPE / PYFUNCTYPE +# Names at runtime are +# ctypes.CFUNCTYPE..CFunctionType +# ctypes.WINFUNCTYPE..WinFunctionType +# ctypes.PYFUNCTYPE..CFunctionType +@type_check_only +class _CFunctionType(_CFuncPtr): + _argtypes_: ClassVar[list[type[_CData | _CDataType]]] + _restype_: ClassVar[type[_CData | _CDataType] | None] + _flags_: ClassVar[int] -class _NamedFuncPointer(_FuncPointer): - __name__: str +# Alias for either function pointer type +_FuncPointer: TypeAlias = _FuncPtr | _CFunctionType # noqa: Y047 # not used here def CFUNCTYPE( restype: type[_CData | _CDataType] | None, *argtypes: type[_CData | _CDataType], - use_errno: bool = ..., - use_last_error: bool = ..., -) -> type[_FuncPointer]: ... + use_errno: bool = False, + use_last_error: bool = False, +) -> type[_CFunctionType]: ... if sys.platform == "win32": def WINFUNCTYPE( restype: type[_CData | _CDataType] | None, *argtypes: type[_CData | _CDataType], - use_errno: bool = ..., - use_last_error: bool = ..., - ) -> type[_FuncPointer]: ... + use_errno: bool = False, + use_last_error: bool = False, + ) -> type[_CFunctionType]: ... -def PYFUNCTYPE(restype: type[_CData | _CDataType] | None, *argtypes: type[_CData | _CDataType]) -> type[_FuncPointer]: ... +def PYFUNCTYPE(restype: type[_CData | _CDataType] | None, *argtypes: type[_CData | _CDataType]) -> type[_CFunctionType]: ... # 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.) @@ -134,8 +155,22 @@ if sys.platform == "win32": def DllGetClassObject(rclsid: Any, riid: Any, ppv: Any) -> int: ... # TODO not documented def GetLastError() -> int: ... -def memmove(dst: _CVoidPLike, src: _CVoidConstPLike, count: int) -> int: ... -def memset(dst: _CVoidPLike, c: int, count: int) -> int: ... +# Actually just an instance of _CFunctionType, but we want to set a more +# specific __call__. +@type_check_only +class _MemmoveFunctionType(_CFunctionType): + def __call__(self, dst: _CVoidPLike, src: _CVoidConstPLike, count: int) -> int: ... + +memmove: _MemmoveFunctionType + +# Actually just an instance of _CFunctionType, but we want to set a more +# specific __call__. +@type_check_only +class _MemsetFunctionType(_CFunctionType): + def __call__(self, dst: _CVoidPLike, c: int, count: int) -> int: ... + +memset: _MemsetFunctionType + def string_at(ptr: _CVoidConstPLike, size: int = -1) -> bytes: ... if sys.platform == "win32":