Skip to content

Commit 133e572

Browse files
authored
Update Field constructor kwargs-only parameters (#491)
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.
1 parent 0ce366f commit 133e572

File tree

6 files changed

+44
-74
lines changed

6 files changed

+44
-74
lines changed

rest_framework-stubs/fields.pyi

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ class Field(Generic[_VT, _DT, _RP, _IN]):
8989
write_only: bool
9090
def __init__(
9191
self,
92+
*,
9293
read_only: bool = ...,
9394
write_only: bool = ...,
9495
required: bool = ...,
@@ -339,7 +340,6 @@ class DecimalField(Field[Decimal, int | float | str | Decimal, str, Any]):
339340
min_value: Decimal | int | float = ...,
340341
localize: bool = ...,
341342
rounding: str | None = ...,
342-
normalize_output: bool = ...,
343343
*,
344344
read_only: bool = ...,
345345
write_only: bool = ...,
@@ -367,6 +367,7 @@ class DateTimeField(Field[datetime.datetime, datetime.datetime | str, str, Any])
367367
format: str | None = ...,
368368
input_formats: Sequence[str] = ...,
369369
default_timezone: datetime.tzinfo | None = ...,
370+
*,
370371
read_only: bool = ...,
371372
write_only: bool = ...,
372373
required: bool = ...,
@@ -391,6 +392,7 @@ class DateField(Field[datetime.date, datetime.date | str, str, Any]):
391392
self,
392393
format: str | None = ...,
393394
input_formats: Sequence[str] = ...,
395+
*,
394396
read_only: bool = ...,
395397
write_only: bool = ...,
396398
required: bool = ...,
@@ -413,6 +415,7 @@ class TimeField(Field[datetime.time, datetime.time | str, str, Any]):
413415
self,
414416
format: str | None = ...,
415417
input_formats: Sequence[str] = ...,
418+
*,
416419
read_only: bool = ...,
417420
write_only: bool = ...,
418421
required: bool = ...,
@@ -493,6 +496,7 @@ class MultipleChoiceField(
493496
allow_empty: bool
494497
def __init__(
495498
self,
499+
*,
496500
choices: Iterable[Any],
497501
read_only: bool = ...,
498502
write_only: bool = ...,
@@ -521,6 +525,7 @@ class FilePathField(ChoiceField):
521525
allow_files: bool = ...,
522526
allow_folders: bool = ...,
523527
required: bool = ...,
528+
*,
524529
read_only: bool = ...,
525530
write_only: bool = ...,
526531
default: _DefaultInitial[str] = ...,
@@ -593,6 +598,7 @@ class ListField(Field[list[Any], list[Any], list[Any], Any]):
593598
min_length: int | None
594599
def __init__(
595600
self,
601+
*,
596602
read_only: bool = ...,
597603
write_only: bool = ...,
598604
required: bool = ...,
@@ -605,7 +611,6 @@ class ListField(Field[list[Any], list[Any], list[Any], Any]):
605611
error_messages: dict[str, StrOrPromise] = ...,
606612
validators: Sequence[Validator[list[Any]]] | None = ...,
607613
allow_null: bool = ...,
608-
*,
609614
child: Field = ...,
610615
allow_empty: bool = ...,
611616
max_length: int = ...,
@@ -618,6 +623,7 @@ class DictField(Field[dict[Any, Any], dict[Any, Any], dict[Any, Any], Any]):
618623
allow_empty: bool
619624
def __init__(
620625
self,
626+
*,
621627
read_only: bool = ...,
622628
write_only: bool = ...,
623629
required: bool = ...,
@@ -630,7 +636,6 @@ class DictField(Field[dict[Any, Any], dict[Any, Any], dict[Any, Any], Any]):
630636
error_messages: dict[str, StrOrPromise] = ...,
631637
validators: Sequence[Validator[dict[Any, Any]]] | None = ...,
632638
allow_null: bool = ...,
633-
*,
634639
child: Field = ...,
635640
allow_empty: bool = ...,
636641
) -> None: ...
@@ -645,6 +650,7 @@ class JSONField(Field[dict[str, Any] | list[dict[str, Any]], dict[str, Any] | li
645650
decoder: type[JSONDecoder] | None
646651
def __init__(
647652
self,
653+
*,
648654
read_only: bool = ...,
649655
write_only: bool = ...,
650656
required: bool = ...,
@@ -657,7 +663,6 @@ class JSONField(Field[dict[str, Any] | list[dict[str, Any]], dict[str, Any] | li
657663
error_messages: dict[str, StrOrPromise] = ...,
658664
validators: Sequence[Validator[Any]] | None = ...,
659665
allow_null: bool = ...,
660-
*,
661666
binary: bool = ...,
662667
encoder: type[JSONEncoder] | None = ...,
663668
decoder: type[JSONDecoder] | None = ...,

rest_framework-stubs/relations.pyi

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class RelatedField(Generic[_MT, _DT, _PT], Field[_MT, _DT, _PT, Any]):
3737
html_cutoff_text: str | None
3838
def __init__(
3939
self,
40+
*,
4041
many: bool = ...,
4142
allow_empty: bool = ...,
4243
queryset: QuerySet[_MT] | Manager[_MT] | None = ...,
@@ -76,6 +77,7 @@ class PrimaryKeyRelatedField(RelatedField[_MT, _MT, Any]):
7677
pk_field: str | None
7778
def __init__(
7879
self,
80+
*,
7981
many: bool = ...,
8082
allow_empty: bool = ...,
8183
queryset: QuerySet[_MT] | Manager[_MT] | None = ...,
@@ -104,6 +106,8 @@ class HyperlinkedRelatedField(RelatedField[_MT, str, Hyperlink]):
104106
view_name: str | None
105107
def __init__(
106108
self,
109+
view_name: str,
110+
*,
107111
many: bool = ...,
108112
allow_empty: bool = ...,
109113
queryset: QuerySet[_MT] | Manager[_MT] | None = ...,
@@ -121,7 +125,6 @@ class HyperlinkedRelatedField(RelatedField[_MT, str, Hyperlink]):
121125
validators: Sequence[Validator[_MT]] | None = ...,
122126
error_messages: dict[str, StrOrPromise] | None = ...,
123127
style: dict[str, str] | None = ...,
124-
view_name: str | None = ...,
125128
lookup_field: str | None = ...,
126129
lookup_url_kwarg: str | None = ...,
127130
format: str | None = ...,
@@ -135,6 +138,8 @@ class SlugRelatedField(RelatedField[_MT, str, str]):
135138
slug_field: str | None
136139
def __init__(
137140
self,
141+
slug_field: str,
142+
*,
138143
many: bool = ...,
139144
allow_empty: bool = ...,
140145
queryset: QuerySet[_MT] | Manager[_MT] | None = ...,
@@ -152,7 +157,6 @@ class SlugRelatedField(RelatedField[_MT, str, str]):
152157
validators: Sequence[Validator[_MT]] | None = ...,
153158
error_messages: dict[str, StrOrPromise] | None = ...,
154159
style: dict[str, str] | None = ...,
155-
slug_field: str | None = ...,
156160
) -> None: ...
157161
def to_internal_value(self, data: Any) -> _MT: ...
158162
def to_representation(self, value: _MT) -> str: ...
@@ -165,6 +169,8 @@ class ManyRelatedField(Field[Sequence[Any], Sequence[Any], list[Any], Any]):
165169
allow_empty: bool
166170
def __init__(
167171
self,
172+
child_relation: RelatedField = ...,
173+
*,
168174
read_only: bool = ...,
169175
write_only: bool = ...,
170176
required: bool = ...,
@@ -178,7 +184,6 @@ class ManyRelatedField(Field[Sequence[Any], Sequence[Any], list[Any], Any]):
178184
validators: Sequence[Validator[Sequence[Any]]] | None = ...,
179185
allow_null: bool = ...,
180186
allow_empty: bool = ...,
181-
child_relation: RelatedField = ...,
182187
) -> None: ...
183188
def get_value(self, dictionary: Mapping[Any, Any]) -> list[Any]: ...
184189
def get_choices(self, cutoff: int | None = ...) -> dict: ...

rest_framework-stubs/serializers.pyi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ class BaseSerializer(Generic[_IN], Field[Any, Any, Any, _IN]):
8383
self,
8484
instance: _IN | None = ...,
8585
data: Any = ...,
86+
*,
8687
partial: bool = ...,
8788
many: bool = ...,
8889
allow_empty: bool = ...,
@@ -203,6 +204,7 @@ class ModelSerializer(Serializer, BaseSerializer[_MT]):
203204
self,
204205
instance: None | _MT | Sequence[_MT] | QuerySet[_MT] | Manager[_MT] = ...,
205206
data: Any = ...,
207+
*,
206208
partial: bool = ...,
207209
many: bool = ...,
208210
context: dict[str, Any] = ...,

scripts/stubtest/allowlist.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,13 @@
44
#
55
# Please, move things here when you are sure that they really should be ignored.
66
# Comments about why things are ignored are mandatory.
7+
8+
# Constructor arguments *appear* optional but actually throw exception
9+
rest_framework.relations.HyperlinkedIdentityField.__init__
10+
rest_framework.relations.HyperlinkedRelatedField.__init__
11+
rest_framework.relations.ManyRelatedField.__init__
12+
rest_framework.relations.SlugRelatedField.__init__
13+
rest_framework.serializers.HyperlinkedIdentityField.__init__
14+
rest_framework.serializers.HyperlinkedRelatedField.__init__
15+
rest_framework.serializers.ManyRelatedField.__init__
16+
rest_framework.serializers.SlugRelatedField.__init__

scripts/stubtest/allowlist_todo.txt

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -46,28 +46,20 @@ rest_framework.fields.DateField.to_internal_value
4646
rest_framework.fields.DateTimeField.__init__
4747
rest_framework.fields.DateTimeField.to_internal_value
4848
rest_framework.fields.DecimalField.__init__
49-
rest_framework.fields.DictField.__init__
5049
rest_framework.fields.DictField.initial
5150
rest_framework.fields.DurationField.to_internal_value
5251
rest_framework.fields.Field.__init__
5352
rest_framework.fields.FilePathField.__init__
54-
rest_framework.fields.HStoreField.__init__
55-
rest_framework.fields.HiddenField.__init__
56-
rest_framework.fields.JSONField.__init__
57-
rest_framework.fields.ListField.__init__
5853
rest_framework.fields.ListField.initial
5954
rest_framework.fields.ListField.to_representation
6055
rest_framework.fields.ModelField.get_attribute
6156
rest_framework.fields.ModelField.to_representation
62-
rest_framework.fields.MultipleChoiceField.__init__
6357
rest_framework.fields.NullBooleanField
6458
rest_framework.fields.Option
6559
rest_framework.fields.REGEX_TYPE
66-
rest_framework.fields.ReadOnlyField.__init__
6760
rest_framework.fields.SupportsToPython
6861
rest_framework.fields.TimeField.__init__
6962
rest_framework.fields.TimeField.to_internal_value
70-
rest_framework.fields._UnvalidatedField.__init__
7163
rest_framework.fields.empty
7264
rest_framework.generics.BaseFilterProtocol
7365
rest_framework.generics.UsesQuerySet
@@ -76,17 +68,10 @@ rest_framework.pagination.HtmlContext
7668
rest_framework.pagination.HtmlContextWithPageLinks
7769
rest_framework.parsers.BaseParser.media_type
7870
rest_framework.parsers.FileUploadParser.get_encoded_filename
79-
rest_framework.relations.HyperlinkedIdentityField.__init__
80-
rest_framework.relations.HyperlinkedRelatedField.__init__
8171
rest_framework.relations.HyperlinkedRelatedField.get_object
82-
rest_framework.relations.ManyRelatedField.__init__
8372
rest_framework.relations.ManyRelatedField.initial
8473
rest_framework.relations.ManyRelatedField.to_representation
85-
rest_framework.relations.PrimaryKeyRelatedField.__init__
86-
rest_framework.relations.RelatedField.__init__
87-
rest_framework.relations.SlugRelatedField.__init__
8874
rest_framework.relations.SlugRelatedField.to_representation
89-
rest_framework.relations.StringRelatedField.__init__
9075
rest_framework.renderers.BaseRenderer.format
9176
rest_framework.renderers.BaseRenderer.media_type
9277
rest_framework.renderers.BrowsableAPIRenderer.get_extra_actions
@@ -124,7 +109,6 @@ rest_framework.schemas.views.SchemaView.get
124109
rest_framework.schemas.views.SchemaView.renderer_classes
125110
rest_framework.serializers.APIException
126111
rest_framework.serializers.AuthenticationFailed
127-
rest_framework.serializers.BaseSerializer.__init__
128112
rest_framework.serializers.BaseSerializer.is_valid
129113
rest_framework.serializers.BooleanField.initial
130114
rest_framework.serializers.CharField.initial
@@ -133,43 +117,28 @@ rest_framework.serializers.DateField.to_internal_value
133117
rest_framework.serializers.DateTimeField.__init__
134118
rest_framework.serializers.DateTimeField.to_internal_value
135119
rest_framework.serializers.DecimalField.__init__
136-
rest_framework.serializers.DictField.__init__
137120
rest_framework.serializers.DictField.initial
138121
rest_framework.serializers.DurationField.to_internal_value
139122
rest_framework.serializers.Field.__init__
140123
rest_framework.serializers.FilePathField.__init__
141-
rest_framework.serializers.HStoreField.__init__
142-
rest_framework.serializers.HiddenField.__init__
143-
rest_framework.serializers.HyperlinkedIdentityField.__init__
144-
rest_framework.serializers.HyperlinkedRelatedField.__init__
145124
rest_framework.serializers.HyperlinkedRelatedField.get_object
146-
rest_framework.serializers.JSONField.__init__
147-
rest_framework.serializers.ListField.__init__
148125
rest_framework.serializers.ListField.initial
149126
rest_framework.serializers.ListField.to_representation
150127
rest_framework.serializers.ListSerializer.is_valid
151128
rest_framework.serializers.ListSerializer.to_representation
152-
rest_framework.serializers.ManyRelatedField.__init__
153129
rest_framework.serializers.ManyRelatedField.initial
154130
rest_framework.serializers.ManyRelatedField.to_representation
155131
rest_framework.serializers.MethodNotAllowed
156132
rest_framework.serializers.ModelField.get_attribute
157133
rest_framework.serializers.ModelField.to_representation
158134
rest_framework.serializers.ModelSerializer.Meta
159-
rest_framework.serializers.ModelSerializer.__init__
160-
rest_framework.serializers.MultipleChoiceField.__init__
161135
rest_framework.serializers.NotAcceptable
162136
rest_framework.serializers.NotAuthenticated
163137
rest_framework.serializers.NotFound
164138
rest_framework.serializers.NullBooleanField
165139
rest_framework.serializers.ParseError
166140
rest_framework.serializers.PermissionDenied
167-
rest_framework.serializers.PrimaryKeyRelatedField.__init__
168-
rest_framework.serializers.ReadOnlyField.__init__
169-
rest_framework.serializers.RelatedField.__init__
170-
rest_framework.serializers.SlugRelatedField.__init__
171141
rest_framework.serializers.SlugRelatedField.to_representation
172-
rest_framework.serializers.StringRelatedField.__init__
173142
rest_framework.serializers.Throttled
174143
rest_framework.serializers.TimeField.__init__
175144
rest_framework.serializers.TimeField.to_internal_value

tests/typecheck/test_fields.yml

Lines changed: 15 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@
99
1010
- case: some_positional_args_fields
1111
main: |
12+
from datetime import datetime, time
1213
from django.db import models
13-
from rest_framework.fields import DecimalField, IPAddressField, SlugField, RegexField, ModelField, SerializerMethodField, ChoiceField
14+
from rest_framework.fields import DecimalField, IPAddressField, SlugField, RegexField, ModelField, SerializerMethodField, ChoiceField, DateTimeField, DateField, TimeField
1415
15-
DecimalField(1, 1, False, 1, 1, False, None, True)
16-
DecimalField(1, 1, False, 1, 1, False, None, True, True) # E: Too many positional arguments for "DecimalField"
16+
DecimalField(1, 1, False, 1, 1, False, None)
17+
DecimalField(1, 1, False, 1, 1, False, None, True) # E: Too many positional arguments for "DecimalField"
1718
1819
IPAddressField('both')
1920
IPAddressField('both', True) # E: Too many positional arguments for "IPAddressField"
@@ -34,37 +35,15 @@
3435
ChoiceField([])
3536
ChoiceField([], False) # E: Too many positional arguments for "ChoiceField"
3637
37-
- case: most_positional_args_fields
38-
main: |
39-
from rest_framework.fields import Field, ListField, DictField, JSONField
40-
f: Field = Field()
41-
42-
ListField(True, True, True, [{"key": "value"}], [{"key": "value"}], 'src', 'l', 'ht', {"key": "value"}, {"key": "value"}, [lambda x: None], True)
43-
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"
44-
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)
45-
46-
DictField(True, True, True, {}, {}, 'src', 'l', 'ht', {}, {}, [], True)
47-
DictField(True, True, True, {}, {}, 'src', 'l', 'ht', {}, {}, [], True, f) # E: Too many positional arguments for "DictField"
48-
DictField(True, True, True, {}, {}, 'src', 'l', 'ht', {}, {}, [], True, child=f, allow_empty=True)
49-
50-
JSONField(True, True, True, {}, {}, 'src', 'l', 'ht', {}, {}, [], True)
51-
JSONField(True, True, True, {}, {}, 'src', 'l', 'ht', {}, {}, [], True, True) # E: Too many positional arguments for "JSONField"
52-
JSONField(True, True, True, {}, {}, 'src', 'l', 'ht', {}, {}, [], True, binary=True, encoder=None, decoder=None)
53-
54-
- case: all_positional_args_fields
55-
main: |
56-
from datetime import datetime, time
57-
from rest_framework.fields import DateTimeField, DateField, TimeField
58-
5938
d: datetime = datetime.now()
60-
DateTimeField('', [], None, True, True, True, d, d, 'src', 'l', 'ht', {}, {}, [], True)
61-
DateTimeField('', [], None, True, True, True, d, d, 'src', 'l', 'ht', {}, {}, [], True, 1) # E: Too many arguments for "DateTimeField"
39+
DateTimeField('', [], None, read_only=True, write_only=True, allow_null=True)
40+
DateTimeField('', [], None, True) # E: Too many positional arguments for "DateTimeField"
6241
63-
DateField('', [], True, True, True, d, d, 'src', 'l', 'ht', {}, {}, [], True)
64-
DateField('', [], True, True, True, d, d, 'src', 'l', 'ht', {}, {}, [], True, 1) # E: Too many arguments for "DateField"
42+
DateField('', [], read_only=True, write_only=True, allow_null=True)
43+
DateField('', [], True) # E: Too many positional arguments for "DateField"
6544
66-
TimeField('', [], True, True, True, time(hour=1), time(hour=1), 'src', 'l', 'ht', {}, {}, [], True)
67-
TimeField('', [], True, True, True, time(hour=1), time(hour=1), 'src', 'l', 'ht', {}, {}, [], True, 1) # E: Too many arguments for "TimeField"
45+
TimeField('', [], read_only=True, write_only=True, allow_null=True)
46+
TimeField('', [], True) # E: Too many positional arguments for "TimeField"
6847
6948
- case: default_and_inital_args_fields
7049
main: |
@@ -107,11 +86,11 @@
10786
def int_set_callback() -> Set[int]: ...
10887
def mixed_set_callback() -> Set[Union[int, str]]: ...
10988
110-
MultipleChoiceField([1], default={1})
111-
MultipleChoiceField(['test'], allow_null=True, default=None)
112-
MultipleChoiceField([1], default=int_set_callback)
113-
MultipleChoiceField([1, 'lulz'], default=mixed_set_callback)
114-
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]]")
89+
MultipleChoiceField(choices=[1], default={1})
90+
MultipleChoiceField(choices=['test'], allow_null=True, default=None)
91+
MultipleChoiceField(choices=[1], default=int_set_callback)
92+
MultipleChoiceField(choices=[1, 'lulz'], default=mixed_set_callback)
93+
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]]")
11594
11695
MultipleChoiceField(choices=[(1, "1"), (2, "2")], default={1})
11796
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]"

0 commit comments

Comments
 (0)