Currently, attributes of ctypes.Structure, such as ctypes.wintypes.POINT, are annotated with names and types that specified in _fields_.
| classPOINT(Structure): |
| x: LONG |
| y: LONG |
The problem with this approach is that at runtime, the attributes return types like int or bytes, while type checkers interpret them as returning c_int or c_char.
Below is the simplest example of a structure created for explanation purposes.
At runtime, the types of these fields are ctypes.CField. In the example below, x is a data descriptor that returns int as the getter and accepts both c_int and int as the setter.
>>>importctypes>>>>>>classFoo(ctypes.Structure):
... pass
... >>>Foo._fields_= [('x', ctypes.c_int)]
>>>Foo.x<Fieldtype=c_long, ofs=0, size=4>>>>type(Foo.x) <class'_ctypes.CField'>>>>foo=Foo()
>>>foo<__main__.Fooobjectat0x0000023CDB80B8C0>>>>foo.x0>>>foo.x=3>>>foo.x3>>>foo.x=ctypes.c_int(2)
>>>foo.x2Having stubs with types that deviate from the runtime is not an ideal situation.
Therefore, I propose modifying CField as shown below for use in annotating fields.
_T=TypeVar("_T")
_CT=TypeVar("_CT", bound=_CData)
classCField(Generic[_T, _CT]):
offset: intsize: int@overloaddef__get__(self, instance: None, owner: type[Any]) ->Self: ...
@overloaddef__get__(self, instance: Any, owner: type[Any] |None) ->_T: ...
def__set__(self, instance: Any, value: _T|_CT) ->None: ...
classFoo(Structure):
x: ClassVar[CField[int, c_int]]
# Foo._fields_ = [('x', c_int)] # required in runtimea=Foo.x# CField[int, c_int]foo=Foo()
b=foo.x# intfoo.x=3# OKfoo.x=c_int(2) # OKfoo.x=3.14# NGfoo.x=c_double(3.14) # NGanother idea
I thought it elegant to specify only subclasses of _SimpleCData[_T] as type parameters, given the potential for inferring _T, as shown below.
classBar(Structure):
x: ClassVar[CField[c_int]] # returns `int`, can take `int` or `c_int`
However, current static type system cannot that.
Moreover, considering that there exist not only subclasses of _SimpleCData defined within ctypes, but also third-party developers who define _SimpleCData subclasses within their own projects.
There is the redundancy, but I believe that utilizing the two type parameters would enhance flexibility.
Currently, attributes of
ctypes.Structure, such asctypes.wintypes.POINT, are annotated with names and types that specified in_fields_.typeshed/stdlib/ctypes/wintypes.pyi
Lines 126 to 128 in df08fce
The problem with this approach is that at runtime, the attributes return types like
intorbytes, while type checkers interpret them as returningc_intorc_char.Below is the simplest example of a structure created for explanation purposes.
At runtime, the types of these fields are
ctypes.CField. In the example below,xis a data descriptor that returnsintas the getter and accepts bothc_intandintas the setter.Having stubs with types that deviate from the runtime is not an ideal situation.
Therefore, I propose modifying
CFieldas shown below for use in annotating fields.another idea
I thought it elegant to specify only subclasses of
_SimpleCData[_T]as type parameters, given the potential for inferring_T, as shown below.However, current static type system cannot that.
Moreover, considering that there exist not only subclasses of
_SimpleCDatadefined withinctypes, but also third-party developers who define_SimpleCDatasubclasses within their own projects.There is the redundancy, but I believe that utilizing the two type parameters would enhance flexibility.