Django 的 Mypy stubs
项目描述
sentry-forked-django-stubs
新版本发布
为上游标签的分支创建一个新的分支
git remote add upstream git@github.com:typeddjango/django-stubs
git fetch upstream --tags
git push origin --tags
git checkout 1.2.3 -b sentry-1.2.3
- 从
master
分支 cherry-pick craft / release 提交到您的分支中 - 从先前版本 cherry-pick 相关提交
发布通过 release.yml 工作流程中的 craft 完成 -- 确保使用带有 .#
发布后缀的特定分支(如 1.2.3.0
)进行目标定位
此软件包包含 类型 stubs 和自定义 mypy 插件,以提供更精确的静态类型和类型推断。Django 使用一些 Python "魔法",这使得某些代码模式的精确类型变得有困难。这就是为什么我们需要这个项目。最终目标是能够为大多数常见模式获取精确的类型。
安装
pip install 'django-stubs[compatible-mypy]'
为了使 mypy 能够识别插件,您需要添加
[mypy]
plugins =
mypy_django_plugin.main
[mypy.plugins.django-stubs]
django_settings_module = "myproject.settings"
到您的 mypy.ini
或 setup.cfg
文件。
也支持 pyproject.toml 配置
[tool.mypy]
plugins = ["mypy_django_plugin.main"]
[tool.django-stubs]
django_settings_module = "myproject.settings"
这里发生了两件事
- 我们需要明确列出插件以由
mypy
加载 - 您可以选择如上所示指定
django_settings_module
,或者让django_stubs
使用您的环境中定义的DJANGO_SETTINGS_MODULE
变量。
这个完全工作的类型化样板代码可以作为示例使用。
版本兼容性
我们依赖于不同的django
和mypy
版本
django-stubs | Mypy版本 | Django版本 | Django部分支持 | Python版本 |
---|---|---|---|---|
5.1.0 | 1.11.x | 5.1 | 4.2 | 3.8 - 3.12 |
5.0.4 | 1.11.x | 5.0 | 4.2 | 3.8 - 3.12 |
5.0.3 | 1.11.x | 5.0 | 4.2 | 3.8 - 3.12 |
5.0.2 | 1.10.x | 5.0 | 4.2 | 3.8 - 3.12 |
5.0.1 | 1.10.x | 5.0 | 4.2 | 3.8 - 3.12 |
5.0.0 | 1.10.x | 5.0 | 4.2, 4.1 | 3.8 - 3.12 |
4.2.7 | 1.7.x | 4.2 | 4.1, 3.2 | 3.8 - 3.12 |
4.2.6 | 1.6.x | 4.2 | 4.1, 3.2 | 3.8 - 3.12 |
4.2.5 | 1.6.x | 4.2 | 4.1, 3.2 | 3.8 - 3.12 |
4.2.4 | 1.5.x | 4.2 | 4.1, 3.2 | 3.8 - 3.11 |
4.2.3 | 1.4.x | 4.2 | 4.1, 3.2 | 3.8 - 3.11 |
4.2.2 | 1.4.x | 4.2 | 4.1, 3.2 | 3.8 - 3.11 |
4.2.1 | 1.3.x | 4.2 | 4.1, 3.2 | 3.8 - 3.11 |
4.2.0 | 1.2.x | 4.2 | 4.1, 4.0, 3.2 | 3.7 - 3.11 |
1.16.0 | 1.1.x | 4.1 | 4.0, 3.2 | 3.7 - 3.11 |
1.15.0 | 1.0.x | 4.1 | 4.0, 3.2 | 3.7 - 3.11 |
1.14.0 | 0.990+ | 4.1 | 4.0, 3.2 | 3.7 - 3.11 |
功能
对Model Meta属性的类型检查
通过从TypedModelMeta
类继承,您可以确保使用正确的类型来设置属性
from django.db import models
from django_stubs_ext.db.models import TypedModelMeta
class MyModel(models.Model):
example = models.CharField(max_length=100)
class Meta(TypedModelMeta):
ordering = ["example"]
constraints = [
models.UniqueConstraint(fields=["example"], name="unique_example"),
]
其他类型化基类
django_stubs_ext.db.router.TypedDatabaseRouter
可以作为实现自定义数据库路由时的基类。
设置
django-stubs有几个设置,您可以在
pyproject.toml
的[tool.django-stubs]
表下列出mypy.ini
的[mypy.plugins.django-stubs]
表下
支持的设置有
-
django_settings_module
,一个字符串,默认为os.getenv(DJANGO_SETTINGS_MODULE)
。指定您的设置模块的导入路径,与Django的
DJANGO_SETTINGS_MODULE
环境变量相同。 -
strict_settings
,一个布尔值,默认为true
。如果使用动态设置,则设置为
false
,如下面所述。
常见问题解答
这是否是官方的Django项目?
不是,目前我们是独立的。有一个提议将我们的项目合并到Django本身中。您可以通过点赞PR来表示您的支持。
在生产中使用它安全吗?
是的,这是安全的!此项目根本不会影响您的运行时。它只影响mypy
类型检查过程。
但是,如果没有mypy
,使用此项目是没有意义的。
安装此插件时,mypy崩溃
当前实现使用Django的运行时来提取有关模型的信息,因此如果您的已安装的应用或models.py
损坏,它可能会崩溃。
换句话说,如果您的manage.py runserver
崩溃,mypy也会崩溃。您也可以使用--tb
选项运行mypy
以获取有关错误的额外信息。
我无法使用带有类型注解的QuerySet或Manager
当您尝试使用QuerySet[MyModel]
、Manager[MyModel]
或其他基于Django的通用类型时,您会收到TypeError: 'type' object is not subscriptable
错误。
这是因为这些Django类在运行时不支持__class_getitem__
魔法方法。
-
您可以使用我们的
django_stubs_ext
助手,该助手修复了我们作为通用在Django中使用的所有类型。安装它
pip install django-stubs-ext # as a production dependency
然后将其放置在您的顶级设置中
import django_stubs_ext django_stubs_ext.monkeypatch()
您可以使用
django_stubs_ext.monkeypatch(extra_classes=[YourDesiredType])
添加要修复的额外类型。 -
您可以使用字符串:`'QuerySet[MyModel]'` 和 `'Manager[MyModel]'`,这样它就会作为mypy的类型,同时在运行时作为常规的
str
。
我如何创建一个保证有已认证用户的HttpRequest?
Django内置的HttpRequest
具有一个属性user
,它解析为类型
Union[User, AnonymousUser]
其中User
是AUTH_USER_MODEL
设置中指定的用户模型。
如果您想要一个您可以为其添加类型注解且知道用户已认证的HttpRequest
,您可以像这样从正常的HttpRequest
类中派生子类
from django.http import HttpRequest
from my_user_app.models import MyUser
class AuthenticatedHttpRequest(HttpRequest):
user: MyUser
然后在知道用户已经认证的情况下,使用 AuthenticatedHttpRequest
来代替标准的 HttpRequest
。例如在视图中使用 @login_required
装饰器。
为什么我的自定义管理器会出现不兼容的返回类型错误?
如果你没有使用泛型声明自定义管理器,并覆盖了内置方法,你可能会看到关于不兼容错误消息的错误,例如
from django.db import models
class MyManager(model.Manager):
def create(self, **kwargs) -> "MyModel":
pass
这会导致这个错误消息
错误:函数 "create" 的返回类型 "MyModel" 与超类型 "BaseManager" 中的返回类型 "_T" 不兼容
这是因为 Manager
类是泛型的,但没有指定泛型,内置的管理器方法预期返回基管理器的泛型类型,即任何模型。为了解决这个问题,你应该使用你的模型作为类型变量声明你的管理器
class MyManager(models.Manager["MyModel"]):
...
如何对调用 QuerySet.annotate 的情况进行注解?
Django-stubs 提供了一个特殊的类型,django_stubs_ext.WithAnnotations[Model, <Annotations>]
,表示 Model
已被注解,这意味着模型实例需要额外的属性。
你应该提供一个 TypedDict
的这些属性,例如 WithAnnotations[MyModel, MyTypedDict]
,来指定哪些注解属性存在。
目前,mypy 插件可以识别传递给 QuerySet.annotate
的特定名称,并将它们包含在类型中,但不会记录这些属性的类型。
对于 QuerySet
的 values
、values_list
或 filter
方法,尚未使用特定注解字段的了解来创建更具体的类型,然而模型被注解的 知识 被用来创建更广泛的类型结果类型为 values
/values_list
,并允许对任何字段进行 filter
。
from typing import TypedDict
from django_stubs_ext import WithAnnotations
from django.db import models
from django.db.models.expressions import Value
class MyModel(models.Model):
username = models.CharField(max_length=100)
class MyTypedDict(TypedDict):
foo: str
def func(m: WithAnnotations[MyModel, MyTypedDict]) -> str:
print(m.bar) # Error, since field "bar" is not in MyModel or MyTypedDict.
return m.foo # OK, since we said field "foo" was allowed
func(MyModel.objects.annotate(foo=Value("")).get(id=1)) # OK
func(MyModel.objects.annotate(bar=Value("")).get(id=1)) # Error
为什么我得到提到 _StrPromise
的不兼容参数类型?
Django 的懒翻译函数(如 gettext_lazy
)返回一个 Promise
而不是 str
。这两个类型 不能互换使用。因此,这些函数的返回类型已被 更改 以反映这一点。
如果你在自己的代码中遇到这个错误,你可以将 Promise
强制转换为 str
(导致翻译被评估),或者使用 django-stubs-ext
中的 StrPromise
或 StrOrPromise
类型在类型提示中。选择哪种解决方案取决于具体情况。有关更多信息,请参阅 Django 文档中的 处理懒翻译对象。
如果这是在 Django 代码中报告的,请报告问题或打开拉取请求以修复类型提示。
如何使用自定义库来处理 Django 设置?
使用类似于 django-split-settings
或 django-configurations
的工具将使 mypy 难以推断你的设置。
使用类似的情况也可能出现
try:
from .local_settings import *
except Exception:
pass
所以,mypy 可能不喜欢这段代码
from django.conf import settings
settings.CUSTOM_VALUE # E: 'Settings' object has no attribute 'CUSTOM_VALUE'
为了处理这个特殊情况,我们有一个特殊的设置 strict_settings
(默认为 True
),你可以将其切换为 False
以始终返回 Any
并在运行时设置模块具有给定值时不会引发任何错误,例如 pyproject.toml
[tool.django-stubs]
strict_settings = false
或 mypy.ini
[mypy.plugins.django-stubs]
strict_settings = false
然后
# Works:
reveal_type(settings.EXISTS_AT_RUNTIME) # N: Any
# Errors:
reveal_type(settings.MISSING) # E: 'Settings' object has no attribute 'MISSING'
相关项目
awesome-python-typing
- 所有与 Python 类型相关的东西的精彩列表。djangorestframework-stubs
- Django REST Framework 的存根。pytest-mypy-plugins
- 我们用于测试mypy
模拟和插件的pytest
插件。wemake-django-template
- 在几秒钟内创建新的类型化 Django 项目。
获取帮助
我们在这里有 Gitter: https://gitter.im/mypy-django/Lobby 如果您认为您有更通用的类型问题,请参阅 https://github.com/python/mypy 及其 Gitter。
贡献
此项目是开源的,由社区驱动。因此,我们鼓励大家做出大小不同的贡献。您可以通过以下任何一种方式做出贡献
- 贡献代码(例如,改进模拟,添加插件功能,编写测试等) - 要这样做,请遵循 贡献指南。
- 协助代码审查和问题讨论。
- 识别错误和问题并报告这些问题。
- 在 StackOverflow 上提问和回答问题
您也可以在 gitter 上联系以讨论您的贡献!
项目详情
散列 for sentry_forked_django_stubs-5.1.0.post2.tar.gz
算法 | 散列摘要 | |
---|---|---|
SHA256 | e1fd57b77715f6cd3d35643c38239c6c864481edfe23142fbcceb458de2454c9 |
|
MD5 | 9a4e179110d0f88e1026b7bb59551229 |
|
BLAKE2b-256 | aa77217c40d907fa236e9573031da65503b813434cdac8592421eec36e78af06 |
散列 for sentry_forked_django_stubs-5.1.0.post2-py3-none-any.whl
算法 | 散列摘要 | |
---|---|---|
SHA256 | b654e0d037f1299a747c1b72d357f13498454acdcdbb258828d5dcb68ccdb856 |
|
MD5 | 34e6f127c8256e6666931c5a749cc850 |
|
BLAKE2b-256 | 5480ee0e804554699f3025a1fd1529d763db7088d21646837407718feb803277 |