diff --git a/rest_framework-stubs/utils/serializer_helpers.pyi b/rest_framework-stubs/utils/serializer_helpers.pyi index e5aecfd5f..52c4f7f46 100644 --- a/rest_framework-stubs/utils/serializer_helpers.pyi +++ b/rest_framework-stubs/utils/serializer_helpers.pyi @@ -1,20 +1,59 @@ -from collections.abc import Iterator, MutableMapping -from typing import Any +from collections.abc import Iterable, Iterator, MutableMapping +from typing import Any, Generic, TypeVar, overload +from _typeshed import SupportsKeysAndGetItem from rest_framework.exceptions import ErrorDetail from rest_framework.fields import Field from rest_framework.serializers import BaseSerializer -class ReturnDict(dict): +_T = TypeVar("_T") +_VT = TypeVar("_VT") +_KT = TypeVar("_KT") + +class ReturnDict(dict[_KT, _VT], Generic[_KT, _VT]): serializer: BaseSerializer - def __init__(self, serializer: BaseSerializer = ..., *args, **kwargs): ... - def copy(self) -> ReturnDict: ... - def __reduce__(self) -> tuple[dict, tuple[dict]]: ... + # Copied from https://github.com/python/typeshed/blob/main/stdlib/builtins.pyi `class dict` + @overload + def __init__(self, *, serializer: BaseSerializer) -> None: ... + @overload + def __init__(self: ReturnDict[str, _VT], *, serializer: BaseSerializer, **kwargs: _VT) -> None: ... + @overload + def __init__(self, __map: SupportsKeysAndGetItem[_KT, _VT], *, serializer: BaseSerializer) -> None: ... + @overload + def __init__( + self: ReturnDict[str, _VT], + __map: SupportsKeysAndGetItem[str, _VT], + *, + serializer: BaseSerializer, + **kwargs: _VT + ) -> None: ... + @overload + def __init__(self, __iterable: Iterable[tuple[_KT, _VT]], *, serializer: BaseSerializer) -> None: ... + @overload + def __init__( + self: ReturnDict[str, _VT], __iterable: Iterable[tuple[str, _VT]], *, serializer: BaseSerializer, **kwargs: _VT + ) -> None: ... + # Next two overloads are for dict(string.split(sep) for string in iterable) + # Cannot be Iterable[Sequence[_T]] or otherwise dict(["foo", "bar", "baz"]) is not an error + @overload + def __init__( + self: ReturnDict[str, str], __iterable: Iterable[list[str]], *, serializer: BaseSerializer + ) -> None: ... + @overload + def __init__( + self: ReturnDict[bytes, bytes], __iterable: Iterable[list[bytes]], *, serializer: BaseSerializer + ) -> None: ... + def copy(self) -> ReturnDict[_KT, _VT]: ... + def __reduce__(self) -> tuple[type[dict[_KT, _VT]], tuple[dict[_KT, _VT]]]: ... -class ReturnList(list): +class ReturnList(list[_T], Generic[_T]): serializer: BaseSerializer - def __init__(self, serializer: BaseSerializer = ..., *args, **kwargs): ... - def __reduce__(self) -> tuple[dict, tuple[dict]]: ... + # Copied from https://github.com/python/typeshed/blob/main/stdlib/builtins.pyi `class list` + @overload + def __init__(self, *, serializer: BaseSerializer) -> None: ... + @overload + def __init__(self, __iterable: Iterable[_T], *, serializer: BaseSerializer) -> None: ... + def __reduce__(self) -> tuple[type[list[_T]], tuple[list[_T]]]: ... class BoundField: """ diff --git a/tests/typecheck/test_serializers.yml b/tests/typecheck/test_serializers.yml index 59e6d5179..a740232c7 100644 --- a/tests/typecheck/test_serializers.yml +++ b/tests/typecheck/test_serializers.yml @@ -75,3 +75,156 @@ @cached_property def fields(self) -> BindingDict: return super().fields + +- case: test_return_list + main: | + from rest_framework import serializers + from rest_framework.utils.serializer_helpers import ReturnList + + class TestSerializer(serializers.Serializer): + def test(self) -> None: + ReturnList([], serializer=self) + ReturnList((), serializer=self) + ReturnList([{}], serializer=self) + ReturnList(serializer=self) + ret = ReturnList(["1"], serializer=self) + reveal_type(ret) # N: Revealed type is "rest_framework.utils.serializer_helpers.ReturnList[builtins.str]" + +- case: test_return_list_reduce + main: | + from rest_framework import serializers + from rest_framework.utils.serializer_helpers import ReturnList + + class TestSerializer(serializers.Serializer): + def test(self) -> None: + ret = ReturnList(["1"], serializer=self) + callable, args = ret.__reduce__() + reveal_type(callable(*args)) # N: Revealed type is "builtins.list[builtins.str]" + +- case: test_return_list_serializer_argument_is_kw_only + parametrized: + - arg: "" + err: No overload variant of "ReturnList" matches argument type "TestSerializer" + - arg: "[]," + err: No overload variant of "ReturnList" matches argument types "List[]", "TestSerializer" + main: | + from rest_framework import serializers + from rest_framework.utils.serializer_helpers import ReturnList + + class TestSerializer(serializers.Serializer): + def test(self) -> None: + ReturnList({{ arg }} self) + out: | + main:6: error: {{ err }} + main:6: note: Possible overload variants: + main:6: note: def [_T] ReturnList(self, *, serializer: BaseSerializer[Any]) -> ReturnList[_T] + main:6: note: def [_T] ReturnList(self, Iterable[_T], /, *, serializer: BaseSerializer[Any]) -> ReturnList[_T] + +- case: test_return_list_serializer_is_required + parametrized: + - arg: "" + err: All overload variants of "ReturnList" require at least one argument + - arg: "[]" + err: No overload variant of "ReturnList" matches argument type "List[]" + main: | + from rest_framework import serializers + from rest_framework.utils.serializer_helpers import ReturnList + + class TestSerializer(serializers.Serializer): + def test(self) -> None: + ReturnList({{ arg }}) + out: | + main:6: error: {{ err }} + main:6: note: Possible overload variants: + main:6: note: def [_T] ReturnList(self, *, serializer: BaseSerializer[Any]) -> ReturnList[_T] + main:6: note: def [_T] ReturnList(self, Iterable[_T], /, *, serializer: BaseSerializer[Any]) -> ReturnList[_T] + +- case: test_return_dict + main: | + from rest_framework import serializers + from rest_framework.utils.serializer_helpers import ReturnDict + from typing import Mapping + + class TestSerializer(serializers.Serializer): + def test(self) -> None: + input: Mapping[str, str] = {} + ReturnDict({}, serializer=self) + ReturnDict([("a", "a")], serializer=self) + ReturnDict(input, serializer=self) + iterable = "" + sep = " " + ReturnDict((string.split(sep) for string in iterable), serializer=self) + ReturnDict(serializer=self) + ret = ReturnDict({"a": "a"}, serializer=self) + reveal_type(ret) # N: Revealed type is "rest_framework.utils.serializer_helpers.ReturnDict[builtins.str, builtins.str]" + copy = ret.copy() + reveal_type(copy) # N: Revealed type is "rest_framework.utils.serializer_helpers.ReturnDict[builtins.str, builtins.str]" + +- case: test_return_dict_reduce + main: | + from rest_framework import serializers + from rest_framework.utils.serializer_helpers import ReturnDict + + class TestSerializer(serializers.Serializer): + def test(self) -> None: + ret = ReturnDict({"a": "1"}, serializer=self) + callable, args = ret.__reduce__() + reveal_type(callable(*args)) # N: Revealed type is "builtins.dict[builtins.str, builtins.str]" + +- case: test_return_dict_serializer_argument_is_kw_only + parametrized: + - arg: "" + err: No overload variant of "ReturnDict" matches argument type "TestSerializer" + - arg: "{}," + err: No overload variant of "ReturnDict" matches argument types "Dict[, ]", "TestSerializer" + - arg: "[]," + err: No overload variant of "ReturnDict" matches argument types "List[]", "TestSerializer" + - arg: "[('a', 'a')]," + err: No overload variant of "ReturnDict" matches argument types "List[Tuple[str, str]]", "TestSerializer" + main: | + from rest_framework import serializers + from rest_framework.utils.serializer_helpers import ReturnDict + + class TestSerializer(serializers.Serializer): + def test(self) -> None: + ReturnDict({{ arg }} self) + out: | + main:6: error: {{ err }} + main:6: note: Possible overload variants: + main:6: note: def [_KT, _VT] ReturnDict(self, *, serializer: BaseSerializer[Any]) -> ReturnDict[_KT, _VT] + main:6: note: def [_KT, _VT] ReturnDict(self, *, serializer: BaseSerializer[Any], **kwargs: _VT) -> ReturnDict[str, _VT] + main:6: note: def [_KT, _VT] ReturnDict(self, SupportsKeysAndGetItem[_KT, _VT], /, *, serializer: BaseSerializer[Any]) -> ReturnDict[_KT, _VT] + main:6: note: def [_KT, _VT] ReturnDict(self, SupportsKeysAndGetItem[str, _VT], /, *, serializer: BaseSerializer[Any], **kwargs: _VT) -> ReturnDict[str, _VT] + main:6: note: def [_KT, _VT] ReturnDict(self, Iterable[Tuple[_KT, _VT]], /, *, serializer: BaseSerializer[Any]) -> ReturnDict[_KT, _VT] + main:6: note: def [_KT, _VT] ReturnDict(self, Iterable[Tuple[str, _VT]], /, *, serializer: BaseSerializer[Any], **kwargs: _VT) -> ReturnDict[str, _VT] + main:6: note: def [_KT, _VT] ReturnDict(self, Iterable[List[str]], /, *, serializer: BaseSerializer[Any]) -> ReturnDict[str, str] + main:6: note: def [_KT, _VT] ReturnDict(self, Iterable[List[bytes]], /, *, serializer: BaseSerializer[Any]) -> ReturnDict[bytes, bytes] + +- case: test_return_dict_serializer_is_required + parametrized: + - arg: "" + err: All overload variants of "ReturnDict" require at least one argument + - arg: "{}" + err: No overload variant of "ReturnDict" matches argument type "Dict[, ]" + - arg: "[]" + err: No overload variant of "ReturnDict" matches argument type "List[]" + - arg: "[('a', 'a')]" + err: No overload variant of "ReturnDict" matches argument type "List[Tuple[str, str]]" + main: | + from rest_framework import serializers + from rest_framework.utils.serializer_helpers import ReturnDict + + class TestSerializer(serializers.Serializer): + def test(self) -> None: + ReturnDict({{ arg }}) + out: | + main:6: error: {{ err }} + main:6: note: Possible overload variants: + main:6: note: def [_KT, _VT] ReturnDict(self, *, serializer: BaseSerializer[Any]) -> ReturnDict[_KT, _VT] + main:6: note: def [_KT, _VT] ReturnDict(self, *, serializer: BaseSerializer[Any], **kwargs: _VT) -> ReturnDict[str, _VT] + main:6: note: def [_KT, _VT] ReturnDict(self, SupportsKeysAndGetItem[_KT, _VT], /, *, serializer: BaseSerializer[Any]) -> ReturnDict[_KT, _VT] + main:6: note: def [_KT, _VT] ReturnDict(self, SupportsKeysAndGetItem[str, _VT], /, *, serializer: BaseSerializer[Any], **kwargs: _VT) -> ReturnDict[str, _VT] + main:6: note: def [_KT, _VT] ReturnDict(self, Iterable[Tuple[_KT, _VT]], /, *, serializer: BaseSerializer[Any]) -> ReturnDict[_KT, _VT] + main:6: note: def [_KT, _VT] ReturnDict(self, Iterable[Tuple[str, _VT]], /, *, serializer: BaseSerializer[Any], **kwargs: _VT) -> ReturnDict[str, _VT] + main:6: note: def [_KT, _VT] ReturnDict(self, Iterable[List[str]], /, *, serializer: BaseSerializer[Any]) -> ReturnDict[str, str] + main:6: note: def [_KT, _VT] ReturnDict(self, Iterable[List[bytes]], /, *, serializer: BaseSerializer[Any]) -> ReturnDict[bytes, bytes]