如何解决pydantic constr 和 mypy 检查之间的冲突
我正在使用 pydantic 来验证 Json/Dict 输入。但我也在使用 mypy 来验证代码的类型完整性。
当使用 pydantic.constr
类型时,它会验证给定的字符串是否符合正则表达式,我收到一个 mypy 错误。
代码如下:
from typing import List
import pydantic
Regex = pydantic.constr(regex="[0-9a-z_]*")
class Data(pydantic.BaseModel):
regex: List[Regex]
data = Data(**{"regex":["abc","123","etc"]})
print(data,data.json())
这是 mypy 的输出:
$ mypy main.py
main.py:9: error: Variable "main.Regex" is not valid as a type
main.py:9: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
我查看了文档,但找不到处理此问题的方法。我知道我可以为该正则表达式创建一个静态类型,但是这种方式违背了 pydantic 的目的。我可以通过的唯一方法是使用远非理想的 # type: ignore
。
那么有没有一种方法可以同时拥有 pydantic 和 mypy 的好处?
解决方法
有几种方法可以实现这一点:
继承自 pydantic.ConstrainedStr
您可以直接从 constr
继承:
pydantic.ConstrainedStr
Mypy 愉快地接受了这一点,pydantic 进行了正确的验证。 pydantic.ConstrainedStr
的类型是 import re
import pydantic
from pydantic import Field
from typing import List
class Regex(pydantic.ConstrainedStr):
regex = re.compile("^[0-9a-z_]*$")
class Data(pydantic.BaseModel):
regex: List[Regex]
data = Data(**{"regex": ["abc","123","asdf"]})
print(data)
# regex=['abc','123','asdf']
print(data.json())
# {"regex": ["abc","asdf"]}
,但由于 data.regex[i]
本身继承自 Regex
,所以它在大多数地方都可以用作字符串。
使用 pydantic.ConstrainedStr
正则表达式约束也可以指定为 Field
的参数:
str
因为 pydantic.Field
不是直接用作 pydantic 模型中的字段(而是在您的示例中作为列表中的条目),所以我们需要强制引入模型。 import pydantic
from pydantic import Field
from typing import List
class Regex(pydantic.BaseModel):
__root__: str = Field(regex="^[0-9a-z_]*$")
class Data(pydantic.BaseModel):
regex: List[Regex]
data = Data(**{"regex": ["abc","asdf"]})
print(data)
# regex=[Regex(__root__='abc'),Regex(__root__='123'),Regex(__root__='asdf')]
print(data.json())
# {"regex": ["abc","asdf"]}
使 Regex
模型在验证和序列化时充当其单个字段(更多详细信息 here)。
但它有一个缺点:__root__
的类型又是 Regex
,但这次不是继承自 data.regex[i]
。这导致例如Regex
不进行类型检查。必须改用 str
。
我仍然在这里提到这一点,因为当约束直接应用于字段而不是列表条目时,它可能是最简单的解决方案(并且 foo: str = data.regex[0]
不可用,见下文)。例如像这样:
foo: str = data.regex[0].__root__
将 typing.Annotated
与 class DataNotList(pydantic.BaseModel):
regex: str = Field(regex="^[0-9a-z_]*$")
一起使用
您可以将其指定为 Field
的参数,然后与 typing.Annotated
结合使用,而不是使用 pydantic.Field
来指定正则表达式约束:
constr
Mypy 将 typing.Annotated
视为 import pydantic
from pydantic import Field
from typing import Annotated
Regex = Annotated[str,Field(regex="^[0-9a-z_]*$")]
class DataNotList(pydantic.BaseModel):
regex: Regex
data = DataNotList(**{"regex": "abc"})
print(data)
# regex='abc'
print(data.json())
# {"regex": "abc"}
的类型别名。但它也告诉 pydantic 进行验证。
这在 pydantic 文档 here 中有描述。
不幸的是,它目前不适用于以下内容:
Annotated[str,Field(regex="^[0-9a-z_]*$")]
验证根本没有运行。这是一个开放的错误 (github issue)。修复错误后,这可能是总体上最好的解决方案。
请注意,str
仅从 Python 3.9 开始可用。对于较旧的 Python 版本,可以使用 typing_extensions.Annotated
。
附带说明:我在正则表达式中使用了 class Data(pydantic.BaseModel):
regex: List[Regex]
而不是 typing.Annotated
,因为后者将接受 any 字符串作为有效字符串,如 {{3} }.
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。