添加类型信息而不依赖于 typing 模块

adding type information without dependency on typing module

我一直在将类型信息添加到包的 .py 文件中,以支持 运行ning mypy 包。除其他外,允许为此第三方包生成 typeshed 信息。

因为我的包必须 Python 2.7 兼容,所以我使用注释作为类型信息:

def __init__(self, s):
    # type: (Text) -> None

但是为了 运行 mypy 这需要我输入输入:

from typing import Text, IO, BinaryIO, Union

这会导致两个问题:

  1. 这不适用于 Python 3.5.0 和 3.5.1,因为它有一个模块 typing 但不包括 Text。从 PyPI 安装 typing 并不能解决这个问题。 (还有用户 运行 那个版本 Python 上的包)。

  2. 这使得我的包依赖 typing 进行 2.7/3.3/3.4 安装,需要额外的下载和安装。

  3. 我定义了自己的 Union 类型:

    StreamType = Union[BinaryIO, IO[str], StringIO]
    StreamTextType = Union[Text, StreamType]
    

    此代码必须根据输入是否可用有条件地执行。

对于第一个问题,由于我在Python 3.5.0/1下没有运行 mypy,我可以这样做:

import sys
if sys.version_info < (3, 5, 0) and sys.version_info >= (3, 5, 2):
    from typing import Text, IO, BinaryIO, Union

但这并不能解决第二个问题。

注释掉import,比如注释中的类型信息,

# from typing import Text, IO, BinaryIO, Union

会导致mypy抛出错误Name 'Text' is not defined

第三个问题可以通过使用 try-except 来解决(丑陋,也可能效率低下)或例如通过针对环境变量进行测试(也可用于解决第一个问题)。

在 运行ning mypy 时是否设置了我可以测试的环境变量,以便仅在 运行ning mypy 时执行导入语句? 针对环境变量进行测试还允许我将自己的类型定义放在 "guarded".

或者其他一些解决方案?

mypy 关联的唯一环境变量是 MYPYPATH,它由包的代码读取,而不是由它设置。尽管可能会设置 MYPYPATH(尤其是在生成 typeshed 信息时,以提供 "other" 类型信息),但不能保证它确实如此。

你不能注释掉 import 语句,但是你 可以 把它放在一个永远不会执行的块中:

if False:  # MYPY
    from typing import Text, IO, BinaryIO, Union

这样做的好处是您不必导入 os 来获取环境变量(and/or sys 来获取 version_info)如果您在特定的 Python 文件中没有其他需要。

您的类型定义也应该像这样指定,并且可以在导入或定义所有使用的类型后出现在任何地方:

# import or define StringIO

if False:  # MYPY
    StreamType = Union[BinaryIO, IO[str], StringIO]
    StreamTextType = Union[Text, StreamType]

如果以上内容在 mytypes.py 中,您的包中的任何其他源文件,在任何类型定义中使用 StreamTypeText,应该:

if False:  # MYPY
    from typing import Text, IO, BinaryIO, Union
    from .mytypes StreamType

上面会满足mypy,所以不会抛出Text未定义的错误。它也可以在 3.5.0/1 上运行,并且不需要让你的包依赖于 typing

如果您要在 Python 2.7 环境中 运行 mypy,您可能仍然需要安装 typing,但是您的普通软件包的用户不会受到影响那。

请注意,我在每个块的 if 之后添加了注释 # MYPY。搜索 from typing 的文件很容易,但是 StreamType 的块将不会那么容易找到,以防 mypy 更改其行为并且您的代码需要调整。