From 2ced2f785d5aca0354abf5841d5449b7a49509dc Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Fri, 2 Sep 2016 20:17:15 +0200 Subject: [PATCH] Replaced smart_* by force_* calls whenever possible The smart_* version should only be used when a lazy string should keep its lazy status. --- django/contrib/admin/filters.py | 8 ++++---- django/contrib/admin/helpers.py | 4 ++-- django/contrib/admin/models.py | 6 +++--- django/contrib/admin/utils.py | 2 +- django/contrib/contenttypes/fields.py | 4 ++-- .../management/commands/collectstatic.py | 7 +++---- django/contrib/syndication/views.py | 8 ++++---- django/core/files/base.py | 4 ++-- django/core/serializers/xml_serializer.py | 16 ++++++++-------- django/db/migrations/state.py | 4 ++-- django/db/models/fields/__init__.py | 6 +++--- django/db/models/fields/reverse_related.py | 4 ++-- django/forms/boundfield.py | 8 +++----- django/forms/fields.py | 8 ++++---- django/forms/models.py | 4 ++-- django/http/request.py | 2 +- django/template/context_processors.py | 4 ++-- django/template/defaulttags.py | 4 ++-- django/views/debug.py | 6 +++--- django/views/i18n.py | 6 +++--- docs/ref/databases.txt | 2 +- docs/ref/unicode.txt | 2 +- tests/file_uploads/views.py | 4 ++-- 23 files changed, 60 insertions(+), 63 deletions(-) diff --git a/django/contrib/admin/filters.py b/django/contrib/admin/filters.py index d6f260bb61..8ff223fb29 100644 --- a/django/contrib/admin/filters.py +++ b/django/contrib/admin/filters.py @@ -14,7 +14,7 @@ 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, smart_text +from django.utils.encoding import force_text from django.utils.translation import ugettext_lazy as _ @@ -207,7 +207,7 @@ class RelatedFieldListFilter(FieldListFilter): } for pk_val, val in self.lookup_choices: yield { - 'selected': self.lookup_val == smart_text(pk_val), + 'selected': self.lookup_val == force_text(pk_val), 'query_string': changelist.get_query_string({ self.lookup_kwarg: pk_val, }, [self.lookup_kwarg_isnull]), @@ -292,7 +292,7 @@ class ChoicesFieldListFilter(FieldListFilter): none_title = title continue yield { - 'selected': smart_text(lookup) == self.lookup_val, + 'selected': force_text(lookup) == self.lookup_val, 'query_string': changelist.get_query_string( {self.lookup_kwarg: lookup}, [self.lookup_kwarg_isnull] ), @@ -417,7 +417,7 @@ class AllValuesFieldListFilter(FieldListFilter): if val is None: include_none = True continue - val = smart_text(val) + val = force_text(val) yield { 'selected': self.lookup_val == val, 'query_string': changelist.get_query_string({ diff --git a/django/contrib/admin/helpers.py b/django/contrib/admin/helpers.py index 3396dc66ad..f808d8a899 100644 --- a/django/contrib/admin/helpers.py +++ b/django/contrib/admin/helpers.py @@ -15,7 +15,7 @@ from django.forms.utils import flatatt from django.template.defaultfilters import capfirst, linebreaksbr from django.utils import six from django.utils.deprecation import RemovedInDjango20Warning -from django.utils.encoding import force_text, smart_text +from django.utils.encoding import force_text from django.utils.html import conditional_escape, format_html from django.utils.safestring import mark_safe from django.utils.translation import ugettext, ugettext_lazy as _ @@ -209,7 +209,7 @@ class AdminReadonlyField(object): if hasattr(value, "__html__"): result_repr = value else: - result_repr = smart_text(value) + result_repr = force_text(value) if getattr(attr, "allow_tags", False): warnings.warn( "Deprecated allow_tags attribute used on %s. " diff --git a/django/contrib/admin/models.py b/django/contrib/admin/models.py index 14304e979d..4f7a225fc5 100644 --- a/django/contrib/admin/models.py +++ b/django/contrib/admin/models.py @@ -8,7 +8,7 @@ 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 python_2_unicode_compatible, smart_text +from django.utils.encoding import force_text, python_2_unicode_compatible from django.utils.text import get_text_list from django.utils.translation import ugettext, ugettext_lazy as _ @@ -26,7 +26,7 @@ class LogEntryManager(models.Manager): self.model.objects.create( user_id=user_id, content_type_id=content_type_id, - object_id=smart_text(object_id), + object_id=force_text(object_id), object_repr=object_repr[:200], action_flag=action_flag, change_message=change_message, @@ -67,7 +67,7 @@ class LogEntry(models.Model): ordering = ('-action_time',) def __repr__(self): - return smart_text(self.action_time) + return force_text(self.action_time) def __str__(self): if self.is_addition(): diff --git a/django/contrib/admin/utils.py b/django/contrib/admin/utils.py index 3a9a28fd3f..9b312ac30e 100644 --- a/django/contrib/admin/utils.py +++ b/django/contrib/admin/utils.py @@ -435,7 +435,7 @@ def display_for_value(value, empty_value_display, boolean=False): elif isinstance(value, (list, tuple)): return ', '.join(force_text(v) for v in value) else: - return smart_text(value) + return force_text(value) class NotRelationField(Exception): diff --git a/django/contrib/contenttypes/fields.py b/django/contrib/contenttypes/fields.py index d4057fcfc6..a273cf0347 100644 --- a/django/contrib/contenttypes/fields.py +++ b/django/contrib/contenttypes/fields.py @@ -13,7 +13,7 @@ from django.db.models.fields.related import ( lazy_related_operation, ) from django.db.models.query_utils import PathInfo -from django.utils.encoding import python_2_unicode_compatible, smart_text +from django.utils.encoding import force_text, python_2_unicode_compatible from django.utils.functional import cached_property @@ -402,7 +402,7 @@ class GenericRelation(ForeignObject): def value_to_string(self, obj): qs = getattr(obj, self.name).all() - return smart_text([instance._get_pk_val() for instance in qs]) + return force_text([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/staticfiles/management/commands/collectstatic.py b/django/contrib/staticfiles/management/commands/collectstatic.py index ef3bb67d7e..38f1f5822e 100644 --- a/django/contrib/staticfiles/management/commands/collectstatic.py +++ b/django/contrib/staticfiles/management/commands/collectstatic.py @@ -9,7 +9,7 @@ from django.contrib.staticfiles.storage import staticfiles_storage from django.core.files.storage import FileSystemStorage from django.core.management.base import BaseCommand, CommandError from django.core.management.color import no_style -from django.utils.encoding import smart_text +from django.utils.encoding import force_text from django.utils.functional import cached_property from django.utils.six.moves import input @@ -237,10 +237,9 @@ class Command(BaseCommand): for f in files: fpath = os.path.join(path, f) if self.dry_run: - self.log("Pretending to delete '%s'" % - smart_text(fpath), level=1) + self.log("Pretending to delete '%s'" % force_text(fpath), level=1) else: - self.log("Deleting '%s'" % smart_text(fpath), level=1) + self.log("Deleting '%s'" % force_text(fpath), level=1) try: full_path = self.storage.path(fpath) except NotImplementedError: diff --git a/django/contrib/syndication/views.py b/django/contrib/syndication/views.py index 6d5c79ffb5..1e3c08d313 100644 --- a/django/contrib/syndication/views.py +++ b/django/contrib/syndication/views.py @@ -8,7 +8,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, six -from django.utils.encoding import force_text, iri_to_uri, smart_text +from django.utils.encoding import force_text, 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 @@ -68,9 +68,9 @@ class Feed(object): enc_url = self._get_dynamic_attr('item_enclosure_url', item) if enc_url: enc = feedgenerator.Enclosure( - url=smart_text(enc_url), - length=smart_text(self._get_dynamic_attr('item_enclosure_length', item)), - mime_type=smart_text(self._get_dynamic_attr('item_enclosure_mime_type', item)), + 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)), ) return [enc] return [] diff --git a/django/core/files/base.py b/django/core/files/base.py index 11049a45de..be889c9bc4 100644 --- a/django/core/files/base.py +++ b/django/core/files/base.py @@ -6,7 +6,7 @@ from io import BytesIO, StringIO, UnsupportedOperation from django.core.files.utils import FileProxyMixin from django.utils import six from django.utils.encoding import ( - force_bytes, force_str, python_2_unicode_compatible, smart_text, + force_bytes, force_str, force_text, python_2_unicode_compatible, ) @@ -23,7 +23,7 @@ class File(FileProxyMixin): self.mode = file.mode def __str__(self): - return smart_text(self.name or '') + return force_text(self.name or '') def __repr__(self): return force_str("<%s: %s>" % (self.__class__.__name__, self or "None")) diff --git a/django/core/serializers/xml_serializer.py b/django/core/serializers/xml_serializer.py index b8e2f72734..5e151108fc 100644 --- a/django/core/serializers/xml_serializer.py +++ b/django/core/serializers/xml_serializer.py @@ -13,7 +13,7 @@ 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 smart_text +from django.utils.encoding import force_text from django.utils.xmlutils import ( SimplerXMLGenerator, UnserializableContentError, ) @@ -52,11 +52,11 @@ class Serializer(base.Serializer): raise base.SerializationError("Non-model object (%s) encountered during serialization" % type(obj)) self.indent(1) - attrs = OrderedDict([("model", smart_text(obj._meta))]) + attrs = OrderedDict([("model", force_text(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'] = smart_text(obj_pk) + attrs['pk'] = force_text(obj_pk) self.xml.startElement("object", attrs) @@ -105,10 +105,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(smart_text(key_value)) + self.xml.characters(force_text(key_value)) self.xml.endElement("natural") else: - self.xml.characters(smart_text(related_att)) + self.xml.characters(force_text(related_att)) else: self.xml.addQuickElement("None") self.xml.endElement("field") @@ -129,13 +129,13 @@ class Serializer(base.Serializer): self.xml.startElement("object", {}) for key_value in natural: self.xml.startElement("natural", {}) - self.xml.characters(smart_text(key_value)) + self.xml.characters(force_text(key_value)) self.xml.endElement("natural") self.xml.endElement("object") else: def handle_m2m(value): self.xml.addQuickElement("object", attrs={ - 'pk': smart_text(value._get_pk_val()) + 'pk': force_text(value._get_pk_val()) }) for relobj in getattr(obj, field.name).iterator(): handle_m2m(relobj) @@ -150,7 +150,7 @@ class Serializer(base.Serializer): self.xml.startElement("field", OrderedDict([ ("name", field.name), ("rel", field.remote_field.__class__.__name__), - ("to", smart_text(field.remote_field.model._meta)), + ("to", force_text(field.remote_field.model._meta)), ])) diff --git a/django/db/migrations/state.py b/django/db/migrations/state.py index 32cfc96afb..9cbf5659d4 100644 --- a/django/db/migrations/state.py +++ b/django/db/migrations/state.py @@ -15,7 +15,7 @@ from django.db.models.options import DEFAULT_NAMES, normalize_together from django.db.models.utils import make_model_tuple from django.utils import six from django.utils.deprecation import RemovedInDjango20Warning -from django.utils.encoding import force_text, smart_text +from django.utils.encoding import force_text from django.utils.functional import cached_property from django.utils.module_loading import import_string from django.utils.version import get_docs_version @@ -494,7 +494,7 @@ class ModelState(object): @classmethod def force_text_recursive(cls, value): if isinstance(value, six.string_types): - return smart_text(value) + return force_text(value) elif isinstance(value, list): return [cls.force_text_recursive(x) for x in value] elif isinstance(value, tuple): diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index f4f3170a39..5ff68b3d32 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -817,7 +817,7 @@ class Field(RegisterLookupMixin): Returns a string value of this field from the passed obj. This is used by the serialization framework. """ - return smart_text(self.value_from_object(obj)) + return force_text(self.value_from_object(obj)) def _get_flatchoices(self): """Flattened version of choices tuple.""" @@ -1075,7 +1075,7 @@ class CharField(Field): def to_python(self, value): if isinstance(value, six.string_types) or value is None: return value - return smart_text(value) + return force_text(value) def get_prep_value(self, value): value = super(CharField, self).get_prep_value(value) @@ -2129,7 +2129,7 @@ class TextField(Field): def to_python(self, value): if isinstance(value, six.string_types) or value is None: return value - return smart_text(value) + return force_text(value) def get_prep_value(self, value): value = super(TextField, self).get_prep_value(value) diff --git a/django/db/models/fields/reverse_related.py b/django/db/models/fields/reverse_related.py index 1554261246..86e96ee133 100644 --- a/django/db/models/fields/reverse_related.py +++ b/django/db/models/fields/reverse_related.py @@ -15,7 +15,7 @@ import warnings from django.core import exceptions from django.utils.deprecation import RemovedInDjango20Warning -from django.utils.encoding import smart_text +from django.utils.encoding import force_text from django.utils.functional import cached_property from . import BLANK_CHOICE_DASH @@ -136,7 +136,7 @@ class ForeignObjectRel(object): initially for utilization by RelatedFieldListFilter. """ return (blank_choice if include_blank else []) + [ - (x._get_pk_val(), smart_text(x)) for x in self.related_model._default_manager.all() + (x._get_pk_val(), force_text(x)) for x in self.related_model._default_manager.all() ] def is_hidden(self): diff --git a/django/forms/boundfield.py b/django/forms/boundfield.py index e68744dbdc..b6be395a67 100644 --- a/django/forms/boundfield.py +++ b/django/forms/boundfield.py @@ -5,9 +5,7 @@ import datetime from django.forms.utils import flatatt, pretty_name from django.forms.widgets import Textarea, TextInput from django.utils import six -from django.utils.encoding import ( - force_text, python_2_unicode_compatible, smart_text, -) +from django.utils.encoding import force_text, python_2_unicode_compatible from django.utils.functional import cached_property from django.utils.html import conditional_escape, format_html, html_safe from django.utils.safestring import mark_safe @@ -196,8 +194,8 @@ class BoundField(object): associated Form has specified auto_id. Returns an empty string otherwise. """ auto_id = self.form.auto_id - if auto_id and '%s' in smart_text(auto_id): - return smart_text(auto_id) % self.html_name + if auto_id and '%s' in force_text(auto_id): + return force_text(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 45b425efd6..4a4b93b2b5 100644 --- a/django/forms/fields.py +++ b/django/forms/fields.py @@ -30,7 +30,7 @@ from django.forms.widgets import ( from django.utils import formats, six from django.utils.dateparse import parse_duration from django.utils.duration import duration_string -from django.utils.encoding import force_str, force_text, smart_text +from django.utils.encoding import force_str, force_text from django.utils.ipv6 import clean_ipv6_address from django.utils.six.moves.urllib.parse import urlsplit, urlunsplit from django.utils.translation import ugettext_lazy as _, ungettext_lazy @@ -349,7 +349,7 @@ class DecimalField(IntegerField): return None if self.localize: value = formats.sanitize_separators(value) - value = smart_text(value).strip() + value = force_text(value).strip() try: value = Decimal(value) except DecimalException: @@ -799,7 +799,7 @@ class ChoiceField(Field): "Returns a Unicode object." if value in self.empty_values: return '' - return smart_text(value) + return force_text(value) def validate(self, value): """ @@ -868,7 +868,7 @@ class MultipleChoiceField(ChoiceField): return [] elif not isinstance(value, (list, tuple)): raise ValidationError(self.error_messages['invalid_list'], code='invalid_list') - return [smart_text(val) for val in value] + return [force_text(val) for val in value] def validate(self, value): """ diff --git a/django/forms/models.py b/django/forms/models.py index ff6b2f0251..f44ce33f65 100644 --- a/django/forms/models.py +++ b/django/forms/models.py @@ -19,7 +19,7 @@ from django.forms.widgets import ( HiddenInput, MultipleHiddenInput, SelectMultiple, ) from django.utils import six -from django.utils.encoding import force_text, smart_text +from django.utils.encoding import force_text from django.utils.text import capfirst, get_text_list from django.utils.translation import ugettext, ugettext_lazy as _ @@ -1186,7 +1186,7 @@ class ModelChoiceField(ChoiceField): generate the labels for the choices presented by this object. Subclasses can override this method to customize the display of the choices. """ - return smart_text(obj) + return force_text(obj) def _get_choices(self): # If self._choices is set, then somebody must have manually set diff --git a/django/http/request.py b/django/http/request.py index 271ab30557..6846068c2f 100644 --- a/django/http/request.py +++ b/django/http/request.py @@ -521,7 +521,7 @@ class QueryDict(MultiValueDict): # It's neither necessary nor appropriate to use -# django.utils.encoding.smart_text for parsing URLs and form inputs. Thus, +# django.utils.encoding.force_text for parsing URLs and form inputs. Thus, # this slightly more restricted function, used by QueryDict. def bytes_to_text(s, encoding): """ diff --git a/django/template/context_processors.py b/django/template/context_processors.py index 5568ed9134..da5276ce44 100644 --- a/django/template/context_processors.py +++ b/django/template/context_processors.py @@ -13,7 +13,7 @@ import itertools from django.conf import settings from django.middleware.csrf import get_token -from django.utils.encoding import smart_text +from django.utils.encoding import force_text from django.utils.functional import SimpleLazyObject, lazy @@ -30,7 +30,7 @@ def csrf(request): # instead of returning an empty dict. return 'NOTPROVIDED' else: - return smart_text(token) + return force_text(token) return {'csrf_token': SimpleLazyObject(_get_val)} diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py index b2681e8bb7..ece6f7501d 100644 --- a/django/template/defaulttags.py +++ b/django/template/defaulttags.py @@ -10,7 +10,7 @@ from itertools import cycle as itertools_cycle, groupby from django.conf import settings from django.utils import six, timezone -from django.utils.encoding import force_text, smart_text +from django.utils.encoding import force_text from django.utils.html import conditional_escape, format_html from django.utils.lorem_ipsum import paragraphs, words from django.utils.safestring import mark_safe @@ -440,7 +440,7 @@ class URLNode(Node): from django.urls import reverse, NoReverseMatch args = [arg.resolve(context) for arg in self.args] kwargs = { - smart_text(k, 'ascii'): v.resolve(context) + force_text(k, 'ascii'): v.resolve(context) for k, v in self.kwargs.items() } view_name = self.view_name.resolve(context) diff --git a/django/views/debug.py b/django/views/debug.py index 1bcaaf8169..ccc2753a00 100644 --- a/django/views/debug.py +++ b/django/views/debug.py @@ -11,7 +11,7 @@ from django.template.defaultfilters import force_escape, pprint from django.urls import Resolver404, resolve from django.utils import lru_cache, six, timezone from django.utils.datastructures import MultiValueDict -from django.utils.encoding import force_bytes, smart_text +from django.utils.encoding import force_bytes, force_text from django.utils.module_loading import import_string from django.utils.translation import ugettext as _ @@ -280,7 +280,7 @@ class ExceptionReporter(object): end = getattr(self.exc_value, 'end', None) if start is not None and end is not None: unicode_str = self.exc_value.args[1] - unicode_hint = smart_text( + unicode_hint = force_text( unicode_str[max(start - 5, 0):min(end + 5, len(unicode_str))], 'ascii', errors='replace' ) @@ -305,7 +305,7 @@ class ExceptionReporter(object): if self.exc_type: c['exception_type'] = self.exc_type.__name__ if self.exc_value: - c['exception_value'] = smart_text(self.exc_value, errors='replace') + c['exception_value'] = force_text(self.exc_value, errors='replace') if frames: c['lastframe'] = frames[-1] return c diff --git a/django/views/i18n.py b/django/views/i18n.py index a6bf8bcff4..5cc6a3ebe1 100644 --- a/django/views/i18n.py +++ b/django/views/i18n.py @@ -12,7 +12,7 @@ from django.urls import translate_url from django.utils import six from django.utils._os import upath from django.utils.deprecation import RemovedInDjango20Warning -from django.utils.encoding import smart_text +from django.utils.encoding import force_text from django.utils.formats import get_format from django.utils.http import is_safe_url, urlunquote from django.utils.translation import ( @@ -81,9 +81,9 @@ def get_formats(): formats = {} for k, v in result.items(): if isinstance(v, (six.string_types, int)): - formats[k] = smart_text(v) + formats[k] = force_text(v) elif isinstance(v, (tuple, list)): - formats[k] = [smart_text(value) for value in v] + formats[k] = [force_text(value) for value in v] return formats diff --git a/docs/ref/databases.txt b/docs/ref/databases.txt index 6d705beb66..6412316a2d 100644 --- a/docs/ref/databases.txt +++ b/docs/ref/databases.txt @@ -369,7 +369,7 @@ to you, the developer, to handle the fact that you will receive bytestrings if you configure your table(s) to use ``utf8_bin`` collation. Django itself should mostly work smoothly with such columns (except for the ``contrib.sessions`` ``Session`` and ``contrib.admin`` ``LogEntry`` tables described below), but -your code must be prepared to call ``django.utils.encoding.smart_text()`` at +your code must be prepared to call ``django.utils.encoding.force_text()`` at times if it really wants to work with consistent data -- Django will not do this for you (the database backend layer and the model population layer are separated internally so the database layer doesn't know it needs to make this diff --git a/docs/ref/unicode.txt b/docs/ref/unicode.txt index d09431cbee..742a5c0d78 100644 --- a/docs/ref/unicode.txt +++ b/docs/ref/unicode.txt @@ -163,7 +163,7 @@ for converting back and forth between Unicode and bytestrings. slightly different semantics from Python's builtin ``str()`` function, but the difference is needed in a few places within Django's internals. -Normally, you'll only need to use ``smart_text()``. Call it as early as +Normally, you'll only need to use ``force_text()``. Call it as early as possible on any input data that might be either Unicode or a bytestring, and from then on, you can treat the result as always being Unicode. diff --git a/tests/file_uploads/views.py b/tests/file_uploads/views.py index c5ecaa2286..058d5b6a14 100644 --- a/tests/file_uploads/views.py +++ b/tests/file_uploads/views.py @@ -8,7 +8,7 @@ import os from django.core.files.uploadedfile import UploadedFile from django.http import HttpResponse, HttpResponseServerError from django.utils import six -from django.utils.encoding import force_bytes, smart_str +from django.utils.encoding import force_bytes, force_str from .models import FileModel from .tests import UNICODE_FILENAME, UPLOAD_TO @@ -158,7 +158,7 @@ def file_upload_content_type_extra(request): params = {} for file_name, uploadedfile in request.FILES.items(): params[file_name] = { - k: smart_str(v) for k, v in uploadedfile.content_type_extra.items() + k: force_str(v) for k, v in uploadedfile.content_type_extra.items() } return HttpResponse(json.dumps(params))