繁体   English   中英

格斗python类型注解

[英]Fighting python type annotations

我有一个非常简单的 class 继承自requests.Session 代码目前看起来像:

import requests
import urllib.parse

from typing import Any, Optional, Union, cast

default_gutendex_baseurl = "https://gutendex.com/"


class Gutendex(requests.Session):
    def __init__(self, baseurl: Optional[str] = None):
        super().__init__()
        self.baseurl = baseurl or default_gutendex_baseurl

    def search(self, keywords: str) -> Any:
        res = self.get("/books", params={"search": keywords})
        res.raise_for_status()
        return res.json()

    def request(
        self, method: str, url: Union[str, bytes], *args, **kwargs
    ) -> requests.Response:
        if self.baseurl and not url.startswith("http"):
            url = urllib.parse.urljoin(self.baseurl, url)

        return super().request(method, url, *args, **kwargs)

我很难让mypyrequest方法感到满意。

第一个挑战是获取要验证的参数; 设置url: Union[str, bytes]是匹配types-requests中的类型注释所必需的。 我刚刚举手使*args**kwargs正确,因为唯一的解决方案似乎是重现单个参数注释,但我很高兴将其保留。

处理完 function 签名后, mypy现在抱怨对startswith的调用:

example.py:23:错误:“bytes”的“startswith”的参数 1 具有不兼容的类型“str”; 预期“联合[字节,元组[字节,...]]”

我可以通过明确的cast来解决这个问题:

        if not cast(str, url).startswith("http"):
            url = urllib.parse.urljoin(self.baseurl, url)

...但这似乎只是引入了复杂性。

然后对urllib.parse.urljoin的调用不满意:

example.py:24:错误:“urljoin”的类型变量“AnyStr”的值不能是“Sequence[object]”
example.py:24:错误:赋值类型不兼容(表达式的类型为“Sequence[object]”,变量的类型为“Union[str, bytes]”)

我不确定如何处理这些错误。

我现在通过将显式强制转换移动到方法的顶部来解决问题:

      def request(
          self, method: str, url: Union[str, bytes], *args, **kwargs
      ) -> requests.Response:
          _url = url.decode() if isinstance(url, bytes) else url

          if not _url.startswith("http"):
              _url = urllib.parse.urljoin(self.baseurl, _url)

          return super().request(method, _url, *args, **kwargs)

但这感觉像是一个 hacky 解决方法。

所以:

  • 我想我的 function 签名和我想得到的一样正确,但是url上的类型注释是正确的还是不正确并导致问题?

  • urljoin周围的错误是怎么回事?


从评论中可以看出:

        if self.baseurl and not url.startswith(
            "http" if isinstance(url, str) else b"http"
        ):

失败:

example.py:25:错误:“str”的“startswith”的参数 1 具有不兼容的类型“Union[str, bytes]”; 预期“联合[str,元组[str,...]]”
example.py:25:错误:“bytes”的“startswith”的参数 1 具有不兼容的类型“Union [str, bytes]”; 预期“联合[字节,元组[字节,...]]”

这解决了整个问题:

import requests
import urllib.parse

from typing import Union, cast

default_gutendex_baseurl = "https://gutendex.com/"


class Gutendex(requests.Session):
    def __init__(self, baseurl: str = None):
        super().__init__()
        self.baseurl = baseurl or default_gutendex_baseurl

    def search(self, keywords: str) -> dict[str, str]:
        res = self.get("/books", params={"search": keywords})
        res.raise_for_status()
        return res.json()

    def request(
        self, method: str, url: Union[str, bytes], *args, **kwargs
    ) -> requests.Response:
        if isinstance(url, str):
            if not url.startswith("http"):
                url = urllib.parse.urljoin(self.baseurl, url)

            return super().request(method, url, *args, **kwargs)
        else:
            raise TypeError('Gutendex does not support bytes type url arguments')

如果你说你接受它,你不能只是不处理bytes 如果bytes被传递,只需引发异常或做一些更好的事情。 或者如果你喜欢危险地生活,甚至只是pass

此代码在mypy中验证得很好。

有点令人失望的是,这样的事情无法验证:

        if not url.startswith("http"):
            url = urllib.parse.urljoin(self.baseurl, url if isinstance(url, str) else url.decode())
        return super().request(method, url, *args, **kwargs)

即使url.startswith无法在str时获取bytes ,反之亦然,它仍然无法验证。 mypy无法通过运行时逻辑进行验证,因此您只能执行以下操作:

    def request(
        self, method: str, url: Union[str, bytes], *args, **kwargs
    ) -> requests.Response:
        if isinstance(url, str):
            if not url.startswith("http"):
                url = urllib.parse.urljoin(self.baseurl, url)

            return super().request(method, url, *args, **kwargs)
        else:
            if not url.startswith(b"http"):
                url = urllib.parse.urljoin(self.baseurl, url.decode())

            return super().request(method, url, *args, **kwargs)

它支持两者,但以丑陋的方式重复逻辑。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM