Mic*_*ert 21 django-rest-framework mypy python-typing typeddict
我正在 Python 3.8+ Django/Rest-Framework 环境中工作,在新代码中强制执行类型,但建立在许多无类型的遗留代码和数据之上。我们广泛使用 TypedDicts 来确保我们生成的数据以正确的数据类型传递到 TypeScript 前端。
MyPy/PyCharm/等。在检查我们的新代码是否输出符合要求的数据方面做得很好,但我们想测试我们的许多 RestSerializers/ModelSerializers 的输出是否符合 TypeDict。如果我有一个序列化器并输入如下字典:
class PersonSerializer(ModelSerializer):
class Meta:
model = Person
fields = ['first', 'last']
class PersonData(TypedDict):
first: str
last: str
email: str
Run Code Online (Sandbox Code Playgroud)
然后运行如下代码:
person_dict: PersonData = PersonSerializer(Person.objects.first()).data
Run Code Online (Sandbox Code Playgroud)
静态类型检查器无法发现person_dict缺少所需email密钥,因为(根据 PEP-589 的设计)它只是一个普通的dict. 但我可以写这样的东西:
annotations = PersonData.__annotations__
for k in annotations:
assert k in person_dict # or something more complex.
assert isinstance(person_dict[k], annotations[k])
Run Code Online (Sandbox Code Playgroud)
它会发现email序列化器的数据丢失了。在这种情况下,这很好,我没有引入任何更改from __future__ import annotations(不确定这是否会破坏它),并且我的所有类型注释都是裸类型。但如果PersonData定义如下:
class PersonData(TypedDict):
email: Optional[str]
affiliations: Union[List[str], Dict[int, str]]
Run Code Online (Sandbox Code Playgroud)
thenisinstance不足以检查数据是否通过(因为“下标泛型不能与类和实例检查一起使用”)。
我想知道是否已经存在一个可调用函数/方法(在 mypy 或另一个检查器中),它允许我根据注释验证 TypedDict (甚至单个变量,因为我可以自己迭代字典)并查看如果它有效?
我不关心速度等,因为这样做的目的是检查一次所有数据/方法/函数,然后在我们对当前数据验证感到满意后删除检查。
小智 11
我发现最简单的解决方案可以使用 pydantic。
import pydantic
from pydantic import TypeAdapter, ValidationError
from typing_extensions import TypedDict # Required by pydantic for python < 3.12
class SomeDict(TypedDict):
val: int
name: str
SomeDictValidator = TypeAdapter(SomeDict)
# this could be a valid/invalid declaration
obj: SomeDict = {
'val': 12,
'name': 'John',
}
# validate with pydantic
try:
obj = SomeDictValidator.validate_python(obj)
except ValidationError as exc:
print(f"ERROR: Invalid schema: {exc}")
Run Code Online (Sandbox Code Playgroud)
请参阅TypeAdapter文档以获取更多信息。
from typing import cast, TypedDict
import pydantic
class SomeDict(TypedDict):
val: int
name: str
# this could be a valid/invalid declaration
obj: SomeDict = {
'val': 12,
'name': 'John',
}
# validate with pydantic
try:
obj = cast(SomeDict, pydantic.create_model_from_typeddict(SomeDict)(**obj).dict())
except pydantic.ValidationError as exc:
print(f"ERROR: Invalid schema: {exc}")
Run Code Online (Sandbox Code Playgroud)
编辑:当类型检查时,它当前返回一个错误,但按预期工作。请参阅此处: https: //github.com/samuelcolvin/pydantic/issues/3008
小智 1
有点黑客,但您可以使用 mypy 命令行-c选项检查两种类型。只需将其包装在 python 函数中即可:
import subprocess
def is_assignable(type_to, type_from) -> bool:
"""
Returns true if `type_from` can be assigned to `type_to`,
e. g. type_to := type_from
Example:
>>> is_assignable(bool, str)
False
>>> from typing import *
>>> is_assignable(Union[List[str], Dict[int, str]], List[str])
True
"""
code = "\n".join((
f"import typing",
f"type_to: {type_to}",
f"type_from: {type_from}",
f"type_to = type_from",
))
return subprocess.call(("mypy", "-c", code)) == 0
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
9429 次 |
| 最近记录: |