如何符合 mypy 类型 'SupportsWrite[str]'?

How does one comply with the mypy type 'SupportsWrite[str]'?

我有一个 'smart' 打开各种文件的函数和 returns 一个 IO-ish 对象类型:

def sopen(anything_at_all: str, mode: str) -> FileIO:
   ...

我在打印语句中使用它,例如:

with sopen('footxt.gz', mode = 'w+') as fout:
    print("hello, world!", file=fout)

然后,当用mypy 0.812分析这段代码时,我得到以下神秘错误:

Argument "fout" to "print" has incompatible type "FileIO"; expected "Optional[SupportsWrite[str]]"

很好:SupportsWrite 肯定比 FileIO 好,只有一个问题:当我调整我的代码以支持使用 [=20= 写入时,没有比 FileIO 更好的了...

def sopen(anything_at_all: str, mode: str) \
  -> Union[SupportsWrite[str],SupportsRead[str]]:
  ...

mypy 想要 正好 Optional[SupportsWrite]:

 Argument "fout" to "print" has incompatible type "Union[SupportsWrite[str], SupportsRead[str]]"; expected "Optional[SupportsWrite[str]]"

接下来我尝试强制转换,并创建某种强制类型转换,但在解释器中尝试我的转换程序以查看出现什么错误时:

>>> from _typeshed import SupportsRead, SupportsWrite
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named '_typeshed'

现在的根本问题是:在这种情况下,如何遵守mypy的意愿?

TL;DR 使用 typing.IO 而不是 FileIOtyping.IO 支持内置 open 可能 return.

的所有 return 类型

print 本身将其 file 参数注释为 Optional[SupportsWrite[str]],因此 mypy 是正确的。

要修复缺少的 _typeshed 模块错误(也是正确的,它仅在类型检查时可用,而不是在解释器执行代码时可用)您可以使用 if TYPE_CHECKING 1 技巧,然后使用字符串注释。

下面几乎满足mypy:

from typing import Optional, TYPE_CHECKING

if TYPE_CHECKING:
    from _typeshed import SupportsWrite

def sopen(anything_at_all: str, mode: str) -> 'Optional[SupportsWrite[str]]':
   ...

with sopen('footxt.gz', mode = 'w+') as fout:
    print("hello, world!", file=fout)

将此提供给 mypy 结果

test.py:9: error: Item "SupportsWrite[str]" of "Optional[SupportsWrite[str]]" has no attribute "__enter__"
test.py:9: error: Item "None" of "Optional[SupportsWrite[str]]" has no attribute "__enter__"
test.py:9: error: Item "SupportsWrite[str]" of "Optional[SupportsWrite[str]]" has no attribute "__exit__"
test.py:9: error: Item "None" of "Optional[SupportsWrite[str]]" has no attribute "__exit__"

输入typing.IO.

您可以简单地使用 typing.IO(它也恰好匹配 open 的 return 类型)而不是直接弄乱 SupportsWrite。以下完全满足mypy

from typing import IO

def sopen(anything_at_all: str, mode: str) -> IO:
   ...

with sopen('footxt.gz', mode = 'w+') as fout:
    print("hello, world!", file=fout)

1 TYPE_CHECKING 是一个常量,默认为 False,并且仅由 [=20= 设置为 True ] and/or 其他类型的分析工具。