From 301de774c21d055e9e5a7073e5bffdb52bc71079 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Fri, 21 Apr 2017 19:52:26 +0200 Subject: [PATCH] Refs #27795 -- Replaced many force_text() with str() Thanks Tim Graham for the review. --- django/contrib/admin/actions.py | 5 ++-- django/contrib/admin/filters.py | 9 ++++---- django/contrib/admin/models.py | 5 ++-- django/contrib/admin/options.py | 17 +++++++------- .../contrib/admin/templatetags/admin_list.py | 5 ++-- django/contrib/admin/utils.py | 19 ++++++++------- django/contrib/admin/widgets.py | 3 +-- django/contrib/auth/mixins.py | 3 +-- django/contrib/auth/password_validation.py | 3 +-- django/contrib/contenttypes/fields.py | 3 +-- django/contrib/contenttypes/models.py | 3 +-- django/contrib/postgres/fields/hstore.py | 7 +++--- django/contrib/postgres/lookups.py | 3 +-- django/contrib/sessions/backends/base.py | 4 ++-- django/contrib/sessions/backends/cached_db.py | 3 +-- django/contrib/sessions/backends/db.py | 3 +-- django/contrib/sessions/backends/file.py | 3 +-- django/contrib/syndication/views.py | 12 +++++----- django/core/checks/messages.py | 4 +--- django/core/exceptions.py | 3 +-- django/core/handlers/exception.py | 3 +-- django/core/mail/message.py | 10 ++++---- django/core/serializers/xml_serializer.py | 15 ++++++------ django/core/validators.py | 4 +--- django/db/backends/base/operations.py | 2 +- django/db/backends/sqlite3/base.py | 3 +-- django/db/migrations/serializer.py | 3 +-- django/db/models/fields/__init__.py | 10 ++++---- django/db/models/fields/reverse_related.py | 3 +-- django/db/models/options.py | 3 +-- django/forms/boundfield.py | 7 +++--- django/forms/fields.py | 23 +++++++++---------- django/forms/forms.py | 7 +++--- django/forms/models.py | 15 ++++++------ django/forms/widgets.py | 7 +++--- django/http/response.py | 4 ++-- django/shortcuts.py | 3 +-- django/urls/resolvers.py | 9 ++++---- django/utils/dateformat.py | 5 ++-- django/utils/html.py | 8 +++---- django/utils/text.py | 23 ++++++++----------- django/utils/translation/__init__.py | 3 +-- django/views/debug.py | 4 ++-- django/views/defaults.py | 3 +-- django/views/generic/edit.py | 11 +++------ docs/howto/custom-model-fields.txt | 8 +++---- docs/topics/serialization.txt | 3 +-- 47 files changed, 135 insertions(+), 181 deletions(-) diff --git a/django/contrib/admin/actions.py b/django/contrib/admin/actions.py index 044e6163ad..bc4e687171 100644 --- a/django/contrib/admin/actions.py +++ b/django/contrib/admin/actions.py @@ -8,7 +8,6 @@ from django.contrib.admin.utils import get_deleted_objects, model_ngettext from django.core.exceptions import PermissionDenied from django.db import router from django.template.response import TemplateResponse -from django.utils.encoding import force_text from django.utils.translation import gettext as _, gettext_lazy @@ -44,7 +43,7 @@ def delete_selected(modeladmin, request, queryset): n = queryset.count() if n: for obj in queryset: - obj_display = force_text(obj) + obj_display = str(obj) modeladmin.log_deletion(request, obj, obj_display) queryset.delete() modeladmin.message_user(request, _("Successfully deleted %(count)d %(items)s.") % { @@ -63,7 +62,7 @@ def delete_selected(modeladmin, request, queryset): context = dict( modeladmin.admin_site.each_context(request), title=title, - objects_name=objects_name, + objects_name=str(objects_name), deletable_objects=[deletable_objects], model_count=dict(model_count).items(), queryset=queryset, diff --git a/django/contrib/admin/filters.py b/django/contrib/admin/filters.py index a15bea9414..9dbd8a8f26 100644 --- a/django/contrib/admin/filters.py +++ b/django/contrib/admin/filters.py @@ -14,7 +14,6 @@ from django.contrib.admin.utils import ( from django.core.exceptions import ImproperlyConfigured, ValidationError from django.db import models from django.utils import timezone -from django.utils.encoding import force_text from django.utils.translation import gettext_lazy as _ @@ -107,7 +106,7 @@ class SimpleListFilter(ListFilter): } for lookup, title in self.lookup_choices: yield { - 'selected': self.value() == force_text(lookup), + 'selected': self.value() == str(lookup), 'query_string': changelist.get_query_string({self.parameter_name: lookup}, []), 'display': title, } @@ -204,7 +203,7 @@ class RelatedFieldListFilter(FieldListFilter): } for pk_val, val in self.lookup_choices: yield { - 'selected': self.lookup_val == force_text(pk_val), + 'selected': self.lookup_val == str(pk_val), 'query_string': changelist.get_query_string({ self.lookup_kwarg: pk_val, }, [self.lookup_kwarg_isnull]), @@ -290,7 +289,7 @@ class ChoicesFieldListFilter(FieldListFilter): none_title = title continue yield { - 'selected': force_text(lookup) == self.lookup_val, + 'selected': str(lookup) == self.lookup_val, 'query_string': changelist.get_query_string( {self.lookup_kwarg: lookup}, [self.lookup_kwarg_isnull] ), @@ -415,7 +414,7 @@ class AllValuesFieldListFilter(FieldListFilter): if val is None: include_none = True continue - val = force_text(val) + val = str(val) yield { 'selected': self.lookup_val == val, 'query_string': changelist.get_query_string({ diff --git a/django/contrib/admin/models.py b/django/contrib/admin/models.py index ca4df1d970..82b3cc0585 100644 --- a/django/contrib/admin/models.py +++ b/django/contrib/admin/models.py @@ -6,7 +6,6 @@ from django.contrib.contenttypes.models import ContentType from django.db import models from django.urls import NoReverseMatch, reverse from django.utils import timezone -from django.utils.encoding import force_text from django.utils.text import get_text_list from django.utils.translation import gettext, gettext_lazy as _ @@ -24,7 +23,7 @@ class LogEntryManager(models.Manager): return self.model.objects.create( user_id=user_id, content_type_id=content_type_id, - object_id=force_text(object_id), + object_id=str(object_id), object_repr=object_repr[:200], action_flag=action_flag, change_message=change_message, @@ -64,7 +63,7 @@ class LogEntry(models.Model): ordering = ('-action_time',) def __repr__(self): - return force_text(self.action_time) + return str(self.action_time) def __str__(self): if self.is_addition(): diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index c3801889f6..80540aa7e6 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -38,7 +38,6 @@ from django.http.response import HttpResponseBase from django.template.response import SimpleTemplateResponse, TemplateResponse from django.urls import reverse from django.utils.decorators import method_decorator -from django.utils.encoding import force_text from django.utils.html import format_html from django.utils.http import urlencode from django.utils.safestring import mark_safe @@ -722,7 +721,7 @@ class ModelAdmin(BaseModelAdmin): user_id=request.user.pk, content_type_id=get_content_type_for_model(object).pk, object_id=object.pk, - object_repr=force_text(object), + object_repr=str(object), action_flag=ADDITION, change_message=message, ) @@ -738,7 +737,7 @@ class ModelAdmin(BaseModelAdmin): user_id=request.user.pk, content_type_id=get_content_type_for_model(object).pk, object_id=object.pk, - object_repr=force_text(object), + object_repr=str(object), action_flag=CHANGE, change_message=message, ) @@ -763,7 +762,7 @@ class ModelAdmin(BaseModelAdmin): """ A list_display column containing a checkbox widget. """ - return helpers.checkbox.render(helpers.ACTION_CHECKBOX_NAME, force_text(obj.pk)) + return helpers.checkbox.render(helpers.ACTION_CHECKBOX_NAME, str(obj.pk)) action_checkbox.short_description = mark_safe('') def get_actions(self, request): @@ -1056,7 +1055,7 @@ class ModelAdmin(BaseModelAdmin): if self.has_change_permission(request, obj): obj_repr = format_html('{}', urlquote(obj_url), obj) else: - obj_repr = force_text(obj) + obj_repr = str(obj) msg_dict = { 'name': opts.verbose_name, 'obj': obj_repr, @@ -1652,7 +1651,7 @@ class ModelAdmin(BaseModelAdmin): context = dict( self.admin_site.each_context(request), - module_name=force_text(opts.verbose_name_plural), + module_name=str(opts.verbose_name_plural), selection_note=_('0 of %(cnt)s selected') % {'cnt': len(cl.result_list)}, selection_note_all=selection_note_all % {'total_count': cl.result_count}, title=cl.title, @@ -1710,7 +1709,7 @@ class ModelAdmin(BaseModelAdmin): if request.POST and not protected: # The user has confirmed the deletion. if perms_needed: raise PermissionDenied - obj_display = force_text(obj) + obj_display = str(obj) attr = str(to_field) if to_field else opts.pk.attname obj_id = obj.serializable_value(attr) self.log_deletion(request, obj, obj_display) @@ -1718,7 +1717,7 @@ class ModelAdmin(BaseModelAdmin): return self.response_delete(request, obj_display, obj_id) - object_name = force_text(opts.verbose_name) + object_name = str(opts.verbose_name) if perms_needed or protected: title = _("Cannot delete %(name)s") % {"name": object_name} @@ -1769,7 +1768,7 @@ class ModelAdmin(BaseModelAdmin): self.admin_site.each_context(request), title=_('Change history: %s') % obj, action_list=action_list, - module_name=capfirst(force_text(opts.verbose_name_plural)), + module_name=str(capfirst(opts.verbose_name_plural)), object=obj, opts=opts, preserved_filters=self.get_preserved_filters(request), diff --git a/django/contrib/admin/templatetags/admin_list.py b/django/contrib/admin/templatetags/admin_list.py index 6901c4cec9..75b117f34e 100644 --- a/django/contrib/admin/templatetags/admin_list.py +++ b/django/contrib/admin/templatetags/admin_list.py @@ -14,7 +14,6 @@ from django.template.loader import get_template from django.templatetags.static import static from django.urls import NoReverseMatch from django.utils import formats -from django.utils.encoding import force_text from django.utils.html import format_html from django.utils.safestring import mark_safe from django.utils.text import capfirst @@ -233,7 +232,7 @@ def items_for_result(cl, result, form): result_repr = display_for_field(value, f, empty_value_display) if isinstance(f, (models.DateField, models.TimeField, models.ForeignKey)): row_classes.append('nowrap') - if force_text(result_repr) == '': + if str(result_repr) == '': result_repr = mark_safe(' ') row_class = mark_safe(' class="%s"' % ' '.join(row_classes)) # If list_display_links not defined, add the link tag to the first field @@ -277,7 +276,7 @@ def items_for_result(cl, result, form): field_name == cl.model._meta.pk.name and form[cl.model._meta.pk.name].is_hidden)): bf = form[field_name] - result_repr = mark_safe(force_text(bf.errors) + force_text(bf)) + result_repr = mark_safe(str(bf.errors) + str(bf)) yield format_html('{}', row_class, result_repr) if form and not form[cl.model._meta.pk.name].is_hidden: yield format_html('{}', form[cl.model._meta.pk.name]) diff --git a/django/contrib/admin/utils.py b/django/contrib/admin/utils.py index 6e59c83548..05fbcebcb9 100644 --- a/django/contrib/admin/utils.py +++ b/django/contrib/admin/utils.py @@ -11,7 +11,6 @@ from django.db.models.sql.constants import QUERY_TERMS from django.forms.utils import pretty_name from django.urls import NoReverseMatch, reverse from django.utils import formats, timezone -from django.utils.encoding import force_text from django.utils.html import format_html from django.utils.text import capfirst from django.utils.translation import ngettext, override as translation_override @@ -338,7 +337,7 @@ def label_for_field(name, model, model_admin=None, return_attr=False): label = field.related_model._meta.verbose_name except FieldDoesNotExist: if name == "__str__": - label = force_text(model._meta.verbose_name) + label = str(model._meta.verbose_name) attr = str else: if callable(name): @@ -427,9 +426,9 @@ def display_for_value(value, empty_value_display, boolean=False): elif isinstance(value, (int, decimal.Decimal, float)): return formats.number_format(value) elif isinstance(value, (list, tuple)): - return ', '.join(force_text(v) for v in value) + return ', '.join(str(v) for v in value) else: - return force_text(value) + return str(value) class NotRelationField(Exception): @@ -512,23 +511,23 @@ def construct_change_message(form, formsets, add): for added_object in formset.new_objects: change_message.append({ 'added': { - 'name': force_text(added_object._meta.verbose_name), - 'object': force_text(added_object), + 'name': str(added_object._meta.verbose_name), + 'object': str(added_object), } }) for changed_object, changed_fields in formset.changed_objects: change_message.append({ 'changed': { - 'name': force_text(changed_object._meta.verbose_name), - 'object': force_text(changed_object), + 'name': str(changed_object._meta.verbose_name), + 'object': str(changed_object), 'fields': changed_fields, } }) for deleted_object in formset.deleted_objects: change_message.append({ 'deleted': { - 'name': force_text(deleted_object._meta.verbose_name), - 'object': force_text(deleted_object), + 'name': str(deleted_object._meta.verbose_name), + 'object': str(deleted_object), } }) return change_message diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py index f073c56c8d..4b932f2c01 100644 --- a/django/contrib/admin/widgets.py +++ b/django/contrib/admin/widgets.py @@ -7,7 +7,6 @@ from django import forms from django.db.models.deletion import CASCADE from django.urls import reverse from django.urls.exceptions import NoReverseMatch -from django.utils.encoding import force_text from django.utils.html import smart_urlquote from django.utils.safestring import mark_safe from django.utils.text import Truncator @@ -215,7 +214,7 @@ class ManyToManyRawIdWidget(ForeignKeyRawIdWidget): return value.split(',') def format_value(self, value): - return ','.join(force_text(v) for v in value) if value else '' + return ','.join(str(v) for v in value) if value else '' class RelatedFieldWidgetWrapper(forms.Widget): diff --git a/django/contrib/auth/mixins.py b/django/contrib/auth/mixins.py index 0949b539c0..c6011225c4 100644 --- a/django/contrib/auth/mixins.py +++ b/django/contrib/auth/mixins.py @@ -2,7 +2,6 @@ from django.conf import settings from django.contrib.auth import REDIRECT_FIELD_NAME from django.contrib.auth.views import redirect_to_login from django.core.exceptions import ImproperlyConfigured, PermissionDenied -from django.utils.encoding import force_text class AccessMixin: @@ -25,7 +24,7 @@ class AccessMixin: '{0} is missing the login_url attribute. Define {0}.login_url, settings.LOGIN_URL, or override ' '{0}.get_login_url().'.format(self.__class__.__name__) ) - return force_text(login_url) + return str(login_url) def get_permission_denied_message(self): """ diff --git a/django/contrib/auth/password_validation.py b/django/contrib/auth/password_validation.py index 926bb69904..1350dec9c8 100644 --- a/django/contrib/auth/password_validation.py +++ b/django/contrib/auth/password_validation.py @@ -8,7 +8,6 @@ from django.conf import settings from django.core.exceptions import ( FieldDoesNotExist, ImproperlyConfigured, ValidationError, ) -from django.utils.encoding import force_text from django.utils.functional import lazy from django.utils.html import format_html from django.utils.module_loading import import_string @@ -145,7 +144,7 @@ class UserAttributeSimilarityValidator: for value_part in value_parts: if SequenceMatcher(a=password.lower(), b=value_part.lower()).quick_ratio() >= self.max_similarity: try: - verbose_name = force_text(user._meta.get_field(attribute_name).verbose_name) + verbose_name = str(user._meta.get_field(attribute_name).verbose_name) except FieldDoesNotExist: verbose_name = attribute_name raise ValidationError( diff --git a/django/contrib/contenttypes/fields.py b/django/contrib/contenttypes/fields.py index df6113f462..fc269bbc73 100644 --- a/django/contrib/contenttypes/fields.py +++ b/django/contrib/contenttypes/fields.py @@ -11,7 +11,6 @@ from django.db.models.fields.related import ( lazy_related_operation, ) from django.db.models.query_utils import PathInfo -from django.utils.encoding import force_text from django.utils.functional import cached_property @@ -398,7 +397,7 @@ class GenericRelation(ForeignObject): def value_to_string(self, obj): qs = getattr(obj, self.name).all() - return force_text([instance._get_pk_val() for instance in qs]) + return str([instance._get_pk_val() for instance in qs]) def contribute_to_class(self, cls, name, **kwargs): kwargs['private_only'] = True diff --git a/django/contrib/contenttypes/models.py b/django/contrib/contenttypes/models.py index 4b9bcdc3ba..ad2e0bc904 100644 --- a/django/contrib/contenttypes/models.py +++ b/django/contrib/contenttypes/models.py @@ -2,7 +2,6 @@ from collections import defaultdict from django.apps import apps from django.db import models -from django.utils.encoding import force_text from django.utils.translation import gettext_lazy as _ @@ -151,7 +150,7 @@ class ContentType(models.Model): model = self.model_class() if not model: return self.model - return force_text(model._meta.verbose_name) + return str(model._meta.verbose_name) def model_class(self): """Return the model class for this type of content.""" diff --git a/django/contrib/postgres/fields/hstore.py b/django/contrib/postgres/fields/hstore.py index e0226a8d4a..82f731f8bf 100644 --- a/django/contrib/postgres/fields/hstore.py +++ b/django/contrib/postgres/fields/hstore.py @@ -4,7 +4,6 @@ from django.contrib.postgres import forms, lookups from django.contrib.postgres.fields.array import ArrayField from django.core import exceptions from django.db.models import Field, TextField, Transform -from django.utils.encoding import force_text from django.utils.translation import gettext_lazy as _ __all__ = ['HStoreField'] @@ -57,14 +56,14 @@ class HStoreField(Field): if isinstance(value, dict): prep_value = {} for key, val in value.items(): - key = force_text(key) + key = str(key) if val is not None: - val = force_text(val) + val = str(val) prep_value[key] = val value = prep_value if isinstance(value, list): - value = [force_text(item) for item in value] + value = [str(item) for item in value] return value diff --git a/django/contrib/postgres/lookups.py b/django/contrib/postgres/lookups.py index 8521b26c6e..afef01ef9e 100644 --- a/django/contrib/postgres/lookups.py +++ b/django/contrib/postgres/lookups.py @@ -1,5 +1,4 @@ from django.db.models import Lookup, Transform -from django.utils.encoding import force_text from .search import SearchVector, SearchVectorExact, SearchVectorField @@ -38,7 +37,7 @@ class HasKeys(PostgresSimpleLookup): operator = '?&' def get_prep_lookup(self): - return [force_text(item) for item in self.rhs] + return [str(item) for item in self.rhs] class HasAnyKeys(HasKeys): diff --git a/django/contrib/sessions/backends/base.py b/django/contrib/sessions/backends/base.py index a395b589b1..64955b8bb7 100644 --- a/django/contrib/sessions/backends/base.py +++ b/django/contrib/sessions/backends/base.py @@ -10,7 +10,7 @@ from django.utils import timezone from django.utils.crypto import ( constant_time_compare, get_random_string, salted_hmac, ) -from django.utils.encoding import force_bytes, force_text +from django.utils.encoding import force_bytes from django.utils.module_loading import import_string # session_key should not be case sensitive because some backends can store it @@ -112,7 +112,7 @@ class SessionBase: # these happen, just return an empty dictionary (an empty session). if isinstance(e, SuspiciousOperation): logger = logging.getLogger('django.security.%s' % e.__class__.__name__) - logger.warning(force_text(e)) + logger.warning(str(e)) return {} def update(self, dict_): diff --git a/django/contrib/sessions/backends/cached_db.py b/django/contrib/sessions/backends/cached_db.py index fd8cc9ebc1..dc5eb27c3d 100644 --- a/django/contrib/sessions/backends/cached_db.py +++ b/django/contrib/sessions/backends/cached_db.py @@ -9,7 +9,6 @@ from django.contrib.sessions.backends.db import SessionStore as DBStore from django.core.cache import caches from django.core.exceptions import SuspiciousOperation from django.utils import timezone -from django.utils.encoding import force_text KEY_PREFIX = "django.contrib.sessions.cached_db" @@ -49,7 +48,7 @@ class SessionStore(DBStore): except (self.model.DoesNotExist, SuspiciousOperation) as e: if isinstance(e, SuspiciousOperation): logger = logging.getLogger('django.security.%s' % e.__class__.__name__) - logger.warning(force_text(e)) + logger.warning(str(e)) self._session_key = None data = {} return data diff --git a/django/contrib/sessions/backends/db.py b/django/contrib/sessions/backends/db.py index 18691c9a27..43bfde628c 100644 --- a/django/contrib/sessions/backends/db.py +++ b/django/contrib/sessions/backends/db.py @@ -6,7 +6,6 @@ from django.contrib.sessions.backends.base import ( from django.core.exceptions import SuspiciousOperation from django.db import DatabaseError, IntegrityError, router, transaction from django.utils import timezone -from django.utils.encoding import force_text from django.utils.functional import cached_property @@ -38,7 +37,7 @@ class SessionStore(SessionBase): except (self.model.DoesNotExist, SuspiciousOperation) as e: if isinstance(e, SuspiciousOperation): logger = logging.getLogger('django.security.%s' % e.__class__.__name__) - logger.warning(force_text(e)) + logger.warning(str(e)) self._session_key = None return {} diff --git a/django/contrib/sessions/backends/file.py b/django/contrib/sessions/backends/file.py index aeec2520fc..818c90e3ba 100644 --- a/django/contrib/sessions/backends/file.py +++ b/django/contrib/sessions/backends/file.py @@ -11,7 +11,6 @@ from django.contrib.sessions.backends.base import ( from django.contrib.sessions.exceptions import InvalidSessionKey from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation from django.utils import timezone -from django.utils.encoding import force_text class SessionStore(SessionBase): @@ -92,7 +91,7 @@ class SessionStore(SessionBase): except (EOFError, SuspiciousOperation) as e: if isinstance(e, SuspiciousOperation): logger = logging.getLogger('django.security.%s' % e.__class__.__name__) - logger.warning(force_text(e)) + logger.warning(str(e)) self.create() # Remove expired sessions. diff --git a/django/contrib/syndication/views.py b/django/contrib/syndication/views.py index 34aa3b7ba6..a8b98c84ae 100644 --- a/django/contrib/syndication/views.py +++ b/django/contrib/syndication/views.py @@ -6,7 +6,7 @@ from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist from django.http import Http404, HttpResponse from django.template import TemplateDoesNotExist, loader from django.utils import feedgenerator -from django.utils.encoding import force_text, iri_to_uri +from django.utils.encoding import iri_to_uri from django.utils.html import escape from django.utils.http import http_date from django.utils.timezone import get_default_timezone, is_naive, make_aware @@ -48,10 +48,10 @@ class Feed: def item_title(self, item): # Titles should be double escaped by default (see #6533) - return escape(force_text(item)) + return escape(str(item)) def item_description(self, item): - return force_text(item) + return str(item) def item_link(self, item): try: @@ -66,9 +66,9 @@ class Feed: enc_url = self._get_dynamic_attr('item_enclosure_url', item) if enc_url: enc = feedgenerator.Enclosure( - url=force_text(enc_url), - length=force_text(self._get_dynamic_attr('item_enclosure_length', item)), - mime_type=force_text(self._get_dynamic_attr('item_enclosure_mime_type', item)), + url=str(enc_url), + length=str(self._get_dynamic_attr('item_enclosure_length', item)), + mime_type=str(self._get_dynamic_attr('item_enclosure_mime_type', item)), ) return [enc] return [] diff --git a/django/core/checks/messages.py b/django/core/checks/messages.py index 62bd909c6f..aacac632eb 100644 --- a/django/core/checks/messages.py +++ b/django/core/checks/messages.py @@ -1,5 +1,3 @@ -from django.utils.encoding import force_text - # Levels DEBUG = 10 INFO = 20 @@ -35,7 +33,7 @@ class CheckMessage: # method doesn't return "applabel.modellabel" and cannot be changed. obj = self.obj._meta.label else: - obj = force_text(self.obj) + obj = str(self.obj) id = "(%s) " % self.id if self.id else "" hint = "\n\tHINT: %s" % self.hint if self.hint else '' return "%s: %s%s%s" % (obj, id, self.msg, hint) diff --git a/django/core/exceptions.py b/django/core/exceptions.py index 983053ca5b..04ed3bd205 100644 --- a/django/core/exceptions.py +++ b/django/core/exceptions.py @@ -1,7 +1,6 @@ """ Global Django exception and warning classes. """ -from django.utils.encoding import force_text class FieldDoesNotExist(Exception): @@ -174,7 +173,7 @@ class ValidationError(Exception): message = error.message if error.params: message %= error.params - yield force_text(message) + yield str(message) def __str__(self): if hasattr(self, 'error_dict'): diff --git a/django/core/handlers/exception.py b/django/core/handlers/exception.py index 91baa54ef6..be2e4734ee 100644 --- a/django/core/handlers/exception.py +++ b/django/core/handlers/exception.py @@ -11,7 +11,6 @@ from django.core.exceptions import ( from django.http import Http404 from django.http.multipartparser import MultiPartParserError from django.urls import get_resolver, get_urlconf -from django.utils.encoding import force_text from django.views import debug logger = logging.getLogger('django.request') @@ -71,7 +70,7 @@ def response_for_exception(request, exc): # The security logger receives events for all SuspiciousOperations security_logger = logging.getLogger('django.security.%s' % exc.__class__.__name__) security_logger.error( - force_text(exc), + str(exc), extra={'status_code': 400, 'request': request}, ) if settings.DEBUG: diff --git a/django/core/mail/message.py b/django/core/mail/message.py index 51023768e6..73329449b6 100644 --- a/django/core/mail/message.py +++ b/django/core/mail/message.py @@ -55,7 +55,7 @@ ADDRESS_HEADERS = { def forbid_multi_line_headers(name, val, encoding): """Forbid multi-line headers to prevent header injection.""" encoding = encoding or settings.DEFAULT_CHARSET - val = force_text(val) + val = str(val) # val may be lazy if '\n' in val or '\r' in val: raise BadHeaderError("Header values can't contain newlines (got %r for header %r)" % (val, name)) try: @@ -100,7 +100,7 @@ def sanitize_address(addr, encoding): Format a pair of (name, address) or an email address string. """ if not isinstance(addr, tuple): - addr = parseaddr(force_text(addr)) + addr = parseaddr(addr) nm, addr = addr localpart, domain = None, None nm = Header(nm, encoding).encode() @@ -258,11 +258,11 @@ class EmailMessage: msg = self._create_message(msg) msg['Subject'] = self.subject msg['From'] = self.extra_headers.get('From', self.from_email) - msg['To'] = self.extra_headers.get('To', ', '.join(map(force_text, self.to))) + msg['To'] = self.extra_headers.get('To', ', '.join(map(str, self.to))) if self.cc: - msg['Cc'] = ', '.join(map(force_text, self.cc)) + msg['Cc'] = ', '.join(map(str, self.cc)) if self.reply_to: - msg['Reply-To'] = self.extra_headers.get('Reply-To', ', '.join(map(force_text, self.reply_to))) + msg['Reply-To'] = self.extra_headers.get('Reply-To', ', '.join(map(str, self.reply_to))) # Email header names are case-insensitive (RFC 2045), so we have to # accommodate that when doing comparisons. diff --git a/django/core/serializers/xml_serializer.py b/django/core/serializers/xml_serializer.py index cf860d1d24..23a07ef43a 100644 --- a/django/core/serializers/xml_serializer.py +++ b/django/core/serializers/xml_serializer.py @@ -11,7 +11,6 @@ from django.apps import apps from django.conf import settings from django.core.serializers import base from django.db import DEFAULT_DB_ALIAS, models -from django.utils.encoding import force_text from django.utils.xmlutils import ( SimplerXMLGenerator, UnserializableContentError, ) @@ -48,11 +47,11 @@ class Serializer(base.Serializer): raise base.SerializationError("Non-model object (%s) encountered during serialization" % type(obj)) self.indent(1) - attrs = OrderedDict([("model", force_text(obj._meta))]) + attrs = OrderedDict([("model", str(obj._meta))]) if not self.use_natural_primary_keys or not hasattr(obj, 'natural_key'): obj_pk = obj._get_pk_val() if obj_pk is not None: - attrs['pk'] = force_text(obj_pk) + attrs['pk'] = str(obj_pk) self.xml.startElement("object", attrs) @@ -101,10 +100,10 @@ class Serializer(base.Serializer): # Iterable natural keys are rolled out as subelements for key_value in related: self.xml.startElement("natural", {}) - self.xml.characters(force_text(key_value)) + self.xml.characters(str(key_value)) self.xml.endElement("natural") else: - self.xml.characters(force_text(related_att)) + self.xml.characters(str(related_att)) else: self.xml.addQuickElement("None") self.xml.endElement("field") @@ -125,13 +124,13 @@ class Serializer(base.Serializer): self.xml.startElement("object", {}) for key_value in natural: self.xml.startElement("natural", {}) - self.xml.characters(force_text(key_value)) + self.xml.characters(str(key_value)) self.xml.endElement("natural") self.xml.endElement("object") else: def handle_m2m(value): self.xml.addQuickElement("object", attrs={ - 'pk': force_text(value._get_pk_val()) + 'pk': str(value._get_pk_val()) }) for relobj in getattr(obj, field.name).iterator(): handle_m2m(relobj) @@ -144,7 +143,7 @@ class Serializer(base.Serializer): self.xml.startElement("field", OrderedDict([ ("name", field.name), ("rel", field.remote_field.__class__.__name__), - ("to", force_text(field.remote_field.model._meta)), + ("to", str(field.remote_field.model._meta)), ])) diff --git a/django/core/validators.py b/django/core/validators.py index 89568cfb05..e454ca213b 100644 --- a/django/core/validators.py +++ b/django/core/validators.py @@ -5,7 +5,6 @@ from urllib.parse import urlsplit, urlunsplit from django.core.exceptions import ValidationError from django.utils.deconstruct import deconstructible -from django.utils.encoding import force_text from django.utils.functional import SimpleLazyObject from django.utils.ipv6 import is_valid_ipv6_address from django.utils.translation import gettext_lazy as _, ngettext_lazy @@ -55,8 +54,7 @@ class RegexValidator: Validate that the input contains a match for the regular expression if inverse_match is False, otherwise raise ValidationError. """ - if not (self.inverse_match is not bool(self.regex.search( - force_text(value)))): + if not (self.inverse_match is not bool(self.regex.search(str(value)))): raise ValidationError(self.message, code=self.code) def __eq__(self, other): diff --git a/django/db/backends/base/operations.py b/django/db/backends/base/operations.py index d1149cc0fe..c3df3465c3 100644 --- a/django/db/backends/base/operations.py +++ b/django/db/backends/base/operations.py @@ -410,7 +410,7 @@ class BaseDatabaseOperations: def prep_for_like_query(self, x): """Prepare a value for use in a LIKE query.""" - return force_text(x).replace("\\", "\\\\").replace("%", r"\%").replace("_", r"\_") + return str(x).replace("\\", "\\\\").replace("%", r"\%").replace("_", r"\_") # Same as prep_for_like_query(), but called for "iexact" matches, which # need not necessarily be implemented using "LIKE" in the backend. diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py index c234a382ca..7806402055 100644 --- a/django/db/backends/sqlite3/base.py +++ b/django/db/backends/sqlite3/base.py @@ -16,7 +16,6 @@ from django.utils import timezone from django.utils.dateparse import ( parse_date, parse_datetime, parse_duration, parse_time, ) -from django.utils.encoding import force_text from .client import DatabaseClient # isort:skip from .creation import DatabaseCreation # isort:skip @@ -456,7 +455,7 @@ def _sqlite_timestamp_diff(lhs, rhs): def _sqlite_regexp(re_pattern, re_string): - return bool(re.search(re_pattern, force_text(re_string))) if re_string is not None else False + return bool(re.search(re_pattern, str(re_string))) if re_string is not None else False def _sqlite_power(x, y): diff --git a/django/db/migrations/serializer.py b/django/db/migrations/serializer.py index f56b505381..9c135a5fa9 100644 --- a/django/db/migrations/serializer.py +++ b/django/db/migrations/serializer.py @@ -13,7 +13,6 @@ from django.db import models from django.db.migrations.operations.base import Operation from django.db.migrations.utils import COMPILED_REGEX_TYPE, RegexObject from django.utils import datetime_safe -from django.utils.encoding import force_text from django.utils.functional import LazyObject, Promise from django.utils.timezone import utc from django.utils.version import get_docs_version @@ -303,7 +302,7 @@ class UUIDSerializer(BaseSerializer): def serializer_factory(value): from django.db.migrations.writer import SettingsReference if isinstance(value, Promise): - value = force_text(value) + value = str(value) elif isinstance(value, LazyObject): # The unwrapped value is returned as the first item of the arguments # tuple. diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index 95630ebb4e..ee48a02d92 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -25,7 +25,7 @@ from django.utils.dateparse import ( parse_date, parse_datetime, parse_duration, parse_time, ) from django.utils.duration import duration_string -from django.utils.encoding import force_bytes, force_text, smart_text +from django.utils.encoding import force_bytes, smart_text from django.utils.functional import Promise, cached_property, curry from django.utils.ipv6 import clean_ipv6_address from django.utils.itercompat import is_iterable @@ -805,7 +805,7 @@ class Field(RegisterLookupMixin): Return a string value of this field from the passed obj. This is used by the serialization framework. """ - return force_text(self.value_from_object(obj)) + return str(self.value_from_object(obj)) def _get_flatchoices(self): """Flattened version of choices tuple.""" @@ -1058,7 +1058,7 @@ class CharField(Field): def to_python(self, value): if isinstance(value, str) or value is None: return value - return force_text(value) + return str(value) def get_prep_value(self, value): value = super().get_prep_value(value) @@ -1920,7 +1920,7 @@ class GenericIPAddressField(Field): if value is None: return None if not isinstance(value, str): - value = force_text(value) + value = str(value) value = value.strip() if ':' in value: return clean_ipv6_address(value, self.unpack_ipv4, self.error_messages['invalid']) @@ -2089,7 +2089,7 @@ class TextField(Field): def to_python(self, value): if isinstance(value, str) or value is None: return value - return force_text(value) + return str(value) def get_prep_value(self, value): value = super().get_prep_value(value) diff --git a/django/db/models/fields/reverse_related.py b/django/db/models/fields/reverse_related.py index 3e0285abea..36396ac282 100644 --- a/django/db/models/fields/reverse_related.py +++ b/django/db/models/fields/reverse_related.py @@ -10,7 +10,6 @@ they're the closest concept currently available. """ from django.core import exceptions -from django.utils.encoding import force_text from django.utils.functional import cached_property from . import BLANK_CHOICE_DASH @@ -123,7 +122,7 @@ class ForeignObjectRel: initially for utilization by RelatedFieldListFilter. """ return (blank_choice if include_blank else []) + [ - (x._get_pk_val(), force_text(x)) for x in self.related_model._default_manager.all() + (x._get_pk_val(), str(x)) for x in self.related_model._default_manager.all() ] def is_hidden(self): diff --git a/django/db/models/options.py b/django/db/models/options.py index c8f7ade82c..cd50ff0aaa 100644 --- a/django/db/models/options.py +++ b/django/db/models/options.py @@ -14,7 +14,6 @@ from django.db.models.fields.proxy import OrderWrt from django.db.models.query_utils import PathInfo from django.utils.datastructures import ImmutableList, OrderedSet from django.utils.deprecation import RemovedInDjango21Warning -from django.utils.encoding import force_text from django.utils.functional import cached_property from django.utils.text import camel_case_to_spaces, format_lazy from django.utils.translation import override @@ -317,7 +316,7 @@ class Options: def verbose_name_raw(self): """Return the untranslated verbose name.""" with override(None): - return force_text(self.verbose_name) + return str(self.verbose_name) @property def swapped(self): diff --git a/django/forms/boundfield.py b/django/forms/boundfield.py index f3f6e3aea0..128d9ce80a 100644 --- a/django/forms/boundfield.py +++ b/django/forms/boundfield.py @@ -4,7 +4,6 @@ import warnings from django.forms.utils import flatatt, pretty_name from django.forms.widgets import Textarea, TextInput from django.utils.deprecation import RemovedInDjango21Warning -from django.utils.encoding import force_text from django.utils.functional import cached_property from django.utils.html import conditional_escape, format_html, html_safe from django.utils.inspect import func_supports_parameter @@ -213,9 +212,9 @@ class BoundField: Calculate and return the ID attribute for this BoundField, if the associated Form has specified auto_id. Return an empty string otherwise. """ - auto_id = self.form.auto_id - if auto_id and '%s' in force_text(auto_id): - return force_text(auto_id) % self.html_name + auto_id = self.form.auto_id # Boolean or string + if auto_id and '%s' in str(auto_id): + return auto_id % self.html_name elif auto_id: return self.html_name return '' diff --git a/django/forms/fields.py b/django/forms/fields.py index e42c1597c7..a4132d8f33 100644 --- a/django/forms/fields.py +++ b/django/forms/fields.py @@ -28,7 +28,6 @@ from django.forms.widgets import ( from django.utils import formats from django.utils.dateparse import parse_duration from django.utils.duration import duration_string -from django.utils.encoding import force_text from django.utils.ipv6 import clean_ipv6_address from django.utils.translation import gettext_lazy as _, ngettext_lazy @@ -221,7 +220,7 @@ class CharField(Field): """Return a string.""" if value in self.empty_values: return self.empty_value - value = force_text(value) + value = str(value) if self.strip: value = value.strip() return value @@ -268,7 +267,7 @@ class IntegerField(Field): value = formats.sanitize_separators(value) # Strip trailing decimal and zeros. try: - value = int(self.re_decimal.sub('', force_text(value))) + value = int(self.re_decimal.sub('', str(value))) except (ValueError, TypeError): raise ValidationError(self.error_messages['invalid'], code='invalid') return value @@ -341,7 +340,7 @@ class DecimalField(IntegerField): return None if self.localize: value = formats.sanitize_separators(value) - value = force_text(value).strip() + value = str(value).strip() try: value = Decimal(value) except DecimalException: @@ -484,7 +483,7 @@ class DurationField(Field): return None if isinstance(value, datetime.timedelta): return value - value = parse_duration(force_text(value)) + value = parse_duration(str(value)) if value is None: raise ValidationError(self.error_messages['invalid'], code='invalid') return value @@ -784,7 +783,7 @@ class ChoiceField(Field): """Return a string.""" if value in self.empty_values: return '' - return force_text(value) + return str(value) def validate(self, value): """Validate that the input is in self.choices.""" @@ -798,15 +797,15 @@ class ChoiceField(Field): def valid_value(self, value): """Check to see if the provided value is a valid choice.""" - text_value = force_text(value) + text_value = str(value) for k, v in self.choices: if isinstance(v, (list, tuple)): # This is an optgroup, so look inside the group for options for k2, v2 in v: - if value == k2 or text_value == force_text(k2): + if value == k2 or text_value == str(k2): return True else: - if value == k or text_value == force_text(k): + if value == k or text_value == str(k): return True return False @@ -851,7 +850,7 @@ class MultipleChoiceField(ChoiceField): return [] elif not isinstance(value, (list, tuple)): raise ValidationError(self.error_messages['invalid_list'], code='invalid_list') - return [force_text(val) for val in value] + return [str(val) for val in value] def validate(self, value): """Validate that the input is a list or tuple.""" @@ -873,8 +872,8 @@ class MultipleChoiceField(ChoiceField): data = [] if len(initial) != len(data): return True - initial_set = set(force_text(value) for value in initial) - data_set = set(force_text(value) for value in data) + initial_set = set(str(value) for value in initial) + data_set = set(str(value) for value in data) return data_set != initial_set diff --git a/django/forms/forms.py b/django/forms/forms.py index 80613570ad..e2e7c645ff 100644 --- a/django/forms/forms.py +++ b/django/forms/forms.py @@ -12,7 +12,6 @@ from django.forms.fields import Field, FileField # pretty_name is imported for backwards compatibility in Django 1.9 from django.forms.utils import ErrorDict, ErrorList, pretty_name # NOQA from django.forms.widgets import Media, MediaDefiningClass -from django.utils.encoding import force_text from django.utils.functional import cached_property from django.utils.html import conditional_escape, html_safe from django.utils.safestring import mark_safe @@ -205,7 +204,7 @@ class BaseForm: if bf.is_hidden: if bf_errors: top_errors.extend( - [_('(Hidden field %(name)s) %(error)s') % {'name': name, 'error': force_text(e)} + [_('(Hidden field %(name)s) %(error)s') % {'name': name, 'error': str(e)} for e in bf_errors]) hidden_fields.append(str(bf)) else: @@ -216,10 +215,10 @@ class BaseForm: html_class_attr = ' class="%s"' % css_classes if errors_on_separate_row and bf_errors: - output.append(error_row % force_text(bf_errors)) + output.append(error_row % str(bf_errors)) if bf.label: - label = conditional_escape(force_text(bf.label)) + label = conditional_escape(bf.label) label = bf.label_tag(label) or '' else: label = '' diff --git a/django/forms/models.py b/django/forms/models.py index b259a8df28..adc31081b7 100644 --- a/django/forms/models.py +++ b/django/forms/models.py @@ -16,7 +16,6 @@ from django.forms.utils import ErrorList from django.forms.widgets import ( HiddenInput, MultipleHiddenInput, SelectMultiple, ) -from django.utils.encoding import force_text from django.utils.text import capfirst, get_text_list from django.utils.translation import gettext, gettext_lazy as _ @@ -1085,7 +1084,7 @@ class InlineForeignKeyField(Field): orig = getattr(self.parent_instance, self.to_field) else: orig = self.parent_instance.pk - if force_text(value) != force_text(orig): + if str(value) != str(orig): raise ValidationError(self.error_messages['invalid_choice'], code='invalid_choice') return self.parent_instance @@ -1176,7 +1175,7 @@ class ModelChoiceField(ChoiceField): presented by this object. Subclasses can override this method to customize the display of the choices. """ - return force_text(obj) + return str(obj) def _get_choices(self): # If self._choices is set, then somebody must have manually set @@ -1219,7 +1218,7 @@ class ModelChoiceField(ChoiceField): def has_changed(self, initial, data): initial_value = initial if initial is not None else '' data_value = data if data is not None else '' - return force_text(self.prepare_value(initial_value)) != force_text(data_value) + return str(self.prepare_value(initial_value)) != str(data_value) class ModelMultipleChoiceField(ModelChoiceField): @@ -1286,9 +1285,9 @@ class ModelMultipleChoiceField(ModelChoiceField): params={'pk': pk}, ) qs = self.queryset.filter(**{'%s__in' % key: value}) - pks = set(force_text(getattr(o, key)) for o in qs) + pks = set(str(getattr(o, key)) for o in qs) for val in value: - if force_text(val) not in pks: + if str(val) not in pks: raise ValidationError( self.error_messages['invalid_choice'], code='invalid_choice', @@ -1310,8 +1309,8 @@ class ModelMultipleChoiceField(ModelChoiceField): data = [] if len(initial) != len(data): return True - initial_set = set(force_text(value) for value in self.prepare_value(initial)) - data_set = set(force_text(value) for value in data) + initial_set = set(str(value) for value in self.prepare_value(initial)) + data_set = set(str(value) for value in data) return data_set != initial_set diff --git a/django/forms/widgets.py b/django/forms/widgets.py index 04f800785b..2feded85c7 100644 --- a/django/forms/widgets.py +++ b/django/forms/widgets.py @@ -12,7 +12,6 @@ from django.forms.utils import to_current_timezone from django.templatetags.static import static from django.utils import datetime_safe, formats from django.utils.dates import MONTHS -from django.utils.encoding import force_text from django.utils.formats import get_format from django.utils.html import format_html, html_safe from django.utils.safestring import mark_safe @@ -184,7 +183,7 @@ class Widget(metaclass=MediaDefiningClass): return None if self.is_localized: return formats.localize_input(value) - return force_text(value) + return str(value) def get_context(self, name, value, attrs): context = {} @@ -483,7 +482,7 @@ class CheckboxInput(Input): """Only return the 'value' attribute if value isn't empty.""" if value is True or value is False or value is None or value == '': return - return force_text(value) + return str(value) def get_context(self, name, value, attrs): if self.check_test(value): @@ -570,7 +569,7 @@ class ChoiceWidget(Widget): for subvalue, sublabel in choices: selected = ( - force_text(subvalue) in value and + str(subvalue) in value and (not has_selected or self.allow_multiple_selected) ) if selected and not has_selected: diff --git a/django/http/response.py b/django/http/response.py index e8417a9fd7..2495ebcc09 100644 --- a/django/http/response.py +++ b/django/http/response.py @@ -13,7 +13,7 @@ from django.core.exceptions import DisallowedRedirect from django.core.serializers.json import DjangoJSONEncoder from django.http.cookie import SimpleCookie from django.utils import timezone -from django.utils.encoding import force_bytes, force_text, iri_to_uri +from django.utils.encoding import force_bytes, iri_to_uri from django.utils.http import cookie_date _charset_from_content_type_re = re.compile(r';\s*charset=(?P[^\s;]+)', re.I) @@ -405,7 +405,7 @@ class HttpResponseRedirectBase(HttpResponse): def __init__(self, redirect_to, *args, **kwargs): super().__init__(*args, **kwargs) self['Location'] = iri_to_uri(redirect_to) - parsed = urlparse(force_text(redirect_to)) + parsed = urlparse(str(redirect_to)) if parsed.scheme and parsed.scheme not in self.allowed_schemes: raise DisallowedRedirect("Unsafe redirect to URL with protocol '%s'" % parsed.scheme) diff --git a/django/shortcuts.py b/django/shortcuts.py index 0ce7ee327f..6fe3d2d590 100644 --- a/django/shortcuts.py +++ b/django/shortcuts.py @@ -11,7 +11,6 @@ from django.http import ( from django.template import loader from django.urls import NoReverseMatch, reverse from django.utils.deprecation import RemovedInDjango30Warning -from django.utils.encoding import force_text from django.utils.functional import Promise @@ -138,7 +137,7 @@ def resolve_url(to, *args, **kwargs): if isinstance(to, Promise): # Expand the lazy instance, as it can cause issues when it is passed # further to some Python functions like urlparse. - to = force_text(to) + to = str(to) if isinstance(to, str): # Handle relative URLs diff --git a/django/urls/resolvers.py b/django/urls/resolvers.py index 9b8462903e..8bb80c86cb 100644 --- a/django/urls/resolvers.py +++ b/django/urls/resolvers.py @@ -16,7 +16,6 @@ from django.core.checks import Warning from django.core.checks.urls import check_resolver from django.core.exceptions import ImproperlyConfigured from django.utils.datastructures import MultiValueDict -from django.utils.encoding import force_text from django.utils.functional import cached_property from django.utils.http import RFC3986_SUBDELIMS from django.utils.regex_helper import normalize @@ -92,7 +91,7 @@ class LocaleRegexDescriptor: return instance.__dict__['regex'] language_code = get_language() if language_code not in instance._regex_dict: - instance._regex_dict[language_code] = self._compile(force_text(instance._regex)) + instance._regex_dict[language_code] = self._compile(str(instance._regex)) return instance._regex_dict[language_code] def _compile(self, regex): @@ -347,7 +346,7 @@ class RegexURLResolver(LocaleRegexProvider): return name in self._callback_strs def resolve(self, path): - path = force_text(path) # path may be a reverse_lazy object + path = str(path) # path may be a reverse_lazy object tried = [] match = self.regex.search(path) if match: @@ -422,8 +421,8 @@ class RegexURLResolver(LocaleRegexProvider): def _reverse_with_prefix(self, lookup_view, _prefix, *args, **kwargs): if args and kwargs: raise ValueError("Don't mix *args and **kwargs in call to reverse()!") - text_args = [force_text(v) for v in args] - text_kwargs = {k: force_text(v) for (k, v) in kwargs.items()} + text_args = [str(v) for v in args] + text_kwargs = {k: str(v) for (k, v) in kwargs.items()} if not self._populated: self._populate() diff --git a/django/utils/dateformat.py b/django/utils/dateformat.py index 6c8cab61f5..d3f586aacf 100644 --- a/django/utils/dateformat.py +++ b/django/utils/dateformat.py @@ -18,7 +18,6 @@ import time from django.utils.dates import ( MONTHS, MONTHS_3, MONTHS_ALT, MONTHS_AP, WEEKDAYS, WEEKDAYS_ABBR, ) -from django.utils.encoding import force_text from django.utils.timezone import get_default_timezone, is_aware, is_naive from django.utils.translation import gettext as _ @@ -29,14 +28,14 @@ re_escaped = re.compile(r'\\(.)') class Formatter: def format(self, formatstr): pieces = [] - for i, piece in enumerate(re_formatchars.split(force_text(formatstr))): + for i, piece in enumerate(re_formatchars.split(str(formatstr))): if i % 2: if type(self.data) is datetime.date and hasattr(TimeFormat, piece): raise TypeError( "The format for date objects may not contain " "time-related format specifiers (found '%s')." % piece ) - pieces.append(force_text(getattr(self, piece)())) + pieces.append(str(getattr(self, piece)())) elif piece: pieces.append(re_escaped.sub(r'\1', piece)) return ''.join(pieces) diff --git a/django/utils/html.py b/django/utils/html.py index 25843ce576..9f4f58c7a1 100644 --- a/django/utils/html.py +++ b/django/utils/html.py @@ -43,7 +43,7 @@ def escape(text): conditional_escape() instead. """ return mark_safe( - force_text(text).replace('&', '&').replace('<', '<') + str(text).replace('&', '&').replace('<', '<') .replace('>', '>').replace('"', '"').replace("'", ''') ) @@ -70,7 +70,7 @@ _js_escapes.update((ord('%c' % z), '\\u%04X' % z) for z in range(32)) @keep_lazy(str, SafeText) def escapejs(value): """Hex encode characters for use in JavaScript strings.""" - return mark_safe(force_text(value).translate(_js_escapes)) + return mark_safe(str(value).translate(_js_escapes)) def conditional_escape(text): @@ -121,8 +121,8 @@ def format_html_join(sep, format_string, args_generator): @keep_lazy_text def linebreaks(value, autoescape=False): """Convert newlines into

and
s.""" - value = normalize_newlines(force_text(value)) - paras = re.split('\n{2,}', value) + value = normalize_newlines(value) + paras = re.split('\n{2,}', str(value)) if autoescape: paras = ['

%s

' % escape(p).replace('\n', '
') for p in paras] else: diff --git a/django/utils/text.py b/django/utils/text.py index cd9e704ea1..3e04f8bec7 100644 --- a/django/utils/text.py +++ b/django/utils/text.py @@ -4,7 +4,6 @@ import unicodedata from gzip import GzipFile from io import BytesIO -from django.utils.encoding import force_text from django.utils.functional import ( SimpleLazyObject, keep_lazy, keep_lazy_text, lazy, ) @@ -15,7 +14,7 @@ from django.utils.translation import gettext as _, gettext_lazy, pgettext @keep_lazy_text def capfirst(x): """Capitalize the first letter of a string.""" - return x and force_text(x)[0].upper() + force_text(x)[1:] + return x and str(x)[0].upper() + str(x)[1:] # Set up regular expressions @@ -62,7 +61,7 @@ class Truncator(SimpleLazyObject): An object used to truncate text, either by characters or words. """ def __init__(self, text): - super().__init__(lambda: force_text(text)) + super().__init__(lambda: str(text)) def add_truncation_text(self, text, truncate=None): if truncate is None: @@ -230,7 +229,7 @@ def get_valid_filename(s): >>> get_valid_filename("john's portrait in 2004.jpg") 'johns_portrait_in_2004.jpg' """ - s = force_text(s).strip().replace(' ', '_') + s = str(s).strip().replace(' ', '_') return re.sub(r'(?u)[^-\w.]', '', s) @@ -251,18 +250,17 @@ def get_text_list(list_, last_word=gettext_lazy('or')): if len(list_) == 0: return '' if len(list_) == 1: - return force_text(list_[0]) + return str(list_[0]) return '%s %s %s' % ( # Translators: This string is used as a separator between list elements - _(', ').join(force_text(i) for i in list_[:-1]), - force_text(last_word), force_text(list_[-1])) + _(', ').join(str(i) for i in list_[:-1]), str(last_word), str(list_[-1]) + ) @keep_lazy_text def normalize_newlines(text): """Normalize CRLF and CR newlines to just LF.""" - text = force_text(text) - return re_newlines.sub('\n', text) + return re_newlines.sub('\n', str(text)) @keep_lazy_text @@ -349,8 +347,7 @@ def smart_split(text): >>> list(smart_split(r'A "\"funky\" style" test.')) ['A', '"\\"funky\\" style"', 'test.'] """ - text = force_text(text) - for bit in smart_split_re.finditer(text): + for bit in smart_split_re.finditer(str(text)): yield bit.group(0) @@ -378,7 +375,7 @@ _entity_re = re.compile(r"&(#?[xX]?(?:[0-9a-fA-F]+|\w{1,8}));") @keep_lazy_text def unescape_entities(text): - return _entity_re.sub(_replace_entity, force_text(text)) + return _entity_re.sub(_replace_entity, str(text)) @keep_lazy_text @@ -409,7 +406,7 @@ def slugify(value, allow_unicode=False): Remove characters that aren't alphanumerics, underscores, or hyphens. Convert to lowercase. Also strip leading and trailing whitespace. """ - value = force_text(value) + value = str(value) if allow_unicode: value = unicodedata.normalize('NFKC', value) else: diff --git a/django/utils/translation/__init__.py b/django/utils/translation/__init__.py index dc4936a976..f342cf7227 100644 --- a/django/utils/translation/__init__.py +++ b/django/utils/translation/__init__.py @@ -6,7 +6,6 @@ import warnings from contextlib import ContextDecorator from django.utils.deprecation import RemovedInDjango21Warning -from django.utils.encoding import force_text from django.utils.functional import lazy __all__ = [ @@ -226,7 +225,7 @@ def _string_concat(*strings): 'django.utils.translate.string_concat() is deprecated in ' 'favor of django.utils.text.format_lazy().', RemovedInDjango21Warning, stacklevel=2) - return ''.join(force_text(s) for s in strings) + return ''.join(str(s) for s in strings) string_concat = lazy(_string_concat, str) diff --git a/django/views/debug.py b/django/views/debug.py index d32188b346..d3361486cb 100644 --- a/django/views/debug.py +++ b/django/views/debug.py @@ -287,7 +287,7 @@ class ExceptionReporter: user_str = None else: try: - user_str = force_text(self.request.user) + user_str = str(self.request.user) except Exception: # request.user may raise OperationalError if the database is # unavailable, for example. @@ -318,7 +318,7 @@ class ExceptionReporter: if self.exc_type: c['exception_type'] = self.exc_type.__name__ if self.exc_value: - c['exception_value'] = force_text(self.exc_value, errors='replace') + c['exception_value'] = str(self.exc_value) if frames: c['lastframe'] = frames[-1] return c diff --git a/django/views/defaults.py b/django/views/defaults.py index e9057c455e..dee081ec3d 100644 --- a/django/views/defaults.py +++ b/django/views/defaults.py @@ -3,7 +3,6 @@ from django.http import ( HttpResponseServerError, ) from django.template import Context, Engine, TemplateDoesNotExist, loader -from django.utils.encoding import force_text from django.views.decorators.csrf import requires_csrf_token ERROR_404_TEMPLATE_NAME = '404.html' @@ -117,5 +116,5 @@ def permission_denied(request, exception, template_name=ERROR_403_TEMPLATE_NAME) raise return HttpResponseForbidden('

403 Forbidden

', content_type='text/html') return HttpResponseForbidden( - template.render(request=request, context={'exception': force_text(exception)}) + template.render(request=request, context={'exception': str(exception)}) ) diff --git a/django/views/generic/edit.py b/django/views/generic/edit.py index 3dff9ece33..ad2f2b1c67 100644 --- a/django/views/generic/edit.py +++ b/django/views/generic/edit.py @@ -1,7 +1,6 @@ from django.core.exceptions import ImproperlyConfigured from django.forms import models as model_forms from django.http import HttpResponseRedirect -from django.utils.encoding import force_text from django.views.generic.base import ContextMixin, TemplateResponseMixin, View from django.views.generic.detail import ( BaseDetailView, SingleObjectMixin, SingleObjectTemplateResponseMixin, @@ -49,13 +48,9 @@ class FormMixin(ContextMixin): def get_success_url(self): """Return the URL to redirect to after processing a valid form.""" - if self.success_url: - # Forcing possible reverse_lazy evaluation - url = force_text(self.success_url) - else: - raise ImproperlyConfigured( - "No URL to redirect to. Provide a success_url.") - return url + if not self.success_url: + raise ImproperlyConfigured("No URL to redirect to. Provide a success_url.") + return str(self.success_url) # success_url may be lazy def form_valid(self, form): """If the form is valid, redirect to the supplied URL.""" diff --git a/docs/howto/custom-model-fields.txt b/docs/howto/custom-model-fields.txt index 54a62c50db..a64790b34c 100644 --- a/docs/howto/custom-model-fields.txt +++ b/docs/howto/custom-model-fields.txt @@ -706,10 +706,10 @@ smoothly: 2. Put a ``__str__()`` method on the class you're wrapping up as a field. There are a lot of places where the default behavior of the field code is to call - :func:`~django.utils.encoding.force_text` on the value. (In our - examples in this document, ``value`` would be a ``Hand`` instance, not a - ``HandField``). So if your ``__str__()`` method automatically converts to - the string form of your Python object, you can save yourself a lot of work. + ``str()`` on the value. (In our examples in this document, ``value`` would + be a ``Hand`` instance, not a ``HandField``). So if your ``__str__()`` + method automatically converts to the string form of your Python object, you + can save yourself a lot of work. Writing a ``FileField`` subclass ================================ diff --git a/docs/topics/serialization.txt b/docs/topics/serialization.txt index fd1c8876ec..9a1b977d98 100644 --- a/docs/topics/serialization.txt +++ b/docs/topics/serialization.txt @@ -256,13 +256,12 @@ For example, if you have some custom type in an object to be serialized, you'll have to write a custom :mod:`json` encoder for it. Something like this will work:: - from django.utils.encoding import force_text from django.core.serializers.json import DjangoJSONEncoder class LazyEncoder(DjangoJSONEncoder): def default(self, obj): if isinstance(obj, YourCustomType): - return force_text(obj) + return str(obj) return super().default(obj) You can then pass ``cls=LazyEncoder`` to the ``serializers.serialize()``