I have the following python snippet that is generating MyPy "problems" (in vscode).
my_struct = MyStruct()
#! set mutable flag to true to place data in our object.
fcntl.ioctl( dev_hand.fileno(), my_ioctl_id, my_struct, True )
The error is:
Argument 3 to "ioctl" has incompatible type "my_struct"; expected "Union[int, str]"
MyStruct is a ctypes structure. All the examples for using ioctl()
with ctypes structures show passing the instance to ioctl()
. Indeed this does work, except now MyPy is complaining.
I’d prefer not to convert to bytes and manually pack/unpack with the struct
module (which I presume is one solution).
I’m using Python 3.7.3
on Linux (Debian Buster), with mypy 0.782
Thanks, Brendan.
NOTE: I forgot to mention that my code is targeting Python 2.7, as it is legacy from a Debian Jessie target system. I am using the --py2
switch for mypy
(which must run on Python 3).
The ioctl()
function has the following signature, which seems to come from the vscode server (remote ssh) ms-python …. typeshed/stdlib/3/fcntl.pyi`
def ioctl(fd: _AnyFile,
request: int,
arg: Union[int, bytes] = ...,
mutate_flag: bool = ...) -> Any: ...
Here is a more complete code example.
from typing import ( BinaryIO, )
import ioctl
import fcntl
from ctypes import ( c_uint32, Structure, addressof )
class Point ( Structure ) :
_fields_ = [ ( 'x', c_uint32 ), ( 'y', c_uint32 ) ]
def ioctl_get_point (
dev_hand,
) :
point = Point()
fcntl.ioctl( dev_hand, 0x12345678, point, True ) #! ** MyPy does NOT complain at all **
def ioctl_get_point_2 (
dev_hand, # type: BinaryIO
) :
point = Point()
fcntl.ioctl( dev_hand, 0x12345678, point, True ) #! ** MyPy complains about arg 3 **
return point
def ioctl_get_point_3 (
dev_hand,
) : # type: (...) -> Point
point = Point()
fcntl.ioctl( dev_hand, 0x12345678, point, True ) #! ** MyPy complains about arg 3 **
return point
def ioctl_get_point_4 (
dev_hand, # type: BinaryIO
) : # type: (...) -> Point
point = Point()
fcntl.ioctl( dev_hand, 0x12345678, point, True ) #! ** MyPy complains about arg 3 **
return point
def ioctl_get_point_5 (
dev_hand, # type: BinaryIO
) : # type: (...) -> Point
point = Point()
fcntl.ioctl( dev_hand, 0x12345678, addressof( point ), True ) #! ** MyPy does NOT complain at all **
return point
To me, it seems like using the ctypes.addressof()
function, that @CristiFati suggested, is the simplest solution.
Unfortunately that doesn’t work. The ioctl()
function needs to know the size of the object.
Thanks, Brendan.
2
Answers
mypy
follows the specs of thefnctl.ioctl
function here:The complaint is thus a legitimate one.
With the help of the
TYPE_CHECKING
constant, you can introduce a local stub with a type hint forfnctl.ioctl
that will override stdlib’s type hint:First, that error message looks like a Python 2 mypy error message, not a Python 3 mypy error message. The stubs for Python 3 have a declaration of
fcntl.ioctl
that doesn’t match that error message. (You’d still get an error message with Python 3 mypy, but it’d be a different message.)Second,
fcntl.ioctl
accepts any object that supports the buffer interface (including your struct), but mypy has no idea what the buffer interface even is. There is no annotation for an object that supports the buffer interface, and no way to statically recognize objects that support the buffer interface. It is currently impossible to correctly annotate functions likefcntl.ioctl
. There are open issues about this, but no resolution in sight.Your best bet may be to slap a
# type: ignore
comment on that line.