当前位置: 首页 > 知识库问答 >
问题:

VSCode MyPy错误:ioctl具有不兼容类型my_struct;预期的Union[int, str]

孟嘉歆
2023-03-14

我有下面的Python片段,正在生成MyPy的问题(在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 )

错误是:

参数3到ioctl具有不兼容类型my_struct;期望联合[int, str]

MyStruct是一个ctypes结构。将ioctl()与ctypes结构一起使用的所有示例都显示了将实例传递给ioctl()。事实上,这确实有效,但现在MyPy正在抱怨。

我不希望转换成字节,而是使用struct模块手动打包/解包(我认为这是一种解决方案)。

我正在使用python3.7。3在Linux(Debian Buster)上,使用mypy 0.782

谢谢,布兰登。

注意:我忘了提到我的代码是针对Python2.7的,因为它是Debian Jessie目标系统遗留下来的。我正在使用--py2开关来切换mypy(它必须在Python 3上运行)。

ioctl()函数具有以下签名,似乎来自vscode服务器(远程ssh)ms-python...

def ioctl(fd: _AnyFile,
          request: int,
          arg: Union[int, bytes] = ...,
          mutate_flag: bool = ...) -> Any: ...

下面是一个更完整的代码示例。

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

对我来说,使用@CristiFati建议的ctypes.addressof()函数似乎是最简单的解决方案。

不幸的是,这不起作用。ioctl()函数需要知道对象的大小。

谢谢,布兰登。

共有2个答案

支嘉祥
2023-03-14

首先,该错误消息看起来像Python 2 mypy错误消息,而不是Python 3 mypy错误消息。Python 3的存根有一个fcntl.ioctl的声明,与该错误消息不匹配。使用Python 3 mypy,您仍然会收到错误消息,但这将是不同的消息。)

第二,fcntl。ioctl接受任何支持缓冲区接口的对象(包括您的结构),但mypy甚至不知道缓冲区接口是什么。没有支持缓冲区接口的对象的注释,也没有静态识别支持缓冲区接口的对象的方法。目前无法正确注释函数,如fcntl。ioctl。关于这一点,还有一些悬而未决的问题,但目前还看不到解决方案。

你最好的办法可能是在那一行打一个#type:ignore注释。

钮高朗
2023-03-14

mypy遵循fnctl的规范。ioctl功能如下:

参数arg可以是整数、支持只读缓冲区接口的对象(如bytes)或支持读写缓冲区接口的对象(如byteray)中的一个。

因此,申诉是合法的。

我宁愿不转换为字节和手动打包/解包与结构模块

借助TYPE_CHECKING常量,您可以为fnctl引入一个带有类型提示的本地存根。ioctl将覆盖stdlib的类型提示:

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
 类似资料: