From 7e0586f73b15195f18be0429159b844db6dd8147 Mon Sep 17 00:00:00 2001 From: Petter Friberg Date: Sun, 5 Nov 2023 21:23:57 +0100 Subject: [PATCH] Improve types for `django.db.models.enums.*` --- django-stubs/db/models/enums.pyi | 57 +++++++++++++++++++---------- scripts/stubtest/allowlist_todo.txt | 12 ------ 2 files changed, 37 insertions(+), 32 deletions(-) diff --git a/django-stubs/db/models/enums.pyi b/django-stubs/db/models/enums.pyi index 594c5c178..9cb862ec1 100644 --- a/django-stubs/db/models/enums.pyi +++ b/django-stubs/db/models/enums.pyi @@ -1,45 +1,62 @@ import enum import sys -from typing import Any +from typing import Any, TypeVar + +from typing_extensions import Self + +_Self = TypeVar("_Self") if sys.version_info >= (3, 11): - enum_property = enum.property + _enum_property = enum.property else: - enum_property = property + _enum_property = property class ChoicesMeta(enum.EnumMeta): - names: list[str] - choices: list[tuple[Any, str]] - labels: list[str] - values: list[Any] + # There's a contradiction between mypy and PYI019 regarding metaclasses. Where mypy + # disallows 'typing_extensions.Self' on metaclasses, while PYI019 try to enforce + # 'typing_extensions.Self' for '__new__' methods.. We've chosen to ignore the + # linter and trust mypy. + def __new__( + metacls: type[_Self], classname: str, bases: tuple[type, ...], classdict: enum._EnumDict, **kwds: Any + ) -> _Self: ... # noqa: PYI019 def __contains__(self, member: Any) -> bool: ... + @property + def names(self) -> list[str]: ... + @property + def choices(self) -> list[tuple[Any, str]]: ... + @property + def labels(self) -> list[str]: ... + @property + def values(self) -> list[Any]: ... class Choices(enum.Enum, metaclass=ChoicesMeta): @property def label(self) -> str: ... - @enum_property + @_enum_property def value(self) -> Any: ... @property def do_not_call_in_templates(self) -> bool: ... -# fake +# fake, to keep simulate class properties class _IntegerChoicesMeta(ChoicesMeta): - names: list[str] - choices: list[tuple[int, str]] - labels: list[str] - values: list[int] + @property + def choices(self) -> list[tuple[int, str]]: ... + @property + def values(self) -> list[int]: ... class IntegerChoices(int, Choices, metaclass=_IntegerChoicesMeta): - @enum_property + def __new__(cls, value: int) -> Self: ... + @_enum_property def value(self) -> int: ... -# fake +# fake, to keep simulate class properties class _TextChoicesMeta(ChoicesMeta): - names: list[str] - choices: list[tuple[str, str]] - labels: list[str] - values: list[str] + @property + def choices(self) -> list[tuple[str, str]]: ... + @property + def values(self) -> list[str]: ... class TextChoices(str, Choices, metaclass=_TextChoicesMeta): - @enum_property + def __new__(cls, value: str) -> Self: ... + @_enum_property def value(self) -> str: ... diff --git a/scripts/stubtest/allowlist_todo.txt b/scripts/stubtest/allowlist_todo.txt index d1423cda6..de9838856 100644 --- a/scripts/stubtest/allowlist_todo.txt +++ b/scripts/stubtest/allowlist_todo.txt @@ -436,7 +436,6 @@ django.contrib.gis.db.models.ImageField.attr_class django.contrib.gis.db.models.ImageField.contribute_to_class django.contrib.gis.db.models.ImageField.descriptor_class django.contrib.gis.db.models.ImageField.formfield -django.contrib.gis.db.models.IntegerChoices.__new__ django.contrib.gis.db.models.IntegerField.class_lookups django.contrib.gis.db.models.IntegerField.formfield django.contrib.gis.db.models.IntegerField.validators @@ -512,7 +511,6 @@ django.contrib.gis.db.models.Subquery.empty_result_set_value django.contrib.gis.db.models.Subquery.external_aliases django.contrib.gis.db.models.Subquery.get_external_cols django.contrib.gis.db.models.Subquery.subquery -django.contrib.gis.db.models.TextChoices.__new__ django.contrib.gis.db.models.TextField.formfield django.contrib.gis.db.models.TimeField.class_lookups django.contrib.gis.db.models.TimeField.formfield @@ -1149,7 +1147,6 @@ django.db.models.ImageField.attr_class django.db.models.ImageField.contribute_to_class django.db.models.ImageField.descriptor_class django.db.models.ImageField.formfield -django.db.models.IntegerChoices.__new__ django.db.models.IntegerField.class_lookups django.db.models.IntegerField.formfield django.db.models.IntegerField.validators @@ -1224,7 +1221,6 @@ django.db.models.Subquery.empty_result_set_value django.db.models.Subquery.external_aliases django.db.models.Subquery.get_external_cols django.db.models.Subquery.subquery -django.db.models.TextChoices.__new__ django.db.models.TextField.formfield django.db.models.TimeField.class_lookups django.db.models.TimeField.formfield @@ -1268,14 +1264,6 @@ django.db.models.constraints.UniqueConstraint.__init__ django.db.models.constraints.UniqueConstraint.contains_expressions django.db.models.constraints.UniqueConstraint.validate django.db.models.deletion.Collector.__init__ -django.db.models.enums.ChoicesMeta.__new__ -django.db.models.enums.ChoicesMeta.choices -django.db.models.enums.ChoicesMeta.labels -django.db.models.enums.ChoicesMeta.names -django.db.models.enums.ChoicesMeta.values -django.db.models.enums.IntegerChoices.__new__ -django.db.models.enums.TextChoices.__new__ -django.db.models.enums.enum_property django.db.models.expressions.BaseExpression.contains_aggregate django.db.models.expressions.BaseExpression.contains_column_references django.db.models.expressions.BaseExpression.contains_over_clause