Refs #27753 -- Favored force/smart_str() over force/smart_text().

This commit is contained in:
Aymeric Augustin 2017-01-26 10:08:08 +01:00 committed by Tim Graham
parent 24b82cd201
commit 3bb6a4390c
21 changed files with 84 additions and 101 deletions

View File

@ -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)

View File

@ -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))

View File

@ -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

View File

@ -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):

View File

@ -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):

View File

@ -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):

View File

@ -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):

View File

@ -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 ####

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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))

View File

@ -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):

View File

@ -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):
"""

View File

@ -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):

View File

@ -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'
)

View File

@ -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.

View File

@ -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 <lazy-translations>` 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 <lazy-translations>` 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

View File

@ -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)

View File

@ -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' (<class 'bytes'>)"
)
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):