繁体   English   中英

如何使用验证器注释 attrs 字段?

[英]How to annotate attrs field with validator?

我在注释 attrs class 属性时遇到问题。

我正在使用 NewType 来定义新的 UserId 类型和属性冻结类。

这是 mypy 没有抱怨的代码,一切正常:

from typing import NewType
from attr import frozen, field


UserId = NewType("UserId", str)


@frozen
class Order:
    id: UserId = field()

mypy 检查此代码时没有任何问题。 使用来自 attrs 的验证器后出现问题。

from typing import NewType
from attr import frozen, field, validators


UserId = NewType("UserId", str)


@frozen
class Order:
    id: UserId = field(validator=validators.matches_re("^\d+$"))

mypy 现在抱怨不正确的类型:

project/test_annotation.py:10: 错误:赋值中的类型不兼容(表达式的类型为“str”,变量的类型为“UserId”)[赋值]

在 1 个文件中发现 1 个错误(已检查 1 个源文件)

我现在不明白 field() 是如何返回字符串类型的。

有人可以解释一下吗? 另外,我们如何解决这个问题?

环境:

Python 3.10.6

属性==22.1.0

cattrs==22.2.0

为了让它开心, cast . field相当复杂,由于此重载,您的field返回一个str

... # other overloads
# This form catches an explicit None or no default and infers the type from the
# other arguments.
@overload
def field(
    *,
    default: None = ...,
    validator: Optional[_ValidatorArgType[_T]] = ...,
    repr: _ReprArgType = ...,
    hash: Optional[bool] = ...,
    init: bool = ...,
    metadata: Optional[Mapping[Any, Any]] = ...,
    converter: Optional[_ConverterType] = ...,
    factory: Optional[Callable[[], _T]] = ...,
    kw_only: bool = ...,
    eq: Optional[_EqOrderType] = ...,
    order: Optional[_EqOrderType] = ...,
    on_setattr: Optional[_OnSetAttrArgType] = ...,
    alias: Optional[str] = ...,
) -> _T: ...

它基本上是说“当validators是在某种类型T上工作的 [sequence of or one of] 验证器时,则field返回T ”。

因此,您传递了一个适用于str的验证器,因此field类型也是str NewType("UserID", str)不是str的子类型,因此此分配失败。 您有两个主要选择:

  • 转换为所需类型:

     from typing import cast... @frozen class Order: id: UserId = cast(str, field(validator=validators.matches_re("^\d+$")))
  • 创建你自己的验证器。 您不需要复制逻辑,只需更改签名并使用type: ignore或强制转换。

     from typing import TYPE_CHECKING, Callable, Match, Optional, Pattern, Union, cast if TYPE_CHECKING: from attr import _ValidatorType def userid_matches_re( regex: Union[Pattern[str], str], flags: int =..., func: Optional[ Callable[[str, str, int], Optional[Match[str]]] ] =..., ) -> '_ValidatorType[UserID]': return cast('_ValidatorType[UserID]', validators.matches_re(regex, flags, func))

    ...并在您的 class 中使用它而不是validators.matches_re 上面的签名是从存根中窃取的, AnyStr替换为str ,因为无论如何您都不允许bytes

我推荐第一个变体,因为另一个解决方案只是更多的样板,结果是相同的cast表,它不会给你更多的安全性。 但是,如果您有很多字段使用此验证器,它可能是可行的。

暂无
暂无

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

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