【问题标题】:VSCode MyPy error: "ioctl" has incompatible type "my_struct"; expected "Union[int, str]"VSCode MyPy 错误:“ioctl”具有不兼容的类型“my_struct”;预期“联合[int,str]”
【发布时间】:2020-11-21 01:23:20
【问题描述】:

我有以下 python sn-p 正在生成 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 )

错误是:

“ioctl”的参数 3 具有不兼容的类型“my_struct”;预期“联合[int,str]”

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

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

我在 Linux (Debian Buster) 上使用 Python 3.7.3mypy 0.782

谢谢,布伦丹。


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

ioctl()函数有如下签名,貌似来自vscode服务器(远程ssh)ms-python ....typeshed/stdlib/3/fcntl.pyi`

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() 函数需要知道对象的大小。

谢谢,布伦丹。

【问题讨论】:

  • ctypes.addressof(my_struct)?
  • 确定您使用的是 Python 3.7.3,并且运行的是 Python 3 版本的 mypy?这看起来像一条 Python 2 错误消息。 (Python 3 mypy 仍然会给你一个错误消息,但它会是一个不同的错误消息。)
  • 是的。我在 python2 模式下使用 mypy (--py2)。我已经用更多信息更新了这个问题。我认为 CristiFati 使用ctypes.addressof() 的答案是最简单的解决方案。

标签: python ctypes mypy ioctl


【解决方案1】:

mypy 在此处遵循fnctl.ioctl 函数的规范:

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

因此投诉是合法的。

我不希望转换为字节并使用 struct 模块手动打包/解包

借助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

【讨论】:

  • ctypes 结构 do 支持缓冲区接口,但字符串不支持!在线文档与mypy感知的docstring和签名相矛盾,而论点的actual treatment似乎是混合的。
  • 谢谢。不过似乎工作量很大。我认为 CristiFati 使用 ctypes.addressof() 的答案是最简单的解决方案。
  • 所以ctypes.addressof() 不会工作。有意义,因为ioctl() 需要知道对象的大小(并且需要用于可变返回值的缓冲区空间)。好的,我再次查看了您的解决方案,现在对我来说更有意义了。我假设我可以使用一次,它将适用于结构的所有子类。打算试一试。
  • 我可以确认这是可行的,并且将是我接受的答案。谢谢大家!!
【解决方案2】:

首先,该错误消息看起来像 Python 2 mypy 错误消息,而不是 Python 3 mypy 错误消息。 Python 3 的 stubs 声明 fcntl.ioctl 与该错误消息不匹配。 (使用 Python 3 mypy 时您仍然会收到一条错误消息,但它会是一条不同的消息。)

其次,fcntl.ioctl 接受任何支持缓冲区接口的对象(包括您的结构),但 mypy 甚至不知道缓冲区接口是什么。支持缓冲区接口的对象没有注解,也无法静态识别支持缓冲区接口的对象。目前不可能正确地注释像fcntl.ioctl 这样的函数。有openissues关于这个,但看不到解决方案。

您最好的选择可能是对该行添加 # type: ignore 评论。

【讨论】:

  • 谢谢。是的。我正在将 mypy 与 python2 模式(--py2)一起使用。我已经用更多信息更新了这个问题。我认为 CristiFati 使用 ctypes.addressof() 的答案是最简单的解决方案。
猜你喜欢
  • 2021-09-19
  • 1970-01-01
  • 1970-01-01
  • 2021-12-21
  • 2022-12-17
  • 2021-12-31
  • 2017-10-10
  • 2017-07-03
  • 1970-01-01
相关资源
最近更新 更多