diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8a8f92b549..1f96a534bc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -28,7 +28,7 @@ jobs: - name: Build ext package run: python -m build ext/ - name: Publish ext to PyPI - uses: pypa/gh-action-pypi-publish@v1.12.3 + uses: pypa/gh-action-pypi-publish@v1.12.4 with: packages-dir: ext/dist/ @@ -53,4 +53,4 @@ jobs: - name: Build package run: python -m build - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@v1.12.3 + uses: pypa/gh-action-pypi-publish@v1.12.4 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4ee6ac4f37..2be5511701 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,13 +17,13 @@ repos: args: [--fix=lf] - id: check-case-conflict - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.9.1 + rev: v0.9.4 hooks: - id: ruff args: ["--fix", "--exit-non-zero-on-fix"] - id: ruff-format - repo: https://github.com/codespell-project/codespell - rev: v2.3.0 + rev: v2.4.1 hooks: - id: codespell additional_dependencies: diff --git a/README.md b/README.md index b244592883..3fe024e4b7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ django-stubs -[![Build status](https://github.com/typeddjango/django-stubs/workflows/test/badge.svg?branch=master&event=push)](https://github.com/typeddjango/django-stubs/actions?query=workflow%3Atest) +[![test](https://github.com/typeddjango/django-stubs/actions/workflows/test.yml/badge.svg?branch=master&event=push)](https://github.com/typeddjango/django-stubs/actions/workflows/test.yml) [![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/) [![Gitter](https://badges.gitter.im/mypy-django/Lobby.svg)](https://gitter.im/mypy-django/Lobby) [![StackOverflow](https://shields.io/badge/ask-stackoverflow-orange?logo=stackoverflow)](https://stackoverflow.com/questions/tagged/django-stubs?tab=Active) @@ -49,7 +49,8 @@ We rely on different `django` and `mypy` versions: | django-stubs | Mypy version | Django version | Django partial support | Python version | |----------------|--------------|----------------|------------------------|----------------| -| 5.1.2 | 1.13+ | 5.1 | 4.2 | 3.8 - 3.13 | +| 5.1.3 | 1.13+ | 5.1 | 4.2 | 3.9 - 3.13 | +| 5.1.2 | 1.13+ | 5.1 | 4.2 | 3.9 - 3.13 | | 5.1.1 | 1.13.x | 5.1 | 4.2 | 3.8 - 3.12 | | 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 | diff --git a/django-stubs/contrib/admin/options.pyi b/django-stubs/contrib/admin/options.pyi index 02e172dc0c..1728034d0b 100644 --- a/django-stubs/contrib/admin/options.pyi +++ b/django-stubs/contrib/admin/options.pyi @@ -1,6 +1,6 @@ import enum from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence -from typing import Any, Generic, Literal, TypeVar, cast, type_check_only +from typing import Any, Generic, Literal, TypeVar, cast, overload, type_check_only from django import forms from django.contrib.admin.filters import FieldListFilter, ListFilter @@ -129,8 +129,11 @@ class BaseModelAdmin(Generic[_ModelT]): def get_prepopulated_fields(self, request: HttpRequest, obj: _ModelT | None = ...) -> dict[str, Sequence[str]]: ... def get_queryset(self, request: HttpRequest) -> QuerySet[_ModelT]: ... def get_sortable_by(self, request: HttpRequest) -> _DisplayT[_ModelT]: ... - @deprecated("The None value for the request parameter will be removed in Django 6.0.") - def lookup_allowed(self, lookup: str, value: str, request: HttpRequest | None = ...) -> bool: ... + @overload + @deprecated("None value for the request parameter will be removed in Django 6.0.") + def lookup_allowed(self, lookup: str, value: str, request: None = None) -> bool: ... + @overload + def lookup_allowed(self, lookup: str, value: str, request: HttpRequest) -> bool: ... def to_field_allowed(self, request: HttpRequest, to_field: str) -> bool: ... def has_add_permission(self, request: HttpRequest) -> bool: ... def has_change_permission(self, request: HttpRequest, obj: _ModelT | None = ...) -> bool: ... diff --git a/django-stubs/contrib/contenttypes/models.pyi b/django-stubs/contrib/contenttypes/models.pyi index e7206aed0a..1c15422e15 100644 --- a/django-stubs/contrib/contenttypes/models.pyi +++ b/django-stubs/contrib/contenttypes/models.pyi @@ -13,8 +13,8 @@ class ContentTypeManager(models.Manager[ContentType]): class ContentType(models.Model): id: int - app_label: models.CharField - model: models.CharField + app_label = models.CharField(max_length=100) + model = models.CharField(max_length=100) objects: ClassVar[ContentTypeManager] @property def name(self) -> str: ... diff --git a/django-stubs/contrib/gis/geoip2/base.pyi b/django-stubs/contrib/gis/geoip2/base.pyi index 3f75eb18cd..30b83fa950 100644 --- a/django-stubs/contrib/gis/geoip2/base.pyi +++ b/django-stubs/contrib/gis/geoip2/base.pyi @@ -29,7 +29,7 @@ class GeoIP2: def is_city(self) -> bool: ... @cached_property def is_country(self) -> bool: ... - @deprecated("The coords() is deprecated and will be removed in Django 6.0. Use lon_lat() instead.") + @deprecated("coords() is deprecated and will be removed in Django 6.0. Use lon_lat() instead.") def coords(self, query: str, ordering: Sequence[str] = ...) -> tuple[float, float] | tuple[None, None]: ... def lon_lat(self, query: str) -> tuple[float, float] | tuple[None, None]: ... def lat_lon(self, query: str) -> tuple[float, float] | tuple[None, None]: ... @@ -37,5 +37,5 @@ class GeoIP2: @property def info(self) -> str: ... @classmethod - @deprecated("The open() is deprecated and will be removed in Django 6.0. Use GeoIP2() instead.") + @deprecated("open() is deprecated and will be removed in Django 6.0. Use GeoIP2() instead.") def open(cls, full_path: Path | str | None, cache: int) -> Any: ... diff --git a/django-stubs/core/serializers/xml_serializer.pyi b/django-stubs/core/serializers/xml_serializer.pyi index c1b6e048fa..2762530fec 100644 --- a/django-stubs/core/serializers/xml_serializer.pyi +++ b/django-stubs/core/serializers/xml_serializer.pyi @@ -1,5 +1,5 @@ from typing import IO, Any -from xml.sax.expatreader import ExpatParser as _ExpatParser # type: ignore[import-not-found] +from xml.sax.expatreader import ExpatParser from django.core.serializers import base @@ -31,7 +31,7 @@ class Deserializer(base.Deserializer): def getInnerText(node: Any) -> str: ... -class DefusedExpatParser(_ExpatParser): +class DefusedExpatParser(ExpatParser): def __init__(self, *args: Any, **kwargs: Any) -> None: ... def start_doctype_decl(self, name: Any, sysid: Any, pubid: Any, has_internal_subset: Any) -> None: ... def entity_decl( diff --git a/django-stubs/core/validators.pyi b/django-stubs/core/validators.pyi index cbe29a1b08..38933e038f 100644 --- a/django-stubs/core/validators.pyi +++ b/django-stubs/core/validators.pyi @@ -30,6 +30,21 @@ class RegexValidator(_Deconstructible): ) -> None: ... def __call__(self, value: Any) -> None: ... +class DomainNameValidator(RegexValidator): + ul: str + hostname_re: str + domain_re: str + tld_re: str + ascii_only_hostname_re: str + ascii_only_domain_re: str + ascii_only_tld_re: str + max_length: int + + def __init__(self, *, accept_idna: bool = True, **kwargs: Any) -> None: ... + def __call__(self, value: Any) -> None: ... + +validate_domain_name: DomainNameValidator + class URLValidator(RegexValidator): ul: str ipv4_re: str diff --git a/django-stubs/db/backends/base/operations.pyi b/django-stubs/db/backends/base/operations.pyi index fc5eac0f87..f08480c926 100644 --- a/django-stubs/db/backends/base/operations.pyi +++ b/django-stubs/db/backends/base/operations.pyi @@ -45,7 +45,7 @@ class BaseDatabaseOperations: def deferrable_sql(self) -> str: ... def distinct_sql(self, fields: list[str], params: list[Any] | None) -> tuple[list[str], list[str]]: ... def fetch_returned_insert_columns(self, cursor: Any, returning_params: Any) -> Any: ... - @deprecated("The field_cast_sql() is deprecated and will be removed in Django 6.0. Use lookup_cast() instead.") + @deprecated("field_cast_sql() is deprecated and will be removed in Django 6.0. Use lookup_cast() instead.") def field_cast_sql(self, db_type: str | None, internal_type: str) -> str: ... def force_no_ordering(self) -> list[Any]: ... def for_update_sql( diff --git a/django-stubs/db/models/constraints.pyi b/django-stubs/db/models/constraints.pyi index e3dd595859..beb69d87ff 100644 --- a/django-stubs/db/models/constraints.pyi +++ b/django-stubs/db/models/constraints.pyi @@ -47,7 +47,7 @@ class CheckConstraint(BaseConstraint): condition: Q | BaseExpression @overload - @deprecated("The check keyword argument is deprecated in favor of condition and will be removed in Django 6.0") + @deprecated("check keyword argument is deprecated in favor of condition and will be removed in Django 6.0") def __init__( self, *, diff --git a/django-stubs/db/models/enums.pyi b/django-stubs/db/models/enums.pyi index e511a4f01c..64328e0c09 100644 --- a/django-stubs/db/models/enums.pyi +++ b/django-stubs/db/models/enums.pyi @@ -4,7 +4,7 @@ from typing import Any, TypeVar, overload, type_check_only from _typeshed import ConvertibleToInt from django.utils.functional import _StrOrPromise -from typing_extensions import TypeAlias, deprecated +from typing_extensions import deprecated _Self = TypeVar("_Self") @@ -21,8 +21,7 @@ else: class IntEnum(int, ReprEnum): ... # type: ignore[misc] class StrEnum(str, ReprEnum): ... # type: ignore[misc] -@deprecated("ChoicesMeta is deprecated in favor of ChoicesType and will be removed in Django 6.0.") -class ChoicesMeta(EnumType): +class ChoicesType(EnumType): # 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 @@ -40,7 +39,8 @@ class ChoicesMeta(EnumType): @property def values(self) -> list[Any]: ... -ChoicesType: TypeAlias = ChoicesMeta +@deprecated("ChoicesMeta is deprecated in favor of ChoicesType and will be removed in Django 6.0.") +class ChoicesMeta(ChoicesType): ... class Choices(enum.Enum, metaclass=ChoicesType): # type: ignore[misc] @property diff --git a/django-stubs/db/models/fields/mixins.pyi b/django-stubs/db/models/fields/mixins.pyi index fe6aaa2af4..297d3230d8 100644 --- a/django-stubs/db/models/fields/mixins.pyi +++ b/django-stubs/db/models/fields/mixins.pyi @@ -9,7 +9,7 @@ NOT_PROVIDED: Any class FieldCacheMixin: def get_cache_name(self) -> str: ... @cached_property - @deprecated("The cache_name() is deprecated and will be removed in Django 6.0. Use get_cache_name() instead.") + @deprecated("cache_name() is deprecated and will be removed in Django 6.0. Use get_cache_name() instead.") def cache_name(self) -> str: ... def get_cached_value(self, instance: Model, default: Any = ...) -> Model | None: ... def is_cached(self, instance: Model) -> bool: ... diff --git a/django-stubs/db/models/fields/reverse_related.pyi b/django-stubs/db/models/fields/reverse_related.pyi index 2a7961de1c..30d23d33c0 100644 --- a/django-stubs/db/models/fields/reverse_related.pyi +++ b/django-stubs/db/models/fields/reverse_related.pyi @@ -79,7 +79,7 @@ class ForeignObjectRel(FieldCacheMixin): ) -> _ChoicesList: ... def is_hidden(self) -> bool: ... @deprecated( - "The get_joining_columns() is deprecated and will be removed in Django 6.0. Use get_joining_fields() instead." + "get_joining_columns() is deprecated and will be removed in Django 6.0. Use get_joining_fields() instead." ) def get_joining_columns(self) -> tuple: ... def get_joining_fields(self) -> tuple[tuple[Field, Field], ...]: ... diff --git a/django-stubs/db/models/query.pyi b/django-stubs/db/models/query.pyi index d6d27f2134..3cc3fed92e 100644 --- a/django-stubs/db/models/query.pyi +++ b/django-stubs/db/models/query.pyi @@ -241,7 +241,7 @@ class Prefetch: def get_current_prefetch_to(self, level: int) -> str: ... def get_current_to_attr(self, level: int) -> tuple[str, str]: ... @deprecated( - "The get_current_queryset() is deprecated and will be removed in Django 6.0. Use get_current_querysets() instead." + "get_current_queryset() is deprecated and will be removed in Django 6.0. Use get_current_querysets() instead." ) def get_current_queryset(self, level: int) -> QuerySet | None: ... def get_current_querysets(self, level: int) -> list[QuerySet] | None: ... diff --git a/django-stubs/forms/renderers.pyi b/django-stubs/forms/renderers.pyi index a4ed0a296a..e88459c683 100644 --- a/django-stubs/forms/renderers.pyi +++ b/django-stubs/forms/renderers.pyi @@ -30,7 +30,7 @@ class Jinja2(EngineMixin, BaseRenderer): def backend(self) -> type[Jinja2R]: ... @deprecated( - "The Jinja2DivFormRenderer transitional form renderer is deprecated and will be removed in Django 6.0. Use Jinja2 instead." + "Jinja2DivFormRenderer transitional form renderer is deprecated and will be removed in Django 6.0. Use Jinja2 instead." ) class Jinja2DivFormRenderer(Jinja2): form_template_name: str diff --git a/django-stubs/test/testcases.pyi b/django-stubs/test/testcases.pyi index 7b898c932b..464e01873b 100644 --- a/django-stubs/test/testcases.pyi +++ b/django-stubs/test/testcases.pyi @@ -152,13 +152,13 @@ class SimpleTestCase(unittest.TestCase): def assertInHTML(self, needle: str, haystack: str, count: int | None = ..., msg_prefix: str = ...) -> None: ... def assertJSONEqual( self, - raw: str, + raw: str | bytes | bytearray, expected_data: dict[str, Any] | list[Any] | str | int | float | bool | None, msg: str | None = ..., ) -> None: ... def assertJSONNotEqual( self, - raw: str, + raw: str | bytes | bytearray, expected_data: dict[str, Any] | list[Any] | str | int | float | bool | None, msg: str | None = ..., ) -> None: ... diff --git a/django-stubs/utils/html.pyi b/django-stubs/utils/html.pyi index 38e059cffc..87ddb2df36 100644 --- a/django-stubs/utils/html.pyi +++ b/django-stubs/utils/html.pyi @@ -3,7 +3,7 @@ from functools import cached_property from html.parser import HTMLParser from json import JSONEncoder from re import Pattern -from typing import Any +from typing import Any, overload from _typeshed import Incomplete from django.utils.functional import SimpleLazyObject, _StrOrPromise @@ -20,7 +20,10 @@ def json_script(value: Any, element_id: str | None = None, encoder: type[JSONEnc # conditional_escape could use a protocol to be more precise, see https://github.com/typeddjango/django-stubs/issues/1474 def conditional_escape(text: _StrOrPromise | SafeData) -> SafeString: ... +@overload @deprecated("Calling format_html() without passing args or kwargs is deprecated.") +def format_html(format_string: str) -> SafeString: ... +@overload def format_html(format_string: str, *args: Any, **kwargs: Any) -> SafeString: ... def format_html_join(sep: str, format_string: str, args_generator: Iterable[Iterable[Any]]) -> SafeString: ... def linebreaks(value: Any, autoescape: bool = False) -> str: ... diff --git a/django-stubs/utils/ipv6.pyi b/django-stubs/utils/ipv6.pyi index 8ee5ecbb46..e88dcf2778 100644 --- a/django-stubs/utils/ipv6.pyi +++ b/django-stubs/utils/ipv6.pyi @@ -1,4 +1,9 @@ +from ipaddress import IPv6Address from typing import Any -def clean_ipv6_address(ip_str: Any, unpack_ipv4: bool = False, error_message: str = ...) -> str: ... -def is_valid_ipv6_address(ip_str: str) -> bool: ... +MAX_IPV6_ADDRESS_LENGTH: int + +def clean_ipv6_address( + ip_str: Any, unpack_ipv4: bool = False, error_message: str = ..., max_length: int = 39 +) -> str: ... +def is_valid_ipv6_address(ip_str: str | IPv6Address) -> bool: ... diff --git a/django-stubs/utils/itercompat.pyi b/django-stubs/utils/itercompat.pyi index 379e860371..8023c9e403 100644 --- a/django-stubs/utils/itercompat.pyi +++ b/django-stubs/utils/itercompat.pyi @@ -3,6 +3,6 @@ from typing import Any from typing_extensions import deprecated @deprecated( - "The django.utils.itercompat.is_iterable() is deprecated and will be removed in Django 6.0. Use isinstance(..., collections.abc.Iterable) instead." + "django.utils.itercompat.is_iterable() is deprecated and will be removed in Django 6.0. Use isinstance(..., collections.abc.Iterable) instead." ) def is_iterable(x: Any) -> bool: ... diff --git a/ext/setup.py b/ext/setup.py index 4137ad3e51..8732472dfb 100755 --- a/ext/setup.py +++ b/ext/setup.py @@ -13,7 +13,7 @@ # It's fine to skip django-stubs-ext releases, but when doing a release, update this to newest django-stubs version. setup( name="django-stubs-ext", - version="5.1.2", + version="5.1.3", description="Monkey-patching and extensions for django-stubs", long_description=readme, long_description_content_type="text/markdown", diff --git a/mypy.ini b/mypy.ini index 428791b818..774e0fc2a6 100644 --- a/mypy.ini +++ b/mypy.ini @@ -18,6 +18,8 @@ disable_error_code = empty-body force_uppercase_builtins = true force_union_syntax = true +enable_error_code = deprecated + plugins = mypy_django_plugin.main, diff --git a/mypy_django_plugin/transformers/fields.py b/mypy_django_plugin/transformers/fields.py index f2bd21009b..0a1f1c435d 100644 --- a/mypy_django_plugin/transformers/fields.py +++ b/mypy_django_plugin/transformers/fields.py @@ -166,11 +166,11 @@ def set_descriptor_types_for_field( if not (isinstance(mapped_set_type, UninhabitedType) or isinstance(mapped_get_type, UninhabitedType)): # always replace set_type and get_type with (non-Any) mapped types set_type = helpers.convert_any_to_type(mapped_set_type, set_type) - get_type = helpers.convert_any_to_type(mapped_get_type, get_type) + get_type = get_proper_type(helpers.convert_any_to_type(mapped_get_type, get_type)) # the get_type must be optional if the field is nullable if (is_get_nullable or is_nullable) and not ( - isinstance(get_proper_type(get_type), NoneType) or helpers.is_optional(get_type) + isinstance(get_type, NoneType) or helpers.is_optional(get_type) or isinstance(get_type, AnyType) ): ctx.api.fail( f"{default_return_type.type.name} is nullable but its generic get type parameter is not optional", diff --git a/requirements.txt b/requirements.txt index 58745b1a60..e066fe559c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ # Dev tools: -pre-commit==4.0.1 +pre-commit==4.1.0 pytest==8.3.4 pytest-mypy-plugins==3.2.0 pytest-shard==0.1.2 @@ -7,10 +7,10 @@ pytest-shard==0.1.2 # Django deps: psycopg2-binary Django==4.2.16; python_version < '3.10' -Django==5.1.4; python_version >= '3.10' +Django==5.1.5; python_version >= '3.10' -e ./ext -e .[redis,compatible-mypy,oracle] # Overrides: -mypy==1.14.1 -pyright==1.1.391 +mypy==1.15.0 +pyright==1.1.393 diff --git a/scripts/stubtest/allowlist_todo_django51.txt b/scripts/stubtest/allowlist_todo_django51.txt index 595cac4ab3..0245d995eb 100644 --- a/scripts/stubtest/allowlist_todo_django51.txt +++ b/scripts/stubtest/allowlist_todo_django51.txt @@ -100,8 +100,6 @@ django.core.files.storage.filesystem.FileSystemStorage.__init__ django.core.files.storage.filesystem.FileSystemStorage.is_name_available django.core.files.storage.get_storage_class django.core.signing.Signer.__init__ -django.core.validators.DomainNameValidator -django.core.validators.validate_domain_name django.db.backends.base.features.BaseDatabaseFeatures.supports_frame_exclusion django.db.backends.base.operations.BaseDatabaseOperations.bulk_insert_sql django.db.backends.base.operations.BaseDatabaseOperations.force_group_by diff --git a/setup.py b/setup.py index c375e5cb33..4eeb55dc01 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ def find_stub_files(name: str) -> list[str]: dependencies = [ "django", "asgiref", - "django-stubs-ext>=5.1.2", + "django-stubs-ext>=5.1.3", "tomli; python_version < '3.11'", # Types: "typing-extensions>=4.11.0", @@ -31,14 +31,14 @@ def find_stub_files(name: str) -> list[str]: # Keep compatible-mypy major.minor version pinned to what we use in CI (requirements.txt) extras_require = { - "compatible-mypy": ["mypy>=1.12,<1.15"], + "compatible-mypy": ["mypy>=1.12,<1.16"], "redis": ["redis"], "oracle": ["oracledb"], } setup( name="django-stubs", - version="5.1.2", + version="5.1.3", description="Mypy stubs for Django", long_description=readme, long_description_content_type="text/markdown", diff --git a/tests/typecheck/contrib/contenttypes/test_models.yml b/tests/typecheck/contrib/contenttypes/test_models.yml new file mode 100644 index 0000000000..284de2b1a8 --- /dev/null +++ b/tests/typecheck/contrib/contenttypes/test_models.yml @@ -0,0 +1,13 @@ +- case: test_contenttypes_models + main: | + from django.contrib.contenttypes.models import ContentType + + c = ContentType.objects.create(app_label='abc', model='abc') + reveal_type(c.id) # N: Revealed type is "builtins.int" + reveal_type(c.app_label) # N: Revealed type is "builtins.str" + reveal_type(c.model) # N: Revealed type is "builtins.str" + + ContentType.objects.create(app_label=[]) # E: Incompatible type for "app_label" of "ContentType" (got "List[Any]", expected "Union[str, int, Combinable]") [misc] + + custom_settings: | + INSTALLED_APPS = ("django.contrib.contenttypes",) diff --git a/tests/typecheck/fields/test_custom_fields.yml b/tests/typecheck/fields/test_custom_fields.yml index 14d175348a..335de9a551 100644 --- a/tests/typecheck/fields/test_custom_fields.yml +++ b/tests/typecheck/fields/test_custom_fields.yml @@ -18,13 +18,12 @@ reveal_type(user.my_custom_field13) # N: Revealed type is "Union[myapp.models.CustomFieldValue, None]" reveal_type(user.my_custom_field14) # N: Revealed type is "Union[builtins.bool, None]" reveal_type(user.my_custom_field15) # N: Revealed type is "None" + + reveal_type(user.my_custom_field_any1) # N: Revealed type is "Any" + reveal_type(user.my_custom_field_any2) # N: Revealed type is "Any" + reveal_type(user.my_custom_field_any3) # N: Revealed type is "Any" + reveal_type(user.my_custom_field_any4) # N: Revealed type is "Any" monkeypatch: true - out: | - myapp/models:31: error: GenericField is nullable but its generic get type parameter is not optional [misc] - myapp/models:32: error: CustomValueField is nullable but its generic get type parameter is not optional [misc] - myapp/models:33: error: SingleTypeField is nullable but its generic get type parameter is not optional [misc] - myapp/models:34: error: AdditionalTypeVarField is nullable but its generic get type parameter is not optional [misc] - myapp/models:35: error: Field is nullable but its generic get type parameter is not optional [misc] installed_apps: - myapp files: @@ -53,19 +52,24 @@ class CustomSmallIntegerField(fields.SmallIntegerField[_ST, _GT]): ... + class FieldImplicitAny(fields.Field): ... + class FieldExplicitAny(fields.Field[Any, Any]): ... + class User(models.Model): id = models.AutoField(primary_key=True) my_custom_field1 = GenericField[Union[CustomFieldValue, int], CustomFieldValue]() my_custom_field2 = CustomValueField() my_custom_field3 = SingleTypeField[bool]() my_custom_field4 = AdditionalTypeVarField[Union[CustomFieldValue, int], CustomFieldValue, bool]() + my_custom_field_any1 = FieldImplicitAny() + my_custom_field_any2 = FieldExplicitAny() # test null=True on fields with non-optional generic types throw error - my_custom_field5 = GenericField[Union[CustomFieldValue, int], CustomFieldValue](null=True) - my_custom_field6 = CustomValueField(null=True) - my_custom_field7 = SingleTypeField[bool](null=True) - my_custom_field8 = AdditionalTypeVarField[Union[CustomFieldValue, int], CustomFieldValue, bool](null=True) - my_custom_field9 = fields.Field[Union[CustomFieldValue, int], CustomFieldValue](null=True) + my_custom_field5 = GenericField[Union[CustomFieldValue, int], CustomFieldValue](null=True) # E: GenericField is nullable but its generic get type parameter is not optional [misc] + my_custom_field6 = CustomValueField(null=True) # E: CustomValueField is nullable but its generic get type parameter is not optional [misc] + my_custom_field7 = SingleTypeField[bool](null=True) # E: SingleTypeField is nullable but its generic get type parameter is not optional [misc] + my_custom_field8 = AdditionalTypeVarField[Union[CustomFieldValue, int], CustomFieldValue, bool](null=True) # E: AdditionalTypeVarField is nullable but its generic get type parameter is not optional [misc] + my_custom_field9 = fields.Field[Union[CustomFieldValue, int], CustomFieldValue](null=True) # E: Field is nullable but its generic get type parameter is not optional [misc] # test overriding fields that set _pyi_private_set_type or _pyi_private_get_type my_custom_field10 = fields.SmallIntegerField[bool, bool]() @@ -76,3 +80,7 @@ my_custom_field13 = GenericField[Union[CustomFieldValue, int], Union[CustomFieldValue, None]](null=True) my_custom_field14 = SingleTypeField[Union[bool, None]](null=True) my_custom_field15 = fields.Field[None, None](null=True) + + # test null=True on Any does not raise + my_custom_field_any3 = FieldImplicitAny(null=True) + my_custom_field_any4 = FieldExplicitAny(null=True) diff --git a/tests/typecheck/managers/querysets/test_from_queryset.yml b/tests/typecheck/managers/querysets/test_from_queryset.yml index 60f6c765f0..5747cac029 100644 --- a/tests/typecheck/managers/querysets/test_from_queryset.yml +++ b/tests/typecheck/managers/querysets/test_from_queryset.yml @@ -948,7 +948,7 @@ main:12: note: Revealed type is "Type[django.db.models.manager.Manager[django.db.models.base.Model]]" main:12: error: Argument 1 to "from_queryset" of "BaseManager" has incompatible type ""; expected "Type[QuerySet[Model, Model]]" [arg-type] main:17: note: Revealed type is "Type[django.db.models.manager.Manager[django.db.models.base.Model]]" - main:17: error: Argument 1 to "from_queryset" of "BaseManager" has incompatible type "Type[NonQSGeneric[Any]]"; expected "Type[QuerySet[Model, Model]]" [arg-type] + main:17: error: Argument 1 to "from_queryset" of "BaseManager" has incompatible type "Type[NonQSGeneric[int]]"; expected "Type[QuerySet[Model, Model]]" [arg-type] - case: test_reverse_manager_with_foreign_key main: |