Skip to content

Tests: Update usages of obsolete Union/Optional types #792

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class MyModelSerializer(serializers.ModelSerializer[MyModel]):
fields = ("id", "example")
```

Which means that methods where the model is being passed around will know the actual type of the model instead of being `Any`. The `instance` attribute on the above serializer will be `Union[MyModel, typing.Sequence[MyModel], None]`.
Which means that methods where the model is being passed around will know the actual type of the model instead of being `Any`. The `instance` attribute on the above serializer will be `MyModel | typing.Sequence[MyModel] | None`.

## To get help

Expand Down
1 change: 0 additions & 1 deletion mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ disallow_incomplete_defs = true
disable_error_code = empty-body
# TODO: update our test error messages to match new mypy output
show_error_codes = false
force_union_syntax = true

plugins =
mypy_django_plugin.main,
Expand Down
4 changes: 2 additions & 2 deletions tests/typecheck/test_decorators.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
main: |
from typing import Any
from rest_framework.decorators import api_view
@api_view # E: Argument 1 to "api_view" has incompatible type "Callable[[Any], Any]"; expected "Optional[Sequence[str]]"
@api_view # E: Argument 1 to "api_view" has incompatible type "Callable[[Any], Any]"; expected "Sequence[str] | None"
def view_func2(request: Any) -> Any: ...
- case: api_view_incorrect_return
main: |
Expand All @@ -33,7 +33,7 @@
- case: permission_classes
main: |
from rest_framework.decorators import permission_classes
reveal_type(permission_classes) # N: Revealed type is "def (permission_classes: typing.Sequence[Union[type[rest_framework.permissions.BasePermission], rest_framework.permissions.OperandHolder, rest_framework.permissions.SingleOperandHolder]]) -> def [_View <: def (*Any, **Any) -> django.http.response.HttpResponseBase] (_View`-1) -> _View`-1"
reveal_type(permission_classes) # N: Revealed type is "def (permission_classes: typing.Sequence[type[rest_framework.permissions.BasePermission] | rest_framework.permissions.OperandHolder | rest_framework.permissions.SingleOperandHolder]) -> def [_View <: def (*Any, **Any) -> django.http.response.HttpResponseBase] (_View`-1) -> _View`-1"

- case: permission_classes_with_operators
main: |
Expand Down
2 changes: 1 addition & 1 deletion tests/typecheck/test_exceptions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,4 @@
APIException(None, None)
APIException(1) # E: Argument 1 to "APIException" has incompatible type "int"; expected "_APIExceptionInput"
APIException({'a': 1}) # E: Argument 1 to "APIException" has incompatible type "dict[str, int]"; expected "_APIExceptionInput"
APIException({'a': ['test', 1]}) # E: Argument 1 to "APIException" has incompatible type "dict[str, list[Union[str, int]]]"; expected "_APIExceptionInput"
APIException({'a': ['test', 1]}) # E: Argument 1 to "APIException" has incompatible type "dict[str, list[str | int]]"; expected "_APIExceptionInput"
34 changes: 16 additions & 18 deletions tests/typecheck/test_fields.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,14 @@
- case: default_and_inital_args_fields
main: |
from rest_framework.fields import DictField, CharField, empty
from typing import Optional, Dict, Any
from typing import Dict, Any

CharField(initial='', default=lambda: '')
CharField(initial=None, default=4) # E: Argument "default" to "CharField" has incompatible type "int"; expected "Union[Union[str, _StrPromise], Callable[[], Union[str, _StrPromise]], _Empty, None]"
CharField(initial={}, default=empty) # E: Argument "initial" to "CharField" has incompatible type "dict[Never, Never]"; expected "Union[str, Callable[[], str], _Empty, None]"
CharField(initial=None, default=4) # E: Argument "default" to "CharField" has incompatible type "int"; expected "str | _StrPromise | Callable[[], str | _StrPromise] | _Empty | None"
CharField(initial={}, default=empty) # E: Argument "initial" to "CharField" has incompatible type "dict[Never, Never]"; expected "str | Callable[[], str] | _Empty | None"

x: Optional[str] = CharField().get_initial()
y: Optional[int] = CharField().get_initial() # E: Incompatible types in assignment (expression has type "Optional[str]", variable has type "Optional[int]")
x: str | None = CharField().get_initial()
y: int | None = CharField().get_initial() # E: Incompatible types in assignment (expression has type "str | None", variable has type "int | None")

- case: float_field_args_fields
main: |
Expand All @@ -66,37 +66,35 @@

- case: ChoiceField_default
main: |
from typing import Union
from rest_framework.fields import ChoiceField

def int_callback() -> int: ...
def mixed_callback() -> Union[int, str]: ...
def mixed_callback() -> int | str: ...

ChoiceField([1], default=1)
ChoiceField(['test'], allow_null=True, default=None)
ChoiceField([1], default=int_callback)
ChoiceField([1, 'lulz'], default=mixed_callback)
ChoiceField([1], default=lambda: None) # E: Argument "default" to "ChoiceField" has incompatible type "Callable[[], None]"; expected "Union[Union[str, _StrPromise], int, Callable[[], Union[Union[str, _StrPromise], int]], _Empty, None]" # E: Incompatible return value type (got "None", expected "Union[Union[str, _StrPromise], int]")
ChoiceField([1], default=lambda: None) # E: Argument "default" to "ChoiceField" has incompatible type "Callable[[], None]"; expected "str | _StrPromise | int | Callable[[], str | _StrPromise | int] | _Empty | None" # E: Incompatible return value type (got "None", expected "str | _StrPromise | int")

- case: MultipleChoiceField_default
main: |
from typing import Union
from rest_framework.fields import MultipleChoiceField

def int_set_callback() -> set[int]: ...
def mixed_set_callback() -> set[Union[int, str]]: ...
def mixed_set_callback() -> set[int | str]: ...

MultipleChoiceField(choices=[1], default={1})
MultipleChoiceField(choices=['test'], allow_null=True, default=None)
MultipleChoiceField(choices=[1], default=int_set_callback)
MultipleChoiceField(choices=[1, 'lulz'], default=mixed_set_callback)
MultipleChoiceField(choices=[1], default=lambda: [1]) # E: Argument "default" to "MultipleChoiceField" has incompatible type "Callable[[], list[int]]"; expected "Union[set[Union[str, int]], set[str], set[int], Callable[[], Union[set[Union[str, int]], set[str], set[int]]], _Empty, None]" # E: Incompatible return value type (got "list[int]", expected "Union[set[Union[str, int]], set[str], set[int]]")
MultipleChoiceField(choices=[1], default=lambda: [1]) # E: Argument "default" to "MultipleChoiceField" has incompatible type "Callable[[], list[int]]"; expected "set[str | int] | set[str] | set[int] | Callable[[], set[str | int] | set[str] | set[int]] | _Empty | None" # E: Incompatible return value type (got "list[int]", expected "set[str | int] | set[str] | set[int]")

MultipleChoiceField(choices=[(1, "1"), (2, "2")], default={1})
MultipleChoiceField(choices=[(1, "1"), (2, "2")], default=[1]) # E: Argument "default" to "MultipleChoiceField" has incompatible type "list[int]"; expected "Union[set[Union[str, int]], set[str], set[int], Callable[[], Union[set[Union[str, int]], set[str], set[int]]], _Empty, None]"
MultipleChoiceField(choices=[(1, "1"), (2, "2")], default=[1]) # E: Argument "default" to "MultipleChoiceField" has incompatible type "list[int]"; expected "set[str | int] | set[str] | set[int] | Callable[[], set[str | int] | set[str] | set[int]] | _Empty | None"

MultipleChoiceField(choices=[(1, "1"), (2, "2")], initial={1})
MultipleChoiceField(choices=[(1, "1"), (2, "2")], initial=[1]) # E: Argument "initial" to "MultipleChoiceField" has incompatible type "list[int]"; expected "Union[set[Union[Union[str, _StrPromise], int]], set[Union[str, _StrPromise]], set[int], Callable[[], Union[set[Union[Union[str, _StrPromise], int]], set[Union[str, _StrPromise]], set[int]]], _Empty, None]"
MultipleChoiceField(choices=[(1, "1"), (2, "2")], initial=[1]) # E: Argument "initial" to "MultipleChoiceField" has incompatible type "list[int]"; expected "set[str | _StrPromise | int] | set[str | _StrPromise] | set[int] | Callable[[], set[str | _StrPromise | int] | set[str | _StrPromise] | set[int]] | _Empty | None"

- case: FileField_default
main: |
Expand All @@ -108,12 +106,12 @@
FileField(allow_null=True, default=None)
FileField(allow_null=True, default=file_callback)
FileField(allow_null=True, default=file_callback())
FileField(allow_null=True, default=123) # E: Argument "default" to "FileField" has incompatible type "int"; expected "Union[File[Any], Callable[[], File[Any]], _Empty, None]"
FileField(allow_null=True, default=123) # E: Argument "default" to "FileField" has incompatible type "int"; expected "File[Any] | Callable[[], File[Any]] | _Empty | None"

ImageField(allow_null=True, default=None)
ImageField(default=file_callback)
ImageField(default=file_callback())
ImageField(default='a') # E: Argument "default" to "ImageField" has incompatible type "str"; expected "Union[File[Any], Callable[[], File[Any]], _Empty, None]"
ImageField(default='a') # E: Argument "default" to "ImageField" has incompatible type "str"; expected "File[Any] | Callable[[], File[Any]] | _Empty | None"

- case: DictField_default
main: |
Expand All @@ -123,13 +121,13 @@
DictField(default={})
DictField(default={'a': 1, 'b': 2})
DictField(default=lambda: {'a': [], 'b': 'str'})
DictField(default=[]) # E: Argument "default" to "DictField" has incompatible type "list[Never]"; expected "Union[dict[Any, Any], Callable[[], dict[Any, Any]], _Empty, None]"
DictField(default=[]) # E: Argument "default" to "DictField" has incompatible type "list[Never]"; expected "dict[Any, Any] | Callable[[], dict[Any, Any]] | _Empty | None"

JSONField(allow_null=True, default=None)
JSONField(default={})
JSONField(default={'a': 1, 'b': 2})
JSONField(default=lambda: {'a': [], 'b': 'str'})
JSONField(default=[]) # E: Argument "default" to "JSONField" has incompatible type "list[Never]"; expected "Union[Mapping[Any, Any], Callable[[], Mapping[Any, Any]], _Empty, None]"
JSONField(default=[]) # E: Argument "default" to "JSONField" has incompatible type "list[Never]"; expected "Mapping[Any, Any] | Callable[[], Mapping[Any, Any]] | _Empty | None"

- case: ListField_default
main: |
Expand All @@ -139,4 +137,4 @@
ListField(default=[])
ListField(default=[0, 'one'])
ListField(default=lambda: [])
ListField(default='wät') # E: Argument "default" to "ListField" has incompatible type "str"; expected "Union[list[Any], Callable[[], list[Any]], _Empty, None]"
ListField(default='wät') # E: Argument "default" to "ListField" has incompatible type "str"; expected "list[Any] | Callable[[], list[Any]] | _Empty | None"
2 changes: 1 addition & 1 deletion tests/typecheck/test_pagination.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@
request: Request
queryset: QuerySet[User]
page = paginator.paginate_queryset(queryset, request)
reveal_type(page) # N: Revealed type is "Union[builtins.list[django.contrib.auth.models.User], None]"
reveal_type(page) # N: Revealed type is "builtins.list[django.contrib.auth.models.User] | None"
6 changes: 2 additions & 4 deletions tests/typecheck/test_routers.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
- case: test_router_urls
main: |
from typing import Union

from django.urls import path, include
from rest_framework.routers import SimpleRouter, DefaultRouter
from rest_framework.urlpatterns import _AnyURL

simple = SimpleRouter()
reveal_type(simple.urls) # N: Revealed type is "builtins.list[Union[django.urls.resolvers.URLPattern, django.urls.resolvers.URLResolver]]"
reveal_type(simple.urls) # N: Revealed type is "builtins.list[django.urls.resolvers.URLPattern | django.urls.resolvers.URLResolver]"
default = DefaultRouter()
reveal_type(default.urls) # N: Revealed type is "builtins.list[Union[django.urls.resolvers.URLPattern, django.urls.resolvers.URLResolver]]"
reveal_type(default.urls) # N: Revealed type is "builtins.list[django.urls.resolvers.URLPattern | django.urls.resolvers.URLResolver]"

urlpatterns: list[_AnyURL] = [
path('api/', include(simple.urls)),
Expand Down
6 changes: 3 additions & 3 deletions tests/typecheck/test_serializers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@

reveal_type(serializers.ModelSerializer.Meta.model) # N: Revealed type is "type[_MT?]"
reveal_type(serializers.ModelSerializer.Meta.fields) # N: Revealed type is "typing.Sequence[builtins.str]"
reveal_type(serializers.ModelSerializer.Meta.read_only_fields) # N: Revealed type is "Union[typing.Sequence[builtins.str], None]"
reveal_type(serializers.ModelSerializer.Meta.exclude) # N: Revealed type is "Union[typing.Sequence[builtins.str], None]"
reveal_type(serializers.ModelSerializer.Meta.depth) # N: Revealed type is "Union[builtins.int, None]"
reveal_type(serializers.ModelSerializer.Meta.read_only_fields) # N: Revealed type is "typing.Sequence[builtins.str] | None"
reveal_type(serializers.ModelSerializer.Meta.exclude) # N: Revealed type is "typing.Sequence[builtins.str] | None"
reveal_type(serializers.ModelSerializer.Meta.depth) # N: Revealed type is "builtins.int | None"
reveal_type(serializers.ModelSerializer.Meta.extra_kwargs) # N: Revealed type is "builtins.dict[builtins.str, builtins.dict[builtins.str, Any]]"

- case: test_model_serializer_passes_check
Expand Down
3 changes: 1 addition & 2 deletions tests/typecheck/test_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,11 @@

- case: test_testcases_client_api_urlpatterns
main: |
from typing import Union
from django.urls import URLPattern, URLResolver
from rest_framework import test, status

class MyTest(test.URLPatternsTestCase):
urlpatterns : list[Union[URLPattern, URLResolver]] = []
urlpatterns : list[URLPattern | URLResolver] = []
def test_example(self) -> None:
reveal_type(self.client) # N: Revealed type is "django.test.client.Client"
response = self.client.get('/', format="json")
Expand Down