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.
mypy
follows the specs of the fnctl.ioctl
function here:
The parameter arg can be one of an integer, an object supporting the read-only buffer interface (like
bytes
) or an object supporting the read-write buffer interface (likebytearray
).
The complaint is thus a legitimate one.
I'd prefer not to convert to bytes and manually pack/unpack with the struct module
With the help of the TYPE_CHECKING
constant, you can introduce a local stub with a type hint for fnctl.ioctl
that will override stdlib's type hint:
import ctypes
from typing import TYPE_CHECKING
class MyStruct(ctypes.Structure):
_fields_ = [...]
if TYPE_CHECKING: # this is only processed by mypy
from typing import Protocol, Union, TypeVar
class HasFileno(Protocol):
def fileno(self) -> int: ...
FileDescriptorLike = Union[int, HasFileno]
_S = TypeVar('_S', bound=ctypes.Structure)
def ioctl(__fd: FileDescriptorLike, __request: int, __arg: Union[int, bytes, _S] = ..., __mutate_flag: bool = ...) -> int: ...
else: # this will be executed at runtime and ignored by mypy
from fcntl import ioctl
my_struct = MyStruct(...)
my_ioctl_id = ...
dev_hand = ...
ioctl(dev_hand.fileno(), my_ioctl_id, my_struct, True) # mypy won't complain here anymore