From 8838d4dd498c8f66ea4237fe8a79a5f77d6f95c9 Mon Sep 17 00:00:00 2001 From: Vytis Banaitis Date: Wed, 1 Feb 2017 18:41:56 +0200 Subject: [PATCH] Refs #23919 -- Replaced kwargs.pop() with keyword-only arguments. --- django/conf/urls/i18n.py | 4 +-- django/contrib/admin/decorators.py | 9 +++--- django/contrib/contenttypes/fields.py | 20 ++++--------- django/contrib/contenttypes/models.py | 3 +- django/contrib/gis/db/models/fields.py | 7 +++-- django/contrib/gis/forms/fields.py | 9 +++--- django/contrib/gis/geos/libgeos.py | 11 ++++--- django/contrib/postgres/search.py | 14 +++------ django/core/mail/backends/filebased.py | 6 ++-- django/core/serializers/base.py | 15 +++++----- django/core/serializers/python.py | 14 ++++----- django/core/serializers/xml_serializer.py | 6 ++-- django/core/servers/basehttp.py | 6 ++-- django/db/backends/oracle/functions.py | 10 +++---- django/db/models/expressions.py | 11 +++---- django/db/models/fields/__init__.py | 10 ++----- django/db/models/fields/related.py | 10 +++---- .../db/models/fields/related_descriptors.py | 30 ++++++------------- django/db/models/functions/base.py | 5 ++-- django/db/models/query.py | 21 ++++--------- django/forms/fields.py | 24 +++++++-------- django/forms/models.py | 10 +++---- django/shortcuts.py | 8 ++--- django/template/loader_tags.py | 6 ++-- django/test/testcases.py | 3 +- django/utils/datastructures.py | 7 +---- django/utils/deconstruct.py | 6 ++-- django/views/decorators/cache.py | 14 ++------- django/views/generic/list.py | 4 +-- docs/topics/forms/formsets.txt | 4 +-- tests/extra_regress/models.py | 6 ++-- tests/forms_tests/tests/test_formsets.py | 4 +-- tests/gis_tests/geos_tests/test_geos.py | 3 +- tests/gis_tests/test_data.py | 10 ++----- tests/i18n/tests.py | 4 --- tests/middleware/test_security.py | 20 ++++++------- tests/multiple_database/models.py | 6 ++-- tests/queries/test_qs_combinators.py | 6 ---- tests/schema/fields.py | 17 ++++++----- tests/schema/tests.py | 4 +-- tests/staticfiles_tests/cases.py | 3 +- 41 files changed, 147 insertions(+), 243 deletions(-) diff --git a/django/conf/urls/i18n.py b/django/conf/urls/i18n.py index 23e4bbcd74b..3731afc8866 100644 --- a/django/conf/urls/i18n.py +++ b/django/conf/urls/i18n.py @@ -6,7 +6,7 @@ from django.urls import LocaleRegexURLResolver, get_resolver from django.views.i18n import set_language -def i18n_patterns(*urls, **kwargs): +def i18n_patterns(*urls, prefix_default_language=True): """ Adds the language code prefix to every URL pattern within this function. This may only be used in the root URLconf, not in an included @@ -14,8 +14,6 @@ def i18n_patterns(*urls, **kwargs): """ if not settings.USE_I18N: return list(urls) - prefix_default_language = kwargs.pop('prefix_default_language', True) - assert not kwargs, 'Unexpected kwargs for i18n_patterns(): %s' % kwargs return [LocaleRegexURLResolver(list(urls), prefix_default_language=prefix_default_language)] diff --git a/django/contrib/admin/decorators.py b/django/contrib/admin/decorators.py index 8b30139ea3d..1a0d9812250 100644 --- a/django/contrib/admin/decorators.py +++ b/django/contrib/admin/decorators.py @@ -1,4 +1,4 @@ -def register(*models, **kwargs): +def register(*models, site=None): """ Registers the given model(s) classes and wrapped ModelAdmin class with admin site: @@ -7,17 +7,16 @@ def register(*models, **kwargs): class AuthorAdmin(admin.ModelAdmin): pass - A kwarg of `site` can be passed as the admin site, otherwise the default - admin site will be used. + The `site` kwarg is an admin site to use instead of the default admin site. """ from django.contrib.admin import ModelAdmin - from django.contrib.admin.sites import site, AdminSite + from django.contrib.admin.sites import site as default_site, AdminSite def _model_admin_wrapper(admin_class): if not models: raise ValueError('At least one model must be passed to register.') - admin_site = kwargs.pop('site', site) + admin_site = site or default_site if not isinstance(admin_site, AdminSite): raise ValueError('site must subclass AdminSite') diff --git a/django/contrib/contenttypes/fields.py b/django/contrib/contenttypes/fields.py index 37a9df14a02..c2b46d12549 100644 --- a/django/contrib/contenttypes/fields.py +++ b/django/contrib/contenttypes/fields.py @@ -496,10 +496,8 @@ def create_generic_related_manager(superclass, rel): self.object_id_field_name: self.pk_val, } - def __call__(self, **kwargs): - # We use **kwargs rather than a kwarg argument to enforce the - # `manager='manager_name'` syntax. - manager = getattr(self.model, kwargs.pop('manager')) + def __call__(self, *, manager): + manager = getattr(self.model, manager) manager_class = create_generic_related_manager(manager.__class__, rel) return manager_class(instance=self.instance) do_not_call_in_templates = True @@ -542,8 +540,7 @@ def create_generic_related_manager(superclass, rel): False, self.prefetch_cache_name) - def add(self, *objs, **kwargs): - bulk = kwargs.pop('bulk', True) + def add(self, *objs, bulk=True): db = router.db_for_write(self.model, instance=self.instance) def check_and_update_obj(obj): @@ -576,15 +573,13 @@ def create_generic_related_manager(superclass, rel): obj.save() add.alters_data = True - def remove(self, *objs, **kwargs): + def remove(self, *objs, bulk=True): if not objs: return - bulk = kwargs.pop('bulk', True) self._clear(self.filter(pk__in=[o.pk for o in objs]), bulk) remove.alters_data = True - def clear(self, **kwargs): - bulk = kwargs.pop('bulk', True) + def clear(self, *, bulk=True): self._clear(self, bulk) clear.alters_data = True @@ -601,14 +596,11 @@ def create_generic_related_manager(superclass, rel): obj.delete() _clear.alters_data = True - def set(self, objs, **kwargs): + def set(self, objs, *, bulk=True, clear=False): # Force evaluation of `objs` in case it's a queryset whose value # could be affected by `manager.clear()`. Refs #19816. objs = tuple(objs) - bulk = kwargs.pop('bulk', True) - clear = kwargs.pop('clear', False) - db = router.db_for_write(self.model, instance=self.instance) with transaction.atomic(using=db, savepoint=False): if clear: diff --git a/django/contrib/contenttypes/models.py b/django/contrib/contenttypes/models.py index ce9afdb8ada..522bf2d2797 100644 --- a/django/contrib/contenttypes/models.py +++ b/django/contrib/contenttypes/models.py @@ -60,11 +60,10 @@ class ContentTypeManager(models.Manager): self._add_to_cache(self.db, ct) return ct - def get_for_models(self, *models, **kwargs): + def get_for_models(self, *models, for_concrete_models=True): """ Given *models, returns a dictionary mapping {model: content_type}. """ - for_concrete_models = kwargs.pop('for_concrete_models', True) results = {} # Models that aren't already in the cache. needed_app_labels = set() diff --git a/django/contrib/gis/db/models/fields.py b/django/contrib/gis/db/models/fields.py index d6cd529dc1e..c8cf827c105 100644 --- a/django/contrib/gis/db/models/fields.py +++ b/django/contrib/gis/db/models/fields.py @@ -263,7 +263,8 @@ class GeometryField(GeoSelectFormatMixin, BaseSpatialField): # The OpenGIS Geometry name. geom_type = 'GEOMETRY' - def __init__(self, verbose_name=None, dim=2, geography=False, **kwargs): + def __init__(self, verbose_name=None, dim=2, geography=False, *, extent=(-180.0, -90.0, 180.0, 90.0), + tolerance=0.05, **kwargs): """ The initialization function for geometry fields. In addition to the parameters from BaseSpatialField, it takes the following as keyword @@ -289,8 +290,8 @@ class GeometryField(GeoSelectFormatMixin, BaseSpatialField): # Oracle-specific private attributes for creating the entry in # `USER_SDO_GEOM_METADATA` - self._extent = kwargs.pop('extent', (-180.0, -90.0, 180.0, 90.0)) - self._tolerance = kwargs.pop('tolerance', 0.05) + self._extent = extent + self._tolerance = tolerance super().__init__(verbose_name=verbose_name, **kwargs) diff --git a/django/contrib/gis/forms/fields.py b/django/contrib/gis/forms/fields.py index f435bd5ab1d..4a2efb51005 100644 --- a/django/contrib/gis/forms/fields.py +++ b/django/contrib/gis/forms/fields.py @@ -22,11 +22,10 @@ class GeometryField(forms.Field): 'to the SRID of the geometry form field.'), } - def __init__(self, **kwargs): - # Pop out attributes from the database field, or use sensible - # defaults (e.g., allow None). - self.srid = kwargs.pop('srid', None) - self.geom_type = kwargs.pop('geom_type', self.geom_type) + def __init__(self, *, srid=None, geom_type=None, **kwargs): + self.srid = srid + if geom_type is not None: + self.geom_type = geom_type super().__init__(**kwargs) self.widget.attrs['geom_type'] = self.geom_type diff --git a/django/contrib/gis/geos/libgeos.py b/django/contrib/gis/geos/libgeos.py index faf29de0c1b..43353e5750d 100644 --- a/django/contrib/gis/geos/libgeos.py +++ b/django/contrib/gis/geos/libgeos.py @@ -147,11 +147,14 @@ class GEOSFuncFactory: restype = None errcheck = None - def __init__(self, func_name, *args, **kwargs): + def __init__(self, func_name, *args, restype=None, errcheck=None, argtypes=None, **kwargs): self.func_name = func_name - self.restype = kwargs.pop('restype', self.restype) - self.errcheck = kwargs.pop('errcheck', self.errcheck) - self.argtypes = kwargs.pop('argtypes', self.argtypes) + if restype is not None: + self.restype = restype + if errcheck is not None: + self.errcheck = errcheck + if argtypes is not None: + self.argtypes = argtypes self.args = args self.kwargs = kwargs self.func = None diff --git a/django/contrib/postgres/search.py b/django/contrib/postgres/search.py index 9a773db1d54..9d66976ae08 100644 --- a/django/contrib/postgres/search.py +++ b/django/contrib/postgres/search.py @@ -125,13 +125,11 @@ class SearchQueryCombinable: class SearchQuery(SearchQueryCombinable, Value): - invert = False _output_field = SearchQueryField() - config = None - def __init__(self, value, output_field=None, **extra): - self.config = extra.pop('config', self.config) - self.invert = extra.pop('invert', self.invert) + def __init__(self, value, output_field=None, *, config=None, invert=False): + self.config = config + self.invert = invert super().__init__(value, output_field=output_field) def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False): @@ -161,11 +159,7 @@ class SearchQuery(SearchQueryCombinable, Value): return combined def __invert__(self): - extra = { - 'invert': not self.invert, - 'config': self.config, - } - return type(self)(self.value, **extra) + return type(self)(self.value, config=self.config, invert=not self.invert) class CombinedSearchQuery(SearchQueryCombinable, CombinedExpression): diff --git a/django/core/mail/backends/filebased.py b/django/core/mail/backends/filebased.py index b051fe6313c..e956a3367ad 100644 --- a/django/core/mail/backends/filebased.py +++ b/django/core/mail/backends/filebased.py @@ -10,10 +10,10 @@ from django.core.mail.backends.console import \ class EmailBackend(ConsoleEmailBackend): - def __init__(self, *args, **kwargs): + def __init__(self, *args, file_path=None, **kwargs): self._fname = None - if 'file_path' in kwargs: - self.file_path = kwargs.pop('file_path') + if file_path is not None: + self.file_path = file_path else: self.file_path = getattr(settings, 'EMAIL_FILE_PATH', None) # Make sure self.file_path is a string. diff --git a/django/core/serializers/base.py b/django/core/serializers/base.py index d1a53440642..2ffe022da92 100644 --- a/django/core/serializers/base.py +++ b/django/core/serializers/base.py @@ -62,19 +62,18 @@ class Serializer: progress_class = ProgressBar stream_class = StringIO - def serialize(self, queryset, **options): + def serialize(self, queryset, *, stream=None, fields=None, use_natural_foreign_keys=False, + use_natural_primary_keys=False, progress_output=None, object_count=0, **options): """ Serialize a queryset. """ self.options = options - self.stream = options.pop("stream", self.stream_class()) - self.selected_fields = options.pop("fields", None) - self.use_natural_foreign_keys = options.pop('use_natural_foreign_keys', False) - self.use_natural_primary_keys = options.pop('use_natural_primary_keys', False) - progress_bar = self.progress_class( - options.pop('progress_output', None), options.pop('object_count', 0) - ) + self.stream = stream if stream is not None else self.stream_class() + self.selected_fields = fields + self.use_natural_foreign_keys = use_natural_foreign_keys + self.use_natural_primary_keys = use_natural_primary_keys + progress_bar = self.progress_class(progress_output, object_count) self.start_serialization() self.first = True diff --git a/django/core/serializers/python.py b/django/core/serializers/python.py index 7b7510be415..a299ed62c87 100644 --- a/django/core/serializers/python.py +++ b/django/core/serializers/python.py @@ -79,15 +79,13 @@ class Serializer(base.Serializer): return self.objects -def Deserializer(object_list, **options): +def Deserializer(object_list, *, using=DEFAULT_DB_ALIAS, ignorenonexistent=False, **options): """ Deserialize simple Python objects back into Django ORM instances. It's expected that you pass the Python objects themselves (instead of a stream or a string) to the constructor """ - db = options.pop('using', DEFAULT_DB_ALIAS) - ignore = options.pop('ignorenonexistent', False) field_names_cache = {} # Model: for d in object_list: @@ -95,7 +93,7 @@ def Deserializer(object_list, **options): try: Model = _get_model(d["model"]) except base.DeserializationError: - if ignore: + if ignorenonexistent: continue else: raise @@ -114,7 +112,7 @@ def Deserializer(object_list, **options): # Handle each field for (field_name, field_value) in d["fields"].items(): - if ignore and field_name not in field_names: + if ignorenonexistent and field_name not in field_names: # skip fields no longer on model continue @@ -131,7 +129,7 @@ def Deserializer(object_list, **options): if hasattr(model._default_manager, 'get_by_natural_key'): def m2m_convert(value): if hasattr(value, '__iter__') and not isinstance(value, str): - return model._default_manager.db_manager(db).get_by_natural_key(*value).pk + return model._default_manager.db_manager(using).get_by_natural_key(*value).pk else: return force_text(model._meta.pk.to_python(value), strings_only=True) else: @@ -154,7 +152,7 @@ def Deserializer(object_list, **options): field_name = field.remote_field.field_name if hasattr(default_manager, 'get_by_natural_key'): if hasattr(field_value, '__iter__') and not isinstance(field_value, str): - obj = default_manager.db_manager(db).get_by_natural_key(*field_value) + obj = default_manager.db_manager(using).get_by_natural_key(*field_value) value = getattr(obj, field.remote_field.field_name) # If this is a natural foreign key to an object that # has a FK/O2O as the foreign key, use the FK value @@ -177,7 +175,7 @@ def Deserializer(object_list, **options): except Exception as e: raise base.DeserializationError.WithData(e, d['model'], d.get('pk'), field_value) - obj = base.build_instance(Model, data, db) + obj = base.build_instance(Model, data, using) yield base.DeserializedObject(obj, m2m_data) diff --git a/django/core/serializers/xml_serializer.py b/django/core/serializers/xml_serializer.py index ddf0bf3e1a7..7e649520748 100644 --- a/django/core/serializers/xml_serializer.py +++ b/django/core/serializers/xml_serializer.py @@ -157,11 +157,11 @@ class Deserializer(base.Deserializer): Deserialize XML. """ - def __init__(self, stream_or_string, **options): + def __init__(self, stream_or_string, *, using=DEFAULT_DB_ALIAS, ignorenonexistent=False, **options): super().__init__(stream_or_string, **options) self.event_stream = pulldom.parse(self.stream, self._make_parser()) - self.db = options.pop('using', DEFAULT_DB_ALIAS) - self.ignore = options.pop('ignorenonexistent', False) + self.db = using + self.ignore = ignorenonexistent def _make_parser(self): """Create a hardened XML parser (no custom/external entities).""" diff --git a/django/core/servers/basehttp.py b/django/core/servers/basehttp.py index ef2edc0f809..68ef2a74b7a 100644 --- a/django/core/servers/basehttp.py +++ b/django/core/servers/basehttp.py @@ -59,10 +59,10 @@ class WSGIServer(simple_server.WSGIServer): request_queue_size = 10 - def __init__(self, *args, **kwargs): - if kwargs.pop('ipv6', False): + def __init__(self, *args, ipv6=False, allow_reuse_address=True, **kwargs): + if ipv6: self.address_family = socket.AF_INET6 - self.allow_reuse_address = kwargs.pop('allow_reuse_address', True) + self.allow_reuse_address = allow_reuse_address super().__init__(*args, **kwargs) def server_bind(self): diff --git a/django/db/backends/oracle/functions.py b/django/db/backends/oracle/functions.py index 7e9b6a62042..1aeb4597e3e 100644 --- a/django/db/backends/oracle/functions.py +++ b/django/db/backends/oracle/functions.py @@ -10,15 +10,13 @@ class IntervalToSeconds(Func): EXTRACT(second from %(expressions)s) """ - def __init__(self, expression, **extra): - output_field = extra.pop('output_field', DecimalField()) - super().__init__(expression, output_field=output_field, **extra) + def __init__(self, expression, *, output_field=None, **extra): + super().__init__(expression, output_field=output_field or DecimalField(), **extra) class SecondsToInterval(Func): function = 'NUMTODSINTERVAL' template = "%(function)s(%(expressions)s, 'SECOND')" - def __init__(self, expression, **extra): - output_field = extra.pop('output_field', DurationField()) - super().__init__(expression, output_field=output_field, **extra) + def __init__(self, expression, *, output_field=None, **extra): + super().__init__(expression, output_field=output_field or DurationField(), **extra) diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py index fcbf867b965..af8891da220 100644 --- a/django/db/models/expressions.py +++ b/django/db/models/expressions.py @@ -506,7 +506,7 @@ class Func(Expression): arg_joiner = ', ' arity = None # The number of arguments the function accepts. - def __init__(self, *expressions, **extra): + def __init__(self, *expressions, output_field=None, **extra): if self.arity is not None and len(expressions) != self.arity: raise TypeError( "'%s' takes exactly %s %s (%s given)" % ( @@ -516,7 +516,6 @@ class Func(Expression): len(expressions), ) ) - output_field = extra.pop('output_field', None) super().__init__(output_field=output_field) self.source_expressions = self._parse_expressions(*expressions) self.extra = extra @@ -828,11 +827,9 @@ class Case(Expression): template = 'CASE %(cases)s ELSE %(default)s END' case_joiner = ' ' - def __init__(self, *cases, **extra): + def __init__(self, *cases, default=None, output_field=None, **extra): if not all(isinstance(case, When) for case in cases): raise TypeError("Positional arguments must all be When objects.") - default = extra.pop('default', None) - output_field = extra.pop('output_field', None) super().__init__(output_field) self.cases = list(cases) self.default = self._parse_expressions(default)[0] @@ -983,8 +980,8 @@ class Subquery(Expression): class Exists(Subquery): template = 'EXISTS(%(subquery)s)' - def __init__(self, *args, **kwargs): - self.negated = kwargs.pop('negated', False) + def __init__(self, *args, negated=False, **kwargs): + self.negated = negated super().__init__(*args, **kwargs) def __invert__(self): diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index 809a14277eb..68a42f02369 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -2045,15 +2045,11 @@ class SlugField(CharField): default_validators = [validators.validate_slug] description = _("Slug (up to %(max_length)s)") - def __init__(self, *args, **kwargs): - kwargs['max_length'] = kwargs.get('max_length', 50) - # Set db_index=True unless it's been set manually. - if 'db_index' not in kwargs: - kwargs['db_index'] = True - self.allow_unicode = kwargs.pop('allow_unicode', False) + def __init__(self, *args, max_length=50, db_index=True, allow_unicode=False, **kwargs): + self.allow_unicode = allow_unicode if self.allow_unicode: self.default_validators = [validators.validate_unicode_slug] - super().__init__(*args, **kwargs) + super().__init__(*args, max_length=max_length, db_index=db_index, **kwargs) def deconstruct(self): name, path, args, kwargs = super().deconstruct() diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index dd84d780fb4..cf39de7287f 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -922,15 +922,14 @@ class ForeignKey(ForeignObject): if self.remote_field.field_name is None: self.remote_field.field_name = cls._meta.pk.name - def formfield(self, **kwargs): - db = kwargs.pop('using', None) + def formfield(self, *, using=None, **kwargs): if isinstance(self.remote_field.model, str): raise ValueError("Cannot create form field for %r yet, because " "its related model %r has not been loaded yet" % (self.name, self.remote_field.model)) defaults = { 'form_class': forms.ModelChoiceField, - 'queryset': self.remote_field.model._default_manager.using(db), + 'queryset': self.remote_field.model._default_manager.using(using), 'to_field_name': self.remote_field.field_name, } defaults.update(kwargs) @@ -1609,11 +1608,10 @@ class ManyToManyField(RelatedField): def save_form_data(self, instance, data): getattr(instance, self.attname).set(data) - def formfield(self, **kwargs): - db = kwargs.pop('using', None) + def formfield(self, *, using=None, **kwargs): defaults = { 'form_class': forms.ModelMultipleChoiceField, - 'queryset': self.remote_field.model._default_manager.using(db), + 'queryset': self.remote_field.model._default_manager.using(using), } defaults.update(kwargs) # If initial is passed in, it's a list of related objects, but the diff --git a/django/db/models/fields/related_descriptors.py b/django/db/models/fields/related_descriptors.py index b3e87cd482d..5899a2324c5 100644 --- a/django/db/models/fields/related_descriptors.py +++ b/django/db/models/fields/related_descriptors.py @@ -510,10 +510,8 @@ def create_reverse_many_to_one_manager(superclass, rel): self.core_filters = {self.field.name: instance} - def __call__(self, **kwargs): - # We use **kwargs rather than a kwarg argument to enforce the - # `manager='manager_name'` syntax. - manager = getattr(self.model, kwargs.pop('manager')) + def __call__(self, *, manager): + manager = getattr(self.model, manager) manager_class = create_reverse_many_to_one_manager(manager.__class__, rel) return manager_class(self.instance) do_not_call_in_templates = True @@ -569,9 +567,8 @@ def create_reverse_many_to_one_manager(superclass, rel): cache_name = self.field.related_query_name() return queryset, rel_obj_attr, instance_attr, False, cache_name - def add(self, *objs, **kwargs): + def add(self, *objs, bulk=True): self._remove_prefetched_objects() - bulk = kwargs.pop('bulk', True) objs = list(objs) db = router.db_for_write(self.model, instance=self.instance) @@ -622,10 +619,9 @@ def create_reverse_many_to_one_manager(superclass, rel): # remove() and clear() are only provided if the ForeignKey can have a value of null. if rel.field.null: - def remove(self, *objs, **kwargs): + def remove(self, *objs, bulk=True): if not objs: return - bulk = kwargs.pop('bulk', True) val = self.field.get_foreign_related_value(self.instance) old_ids = set() for obj in objs: @@ -639,8 +635,7 @@ def create_reverse_many_to_one_manager(superclass, rel): self._clear(self.filter(pk__in=old_ids), bulk) remove.alters_data = True - def clear(self, **kwargs): - bulk = kwargs.pop('bulk', True) + def clear(self, *, bulk=True): self._clear(self, bulk) clear.alters_data = True @@ -658,14 +653,11 @@ def create_reverse_many_to_one_manager(superclass, rel): obj.save(update_fields=[self.field.name]) _clear.alters_data = True - def set(self, objs, **kwargs): + def set(self, objs, *, bulk=True, clear=False): # Force evaluation of `objs` in case it's a queryset whose value # could be affected by `manager.clear()`. Refs #19816. objs = tuple(objs) - bulk = kwargs.pop('bulk', True) - clear = kwargs.pop('clear', False) - if self.field.null: db = router.db_for_write(self.model, instance=self.instance) with transaction.atomic(using=db, savepoint=False): @@ -791,10 +783,8 @@ def create_forward_many_to_many_manager(superclass, rel, reverse): "a many-to-many relationship can be used." % instance.__class__.__name__) - def __call__(self, **kwargs): - # We use **kwargs rather than a kwarg argument to enforce the - # `manager='manager_name'` syntax. - manager = getattr(self.model, kwargs.pop('manager')) + def __call__(self, *, manager): + manager = getattr(self.model, manager) manager_class = create_forward_many_to_many_manager(manager.__class__, rel, reverse) return manager_class(instance=self.instance) do_not_call_in_templates = True @@ -924,7 +914,7 @@ def create_forward_many_to_many_manager(superclass, rel, reverse): ) clear.alters_data = True - def set(self, objs, **kwargs): + def set(self, objs, *, clear=False): if not rel.through._meta.auto_created: opts = self.through._meta raise AttributeError( @@ -937,8 +927,6 @@ def create_forward_many_to_many_manager(superclass, rel, reverse): # could be affected by `manager.clear()`. Refs #19816. objs = tuple(objs) - clear = kwargs.pop('clear', False) - db = router.db_for_write(self.through, instance=self.instance) with transaction.atomic(using=db, savepoint=False): if clear: diff --git a/django/db/models/functions/base.py b/django/db/models/functions/base.py index e9bf01ff0dc..905f740a6c1 100644 --- a/django/db/models/functions/base.py +++ b/django/db/models/functions/base.py @@ -165,9 +165,8 @@ class Length(Transform): function = 'LENGTH' lookup_name = 'length' - def __init__(self, expression, **extra): - output_field = extra.pop('output_field', fields.IntegerField()) - super().__init__(expression, output_field=output_field, **extra) + def __init__(self, expression, *, output_field=None, **extra): + super().__init__(expression, output_field=output_field or fields.IntegerField(), **extra) def as_mysql(self, compiler, connection): return super().as_sql(compiler, connection, function='CHAR_LENGTH') diff --git a/django/db/models/query.py b/django/db/models/query.py index dcb13600545..244cfaf1646 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -691,11 +691,7 @@ class QuerySet: clone._iterable_class = ValuesIterable return clone - def values_list(self, *fields, **kwargs): - flat = kwargs.pop('flat', False) - if kwargs: - raise TypeError('Unexpected keyword arguments to values_list: %s' % (list(kwargs),)) - + def values_list(self, *fields, flat=False): if flat and len(fields) > 1: raise TypeError("'flat' is not valid when values_list is called with more than one field.") @@ -812,7 +808,7 @@ class QuerySet: else: return self._filter_or_exclude(None, **filter_obj) - def _combinator_query(self, combinator, *other_qs, **kwargs): + def _combinator_query(self, combinator, *other_qs, all=False): # Clone the query to inherit the select list and everything clone = self._clone() # Clear limits and ordering so they can be reapplied @@ -820,18 +816,11 @@ class QuerySet: clone.query.clear_limits() clone.query.combined_queries = (self.query,) + tuple(qs.query for qs in other_qs) clone.query.combinator = combinator - clone.query.combinator_all = kwargs.pop('all', False) + clone.query.combinator_all = all return clone - def union(self, *other_qs, **kwargs): - if kwargs: - unexpected_kwarg = next((k for k in kwargs.keys() if k != 'all'), None) - if unexpected_kwarg: - raise TypeError( - "union() received an unexpected keyword argument '%s'" % - (unexpected_kwarg,) - ) - return self._combinator_query('union', *other_qs, **kwargs) + def union(self, *other_qs, all=False): + return self._combinator_query('union', *other_qs, all=all) def intersection(self, *other_qs): return self._combinator_query('intersection', *other_qs) diff --git a/django/forms/fields.py b/django/forms/fields.py index 979a13835df..13899c3ab15 100644 --- a/django/forms/fields.py +++ b/django/forms/fields.py @@ -541,9 +541,9 @@ class FileField(Field): 'contradiction': _('Please either submit a file or check the clear checkbox, not both.') } - def __init__(self, *args, **kwargs): - self.max_length = kwargs.pop('max_length', None) - self.allow_empty_file = kwargs.pop('allow_empty_file', False) + def __init__(self, *args, max_length=None, allow_empty_file=False, **kwargs): + self.max_length = max_length + self.allow_empty_file = allow_empty_file super().__init__(*args, **kwargs) def to_python(self, data): @@ -821,9 +821,9 @@ class ChoiceField(Field): class TypedChoiceField(ChoiceField): - def __init__(self, *args, **kwargs): - self.coerce = kwargs.pop('coerce', lambda val: val) - self.empty_value = kwargs.pop('empty_value', '') + def __init__(self, *args, coerce=lambda val: val, empty_value='', **kwargs): + self.coerce = coerce + self.empty_value = empty_value super().__init__(*args, **kwargs) def _coerce(self, value): @@ -890,8 +890,8 @@ class MultipleChoiceField(ChoiceField): class TypedMultipleChoiceField(MultipleChoiceField): - def __init__(self, *args, **kwargs): - self.coerce = kwargs.pop('coerce', lambda val: val) + def __init__(self, *args, coerce=lambda val: val, **kwargs): + self.coerce = coerce self.empty_value = kwargs.pop('empty_value', []) super().__init__(*args, **kwargs) @@ -971,8 +971,8 @@ class MultiValueField(Field): 'incomplete': _('Enter a complete value.'), } - def __init__(self, fields=(), *args, **kwargs): - self.require_all_fields = kwargs.pop('require_all_fields', True) + def __init__(self, fields=(), *args, require_all_fields=True, **kwargs): + self.require_all_fields = require_all_fields super().__init__(*args, **kwargs) for f in fields: f.error_messages.setdefault('incomplete', @@ -1174,8 +1174,8 @@ class GenericIPAddressField(CharField): class SlugField(CharField): default_validators = [validators.validate_slug] - def __init__(self, *args, **kwargs): - self.allow_unicode = kwargs.pop('allow_unicode', False) + def __init__(self, *args, allow_unicode=False, **kwargs): + self.allow_unicode = allow_unicode if self.allow_unicode: self.default_validators = [validators.validate_unicode_slug] super().__init__(*args, **kwargs) diff --git a/django/forms/models.py b/django/forms/models.py index a95f281b453..03688394158 100644 --- a/django/forms/models.py +++ b/django/forms/models.py @@ -553,9 +553,9 @@ class BaseModelFormSet(BaseFormSet): unique_fields = set() def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, - queryset=None, **kwargs): + queryset=None, *, initial=None, **kwargs): self.queryset = queryset - self.initial_extra = kwargs.pop('initial', None) + self.initial_extra = initial defaults = {'data': data, 'files': files, 'auto_id': auto_id, 'prefix': prefix} defaults.update(kwargs) super().__init__(**defaults) @@ -1065,10 +1065,10 @@ class InlineForeignKeyField(Field): 'invalid_choice': _('The inline foreign key did not match the parent instance primary key.'), } - def __init__(self, parent_instance, *args, **kwargs): + def __init__(self, parent_instance, *args, pk_field=False, to_field=None, **kwargs): self.parent_instance = parent_instance - self.pk_field = kwargs.pop("pk_field", False) - self.to_field = kwargs.pop("to_field", None) + self.pk_field = pk_field + self.to_field = to_field if self.parent_instance is not None: if self.to_field: kwargs["initial"] = getattr(self.parent_instance, self.to_field) diff --git a/django/shortcuts.py b/django/shortcuts.py index b27a75206f9..04e2f3e103c 100644 --- a/django/shortcuts.py +++ b/django/shortcuts.py @@ -30,7 +30,7 @@ def render(request, template_name, context=None, content_type=None, status=None, return HttpResponse(content, content_type, status) -def redirect(to, *args, **kwargs): +def redirect(to, *args, permanent=False, **kwargs): """ Returns an HttpResponseRedirect to the appropriate URL for the arguments passed. @@ -47,11 +47,7 @@ def redirect(to, *args, **kwargs): By default issues a temporary redirect; pass permanent=True to issue a permanent redirect """ - if kwargs.pop('permanent', False): - redirect_class = HttpResponsePermanentRedirect - else: - redirect_class = HttpResponseRedirect - + redirect_class = HttpResponsePermanentRedirect if permanent else HttpResponseRedirect return redirect_class(resolve_url(to, *args, **kwargs)) diff --git a/django/template/loader_tags.py b/django/template/loader_tags.py index 6a45b8c9853..a0b0de54d0d 100644 --- a/django/template/loader_tags.py +++ b/django/template/loader_tags.py @@ -157,10 +157,10 @@ class ExtendsNode(Node): class IncludeNode(Node): context_key = '__include_context' - def __init__(self, template, *args, **kwargs): + def __init__(self, template, *args, extra_context=None, isolated_context=False, **kwargs): self.template = template - self.extra_context = kwargs.pop('extra_context', {}) - self.isolated_context = kwargs.pop('isolated_context', False) + self.extra_context = extra_context or {} + self.isolated_context = isolated_context super().__init__(*args, **kwargs) def render(self, context): diff --git a/django/test/testcases.py b/django/test/testcases.py index 4c1d778795d..0f879d42221 100644 --- a/django/test/testcases.py +++ b/django/test/testcases.py @@ -929,8 +929,7 @@ class TransactionTestCase(SimpleTestCase): "against more than one ordered values") return self.assertEqual(list(items), values, msg=msg) - def assertNumQueries(self, num, func=None, *args, **kwargs): - using = kwargs.pop("using", DEFAULT_DB_ALIAS) + def assertNumQueries(self, num, func=None, *args, using=DEFAULT_DB_ALIAS, **kwargs): conn = connections[using] context = _AssertNumQueriesContext(self, num, conn) diff --git a/django/utils/datastructures.py b/django/utils/datastructures.py index 8e64328a2cd..c15cb5eb49b 100644 --- a/django/utils/datastructures.py +++ b/django/utils/datastructures.py @@ -235,12 +235,7 @@ class ImmutableList(tuple): AttributeError: You cannot mutate this. """ - def __new__(cls, *args, **kwargs): - if 'warning' in kwargs: - warning = kwargs['warning'] - del kwargs['warning'] - else: - warning = 'ImmutableList object is immutable.' + def __new__(cls, *args, warning='ImmutableList object is immutable.', **kwargs): self = tuple.__new__(cls, *args, **kwargs) self.warning = warning return self diff --git a/django/utils/deconstruct.py b/django/utils/deconstruct.py index 6bc70f7e53d..c546fc1deab 100644 --- a/django/utils/deconstruct.py +++ b/django/utils/deconstruct.py @@ -3,15 +3,13 @@ from importlib import import_module from django.utils.version import get_docs_version -def deconstructible(*args, **kwargs): +def deconstructible(*args, path=None): """ Class decorator that allow the decorated class to be serialized by the migrations subsystem. Accepts an optional kwarg `path` to specify the import path. """ - path = kwargs.pop('path', None) - def decorator(klass): def __new__(cls, *args, **kwargs): # We capture the arguments to make returning them trivial @@ -54,4 +52,4 @@ def deconstructible(*args, **kwargs): if not args: return decorator - return decorator(*args, **kwargs) + return decorator(*args) diff --git a/django/views/decorators/cache.py b/django/views/decorators/cache.py index 18238aaae7e..7c5d157ea56 100644 --- a/django/views/decorators/cache.py +++ b/django/views/decorators/cache.py @@ -5,7 +5,7 @@ from django.utils.cache import add_never_cache_headers, patch_cache_control from django.utils.decorators import decorator_from_middleware_with_args -def cache_page(*args, **kwargs): +def cache_page(timeout, *, cache=None, key_prefix=None): """ Decorator for views that tries getting the page from the cache and populates the cache if the page isn't in the cache yet. @@ -19,18 +19,8 @@ def cache_page(*args, **kwargs): Additionally, all headers from the response's Vary header will be taken into account on caching -- just like the middleware does. """ - # We also add some asserts to give better error messages in case people are - # using other ways to call cache_page that no longer work. - if len(args) != 1 or callable(args[0]): - raise TypeError("cache_page has a single mandatory positional argument: timeout") - cache_timeout = args[0] - cache_alias = kwargs.pop('cache', None) - key_prefix = kwargs.pop('key_prefix', None) - if kwargs: - raise TypeError("cache_page has two optional keyword arguments: cache and key_prefix") - return decorator_from_middleware_with_args(CacheMiddleware)( - cache_timeout=cache_timeout, cache_alias=cache_alias, key_prefix=key_prefix + cache_timeout=timeout, cache_alias=cache, key_prefix=key_prefix ) diff --git a/django/views/generic/list.py b/django/views/generic/list.py index 39dc856e554..61320fccbe5 100644 --- a/django/views/generic/list.py +++ b/django/views/generic/list.py @@ -120,11 +120,11 @@ class MultipleObjectMixin(ContextMixin): else: return None - def get_context_data(self, **kwargs): + def get_context_data(self, *, object_list=None, **kwargs): """ Get the context for this view. """ - queryset = kwargs.pop('object_list', self.object_list) + queryset = object_list if object_list is not None else self.object_list page_size = self.get_paginate_by(queryset) context_object_name = self.get_context_object_name(queryset) if page_size: diff --git a/docs/topics/forms/formsets.txt b/docs/topics/forms/formsets.txt index a41c4a01960..08acde1d4fe 100644 --- a/docs/topics/forms/formsets.txt +++ b/docs/topics/forms/formsets.txt @@ -557,8 +557,8 @@ You can pass this parameter when instantiating the formset:: >>> from myapp.forms import ArticleForm >>> class MyArticleForm(ArticleForm): - ... def __init__(self, *args, **kwargs): - ... self.user = kwargs.pop('user') + ... def __init__(self, *args, user, **kwargs): + ... self.user = user ... super().__init__(*args, **kwargs) >>> ArticleFormSet = formset_factory(MyArticleForm) diff --git a/tests/extra_regress/models.py b/tests/extra_regress/models.py index b069affccd5..3765601f7f2 100644 --- a/tests/extra_regress/models.py +++ b/tests/extra_regress/models.py @@ -13,12 +13,10 @@ class RevisionableModel(models.Model): def __str__(self): return "%s (%s, %s)" % (self.title, self.id, self.base.id) - def save(self, *args, **kwargs): - super().save(*args, **kwargs) + def save(self, *args, force_insert=None, force_update=None, **kwargs): + super().save(*args, force_insert=force_insert, force_update=force_update, **kwargs) if not self.base: self.base = self - kwargs.pop('force_insert', None) - kwargs.pop('force_update', None) super().save(*args, **kwargs) def new_revision(self): diff --git a/tests/forms_tests/tests/test_formsets.py b/tests/forms_tests/tests/test_formsets.py index 26d9e156052..f552f689dc6 100644 --- a/tests/forms_tests/tests/test_formsets.py +++ b/tests/forms_tests/tests/test_formsets.py @@ -56,8 +56,8 @@ SplitDateTimeFormSet = formset_factory(SplitDateTimeForm) class CustomKwargForm(Form): - def __init__(self, *args, **kwargs): - self.custom_kwarg = kwargs.pop('custom_kwarg') + def __init__(self, *args, custom_kwarg, **kwargs): + self.custom_kwarg = custom_kwarg super().__init__(*args, **kwargs) diff --git a/tests/gis_tests/geos_tests/test_geos.py b/tests/gis_tests/geos_tests/test_geos.py index c90927c5ccd..bb3341493c0 100644 --- a/tests/gis_tests/geos_tests/test_geos.py +++ b/tests/gis_tests/geos_tests/test_geos.py @@ -1258,8 +1258,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin): to the parent class during `__init__`. """ class ExtendedPolygon(Polygon): - def __init__(self, *args, **kwargs): - data = kwargs.pop('data', 0) + def __init__(self, *args, data=0, **kwargs): super().__init__(*args, **kwargs) self._data = data diff --git a/tests/gis_tests/test_data.py b/tests/gis_tests/test_data.py index adcd57b48b3..c1e1f5efecf 100644 --- a/tests/gis_tests/test_data.py +++ b/tests/gis_tests/test_data.py @@ -43,9 +43,8 @@ class TestDS(TestObj): """ Object for testing GDAL data sources. """ - def __init__(self, name, **kwargs): + def __init__(self, name, *, ext='shp', **kwargs): # Shapefile is default extension, unless specified otherwise. - ext = kwargs.pop('ext', 'shp') self.ds = get_ds_file(name, ext) super().__init__(**kwargs) @@ -55,19 +54,14 @@ class TestGeom(TestObj): Testing object used for wrapping reference geometry data in GEOS/GDAL tests. """ - def __init__(self, **kwargs): + def __init__(self, *, coords=None, centroid=None, ext_ring_cs=None, **kwargs): # Converting lists to tuples of certain keyword args # so coordinate test cases will match (JSON has no # concept of tuple). - coords = kwargs.pop('coords', None) if coords: self.coords = tuplize(coords) - - centroid = kwargs.pop('centroid', None) if centroid: self.centroid = tuple(centroid) - - ext_ring_cs = kwargs.pop('ext_ring_cs', None) if ext_ring_cs: ext_ring_cs = tuplize(ext_ring_cs) self.ext_ring_cs = ext_ring_cs diff --git a/tests/i18n/tests.py b/tests/i18n/tests.py index d2b878b4351..9aa8821ef99 100644 --- a/tests/i18n/tests.py +++ b/tests/i18n/tests.py @@ -1388,10 +1388,6 @@ class UnprefixedDefaultLanguageTests(SimpleTestCase): response = self.client.get('/simple/', HTTP_ACCEPT_LANGUAGE='fr') self.assertEqual(response.content, b'Yes') - def test_unexpected_kwarg_to_i18n_patterns(self): - with self.assertRaisesMessage(AssertionError, "Unexpected kwargs for i18n_patterns(): {'foo':"): - i18n_patterns(object(), foo='bar') - def test_page_with_dash(self): # A page starting with /de* shouldn't match the 'de' langauge code. response = self.client.get('/de-simple-page/') diff --git a/tests/middleware/test_security.py b/tests/middleware/test_security.py index b8e77f76e44..31a3dbb74c5 100644 --- a/tests/middleware/test_security.py +++ b/tests/middleware/test_security.py @@ -13,19 +13,19 @@ class SecurityMiddlewareTest(SimpleTestCase): def secure_request_kwargs(self): return {"wsgi.url_scheme": "https"} - def response(self, *args, **kwargs): - headers = kwargs.pop("headers", {}) + def response(self, *args, headers=None, **kwargs): response = HttpResponse(*args, **kwargs) - for k, v in headers.items(): - response[k] = v + if headers: + for k, v in headers.items(): + response[k] = v return response - def process_response(self, *args, **kwargs): + def process_response(self, *args, secure=False, request=None, **kwargs): request_kwargs = {} - if kwargs.pop("secure", False): + if secure: request_kwargs.update(self.secure_request_kwargs) - request = (kwargs.pop("request", None) or - self.request.get("/some/url", **request_kwargs)) + if request is None: + request = self.request.get("/some/url", **request_kwargs) ret = self.middleware.process_request(request) if ret: return ret @@ -34,8 +34,8 @@ class SecurityMiddlewareTest(SimpleTestCase): request = RequestFactory() - def process_request(self, method, *args, **kwargs): - if kwargs.pop("secure", False): + def process_request(self, method, *args, secure=False, **kwargs): + if secure: kwargs.update(self.secure_request_kwargs) req = getattr(self.request, method.lower())(*args, **kwargs) return self.middleware.process_request(req) diff --git a/tests/multiple_database/models.py b/tests/multiple_database/models.py index 3e93bb67bc3..14cb946f627 100644 --- a/tests/multiple_database/models.py +++ b/tests/multiple_database/models.py @@ -40,12 +40,10 @@ class Person(models.Model): # calls. This argument is used to establish that the BookManager # is actually getting used when it should be. class BookManager(models.Manager): - def create(self, *args, **kwargs): - kwargs.pop('extra_arg', None) + def create(self, *args, extra_arg=None, **kwargs): return super().create(*args, **kwargs) - def get_or_create(self, *args, **kwargs): - kwargs.pop('extra_arg', None) + def get_or_create(self, *args, extra_arg=None, **kwargs): return super().get_or_create(*args, **kwargs) diff --git a/tests/queries/test_qs_combinators.py b/tests/queries/test_qs_combinators.py index 0528f1c08db..bfb3d308256 100644 --- a/tests/queries/test_qs_combinators.py +++ b/tests/queries/test_qs_combinators.py @@ -42,12 +42,6 @@ class QuerySetSetOperationTests(TestCase): self.assertEqual(len(list(qs1.union(qs2, all=True))), 20) self.assertEqual(len(list(qs1.union(qs2))), 10) - def test_union_bad_kwarg(self): - qs1 = Number.objects.all() - msg = "union() received an unexpected keyword argument 'bad'" - with self.assertRaisesMessage(TypeError, msg): - self.assertEqual(len(list(qs1.union(qs1, bad=True))), 20) - def test_limits(self): qs1 = Number.objects.all() qs2 = Number.objects.all() diff --git a/tests/schema/fields.py b/tests/schema/fields.py index 16a61ee5f6c..f03b9813b6c 100644 --- a/tests/schema/fields.py +++ b/tests/schema/fields.py @@ -13,23 +13,24 @@ class CustomManyToManyField(RelatedField): """ many_to_many = True - def __init__(self, to, db_constraint=True, swappable=True, **kwargs): + def __init__(self, to, db_constraint=True, swappable=True, related_name=None, related_query_name=None, + limit_choices_to=None, symmetrical=None, through=None, through_fields=None, db_table=None, **kwargs): try: to._meta except AttributeError: to = str(to) kwargs['rel'] = ManyToManyRel( self, to, - related_name=kwargs.pop('related_name', None), - related_query_name=kwargs.pop('related_query_name', None), - limit_choices_to=kwargs.pop('limit_choices_to', None), - symmetrical=kwargs.pop('symmetrical', to == RECURSIVE_RELATIONSHIP_CONSTANT), - through=kwargs.pop('through', None), - through_fields=kwargs.pop('through_fields', None), + related_name=related_name, + related_query_name=related_query_name, + limit_choices_to=limit_choices_to, + symmetrical=symmetrical if symmetrical is not None else (to == RECURSIVE_RELATIONSHIP_CONSTANT), + through=through, + through_fields=through_fields, db_constraint=db_constraint, ) self.swappable = swappable - self.db_table = kwargs.pop('db_table', None) + self.db_table = db_table if kwargs['rel'].through is not None: assert self.db_table is None, "Cannot specify a db_table if an intermediary model is used." super().__init__(**kwargs) diff --git a/tests/schema/tests.py b/tests/schema/tests.py index d21f608e025..abcaa5b4801 100644 --- a/tests/schema/tests.py +++ b/tests/schema/tests.py @@ -1819,9 +1819,9 @@ class SchemaTests(TransactionTestCase): """ #23065 - Constraint names must be quoted if they contain capital letters. """ - def get_field(*args, **kwargs): + def get_field(*args, field_class=IntegerField, **kwargs): kwargs['db_column'] = "CamelCase" - field = kwargs.pop('field_class', IntegerField)(*args, **kwargs) + field = field_class(*args, **kwargs) field.set_attributes_from_name("CamelCase") return field diff --git a/tests/staticfiles_tests/cases.py b/tests/staticfiles_tests/cases.py index 593739d4013..81faf6d7d98 100644 --- a/tests/staticfiles_tests/cases.py +++ b/tests/staticfiles_tests/cases.py @@ -76,8 +76,7 @@ class CollectionTestCase(BaseStaticFilesMixin, SimpleTestCase): self.patched_settings.disable() super().tearDown() - def run_collectstatic(self, **kwargs): - verbosity = kwargs.pop('verbosity', 0) + def run_collectstatic(self, *, verbosity=0, **kwargs): call_command('collectstatic', interactive=False, verbosity=verbosity, ignore_patterns=['*.ignoreme'], **kwargs)