扩展第三方库/模块的存根文件

时间:2019-09-17 13:30:07

标签: python python-3.x mypy

我正在使用yarl库的URL对象。

它具有一个准私有属性._val,它是一个urllib.parse.SplitResult对象,但在yarl/__init__.pyi中没有类型注释。 (这是可以理解的,如果开发人员不想将其正式纳入公共API的话。)

但是,我选择使用URL._val,后果自负。一个虚拟的例子:

# urltest.py
from urllib.parse import SplitResult
from typing import Tuple

from yarl import URL


def foo(u: URL) -> Tuple[str, str, str]:
    sr: SplitResult = u._val
    return sr[:3]

但是mypy不喜欢这样,因为它抱怨:

$ mypy urltest.py
"URL" has no attribute "_val"

那么,我如何在自己的项目中“附加”(或扩展)实例属性注释到URL上,以便可以在项目的其余部分中使用它?即

from yarl import URL

URL._val: SplitResult
# ...

(mypy也不喜欢;“无法在分配给非self属性时声明类型。”)


更新

我尝试在stubs/yarl/__init__.pyi中创建一个新的存根文件:

from urllib.parse import SplitResult

class URL:
    _val: SplitResult

然后按照stub files中所述设置export MYPYPATH='.../stubs'。但是,此会覆盖而不是扩展现有的注释,因此所有 ._val都会抛出错误:

  

错误:“ URL”没有属性“ with_scheme”

     

错误:“ URL”没有属性“主机”

     

错误:“ URL”没有属性“片段”

...等等。

2 个答案:

答案 0 :(得分:1)

一种可能性是简单地忽略此分配的u类型:

def foo(u: URL) -> Tuple[str, str, str, str]:
    sr: SplitResult = typing.cast(typing.Any, u)._val
    return sr[:3]

mypy将假定您知道自己在做什么,并且u具有类型为_val的{​​{1}}属性。

答案 1 :(得分:1)

不幸的是,我认为没有真正的方法可以对某些第三方库的类型提示进行“部分”更改-至少不是mypy。

我会尝试以下三个选项之一:

  1. 只需# type: ignore属性访问权限即可:

    def foo(u: URL) -> Tuple[str, str, str]:
        sr: SplitResult = u._val  # type: ignore
        return sr[:3]
    

    此类型忽略将禁止在该行上生成的任何错误消息。如果您要采用这种方法,我还建议您使用--warn-unused-ignores标志运行mypy,该标志将报告任何冗余和未使用的# type: ignore语句。这个特定的# type: ignore不太可能随着mypy更新/作为第三方库存根的更新而变得多余,但这是一个很好的标记,可以启用它。

  2. 与该库的维护者交谈,看看他们是否愿意为此属性添加类型提示(即使它是私有的),还是愿意通过一些新的API公开此信息。

    如果有帮助,就有 先例,即使在Typeshed(标准库的类型存储库)中,也可以为私有或未记录的属性添加类型提示-请参见"What to include" section in their contribution guidelines。 / p>

  3. 如果库维护者不愿意添加此属性,则始终可以只为该库创建存根,对派生的存根进行更改,然后开始使用它。

我个人将首先尝试解决方案2,然后再尝试解决方案1,但这就是我自己。