Skip to content
Merged
13 changes: 9 additions & 4 deletions rest_framework-stubs/fields.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ class Field(Generic[_VT, _DT, _RP, _IN]):
write_only: bool
def __init__(
self,
*,
read_only: bool = ...,
write_only: bool = ...,
required: bool = ...,
Expand Down Expand Up @@ -339,7 +340,6 @@ class DecimalField(Field[Decimal, int | float | str | Decimal, str, Any]):
min_value: Decimal | int | float = ...,
localize: bool = ...,
rounding: str | None = ...,
normalize_output: bool = ...,
*,
read_only: bool = ...,
write_only: bool = ...,
Expand Down Expand Up @@ -367,6 +367,7 @@ class DateTimeField(Field[datetime.datetime, datetime.datetime | str, str, Any])
format: str | None = ...,
input_formats: Sequence[str] = ...,
default_timezone: datetime.tzinfo | None = ...,
*,
read_only: bool = ...,
write_only: bool = ...,
required: bool = ...,
Expand All @@ -391,6 +392,7 @@ class DateField(Field[datetime.date, datetime.date | str, str, Any]):
self,
format: str | None = ...,
input_formats: Sequence[str] = ...,
*,
read_only: bool = ...,
write_only: bool = ...,
required: bool = ...,
Expand All @@ -413,6 +415,7 @@ class TimeField(Field[datetime.time, datetime.time | str, str, Any]):
self,
format: str | None = ...,
input_formats: Sequence[str] = ...,
*,
read_only: bool = ...,
write_only: bool = ...,
required: bool = ...,
Expand Down Expand Up @@ -493,6 +496,7 @@ class MultipleChoiceField(
allow_empty: bool
def __init__(
self,
*,
choices: Iterable[Any],
read_only: bool = ...,
write_only: bool = ...,
Expand Down Expand Up @@ -521,6 +525,7 @@ class FilePathField(ChoiceField):
allow_files: bool = ...,
allow_folders: bool = ...,
required: bool = ...,
*,
read_only: bool = ...,
write_only: bool = ...,
default: _DefaultInitial[str] = ...,
Expand Down Expand Up @@ -593,6 +598,7 @@ class ListField(Field[list[Any], list[Any], list[Any], Any]):
min_length: int | None
def __init__(
self,
*,
read_only: bool = ...,
write_only: bool = ...,
required: bool = ...,
Expand All @@ -605,7 +611,6 @@ class ListField(Field[list[Any], list[Any], list[Any], Any]):
error_messages: dict[str, StrOrPromise] = ...,
validators: Sequence[Validator[list[Any]]] | None = ...,
allow_null: bool = ...,
*,
child: Field = ...,
allow_empty: bool = ...,
max_length: int = ...,
Expand All @@ -618,6 +623,7 @@ class DictField(Field[dict[Any, Any], dict[Any, Any], dict[Any, Any], Any]):
allow_empty: bool
def __init__(
self,
*,
read_only: bool = ...,
write_only: bool = ...,
required: bool = ...,
Expand All @@ -630,7 +636,6 @@ class DictField(Field[dict[Any, Any], dict[Any, Any], dict[Any, Any], Any]):
error_messages: dict[str, StrOrPromise] = ...,
validators: Sequence[Validator[dict[Any, Any]]] | None = ...,
allow_null: bool = ...,
*,
child: Field = ...,
allow_empty: bool = ...,
) -> None: ...
Expand All @@ -645,6 +650,7 @@ class JSONField(Field[dict[str, Any] | list[dict[str, Any]], dict[str, Any] | li
decoder: type[JSONDecoder] | None
def __init__(
self,
*,
read_only: bool = ...,
write_only: bool = ...,
required: bool = ...,
Expand All @@ -657,7 +663,6 @@ class JSONField(Field[dict[str, Any] | list[dict[str, Any]], dict[str, Any] | li
error_messages: dict[str, StrOrPromise] = ...,
validators: Sequence[Validator[Any]] | None = ...,
allow_null: bool = ...,
*,
binary: bool = ...,
encoder: type[JSONEncoder] | None = ...,
decoder: type[JSONDecoder] | None = ...,
Expand Down
11 changes: 8 additions & 3 deletions rest_framework-stubs/relations.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class RelatedField(Generic[_MT, _DT, _PT], Field[_MT, _DT, _PT, Any]):
html_cutoff_text: str | None
def __init__(
self,
*,
many: bool = ...,
allow_empty: bool = ...,
queryset: QuerySet[_MT] | Manager[_MT] | None = ...,
Expand Down Expand Up @@ -76,6 +77,7 @@ class PrimaryKeyRelatedField(RelatedField[_MT, _MT, Any]):
pk_field: str | None
def __init__(
self,
*,
many: bool = ...,
allow_empty: bool = ...,
queryset: QuerySet[_MT] | Manager[_MT] | None = ...,
Expand Down Expand Up @@ -104,6 +106,8 @@ class HyperlinkedRelatedField(RelatedField[_MT, str, Hyperlink]):
view_name: str | None
def __init__(
self,
view_name: str,
*,
many: bool = ...,
allow_empty: bool = ...,
queryset: QuerySet[_MT] | Manager[_MT] | None = ...,
Expand All @@ -121,7 +125,6 @@ class HyperlinkedRelatedField(RelatedField[_MT, str, Hyperlink]):
validators: Sequence[Validator[_MT]] | None = ...,
error_messages: dict[str, StrOrPromise] | None = ...,
style: dict[str, str] | None = ...,
view_name: str | None = ...,
lookup_field: str | None = ...,
lookup_url_kwarg: str | None = ...,
format: str | None = ...,
Expand All @@ -135,6 +138,8 @@ class SlugRelatedField(RelatedField[_MT, str, str]):
slug_field: str | None
def __init__(
self,
slug_field: str,
*,
many: bool = ...,
allow_empty: bool = ...,
queryset: QuerySet[_MT] | Manager[_MT] | None = ...,
Expand All @@ -152,7 +157,6 @@ class SlugRelatedField(RelatedField[_MT, str, str]):
validators: Sequence[Validator[_MT]] | None = ...,
error_messages: dict[str, StrOrPromise] | None = ...,
style: dict[str, str] | None = ...,
slug_field: str | None = ...,
) -> None: ...
def to_internal_value(self, data: Any) -> _MT: ...
def to_representation(self, value: _MT) -> str: ...
Expand All @@ -165,6 +169,8 @@ class ManyRelatedField(Field[Sequence[Any], Sequence[Any], list[Any], Any]):
allow_empty: bool
def __init__(
self,
child_relation: RelatedField = ...,
*,
read_only: bool = ...,
write_only: bool = ...,
required: bool = ...,
Expand All @@ -178,7 +184,6 @@ class ManyRelatedField(Field[Sequence[Any], Sequence[Any], list[Any], Any]):
validators: Sequence[Validator[Sequence[Any]]] | None = ...,
allow_null: bool = ...,
allow_empty: bool = ...,
child_relation: RelatedField = ...,
) -> None: ...
def get_value(self, dictionary: Mapping[Any, Any]) -> list[Any]: ...
def get_choices(self, cutoff: int | None = ...) -> dict: ...
Expand Down
2 changes: 2 additions & 0 deletions rest_framework-stubs/serializers.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ class BaseSerializer(Generic[_IN], Field[Any, Any, Any, _IN]):
self,
instance: _IN | None = ...,
data: Any = ...,
*,
partial: bool = ...,
many: bool = ...,
allow_empty: bool = ...,
Expand Down Expand Up @@ -203,6 +204,7 @@ class ModelSerializer(Serializer, BaseSerializer[_MT]):
self,
instance: None | _MT | Sequence[_MT] | QuerySet[_MT] | Manager[_MT] = ...,
data: Any = ...,
*,
partial: bool = ...,
many: bool = ...,
context: dict[str, Any] = ...,
Expand Down
10 changes: 10 additions & 0 deletions scripts/stubtest/allowlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,13 @@
#
# Please, move things here when you are sure that they really should be ignored.
# Comments about why things are ignored are mandatory.

# Constructor arguments *appear* optional but actually throw exception
rest_framework.relations.HyperlinkedIdentityField.__init__
rest_framework.relations.HyperlinkedRelatedField.__init__
rest_framework.relations.ManyRelatedField.__init__
rest_framework.relations.SlugRelatedField.__init__
rest_framework.serializers.HyperlinkedIdentityField.__init__
rest_framework.serializers.HyperlinkedRelatedField.__init__
rest_framework.serializers.ManyRelatedField.__init__
rest_framework.serializers.SlugRelatedField.__init__
31 changes: 0 additions & 31 deletions scripts/stubtest/allowlist_todo.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,28 +46,20 @@ rest_framework.fields.DateField.to_internal_value
rest_framework.fields.DateTimeField.__init__
rest_framework.fields.DateTimeField.to_internal_value
rest_framework.fields.DecimalField.__init__
rest_framework.fields.DictField.__init__
rest_framework.fields.DictField.initial
rest_framework.fields.DurationField.to_internal_value
rest_framework.fields.Field.__init__
rest_framework.fields.FilePathField.__init__
Comment on lines 51 to 52
Copy link
Contributor Author

@intgr intgr Oct 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: some XxxField.__init__ methods remain because our argument types aren't precise. But I'd rather fix that separately.

rest_framework.fields.HStoreField.__init__
rest_framework.fields.HiddenField.__init__
rest_framework.fields.JSONField.__init__
rest_framework.fields.ListField.__init__
rest_framework.fields.ListField.initial
rest_framework.fields.ListField.to_representation
rest_framework.fields.ModelField.get_attribute
rest_framework.fields.ModelField.to_representation
rest_framework.fields.MultipleChoiceField.__init__
rest_framework.fields.NullBooleanField
rest_framework.fields.Option
rest_framework.fields.REGEX_TYPE
rest_framework.fields.ReadOnlyField.__init__
rest_framework.fields.SupportsToPython
rest_framework.fields.TimeField.__init__
rest_framework.fields.TimeField.to_internal_value
rest_framework.fields._UnvalidatedField.__init__
rest_framework.fields.empty
rest_framework.generics.BaseFilterProtocol
rest_framework.generics.UsesQuerySet
Expand All @@ -76,17 +68,10 @@ rest_framework.pagination.HtmlContext
rest_framework.pagination.HtmlContextWithPageLinks
rest_framework.parsers.BaseParser.media_type
rest_framework.parsers.FileUploadParser.get_encoded_filename
rest_framework.relations.HyperlinkedIdentityField.__init__
rest_framework.relations.HyperlinkedRelatedField.__init__
rest_framework.relations.HyperlinkedRelatedField.get_object
rest_framework.relations.ManyRelatedField.__init__
rest_framework.relations.ManyRelatedField.initial
rest_framework.relations.ManyRelatedField.to_representation
rest_framework.relations.PrimaryKeyRelatedField.__init__
rest_framework.relations.RelatedField.__init__
rest_framework.relations.SlugRelatedField.__init__
rest_framework.relations.SlugRelatedField.to_representation
rest_framework.relations.StringRelatedField.__init__
rest_framework.renderers.BaseRenderer.format
rest_framework.renderers.BaseRenderer.media_type
rest_framework.renderers.BrowsableAPIRenderer.get_extra_actions
Expand Down Expand Up @@ -124,7 +109,6 @@ rest_framework.schemas.views.SchemaView.get
rest_framework.schemas.views.SchemaView.renderer_classes
rest_framework.serializers.APIException
rest_framework.serializers.AuthenticationFailed
rest_framework.serializers.BaseSerializer.__init__
rest_framework.serializers.BaseSerializer.is_valid
rest_framework.serializers.BooleanField.initial
rest_framework.serializers.CharField.initial
Expand All @@ -133,43 +117,28 @@ rest_framework.serializers.DateField.to_internal_value
rest_framework.serializers.DateTimeField.__init__
rest_framework.serializers.DateTimeField.to_internal_value
rest_framework.serializers.DecimalField.__init__
rest_framework.serializers.DictField.__init__
rest_framework.serializers.DictField.initial
rest_framework.serializers.DurationField.to_internal_value
rest_framework.serializers.Field.__init__
rest_framework.serializers.FilePathField.__init__
rest_framework.serializers.HStoreField.__init__
rest_framework.serializers.HiddenField.__init__
rest_framework.serializers.HyperlinkedIdentityField.__init__
rest_framework.serializers.HyperlinkedRelatedField.__init__
rest_framework.serializers.HyperlinkedRelatedField.get_object
rest_framework.serializers.JSONField.__init__
rest_framework.serializers.ListField.__init__
rest_framework.serializers.ListField.initial
rest_framework.serializers.ListField.to_representation
rest_framework.serializers.ListSerializer.is_valid
rest_framework.serializers.ListSerializer.to_representation
rest_framework.serializers.ManyRelatedField.__init__
rest_framework.serializers.ManyRelatedField.initial
rest_framework.serializers.ManyRelatedField.to_representation
rest_framework.serializers.MethodNotAllowed
rest_framework.serializers.ModelField.get_attribute
rest_framework.serializers.ModelField.to_representation
rest_framework.serializers.ModelSerializer.Meta
rest_framework.serializers.ModelSerializer.__init__
rest_framework.serializers.MultipleChoiceField.__init__
rest_framework.serializers.NotAcceptable
rest_framework.serializers.NotAuthenticated
rest_framework.serializers.NotFound
rest_framework.serializers.NullBooleanField
rest_framework.serializers.ParseError
rest_framework.serializers.PermissionDenied
rest_framework.serializers.PrimaryKeyRelatedField.__init__
rest_framework.serializers.ReadOnlyField.__init__
rest_framework.serializers.RelatedField.__init__
rest_framework.serializers.SlugRelatedField.__init__
rest_framework.serializers.SlugRelatedField.to_representation
rest_framework.serializers.StringRelatedField.__init__
rest_framework.serializers.Throttled
rest_framework.serializers.TimeField.__init__
rest_framework.serializers.TimeField.to_internal_value
Expand Down
51 changes: 15 additions & 36 deletions tests/typecheck/test_fields.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@

- case: some_positional_args_fields
main: |
from datetime import datetime, time
from django.db import models
from rest_framework.fields import DecimalField, IPAddressField, SlugField, RegexField, ModelField, SerializerMethodField, ChoiceField
from rest_framework.fields import DecimalField, IPAddressField, SlugField, RegexField, ModelField, SerializerMethodField, ChoiceField, DateTimeField, DateField, TimeField

DecimalField(1, 1, False, 1, 1, False, None, True)
DecimalField(1, 1, False, 1, 1, False, None, True, True) # E: Too many positional arguments for "DecimalField"
DecimalField(1, 1, False, 1, 1, False, None)
DecimalField(1, 1, False, 1, 1, False, None, True) # E: Too many positional arguments for "DecimalField"

IPAddressField('both')
IPAddressField('both', True) # E: Too many positional arguments for "IPAddressField"
Expand All @@ -34,37 +35,15 @@
ChoiceField([])
ChoiceField([], False) # E: Too many positional arguments for "ChoiceField"

- case: most_positional_args_fields
main: |
from rest_framework.fields import Field, ListField, DictField, JSONField
f: Field = Field()

ListField(True, True, True, [{"key": "value"}], [{"key": "value"}], 'src', 'l', 'ht', {"key": "value"}, {"key": "value"}, [lambda x: None], True)
ListField(True, True, True, [{"key": "value"}], [{"key": "value"}], 'src', 'l', 'ht', {"key": "value"}, {"key": "value"}, [lambda x: None], True, f) # E: Too many positional arguments for "ListField"
ListField(True, True, True, [{"key": "value"}], [{"key": "value"}], 'src', 'l', 'ht', {"key": "value"}, {"key": "value"}, [lambda x: None], True, child=f, allow_empty=True, max_length=1, min_length=1)

DictField(True, True, True, {}, {}, 'src', 'l', 'ht', {}, {}, [], True)
DictField(True, True, True, {}, {}, 'src', 'l', 'ht', {}, {}, [], True, f) # E: Too many positional arguments for "DictField"
DictField(True, True, True, {}, {}, 'src', 'l', 'ht', {}, {}, [], True, child=f, allow_empty=True)

JSONField(True, True, True, {}, {}, 'src', 'l', 'ht', {}, {}, [], True)
JSONField(True, True, True, {}, {}, 'src', 'l', 'ht', {}, {}, [], True, True) # E: Too many positional arguments for "JSONField"
JSONField(True, True, True, {}, {}, 'src', 'l', 'ht', {}, {}, [], True, binary=True, encoder=None, decoder=None)

- case: all_positional_args_fields
main: |
from datetime import datetime, time
from rest_framework.fields import DateTimeField, DateField, TimeField

d: datetime = datetime.now()
DateTimeField('', [], None, True, True, True, d, d, 'src', 'l', 'ht', {}, {}, [], True)
DateTimeField('', [], None, True, True, True, d, d, 'src', 'l', 'ht', {}, {}, [], True, 1) # E: Too many arguments for "DateTimeField"
DateTimeField('', [], None, read_only=True, write_only=True, allow_null=True)
DateTimeField('', [], None, True) # E: Too many positional arguments for "DateTimeField"

DateField('', [], True, True, True, d, d, 'src', 'l', 'ht', {}, {}, [], True)
DateField('', [], True, True, True, d, d, 'src', 'l', 'ht', {}, {}, [], True, 1) # E: Too many arguments for "DateField"
DateField('', [], read_only=True, write_only=True, allow_null=True)
DateField('', [], True) # E: Too many positional arguments for "DateField"

TimeField('', [], True, True, True, time(hour=1), time(hour=1), 'src', 'l', 'ht', {}, {}, [], True)
TimeField('', [], True, True, True, time(hour=1), time(hour=1), 'src', 'l', 'ht', {}, {}, [], True, 1) # E: Too many arguments for "TimeField"
TimeField('', [], read_only=True, write_only=True, allow_null=True)
TimeField('', [], True) # E: Too many positional arguments for "TimeField"

- case: default_and_inital_args_fields
main: |
Expand Down Expand Up @@ -107,11 +86,11 @@
def int_set_callback() -> Set[int]: ...
def mixed_set_callback() -> Set[Union[int, str]]: ...

MultipleChoiceField([1], default={1})
MultipleChoiceField(['test'], allow_null=True, default=None)
MultipleChoiceField([1], default=int_set_callback)
MultipleChoiceField([1, 'lulz'], default=mixed_set_callback)
MultipleChoiceField([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]]], None, _Empty]" # E: Incompatible return value type (got "List[int]", expected "Union[Set[Union[str, int]], Set[str], Set[int]]")
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]]], None, _Empty]" # E: Incompatible return value type (got "List[int]", expected "Union[Set[Union[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]]], None, _Empty]"
Expand Down