Modernizing the ctypes API or Providing an alternative

I was recently creating a Python wrapper for some C code. I was frustrated doing this that I couldn’t get any type hints about the DLL I loaded. This felt like taking a step back 10 years in Python.

Furthermore, some aspects of the ctypes API just felt a bit strange. E.g. the Structure class, where fields are set by a _fields_ attribute.

Today type hinting is basically the norm, and now we have dataclasses which I think are the go-to way to define the Python equivalent to a struct. These are readable, and play very well with type-checkers/ linters.

I’ve had a go myself at creating something which can allow what I think is a more modern definition of both Structures, and Protocol-like objects for dll, which allow static type-checking and all that nice stuff.

This allows defining a DLL ‘Protocol’ like:

from ctypes import CDLL
from c_dll_protocol imoprt CInt, CBool, DLLProtocolBase

class MyStruct(CStruct):
    a: CInt
    b: CBool

class MyDLLProtocol(DLLProtocolBase):

    def my_func(self, a: CInt) -> MyStruct: ...

# Here type checkers are fooled into thinking this returns a MyDLLProtocol instance
# but in fact it is just the original DLL object 😈
wrapped_dll = MyDLLProtocol.wrap(CDLL('my_dll.dll'))

result = wrapped_dll.my_func(5)  # hints for paramters and return types are provided.
result.a  # type checkers know a is an int.

# c_dll_protocol takes care of settings these on your behalf.
wrapped_dll.my_func.restype  # returns CMyStuct
wrapped_dll.my_func.argtypes  # returns [c_int]

I’m interested in people’s thoughts.

This low-level programming isn’t something I’m very used to.

2 Likes

Ok, I have found this Issue which is basically discussing a similar thing:

5 Likes