Skip to content

Commit b2c964f

Browse files
authored
Make inheritance and metaclass of ctypes better match reality (#12982)
1 parent 780626e commit b2c964f

File tree

6 files changed

+197
-66
lines changed

6 files changed

+197
-66
lines changed

stdlib/@tests/stubtest_allowlists/common.txt

+2
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,8 @@ collections.abc.* # Types are re-exported from _collections_abc, so errors shou
312312
_?ctypes.Array.raw # exists but stubtest can't see it; only available if _CT == c_char
313313
_?ctypes.Array._type_ # _type_ is abstract, https://github.com/python/typeshed/pull/6361
314314
_?ctypes.Array._length_ # _length_ is abstract, https://github.com/python/typeshed/pull/6361
315+
_?ctypes.Structure.__getattr__ # doesn't exist, but makes things easy if we pretend it does
316+
_?ctypes.Union.__getattr__ # doesn't exist, but makes things easy if we pretend it does
315317

316318
# runtime is *args, **kwargs due to a wrapper
317319
# we have more accurate signatures in the stubs

stdlib/_ctypes.pyi

+158-49
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
import _typeshed
12
import sys
23
from _typeshed import ReadableBuffer, WriteableBuffer
34
from abc import abstractmethod
45
from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence
56
from ctypes import CDLL, ArgumentError as ArgumentError, c_void_p
6-
from typing import Any, ClassVar, Generic, TypeVar, overload
7+
from typing import Any, ClassVar, Generic, TypeVar, final, overload, type_check_only
78
from typing_extensions import Self, TypeAlias
89

910
if sys.version_info >= (3, 9):
@@ -47,46 +48,73 @@ if sys.platform == "win32":
4748
def LoadLibrary(name: str, load_flags: int = 0, /) -> int: ...
4849
def FreeLibrary(handle: int, /) -> None: ...
4950

50-
class _CDataMeta(type):
51-
# By default mypy complains about the following two methods, because strictly speaking cls
52-
# might not be a Type[_CT]. However this can never actually happen, because the only class that
53-
# uses _CDataMeta as its metaclass is _CData. So it's safe to ignore the errors here.
54-
def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
55-
def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
51+
if sys.version_info >= (3, 13):
52+
# This class is not exposed. It calls itself _ctypes.CType_Type.
53+
@type_check_only
54+
class _CType_Type(type):
55+
# By default mypy complains about the following two methods, because strictly speaking cls
56+
# might not be a Type[_CT]. However this doesn't happen because this is only a
57+
# metaclass for subclasses of _CData.
58+
def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
59+
def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
5660

57-
class _CData(metaclass=_CDataMeta):
61+
_CTypeBaseType = _CType_Type
62+
63+
else:
64+
_CTypeBaseType = type
65+
66+
# This class is not exposed.
67+
@type_check_only
68+
class _CData:
5869
_b_base_: int
5970
_b_needsfree_: bool
6071
_objects: Mapping[Any, int] | None
61-
# At runtime the following classmethods are available only on classes, not
62-
# on instances. This can't be reflected properly in the type system:
63-
#
64-
# Structure.from_buffer(...) # valid at runtime
65-
# Structure(...).from_buffer(...) # invalid at runtime
66-
#
67-
@classmethod
68-
def from_buffer(cls, source: WriteableBuffer, offset: int = ...) -> Self: ...
69-
@classmethod
70-
def from_buffer_copy(cls, source: ReadableBuffer, offset: int = ...) -> Self: ...
71-
@classmethod
72-
def from_address(cls, address: int) -> Self: ...
73-
@classmethod
74-
def from_param(cls, value: Any, /) -> Self | _CArgObject: ...
75-
@classmethod
76-
def in_dll(cls, library: CDLL, name: str) -> Self: ...
7772
def __buffer__(self, flags: int, /) -> memoryview: ...
78-
def __release_buffer__(self, buffer: memoryview, /) -> None: ...
73+
def __ctypes_from_outparam__(self, /) -> Self: ...
74+
75+
# this is a union of all the subclasses of _CData, which is useful because of
76+
# the methods that are present on each of those subclasses which are not present
77+
# on _CData itself.
78+
_CDataType: TypeAlias = _SimpleCData[Any] | _Pointer[Any] | CFuncPtr | Union | Structure | Array[Any]
7979

80-
class _SimpleCData(_CData, Generic[_T]):
80+
# This class is not exposed. It calls itself _ctypes.PyCSimpleType.
81+
@type_check_only
82+
class _PyCSimpleType(_CTypeBaseType):
83+
def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ...
84+
def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
85+
def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
86+
def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ...
87+
def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ...
88+
if sys.version_info < (3, 13):
89+
# Inherited from CType_Type starting on 3.13
90+
def __mul__(self: type[_CT], value: int, /) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
91+
def __rmul__(self: type[_CT], value: int, /) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
92+
93+
class _SimpleCData(_CData, Generic[_T], metaclass=_PyCSimpleType):
8194
value: _T
8295
# The TypeVar can be unsolved here,
8396
# but we can't use overloads without creating many, many mypy false-positive errors
8497
def __init__(self, value: _T = ...) -> None: ... # pyright: ignore[reportInvalidTypeVarUse]
98+
def __ctypes_from_outparam__(self, /) -> _T: ... # type: ignore[override]
8599

86100
class _CanCastTo(_CData): ...
87101
class _PointerLike(_CanCastTo): ...
88102

89-
class _Pointer(_PointerLike, _CData, Generic[_CT]):
103+
# This type is not exposed. It calls itself _ctypes.PyCPointerType.
104+
@type_check_only
105+
class _PyCPointerType(_CTypeBaseType):
106+
def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ...
107+
def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
108+
def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
109+
def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ...
110+
def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ...
111+
def set_type(self, type: Any, /) -> None: ...
112+
if sys.version_info < (3, 13):
113+
# Inherited from CType_Type starting on 3.13
114+
def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
115+
def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
116+
117+
class _Pointer(_PointerLike, _CData, Generic[_CT], metaclass=_PyCPointerType):
90118
_type_: type[_CT]
91119
contents: _CT
92120
@overload
@@ -105,16 +133,32 @@ def POINTER(type: None, /) -> type[c_void_p]: ...
105133
def POINTER(type: type[_CT], /) -> type[_Pointer[_CT]]: ...
106134
def pointer(obj: _CT, /) -> _Pointer[_CT]: ...
107135

136+
# This class is not exposed. It calls itself _ctypes.CArgObject.
137+
@final
138+
@type_check_only
108139
class _CArgObject: ...
109140

110-
def byref(obj: _CData, offset: int = ...) -> _CArgObject: ...
141+
def byref(obj: _CData | _CDataType, offset: int = ...) -> _CArgObject: ...
111142

112-
_ECT: TypeAlias = Callable[[_CData | None, CFuncPtr, tuple[_CData, ...]], _CData]
143+
_ECT: TypeAlias = Callable[[_CData | _CDataType | None, CFuncPtr, tuple[_CData | _CDataType, ...]], _CDataType]
113144
_PF: TypeAlias = tuple[int] | tuple[int, str | None] | tuple[int, str | None, Any]
114145

115-
class CFuncPtr(_PointerLike, _CData):
116-
restype: type[_CData] | Callable[[int], Any] | None
117-
argtypes: Sequence[type[_CData]]
146+
# This class is not exposed. It calls itself _ctypes.PyCFuncPtrType.
147+
@type_check_only
148+
class _PyCFuncPtrType(_CTypeBaseType):
149+
def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ...
150+
def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
151+
def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
152+
def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ...
153+
def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ...
154+
if sys.version_info < (3, 13):
155+
# Inherited from CType_Type starting on 3.13
156+
def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
157+
def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
158+
159+
class CFuncPtr(_PointerLike, _CData, metaclass=_PyCFuncPtrType):
160+
restype: type[_CDataType] | Callable[[int], Any] | None
161+
argtypes: Sequence[type[_CDataType]]
118162
errcheck: _ECT
119163
# Abstract attribute that must be defined on subclasses
120164
_flags_: ClassVar[int]
@@ -129,38 +173,103 @@ class CFuncPtr(_PointerLike, _CData):
129173
if sys.platform == "win32":
130174
@overload
131175
def __init__(
132-
self, vtbl_index: int, name: str, paramflags: tuple[_PF, ...] | None = ..., iid: _CData | None = ..., /
176+
self, vtbl_index: int, name: str, paramflags: tuple[_PF, ...] | None = ..., iid: _CData | _CDataType | None = ..., /
133177
) -> None: ...
134178

135179
def __call__(self, *args: Any, **kwargs: Any) -> Any: ...
136180

137181
_GetT = TypeVar("_GetT")
138182
_SetT = TypeVar("_SetT")
139183

184+
# This class is not exposed. It calls itself _ctypes.CField.
185+
@final
186+
@type_check_only
140187
class _CField(Generic[_CT, _GetT, _SetT]):
141188
offset: int
142189
size: int
143-
@overload
144-
def __get__(self, instance: None, owner: type[Any] | None, /) -> Self: ...
145-
@overload
146-
def __get__(self, instance: Any, owner: type[Any] | None, /) -> _GetT: ...
190+
if sys.version_info >= (3, 10):
191+
@overload
192+
def __get__(self, instance: None, owner: type[Any] | None = None, /) -> Self: ...
193+
@overload
194+
def __get__(self, instance: Any, owner: type[Any] | None = None, /) -> _GetT: ...
195+
else:
196+
@overload
197+
def __get__(self, instance: None, owner: type[Any] | None, /) -> Self: ...
198+
@overload
199+
def __get__(self, instance: Any, owner: type[Any] | None, /) -> _GetT: ...
200+
147201
def __set__(self, instance: Any, value: _SetT, /) -> None: ...
148202

149-
class _StructUnionMeta(_CDataMeta):
150-
_fields_: Sequence[tuple[str, type[_CData]] | tuple[str, type[_CData], int]]
151-
_pack_: int
152-
_anonymous_: Sequence[str]
203+
# This class is not exposed. It calls itself _ctypes.UnionType.
204+
@type_check_only
205+
class _UnionType(_CTypeBaseType):
206+
def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ...
207+
def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
208+
def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
209+
def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ...
210+
def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ...
211+
# At runtime, various attributes are created on a Union subclass based
212+
# on its _fields_. This method doesn't exist, but represents those
213+
# dynamically created attributes.
214+
def __getattr__(self, name: str) -> _CField[Any, Any, Any]: ...
215+
if sys.version_info < (3, 13):
216+
# Inherited from CType_Type starting on 3.13
217+
def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
218+
def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
219+
220+
class Union(_CData, metaclass=_UnionType):
221+
_fields_: ClassVar[Sequence[tuple[str, type[_CDataType]] | tuple[str, type[_CDataType], int]]]
222+
_pack_: ClassVar[int]
223+
_anonymous_: ClassVar[Sequence[str]]
224+
if sys.version_info >= (3, 13):
225+
_align_: ClassVar[int]
226+
227+
def __init__(self, *args: Any, **kw: Any) -> None: ...
228+
def __getattr__(self, name: str) -> Any: ...
229+
def __setattr__(self, name: str, value: Any) -> None: ...
230+
231+
# This class is not exposed. It calls itself _ctypes.PyCStructType.
232+
@type_check_only
233+
class _PyCStructType(_CTypeBaseType):
234+
def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ...
235+
def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
236+
def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
237+
def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ...
238+
def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ...
239+
# At runtime, various attributes are created on a Structure subclass based
240+
# on its _fields_. This method doesn't exist, but represents those
241+
# dynamically created attributes.
153242
def __getattr__(self, name: str) -> _CField[Any, Any, Any]: ...
243+
if sys.version_info < (3, 13):
244+
# Inherited from CType_Type starting on 3.13
245+
def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
246+
def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
247+
248+
class Structure(_CData, metaclass=_PyCStructType):
249+
_fields_: ClassVar[Sequence[tuple[str, type[_CDataType]] | tuple[str, type[_CDataType], int]]]
250+
_pack_: ClassVar[int]
251+
_anonymous_: ClassVar[Sequence[str]]
252+
if sys.version_info >= (3, 13):
253+
_align_: ClassVar[int]
154254

155-
class _StructUnionBase(_CData, metaclass=_StructUnionMeta):
156255
def __init__(self, *args: Any, **kw: Any) -> None: ...
157256
def __getattr__(self, name: str) -> Any: ...
158257
def __setattr__(self, name: str, value: Any) -> None: ...
159258

160-
class Union(_StructUnionBase): ...
161-
class Structure(_StructUnionBase): ...
259+
# This class is not exposed. It calls itself _ctypes.PyCArrayType.
260+
@type_check_only
261+
class _PyCArrayType(_CTypeBaseType):
262+
def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ...
263+
def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
264+
def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
265+
def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ...
266+
def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ...
267+
if sys.version_info < (3, 13):
268+
# Inherited from CType_Type starting on 3.13
269+
def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
270+
def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
162271

163-
class Array(_CData, Generic[_CT]):
272+
class Array(_CData, Generic[_CT], metaclass=_PyCArrayType):
164273
@property
165274
@abstractmethod
166275
def _length_(self) -> int: ...
@@ -205,9 +314,9 @@ class Array(_CData, Generic[_CT]):
205314
if sys.version_info >= (3, 9):
206315
def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
207316

208-
def addressof(obj: _CData, /) -> int: ...
209-
def alignment(obj_or_type: _CData | type[_CData], /) -> int: ...
317+
def addressof(obj: _CData | _CDataType, /) -> int: ...
318+
def alignment(obj_or_type: _CData | _CDataType | type[_CData | _CDataType], /) -> int: ...
210319
def get_errno() -> int: ...
211-
def resize(obj: _CData, size: int, /) -> None: ...
320+
def resize(obj: _CData | _CDataType, size: int, /) -> None: ...
212321
def set_errno(value: int, /) -> int: ...
213-
def sizeof(obj_or_type: _CData | type[_CData], /) -> int: ...
322+
def sizeof(obj_or_type: _CData | _CDataType | type[_CData | _CDataType], /) -> int: ...

stdlib/ctypes/__init__.pyi

+22-10
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,11 @@ from _ctypes import (
1010
_CanCastTo as _CanCastTo,
1111
_CArgObject as _CArgObject,
1212
_CData as _CData,
13-
_CDataMeta as _CDataMeta,
13+
_CDataType as _CDataType,
1414
_CField as _CField,
1515
_Pointer as _Pointer,
1616
_PointerLike as _PointerLike,
1717
_SimpleCData as _SimpleCData,
18-
_StructUnionBase as _StructUnionBase,
19-
_StructUnionMeta as _StructUnionMeta,
2018
addressof as addressof,
2119
alignment as alignment,
2220
byref as byref,
@@ -28,7 +26,7 @@ from _ctypes import (
2826
)
2927
from ctypes._endian import BigEndianStructure as BigEndianStructure, LittleEndianStructure as LittleEndianStructure
3028
from typing import Any, ClassVar, Generic, TypeVar
31-
from typing_extensions import TypeAlias
29+
from typing_extensions import Self, TypeAlias
3230

3331
if sys.platform == "win32":
3432
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): ...
4846

4947
class CDLL:
5048
_func_flags_: ClassVar[int]
51-
_func_restype_: ClassVar[_CData]
49+
_func_restype_: ClassVar[_CDataType]
5250
_name: str
5351
_handle: int
5452
_FuncPtr: type[_FuncPointer]
@@ -91,15 +89,21 @@ class _NamedFuncPointer(_FuncPointer):
9189
__name__: str
9290

9391
def CFUNCTYPE(
94-
restype: type[_CData] | None, *argtypes: type[_CData], use_errno: bool = ..., use_last_error: bool = ...
92+
restype: type[_CData | _CDataType] | None,
93+
*argtypes: type[_CData | _CDataType],
94+
use_errno: bool = ...,
95+
use_last_error: bool = ...,
9596
) -> type[_FuncPointer]: ...
9697

9798
if sys.platform == "win32":
9899
def WINFUNCTYPE(
99-
restype: type[_CData] | None, *argtypes: type[_CData], use_errno: bool = ..., use_last_error: bool = ...
100+
restype: type[_CData | _CDataType] | None,
101+
*argtypes: type[_CData | _CDataType],
102+
use_errno: bool = ...,
103+
use_last_error: bool = ...,
100104
) -> type[_FuncPointer]: ...
101105

102-
def PYFUNCTYPE(restype: type[_CData] | None, *argtypes: type[_CData]) -> type[_FuncPointer]: ...
106+
def PYFUNCTYPE(restype: type[_CData | _CDataType] | None, *argtypes: type[_CData | _CDataType]) -> type[_FuncPointer]: ...
103107

104108
# Any type that can be implicitly converted to c_void_p when passed as a C function argument.
105109
# (bytes is not included here, see below.)
@@ -112,7 +116,7 @@ _CVoidConstPLike: TypeAlias = _CVoidPLike | bytes
112116

113117
_CastT = TypeVar("_CastT", bound=_CanCastTo)
114118

115-
def cast(obj: _CData | _CArgObject | int, typ: type[_CastT]) -> _CastT: ...
119+
def cast(obj: _CData | _CDataType | _CArgObject | int, typ: type[_CastT]) -> _CastT: ...
116120
def create_string_buffer(init: int | bytes, size: int | None = None) -> Array[c_char]: ...
117121

118122
c_buffer = create_string_buffer
@@ -140,6 +144,8 @@ class c_char(_SimpleCData[bytes]):
140144

141145
class c_char_p(_PointerLike, _SimpleCData[bytes | None]):
142146
def __init__(self, value: int | bytes | None = ...) -> None: ...
147+
@classmethod
148+
def from_param(cls, value: Any, /) -> Self | _CArgObject: ...
143149

144150
class c_double(_SimpleCData[float]): ...
145151
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
155161
class c_ulong(_SimpleCData[int]): ...
156162
class c_ulonglong(_SimpleCData[int]): ... # can be an alias for c_ulong
157163
class c_ushort(_SimpleCData[int]): ...
158-
class c_void_p(_PointerLike, _SimpleCData[int | None]): ...
164+
165+
class c_void_p(_PointerLike, _SimpleCData[int | None]):
166+
@classmethod
167+
def from_param(cls, value: Any, /) -> Self | _CArgObject: ...
168+
159169
class c_wchar(_SimpleCData[str]): ...
160170

161171
c_int8 = c_byte
@@ -174,6 +184,8 @@ class c_uint64(_SimpleCData[int]): ...
174184

175185
class c_wchar_p(_PointerLike, _SimpleCData[str | None]):
176186
def __init__(self, value: int | str | None = ...) -> None: ...
187+
@classmethod
188+
def from_param(cls, value: Any, /) -> Self | _CArgObject: ...
177189

178190
class c_bool(_SimpleCData[bool]):
179191
def __init__(self, value: bool = ...) -> None: ...

0 commit comments

Comments
 (0)