Skip to content

Commit

Permalink
Update Field constructor kwargs-only parameters (#491)
Browse files Browse the repository at this point in the history
The number of allowed positional parameters of Field class constructors was significantly reduced by DRF upstream, but some remain regardless.

Also correctly reordered arguments where positional args are allowed.
  • Loading branch information
intgr authored Oct 18, 2023
1 parent 0ce366f commit 133e572
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 74 deletions.
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__
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

0 comments on commit 133e572

Please sign in to comment.