From 3bb6a4390c0a57da991fcb1c0642b9b3fccff751 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Thu, 26 Jan 2017 10:08:08 +0100 Subject: [PATCH] Refs #27753 -- Favored force/smart_str() over force/smart_text(). --- django/contrib/gis/gdal/datasource.py | 4 ++-- django/contrib/gis/gdal/driver.py | 4 ++-- django/contrib/gis/gdal/feature.py | 6 ++--- django/contrib/gis/gdal/field.py | 6 ++--- django/contrib/gis/gdal/layer.py | 11 +++++---- django/contrib/gis/gdal/raster/band.py | 4 ++-- django/contrib/gis/gdal/raster/source.py | 4 ++-- django/contrib/gis/gdal/srs.py | 4 ++-- django/contrib/gis/geos/geometry.py | 4 ++-- django/contrib/gis/utils/layermapping.py | 4 ++-- django/core/mail/message.py | 4 ++-- django/db/backends/base/operations.py | 4 ++-- django/db/backends/oracle/base.py | 4 ++-- django/http/multipartparser.py | 10 ++++---- django/http/request.py | 2 +- django/utils/encoding.py | 24 ++++++-------------- django/views/debug.py | 4 ++-- docs/ref/settings.txt | 2 +- docs/ref/unicode.txt | 27 +++++++++++----------- docs/ref/utils.txt | 29 +++++++++--------------- tests/utils_tests/test_encoding.py | 24 ++++++++++---------- 21 files changed, 84 insertions(+), 101 deletions(-) diff --git a/django/contrib/gis/gdal/datasource.py b/django/contrib/gis/gdal/datasource.py index 897699a975..602b244d31 100644 --- a/django/contrib/gis/gdal/datasource.py +++ b/django/contrib/gis/gdal/datasource.py @@ -40,7 +40,7 @@ from django.contrib.gis.gdal.driver import Driver from django.contrib.gis.gdal.error import GDALException from django.contrib.gis.gdal.layer import Layer from django.contrib.gis.gdal.prototypes import ds as capi -from django.utils.encoding import force_bytes, force_text +from django.utils.encoding import force_bytes, force_str # For more information, see the OGR C API source code: @@ -117,4 +117,4 @@ class DataSource(GDALBase): def name(self): "Return the name of the data source." name = capi.get_ds_name(self._ptr) - return force_text(name, self.encoding, strings_only=True) + return force_str(name, self.encoding, strings_only=True) diff --git a/django/contrib/gis/gdal/driver.py b/django/contrib/gis/gdal/driver.py index 7243263575..13d92e96d9 100644 --- a/django/contrib/gis/gdal/driver.py +++ b/django/contrib/gis/gdal/driver.py @@ -3,7 +3,7 @@ from ctypes import c_void_p from django.contrib.gis.gdal.base import GDALBase from django.contrib.gis.gdal.error import GDALException from django.contrib.gis.gdal.prototypes import ds as vcapi, raster as rcapi -from django.utils.encoding import force_bytes, force_text +from django.utils.encoding import force_bytes, force_str class Driver(GDALBase): @@ -94,4 +94,4 @@ class Driver(GDALBase): """ Return description/name string for this driver. """ - return force_text(rcapi.get_driver_description(self.ptr)) + return force_str(rcapi.get_driver_description(self.ptr)) diff --git a/django/contrib/gis/gdal/feature.py b/django/contrib/gis/gdal/feature.py index c4a23c0996..9f8063d1f5 100644 --- a/django/contrib/gis/gdal/feature.py +++ b/django/contrib/gis/gdal/feature.py @@ -3,7 +3,7 @@ from django.contrib.gis.gdal.error import GDALException from django.contrib.gis.gdal.field import Field from django.contrib.gis.gdal.geometries import OGRGeometry, OGRGeomType from django.contrib.gis.gdal.prototypes import ds as capi, geom as geom_api -from django.utils.encoding import force_bytes, force_text +from django.utils.encoding import force_bytes, force_str # For more information, see the OGR C API source code: @@ -67,7 +67,7 @@ class Feature(GDALBase): def layer_name(self): "Return the name of the layer for the feature." name = capi.get_feat_name(self._layer._ldefn) - return force_text(name, self.encoding, strings_only=True) + return force_str(name, self.encoding, strings_only=True) @property def num_fields(self): @@ -78,7 +78,7 @@ class Feature(GDALBase): def fields(self): "Return a list of fields in the Feature." return [ - force_text( + force_str( capi.get_field_name(capi.get_field_defn(self._layer._ldefn, i)), self.encoding, strings_only=True diff --git a/django/contrib/gis/gdal/field.py b/django/contrib/gis/gdal/field.py index f67e7a5c03..dfd25d7bfb 100644 --- a/django/contrib/gis/gdal/field.py +++ b/django/contrib/gis/gdal/field.py @@ -4,7 +4,7 @@ from datetime import date, datetime, time from django.contrib.gis.gdal.base import GDALBase from django.contrib.gis.gdal.error import GDALException from django.contrib.gis.gdal.prototypes import ds as capi -from django.utils.encoding import force_text +from django.utils.encoding import force_str # For more information, see the OGR C API source code: @@ -55,7 +55,7 @@ class Field(GDALBase): if not self.is_set: return None string = capi.get_field_as_string(self._feat.ptr, self._index) - return force_text(string, encoding=self._feat.encoding, strings_only=True) + return force_str(string, encoding=self._feat.encoding, strings_only=True) def as_datetime(self): "Retrieve the Field's value as a tuple of date & time components." @@ -80,7 +80,7 @@ class Field(GDALBase): def name(self): "Return the name of this Field." name = capi.get_field_name(self.ptr) - return force_text(name, encoding=self._feat.encoding, strings_only=True) + return force_str(name, encoding=self._feat.encoding, strings_only=True) @property def precision(self): diff --git a/django/contrib/gis/gdal/layer.py b/django/contrib/gis/gdal/layer.py index 603188c9d7..4f7a2b3f83 100644 --- a/django/contrib/gis/gdal/layer.py +++ b/django/contrib/gis/gdal/layer.py @@ -11,7 +11,7 @@ from django.contrib.gis.gdal.prototypes import ( ds as capi, geom as geom_api, srs as srs_api, ) from django.contrib.gis.gdal.srs import SpatialReference -from django.utils.encoding import force_bytes, force_text +from django.utils.encoding import force_bytes, force_str # For more information, see the OGR C API source code: @@ -101,7 +101,7 @@ class Layer(GDALBase): def name(self): "Return the name of this layer in the Data Source." name = capi.get_fd_name(self._ldefn) - return force_text(name, self._ds.encoding, strings_only=True) + return force_str(name, self._ds.encoding, strings_only=True) @property def num_feat(self, force=1): @@ -133,9 +133,10 @@ class Layer(GDALBase): Return a list of string names corresponding to each of the Fields available in this Layer. """ - return [force_text(capi.get_field_name(capi.get_field_defn(self._ldefn, i)), - self._ds.encoding, strings_only=True) - for i in range(self.num_fields)] + return [force_str( + capi.get_field_name(capi.get_field_defn(self._ldefn, i)), + self._ds.encoding, strings_only=True, + ) for i in range(self.num_fields)] @property def field_types(self): diff --git a/django/contrib/gis/gdal/raster/band.py b/django/contrib/gis/gdal/raster/band.py index e1ead63dc1..61c6535213 100644 --- a/django/contrib/gis/gdal/raster/band.py +++ b/django/contrib/gis/gdal/raster/band.py @@ -4,7 +4,7 @@ from django.contrib.gis.gdal.error import GDALException from django.contrib.gis.gdal.prototypes import raster as capi from django.contrib.gis.gdal.raster.base import GDALRasterBase from django.contrib.gis.shortcuts import numpy -from django.utils.encoding import force_text +from django.utils.encoding import force_str from .const import ( GDAL_COLOR_TYPES, GDAL_INTEGER_TYPES, GDAL_PIXEL_TYPES, GDAL_TO_CTYPES, @@ -32,7 +32,7 @@ class GDALBand(GDALRasterBase): """ Return the description string of the band. """ - return force_text(capi.get_band_description(self._ptr)) + return force_str(capi.get_band_description(self._ptr)) @property def width(self): diff --git a/django/contrib/gis/gdal/raster/source.py b/django/contrib/gis/gdal/raster/source.py index fc02bce960..05475434c4 100644 --- a/django/contrib/gis/gdal/raster/source.py +++ b/django/contrib/gis/gdal/raster/source.py @@ -17,7 +17,7 @@ from django.contrib.gis.gdal.raster.const import ( ) from django.contrib.gis.gdal.srs import SpatialReference, SRSException from django.contrib.gis.geometry import json_regex -from django.utils.encoding import force_bytes, force_text +from django.utils.encoding import force_bytes, force_str from django.utils.functional import cached_property @@ -233,7 +233,7 @@ class GDALRaster(GDALRasterBase): Return the name of this raster. Corresponds to filename for file-based rasters. """ - return force_text(capi.get_ds_description(self._ptr)) + return force_str(capi.get_ds_description(self._ptr)) @cached_property def driver(self): diff --git a/django/contrib/gis/gdal/srs.py b/django/contrib/gis/gdal/srs.py index b2e0b090bc..850196ccc3 100644 --- a/django/contrib/gis/gdal/srs.py +++ b/django/contrib/gis/gdal/srs.py @@ -31,7 +31,7 @@ from ctypes import byref, c_char_p, c_int from django.contrib.gis.gdal.base import GDALBase from django.contrib.gis.gdal.error import SRSException from django.contrib.gis.gdal.prototypes import srs as capi -from django.utils.encoding import force_bytes, force_text +from django.utils.encoding import force_bytes, force_str class SpatialReference(GDALBase): @@ -222,7 +222,7 @@ class SpatialReference(GDALBase): elif self.geographic: units, name = capi.angular_units(self.ptr, byref(c_char_p())) if name is not None: - name = force_text(name) + name = force_str(name) return (units, name) # #### Spheroid/Ellipsoid Properties #### diff --git a/django/contrib/gis/geos/geometry.py b/django/contrib/gis/geos/geometry.py index 4b3779938e..0a338d414b 100644 --- a/django/contrib/gis/geos/geometry.py +++ b/django/contrib/gis/geos/geometry.py @@ -18,7 +18,7 @@ from django.contrib.gis.geos.prototypes.io import ( ewkb_w, wkb_r, wkb_w, wkt_r, wkt_w, ) from django.utils.deconstruct import deconstructible -from django.utils.encoding import force_bytes, force_text +from django.utils.encoding import force_bytes, force_str class GEOSGeometryBase(GEOSBase): @@ -695,7 +695,7 @@ class GEOSGeometry(GEOSGeometryBase, ListMixin): """ input_srid = None if isinstance(geo_input, bytes): - geo_input = force_text(geo_input) + geo_input = force_str(geo_input) if isinstance(geo_input, str): wkt_m = wkt_regex.match(geo_input) if wkt_m: diff --git a/django/contrib/gis/utils/layermapping.py b/django/contrib/gis/utils/layermapping.py index 32434bb457..ff843cab58 100644 --- a/django/contrib/gis/utils/layermapping.py +++ b/django/contrib/gis/utils/layermapping.py @@ -20,7 +20,7 @@ from django.contrib.gis.gdal.field import ( ) from django.core.exceptions import FieldDoesNotExist, ObjectDoesNotExist from django.db import connections, models, router, transaction -from django.utils.encoding import force_text +from django.utils.encoding import force_str # LayerMapping exceptions. @@ -348,7 +348,7 @@ class LayerMapping: if self.encoding and ogr_field.value is not None: # The encoding for OGR data sources may be specified here # (e.g., 'cp437' for Census Bureau boundary files). - val = force_text(ogr_field.value, self.encoding) + val = force_str(ogr_field.value, self.encoding) else: val = ogr_field.value if model_field.max_length and val is not None and len(val) > model_field.max_length: diff --git a/django/core/mail/message.py b/django/core/mail/message.py index 2f89ffbfc4..7a790711cc 100644 --- a/django/core/mail/message.py +++ b/django/core/mail/message.py @@ -16,7 +16,7 @@ from pathlib import Path from django.conf import settings from django.core.mail.utils import DNS_NAME -from django.utils.encoding import force_text +from django.utils.encoding import force_str # Don't BASE64-encode UTF-8 messages so that we avoid unwanted attention from # some spam filters. @@ -375,7 +375,7 @@ class EmailMessage: elif not isinstance(content, Message): # For compatibility with existing code, parse the message # into an email.Message object if it is not one already. - content = message_from_string(force_text(content)) + content = message_from_string(force_str(content)) attachment = SafeMIMEMessage(content, subtype) else: diff --git a/django/db/backends/base/operations.py b/django/db/backends/base/operations.py index 2a2227435d..b4c2250dfa 100644 --- a/django/db/backends/base/operations.py +++ b/django/db/backends/base/operations.py @@ -8,7 +8,7 @@ from django.conf import settings from django.db import NotSupportedError, transaction from django.db.backends import utils from django.utils import timezone -from django.utils.encoding import force_text +from django.utils.encoding import force_str class BaseDatabaseOperations: @@ -235,7 +235,7 @@ class BaseDatabaseOperations: """ # Convert params to contain string values. def to_string(s): - return force_text(s, strings_only=True, errors='replace') + return force_str(s, strings_only=True, errors='replace') if isinstance(params, (list, tuple)): u_params = tuple(to_string(val) for val in params) elif params is None: diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py index 512b15317a..f80c6f3024 100644 --- a/django/db/backends/oracle/base.py +++ b/django/db/backends/oracle/base.py @@ -13,7 +13,7 @@ from django.conf import settings from django.core.exceptions import ImproperlyConfigured from django.db import utils from django.db.backends.base.base import BaseDatabaseWrapper -from django.utils.encoding import force_bytes, force_text +from django.utils.encoding import force_bytes, force_str from django.utils.functional import cached_property @@ -342,7 +342,7 @@ class OracleParam: else: # To transmit to the database, we need Unicode if supported # To get size right, we must consider bytes. - self.force_bytes = force_text(param, cursor.charset, strings_only) + self.force_bytes = force_str(param, cursor.charset, strings_only) if isinstance(self.force_bytes, str): # We could optimize by only converting up to 4000 bytes here string_size = len(force_bytes(param, cursor.charset, strings_only)) diff --git a/django/http/multipartparser.py b/django/http/multipartparser.py index f6f12ca718..e235024d0e 100644 --- a/django/http/multipartparser.py +++ b/django/http/multipartparser.py @@ -17,7 +17,7 @@ from django.core.files.uploadhandler import ( SkipFile, StopFutureHandlers, StopUpload, ) from django.utils.datastructures import MultiValueDict -from django.utils.encoding import force_text +from django.utils.encoding import force_str from django.utils.text import unescape_entities __all__ = ('MultiPartParser', 'MultiPartParserError', 'InputStreamExhausted') @@ -164,7 +164,7 @@ class MultiPartParser: transfer_encoding = meta_data.get('content-transfer-encoding') if transfer_encoding is not None: transfer_encoding = transfer_encoding[0].strip() - field_name = force_text(field_name, encoding, errors='replace') + field_name = force_str(field_name, encoding, errors='replace') if item_type == FIELD: # Avoid storing more than DATA_UPLOAD_MAX_NUMBER_FIELDS. @@ -199,12 +199,12 @@ class MultiPartParser: num_bytes_read > settings.DATA_UPLOAD_MAX_MEMORY_SIZE): raise RequestDataTooBig('Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE.') - self._post.appendlist(field_name, force_text(data, encoding, errors='replace')) + self._post.appendlist(field_name, force_str(data, encoding, errors='replace')) elif item_type == FILE: # This is a file, use the handler... file_name = disposition.get('filename') if file_name: - file_name = force_text(file_name, encoding, errors='replace') + file_name = force_str(file_name, encoding, errors='replace') file_name = self.IE_sanitize(unescape_entities(file_name)) if not file_name: continue @@ -290,7 +290,7 @@ class MultiPartParser: file_obj = handler.file_complete(counters[i]) if file_obj: # If it returns a file object, then set the files dict. - self._files.appendlist(force_text(old_field_name, self._encoding, errors='replace'), file_obj) + self._files.appendlist(force_str(old_field_name, self._encoding, errors='replace'), file_obj) break def IE_sanitize(self, filename): diff --git a/django/http/request.py b/django/http/request.py index 51d0a8dfc3..1e1cc6c397 100644 --- a/django/http/request.py +++ b/django/http/request.py @@ -537,7 +537,7 @@ class QueryDict(MultiValueDict): # It's neither necessary nor appropriate to use -# django.utils.encoding.force_text for parsing URLs and form inputs. Thus, +# django.utils.encoding.force_str() 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/utils/encoding.py b/django/utils/encoding.py index bb5aefcf7a..42adb45b8e 100644 --- a/django/utils/encoding.py +++ b/django/utils/encoding.py @@ -16,7 +16,7 @@ class DjangoUnicodeDecodeError(UnicodeDecodeError): return '%s. You passed in %r (%s)' % (super().__str__(), self.obj, type(self.obj)) -def smart_text(s, encoding='utf-8', strings_only=False, errors='strict'): +def smart_str(s, encoding='utf-8', strings_only=False, errors='strict'): """ Return a string representing 's'. Treat bytestrings using the 'encoding' codec. @@ -26,7 +26,7 @@ def smart_text(s, encoding='utf-8', strings_only=False, errors='strict'): if isinstance(s, Promise): # The input is the result of a gettext_lazy() call. return s - return force_text(s, encoding, strings_only, errors) + return force_str(s, encoding, strings_only, errors) _PROTECTED_TYPES = ( @@ -38,14 +38,14 @@ def is_protected_type(obj): """Determine if the object instance is of a protected type. Objects of protected types are preserved as-is when passed to - force_text(strings_only=True). + force_str(strings_only=True). """ return isinstance(obj, _PROTECTED_TYPES) -def force_text(s, encoding='utf-8', strings_only=False, errors='strict'): +def force_str(s, encoding='utf-8', strings_only=False, errors='strict'): """ - Similar to smart_text, except that lazy instances are resolved to + Similar to smart_str(), except that lazy instances are resolved to strings, rather than kept as lazy objects. If strings_only is True, don't convert (some) non-string-like objects. @@ -97,18 +97,8 @@ def force_bytes(s, encoding='utf-8', strings_only=False, errors='strict'): return str(s).encode(encoding, errors) -smart_str = smart_text -force_str = force_text - -smart_str.__doc__ = """ -Apply smart_text in Python 3 and smart_bytes in Python 2. - -This is suitable for writing to sys.stdout (for instance). -""" - -force_str.__doc__ = """ -Apply force_text in Python 3 and force_bytes in Python 2. -""" +smart_text = smart_str +force_text = force_str def iri_to_uri(iri): diff --git a/django/views/debug.py b/django/views/debug.py index 0efaf0c6af..601aca57af 100644 --- a/django/views/debug.py +++ b/django/views/debug.py @@ -11,7 +11,7 @@ from django.template.defaultfilters import pprint from django.urls import Resolver404, resolve from django.utils import timezone from django.utils.datastructures import MultiValueDict -from django.utils.encoding import force_text +from django.utils.encoding import force_str from django.utils.module_loading import import_string from django.utils.version import get_docs_version @@ -280,7 +280,7 @@ class ExceptionReporter: 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 = force_text( + unicode_hint = force_str( unicode_str[max(start - 5, 0):min(end + 5, len(unicode_str))], 'ascii', errors='replace' ) diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index d86769c4fb..46e99af993 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -2079,7 +2079,7 @@ unpredictable value. randomly-generated ``SECRET_KEY`` to each new project. Uses of the key shouldn't assume that it's text or bytes. Every use should go -through :func:`~django.utils.encoding.force_text` or +through :func:`~django.utils.encoding.force_str` or :func:`~django.utils.encoding.force_bytes` to convert it to the desired type. Django will refuse to start if :setting:`SECRET_KEY` is not set. diff --git a/docs/ref/unicode.txt b/docs/ref/unicode.txt index 9d0311deef..177ada4ef1 100644 --- a/docs/ref/unicode.txt +++ b/docs/ref/unicode.txt @@ -108,7 +108,7 @@ Conversion functions The ``django.utils.encoding`` module contains a few functions that are handy for converting back and forth between strings and bytestrings. -* ``smart_text(s, encoding='utf-8', strings_only=False, errors='strict')`` +* ``smart_str(s, encoding='utf-8', strings_only=False, errors='strict')`` converts its input to a string. The ``encoding`` parameter specifies the input encoding. (For example, Django uses this internally when processing form input data, which might not be UTF-8 encoded.) The @@ -118,24 +118,23 @@ for converting back and forth between strings and bytestrings. that are accepted by Python's ``str()`` function for its error handling. -* ``force_text(s, encoding='utf-8', strings_only=False, - errors='strict')`` is identical to ``smart_text()`` in almost all - cases. The difference is when the first argument is a :ref:`lazy - translation ` instance. While ``smart_text()`` - preserves lazy translations, ``force_text()`` forces those objects to a - string (causing the translation to occur). Normally, you'll want - to use ``smart_text()``. However, ``force_text()`` is useful in - template tags and filters that absolutely *must* have a string to work - with, not just something that can be converted to a string. +* ``force_str(s, encoding='utf-8', strings_only=False, errors='strict')`` is + identical to ``smart_str()`` in almost all cases. The difference is when the + first argument is a :ref:`lazy translation ` instance. + While ``smart_str()`` preserves lazy translations, ``force_str()`` forces + those objects to a string (causing the translation to occur). Normally, + you'll want to use ``smart_str()``. However, ``force_str()`` is useful in + template tags and filters that absolutely *must* have a string to work with, + not just something that can be converted to a string. * ``smart_bytes(s, encoding='utf-8', strings_only=False, errors='strict')`` - is essentially the opposite of ``smart_text()``. It forces the first + is essentially the opposite of ``smart_str()``. It forces the first argument to a bytestring. The ``strings_only`` parameter has the same - behavior as for ``smart_text()`` and ``force_text()``. This is + behavior as for ``smart_str()`` and ``force_str()``. This is 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 ``force_text()``. Call it as early as +Normally, you'll only need to use ``force_str()``. Call it as early as possible on any input data that might be either a string or a bytestring, and from then on, you can treat the result as always being a string. @@ -280,7 +279,7 @@ A couple of tips to remember when writing your own template tags and filters: * Always return strings from a template tag's ``render()`` method and from template filters. -* Use ``force_text()`` in preference to ``smart_text()`` in these +* Use ``force_str()`` in preference to ``smart_str()`` in these places. Tag rendering and filter calls occur as the template is being rendered, so there is no advantage to postponing the conversion of lazy translation objects into strings. It's easier to work solely with diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt index 23a4d6dedd..62625cf62c 100644 --- a/docs/ref/utils.txt +++ b/docs/ref/utils.txt @@ -191,7 +191,7 @@ The functions defined in this module share the following properties: .. module:: django.utils.encoding :synopsis: A series of helper functions to manage character encoding. -.. function:: smart_text(s, encoding='utf-8', strings_only=False, errors='strict') +.. function:: smart_str(s, encoding='utf-8', strings_only=False, errors='strict') Returns a ``str`` object representing arbitrary object ``s``. Treats bytestrings using the ``encoding`` codec. @@ -204,11 +204,11 @@ The functions defined in this module share the following properties: Determine if the object instance is of a protected type. Objects of protected types are preserved as-is when passed to - ``force_text(strings_only=True)``. + ``force_str(strings_only=True)``. -.. function:: force_text(s, encoding='utf-8', strings_only=False, errors='strict') +.. function:: force_str(s, encoding='utf-8', strings_only=False, errors='strict') - Similar to ``smart_text``, except that lazy instances are resolved to + Similar to ``smart_str()``, except that lazy instances are resolved to strings, rather than kept as lazy objects. If ``strings_only`` is ``True``, don't convert (some) non-string-like @@ -230,22 +230,15 @@ The functions defined in this module share the following properties: If ``strings_only`` is ``True``, don't convert (some) non-string-like objects. -.. function:: smart_str(s, encoding='utf-8', strings_only=False, errors='strict') +.. function:: smart_text(s, encoding='utf-8', strings_only=False, errors='strict') - Alias of :func:`smart_text`. This function returns a ``str`` or a lazy - string. + Alias of :func:`force_str` for backwards compatibility, especially in code + that supports Python 2. - For instance, this is suitable for writing to :data:`sys.stdout`. +.. function:: force_text(s, encoding='utf-8', strings_only=False, errors='strict') - Alias of :func:`smart_bytes` on Python 2 (in older versions of Django that - support it). - -.. function:: force_str(s, encoding='utf-8', strings_only=False, errors='strict') - - Alias of :func:`force_text`. This function always returns a ``str``. - - Alias of :func:`force_bytes` on Python 2 (in older versions of Django that - support it). + Alias of :func:`force_str` for backwards compatibility, especially in code + that supports Python 2. .. function:: iri_to_uri(iri) @@ -622,7 +615,7 @@ escaping HTML. interpolation, some of the formatting options provided by ``str.format()`` (e.g. number formatting) will not work, since all arguments are passed through :func:`conditional_escape` which (ultimately) calls - :func:`~django.utils.encoding.force_text` on the values. + :func:`~django.utils.encoding.force_str` on the values. .. function:: format_html_join(sep, format_string, args_generator) diff --git a/tests/utils_tests/test_encoding.py b/tests/utils_tests/test_encoding.py index c461df71ee..b40d49987f 100644 --- a/tests/utils_tests/test_encoding.py +++ b/tests/utils_tests/test_encoding.py @@ -6,7 +6,7 @@ from urllib.parse import quote_plus from django.test import SimpleTestCase from django.utils.encoding import ( DjangoUnicodeDecodeError, escape_uri_path, filepath_to_uri, force_bytes, - force_text, get_system_encoding, iri_to_uri, smart_bytes, smart_text, + force_str, get_system_encoding, iri_to_uri, smart_bytes, smart_str, uri_to_iri, ) from django.utils.functional import SimpleLazyObject @@ -14,7 +14,7 @@ from django.utils.translation import gettext_lazy class TestEncodingUtils(SimpleTestCase): - def test_force_text_exception(self): + def test_force_str_exception(self): """ Broken __str__ actually raises an error. """ @@ -24,19 +24,19 @@ class TestEncodingUtils(SimpleTestCase): # str(s) raises a TypeError if the result is not a text type. with self.assertRaises(TypeError): - force_text(MyString()) + force_str(MyString()) - def test_force_text_lazy(self): + def test_force_str_lazy(self): s = SimpleLazyObject(lambda: 'x') - self.assertIs(type(force_text(s)), str) + self.assertIs(type(force_str(s)), str) - def test_force_text_DjangoUnicodeDecodeError(self): + def test_force_str_DjangoUnicodeDecodeError(self): msg = ( "'utf-8' codec can't decode byte 0xff in position 0: invalid " "start byte. You passed in b'\\xff' ()" ) with self.assertRaisesMessage(DjangoUnicodeDecodeError, msg): - force_text(b'\xff') + force_str(b'\xff') def test_force_bytes_exception(self): """ @@ -75,16 +75,16 @@ class TestEncodingUtils(SimpleTestCase): self.assertEqual(smart_bytes(1), b'1') self.assertEqual(smart_bytes('foo'), b'foo') - def test_smart_text(self): + def test_smart_str(self): class Test: def __str__(self): return 'ŠĐĆŽćžšđ' lazy_func = gettext_lazy('x') - self.assertIs(smart_text(lazy_func), lazy_func) - self.assertEqual(smart_text(Test()), '\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111') - self.assertEqual(smart_text(1), '1') - self.assertEqual(smart_text('foo'), 'foo') + self.assertIs(smart_str(lazy_func), lazy_func) + self.assertEqual(smart_str(Test()), '\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111') + self.assertEqual(smart_str(1), '1') + self.assertEqual(smart_str('foo'), 'foo') def test_get_default_encoding(self): with mock.patch('locale.getdefaultlocale', side_effect=Exception):