From a094f59b2bbedea4048d16b4803e68d17b58b632 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Tue, 10 Nov 2020 10:16:11 +0200 Subject: [PATCH] Make Field constructors keyword-only --- rest_framework/fields.py | 46 ++++++++++++++++++++-------------------- tests/test_fields.py | 5 +++++ 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index e4be54751d..1434116d82 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -320,7 +320,7 @@ class Field: default_empty_html = empty initial = None - def __init__(self, read_only=False, write_only=False, + def __init__(self, *, read_only=False, write_only=False, required=None, default=empty, initial=empty, source=None, label=None, help_text=None, style=None, error_messages=None, validators=None, allow_null=False): @@ -1161,14 +1161,14 @@ class DateTimeField(Field): } datetime_parser = datetime.datetime.strptime - def __init__(self, format=empty, input_formats=None, default_timezone=None, *args, **kwargs): + def __init__(self, format=empty, input_formats=None, default_timezone=None, **kwargs): if format is not empty: self.format = format if input_formats is not None: self.input_formats = input_formats if default_timezone is not None: self.timezone = default_timezone - super().__init__(*args, **kwargs) + super().__init__(**kwargs) def enforce_timezone(self, value): """ @@ -1247,12 +1247,12 @@ class DateField(Field): } datetime_parser = datetime.datetime.strptime - def __init__(self, format=empty, input_formats=None, *args, **kwargs): + def __init__(self, format=empty, input_formats=None, **kwargs): if format is not empty: self.format = format if input_formats is not None: self.input_formats = input_formats - super().__init__(*args, **kwargs) + super().__init__(**kwargs) def to_internal_value(self, value): input_formats = getattr(self, 'input_formats', api_settings.DATE_INPUT_FORMATS) @@ -1313,12 +1313,12 @@ class TimeField(Field): } datetime_parser = datetime.datetime.strptime - def __init__(self, format=empty, input_formats=None, *args, **kwargs): + def __init__(self, format=empty, input_formats=None, **kwargs): if format is not empty: self.format = format if input_formats is not None: self.input_formats = input_formats - super().__init__(*args, **kwargs) + super().__init__(**kwargs) def to_internal_value(self, value): input_formats = getattr(self, 'input_formats', api_settings.TIME_INPUT_FORMATS) @@ -1468,9 +1468,9 @@ class MultipleChoiceField(ChoiceField): } default_empty_html = [] - def __init__(self, *args, **kwargs): + def __init__(self, **kwargs): self.allow_empty = kwargs.pop('allow_empty', True) - super().__init__(*args, **kwargs) + super().__init__(**kwargs) def get_value(self, dictionary): if self.field_name not in dictionary: @@ -1527,12 +1527,12 @@ class FileField(Field): 'max_length': _('Ensure this filename has at most {max_length} characters (it has {length}).'), } - def __init__(self, *args, **kwargs): + def __init__(self, **kwargs): self.max_length = kwargs.pop('max_length', None) self.allow_empty_file = kwargs.pop('allow_empty_file', False) if 'use_url' in kwargs: self.use_url = kwargs.pop('use_url') - super().__init__(*args, **kwargs) + super().__init__(**kwargs) def to_internal_value(self, data): try: @@ -1576,9 +1576,9 @@ class ImageField(FileField): ), } - def __init__(self, *args, **kwargs): + def __init__(self, **kwargs): self._DjangoImageField = kwargs.pop('_DjangoImageField', DjangoImageField) - super().__init__(*args, **kwargs) + super().__init__(**kwargs) def to_internal_value(self, data): # Image validation is a bit grungy, so we'll just outright @@ -1593,8 +1593,8 @@ def to_internal_value(self, data): # Composite field types... class _UnvalidatedField(Field): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + def __init__(self, **kwargs): + super().__init__(**kwargs) self.allow_blank = True self.allow_null = True @@ -1615,7 +1615,7 @@ class ListField(Field): 'max_length': _('Ensure this field has no more than {max_length} elements.') } - def __init__(self, *args, **kwargs): + def __init__(self, **kwargs): self.child = kwargs.pop('child', copy.deepcopy(self.child)) self.allow_empty = kwargs.pop('allow_empty', True) self.max_length = kwargs.pop('max_length', None) @@ -1627,7 +1627,7 @@ def __init__(self, *args, **kwargs): "Remove `source=` from the field declaration." ) - super().__init__(*args, **kwargs) + super().__init__(**kwargs) self.child.bind(field_name='', parent=self) if self.max_length is not None: message = lazy_format(self.error_messages['max_length'], max_length=self.max_length) @@ -1692,7 +1692,7 @@ class DictField(Field): 'empty': _('This dictionary may not be empty.'), } - def __init__(self, *args, **kwargs): + def __init__(self, **kwargs): self.child = kwargs.pop('child', copy.deepcopy(self.child)) self.allow_empty = kwargs.pop('allow_empty', True) @@ -1702,7 +1702,7 @@ def __init__(self, *args, **kwargs): "Remove `source=` from the field declaration." ) - super().__init__(*args, **kwargs) + super().__init__(**kwargs) self.child.bind(field_name='', parent=self) def get_value(self, dictionary): @@ -1751,8 +1751,8 @@ def run_child_validation(self, data): class HStoreField(DictField): child = CharField(allow_blank=True, allow_null=True) - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + def __init__(self, **kwargs): + super().__init__(**kwargs) assert isinstance(self.child, CharField), ( "The `child` argument must be an instance of `CharField`, " "as the hstore extension stores values as strings." @@ -1767,11 +1767,11 @@ class JSONField(Field): # Workaround for isinstance calls when importing the field isn't possible _is_jsonfield = True - def __init__(self, *args, **kwargs): + def __init__(self, **kwargs): self.binary = kwargs.pop('binary', False) self.encoder = kwargs.pop('encoder', None) self.decoder = kwargs.pop('decoder', None) - super().__init__(*args, **kwargs) + super().__init__(**kwargs) def get_value(self, dictionary): if html.is_html_input(dictionary) and self.field_name in dictionary: diff --git a/tests/test_fields.py b/tests/test_fields.py index 78a9effb8c..dd4b7904a3 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1986,6 +1986,11 @@ def test_collection_types_are_invalid_input(self): field.to_internal_value(input_value) assert exc_info.value.detail == ['Expected a list of items but got type "dict".'] + def test_constructor_misuse_raises(self): + # Test that `ListField` can only be instantiated with keyword arguments + with pytest.raises(TypeError): + serializers.ListField(serializers.CharField()) + class TestNestedListField(FieldValues): """ pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy