Move _SimpleCData and Array from ctypes/__init__.pyi to _ctypes.pyi (#10118)

This commit is contained in:
Jun Komoda
2023-05-01 05:22:46 +09:00
committed by GitHub
parent 2c3449694b
commit ae0c9f9dad
3 changed files with 99 additions and 81 deletions

View File

@@ -1,6 +1,16 @@
import sys
from ctypes import _CArgObject, _PointerLike
from typing_extensions import TypeAlias
from _typeshed import ReadableBuffer, WriteableBuffer
from abc import abstractmethod
from collections.abc import Iterable, Iterator, Mapping
from ctypes import CDLL, _CArgObject, _PointerLike
from typing import Any, Generic, TypeVar, overload
from typing_extensions import Self, TypeAlias
if sys.version_info >= (3, 9):
from types import GenericAlias
_T = TypeVar("_T")
_CT = TypeVar("_CT", bound=_CData)
FUNCFLAG_CDECL: int
FUNCFLAG_PYTHONAPI: int
@@ -27,3 +37,76 @@ if sys.platform == "win32":
FUNCFLAG_HRESULT: int
FUNCFLAG_STDCALL: int
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]
class _CData(metaclass=_CDataMeta):
_b_base_: int
_b_needsfree_: bool
_objects: Mapping[Any, int] | None
@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, obj: Any) -> Self | _CArgObject: ...
@classmethod
def in_dll(cls, library: CDLL, name: str) -> Self: ...
class _SimpleCData(Generic[_T], _CData):
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]
class Array(Generic[_CT], _CData):
@property
@abstractmethod
def _length_(self) -> int: ...
@_length_.setter
def _length_(self, value: int) -> None: ...
@property
@abstractmethod
def _type_(self) -> type[_CT]: ...
@_type_.setter
def _type_(self, value: type[_CT]) -> None: ...
# Note: only available if _CT == c_char
@property
def raw(self) -> bytes: ...
@raw.setter
def raw(self, value: ReadableBuffer) -> None: ...
value: Any # Note: bytes if _CT == c_char, str if _CT == c_wchar, unavailable otherwise
# TODO These methods cannot be annotated correctly at the moment.
# All of these "Any"s stand for the array's element type, but it's not possible to use _CT
# here, because of a special feature of ctypes.
# By default, when accessing an element of an Array[_CT], the returned object has type _CT.
# However, when _CT is a "simple type" like c_int, ctypes automatically "unboxes" the object
# and converts it to the corresponding Python primitive. For example, when accessing an element
# of an Array[c_int], a Python int object is returned, not a c_int.
# This behavior does *not* apply to subclasses of "simple types".
# If MyInt is a subclass of c_int, then accessing an element of an Array[MyInt] returns
# a MyInt, not an int.
# This special behavior is not easy to model in a stub, so for now all places where
# the array element type would belong are annotated with Any instead.
def __init__(self, *args: Any) -> None: ...
@overload
def __getitem__(self, __key: int) -> Any: ...
@overload
def __getitem__(self, __key: slice) -> list[Any]: ...
@overload
def __setitem__(self, __key: int, __value: Any) -> None: ...
@overload
def __setitem__(self, __key: slice, __value: Iterable[Any]) -> None: ...
def __iter__(self) -> Iterator[Any]: ...
# Can't inherit from Sized because the metaclass conflict between
# Sized and _CData prevents using _CDataMeta.
def __len__(self) -> int: ...
if sys.version_info >= (3, 9):
def __class_getitem__(cls, item: Any) -> GenericAlias: ...

View File

@@ -1,10 +1,15 @@
import sys
from _ctypes import RTLD_GLOBAL as RTLD_GLOBAL, RTLD_LOCAL as RTLD_LOCAL
from _typeshed import ReadableBuffer, WriteableBuffer
from abc import abstractmethod
from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence
from _ctypes import (
RTLD_GLOBAL as RTLD_GLOBAL,
RTLD_LOCAL as RTLD_LOCAL,
Array as Array,
_CData as _CData,
_CDataMeta as _CDataMeta,
_SimpleCData as _SimpleCData,
)
from collections.abc import Callable, Sequence
from typing import Any, ClassVar, Generic, TypeVar, overload
from typing_extensions import Self, TypeAlias
from typing_extensions import TypeAlias
if sys.version_info >= (3, 9):
from types import GenericAlias
@@ -65,28 +70,6 @@ if sys.platform == "win32":
pydll: LibraryLoader[PyDLL]
pythonapi: PyDLL
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]
class _CData(metaclass=_CDataMeta):
_b_base_: int
_b_needsfree_: bool
_objects: Mapping[Any, int] | None
@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, obj: Any) -> Self | _CArgObject: ...
@classmethod
def in_dll(cls, library: CDLL, name: str) -> Self: ...
class _CanCastTo(_CData): ...
class _PointerLike(_CanCastTo): ...
@@ -190,12 +173,6 @@ if sys.platform == "win32":
def wstring_at(address: _CVoidConstPLike, size: int = -1) -> str: ...
class _SimpleCData(Generic[_T], _CData):
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]
class c_byte(_SimpleCData[int]): ...
class c_char(_SimpleCData[bytes]):
@@ -259,48 +236,3 @@ class Union(_StructUnionBase): ...
class Structure(_StructUnionBase): ...
class BigEndianStructure(Structure): ...
class LittleEndianStructure(Structure): ...
class Array(Generic[_CT], _CData):
@property
@abstractmethod
def _length_(self) -> int: ...
@_length_.setter
def _length_(self, value: int) -> None: ...
@property
@abstractmethod
def _type_(self) -> type[_CT]: ...
@_type_.setter
def _type_(self, value: type[_CT]) -> None: ...
# Note: only available if _CT == c_char
@property
def raw(self) -> bytes: ...
@raw.setter
def raw(self, value: ReadableBuffer) -> None: ...
value: Any # Note: bytes if _CT == c_char, str if _CT == c_wchar, unavailable otherwise
# TODO These methods cannot be annotated correctly at the moment.
# All of these "Any"s stand for the array's element type, but it's not possible to use _CT
# here, because of a special feature of ctypes.
# By default, when accessing an element of an Array[_CT], the returned object has type _CT.
# However, when _CT is a "simple type" like c_int, ctypes automatically "unboxes" the object
# and converts it to the corresponding Python primitive. For example, when accessing an element
# of an Array[c_int], a Python int object is returned, not a c_int.
# This behavior does *not* apply to subclasses of "simple types".
# If MyInt is a subclass of c_int, then accessing an element of an Array[MyInt] returns
# a MyInt, not an int.
# This special behavior is not easy to model in a stub, so for now all places where
# the array element type would belong are annotated with Any instead.
def __init__(self, *args: Any) -> None: ...
@overload
def __getitem__(self, __key: int) -> Any: ...
@overload
def __getitem__(self, __key: slice) -> list[Any]: ...
@overload
def __setitem__(self, __key: int, __value: Any) -> None: ...
@overload
def __setitem__(self, __key: slice, __value: Iterable[Any]) -> None: ...
def __iter__(self) -> Iterator[Any]: ...
# Can't inherit from Sized because the metaclass conflict between
# Sized and _CData prevents using _CDataMeta.
def __len__(self) -> int: ...
if sys.version_info >= (3, 9):
def __class_getitem__(cls, item: Any) -> GenericAlias: ...

View File

@@ -78,7 +78,9 @@ 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._type_ # _type_ and _length_ are abstract, https://github.com/python/typeshed/pull/6361
_ctypes.Array._type_
ctypes.Array._length_
_ctypes.Array._length_
ctypes.CDLL._FuncPtr # None at class level but initialized in __init__ to this value
ctypes.memmove # CFunctionType
ctypes.memset # CFunctionType
@@ -332,7 +334,6 @@ turtle.ScrolledCanvas.onResize
wave.Wave_read.initfp
wave.Wave_write.initfp
_ctypes.Array
_ctypes.CFuncPtr
_ctypes.POINTER
_ctypes.PyObj_FromPtr
@@ -357,6 +358,7 @@ _ctypes.sizeof
# ==========
ctypes.Array.raw # exists but stubtest can't see it; only available if _CT == c_char
_ctypes.Array.raw
_collections_abc.AsyncGenerator.asend # async at runtime, deliberately not in the stub, see #7491. Pos-only differences also.
_collections_abc.AsyncGenerator.__anext__ # async at runtime, deliberately not in the stub, see #7491
@@ -605,6 +607,7 @@ wsgiref.handlers.BaseHandler.status
# Iterable classes that don't define __iter__ at runtime (usually iterable via __getitem__)
# These would ideally be special-cased by type checkers; see https://github.com/python/mypy/issues/2220
_ctypes.Array.__iter__
ctypes.Array.__iter__
mmap.mmap.__iter__
mmap.mmap.__contains__