[英]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.