diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py index 25835dad24..1f78dfa142 100644 --- a/django/contrib/auth/forms.py +++ b/django/contrib/auth/forms.py @@ -275,7 +275,7 @@ class PasswordResetForm(forms.Form): 'email': email, 'domain': domain, 'site_name': site_name, - 'uid': urlsafe_base64_encode(force_bytes(user.pk)), + 'uid': urlsafe_base64_encode(force_bytes(user.pk)).decode(), 'user': user, 'token': token_generator.make_token(user), 'protocol': 'https' if use_https else 'http', diff --git a/django/contrib/humanize/templatetags/humanize.py b/django/contrib/humanize/templatetags/humanize.py index d00f597ea8..c119395fde 100644 --- a/django/contrib/humanize/templatetags/humanize.py +++ b/django/contrib/humanize/templatetags/humanize.py @@ -5,7 +5,6 @@ from decimal import Decimal from django import template from django.conf import settings from django.template import defaultfilters -from django.utils.encoding import force_text from django.utils.formats import number_format from django.utils.safestring import mark_safe from django.utils.timezone import is_aware, utc @@ -45,7 +44,7 @@ def intcomma(value, use_l10n=True): return intcomma(value, False) else: return number_format(value, force_grouping=True) - orig = force_text(value) + orig = str(value) new = re.sub(r"^(-?\d+)(\d{3})", r'\g<1>,\g<2>', orig) if orig == new: return new diff --git a/django/core/signing.py b/django/core/signing.py index acf4959fc0..2ab3ae89cf 100644 --- a/django/core/signing.py +++ b/django/core/signing.py @@ -43,7 +43,7 @@ import zlib from django.conf import settings from django.utils import baseconv from django.utils.crypto import constant_time_compare, 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 _SEP_UNSAFE = re.compile(r'^[A-z0-9-_=]*$') @@ -73,7 +73,7 @@ def b64_decode(s): def base64_hmac(salt, value, key): - return b64_encode(salted_hmac(salt, value, key).digest()) + return b64_encode(salted_hmac(salt, value, key).digest()).decode() def get_cookie_signer(salt='django.core.signing.get_cookie_signer'): @@ -121,9 +121,9 @@ def dumps(obj, key=None, salt='django.core.signing', serializer=JSONSerializer, if len(compressed) < (len(data) - 1): data = compressed is_compressed = True - base64d = b64_encode(data) + base64d = b64_encode(data).decode() if is_compressed: - base64d = b'.' + base64d + base64d = '.' + base64d return TimestampSigner(key, salt=salt).sign(base64d) @@ -161,7 +161,7 @@ class Signer: self.salt = salt or '%s.%s' % (self.__class__.__module__, self.__class__.__name__) def signature(self, value): - return force_text(base64_hmac(self.salt + 'signer', value, self.key)) + return base64_hmac(self.salt + 'signer', value, self.key) def sign(self, value): return '%s%s%s' % (value, self.sep, self.signature(value)) @@ -171,7 +171,7 @@ class Signer: raise BadSignature('No "%s" found in value' % self.sep) value, sig = signed_value.rsplit(self.sep, 1) if constant_time_compare(sig, self.signature(value)): - return force_text(value) + return value raise BadSignature('Signature "%s" does not match' % sig) @@ -181,7 +181,7 @@ class TimestampSigner(Signer): return baseconv.base62.encode(int(time.time())) def sign(self, value): - value = '%s%s%s' % (force_text(value), self.sep, self.timestamp()) + value = '%s%s%s' % (value, self.sep, self.timestamp()) return super().sign(value) def unsign(self, value, max_age=None): diff --git a/django/template/base.py b/django/template/base.py index 163ea3043d..3946af3802 100644 --- a/django/template/base.py +++ b/django/template/base.py @@ -56,7 +56,6 @@ import re from django.template.context import ( # NOQA: imported for backwards compatibility BaseContext, Context, ContextPopException, RequestContext, ) -from django.utils.encoding import force_text from django.utils.formats import localize from django.utils.html import conditional_escape, escape from django.utils.inspect import getargspec @@ -108,10 +107,6 @@ tag_re = (re.compile('(%s.*?%s|%s.*?%s|%s.*?%s)' % logger = logging.getLogger('django.template') -class TemplateEncodingError(Exception): - pass - - class VariableDoesNotExist(Exception): def __init__(self, msg, params=()): @@ -150,13 +145,6 @@ class Origin: class Template: def __init__(self, template_string, origin=None, name=None, engine=None): - try: - template_string = force_text(template_string) - except UnicodeDecodeError: - raise TemplateEncodingError( - "Templates can only be constructed from strings or UTF-8 " - "bytestrings." - ) # If Template is instantiated directly rather than from an Engine and # exactly one Django template engine is configured, use that engine. # This is required to preserve backwards-compatibility for direct use @@ -274,7 +262,7 @@ class Template: # In some rare cases exc_value.args can be empty or an invalid # string. try: - message = force_text(exception.args[0]) + message = str(exception.args[0]) except (IndexError, UnicodeDecodeError): message = '(Could not get exception message)' @@ -957,7 +945,7 @@ class NodeList(list): bit = node.render_annotated(context) else: bit = node - bits.append(force_text(bit)) + bits.append(str(bit)) return mark_safe(''.join(bits)) def get_nodes_by_type(self, nodetype): @@ -987,11 +975,12 @@ def render_value_in_context(value, context): """ value = template_localtime(value, use_tz=context.use_tz) value = localize(value, use_l10n=context.use_l10n) - value = force_text(value) if context.autoescape: + if not issubclass(type(value), str): + value = str(value) return conditional_escape(value) else: - return value + return str(value) class VariableNode(Node): diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py index 09064569eb..c8e8fb8591 100644 --- a/django/template/defaultfilters.py +++ b/django/template/defaultfilters.py @@ -9,7 +9,7 @@ from urllib.parse import quote from django.utils import formats from django.utils.dateformat import format, time_format -from django.utils.encoding import force_text, iri_to_uri +from django.utils.encoding import iri_to_uri from django.utils.html import ( avoid_wrapping, conditional_escape, escape, escapejs, linebreaks, strip_tags, urlize as _urlize, @@ -39,7 +39,7 @@ def stringfilter(func): def _dec(*args, **kwargs): if args: args = list(args) - args[0] = force_text(args[0]) + args[0] = str(args[0]) if (isinstance(args[0], SafeData) and getattr(_dec._decorated_function, 'is_safe', False)): return mark_safe(func(*args, **kwargs)) @@ -120,7 +120,7 @@ def floatformat(text, arg=-1): d = Decimal(input_val) except InvalidOperation: try: - d = Decimal(force_text(float(text))) + d = Decimal(str(float(text))) except (ValueError, InvalidOperation, TypeError): return '' try: @@ -473,7 +473,7 @@ def safeseq(value): individually, as safe, after converting them to strings. Returns a list with the results. """ - return [mark_safe(force_text(obj)) for obj in value] + return [mark_safe(str(obj)) for obj in value] @register.filter(is_safe=True) @@ -551,7 +551,6 @@ def join(value, arg, autoescape=True): """ Joins a list with a string, like Python's ``str.join(list)``. """ - value = map(force_text, value) if autoescape: value = [conditional_escape(v) for v in value] try: @@ -677,7 +676,7 @@ def unordered_list(value, autoescape=True): sublist = '\n%s\n%s' % ( indent, list_formatter(children, tabs + 1), indent, indent) output.append('%s
  • %s%s
  • ' % ( - indent, escaper(force_text(item)), sublist)) + indent, escaper(item), sublist)) return '\n'.join(output) return mark_safe(list_formatter(value)) @@ -937,4 +936,4 @@ def pprint(value): try: return pformat(value) except Exception as e: - return "Error in formatting: %s: %s" % (e.__class__.__name__, force_text(e, errors="replace")) + return "Error in formatting: %s: %s" % (e.__class__.__name__, e) diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py index cbea6a4394..d0e6839c5b 100644 --- a/django/template/defaulttags.py +++ b/django/template/defaulttags.py @@ -8,7 +8,6 @@ from itertools import cycle as itertools_cycle, groupby from django.conf import settings from django.utils import timezone -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 @@ -96,9 +95,9 @@ class CycleNode(Node): class DebugNode(Node): def render(self, context): from pprint import pformat - output = [force_text(pformat(val)) for val in context] + output = [pformat(val) for val in context] output.append('\n\n') - output.append(force_text(pformat(sys.modules))) + output.append(pformat(sys.modules)) return ''.join(output) @@ -220,7 +219,7 @@ class ForNode(Node): # don't want to leave any vars from the previous loop on the # context. context.pop() - return mark_safe(''.join(force_text(n) for n in nodelist)) + return mark_safe(''.join(nodelist)) class IfChangedNode(Node): @@ -437,10 +436,7 @@ class URLNode(Node): def render(self, context): from django.urls import reverse, NoReverseMatch args = [arg.resolve(context) for arg in self.args] - kwargs = { - force_text(k, 'ascii'): v.resolve(context) - for k, v in self.kwargs.items() - } + kwargs = {k: v.resolve(context) for k, v in self.kwargs.items()} view_name = self.view_name.resolve(context) try: current_app = context.request.current_app diff --git a/django/template/loaders/cached.py b/django/template/loaders/cached.py index b86001a2f7..af401827a5 100644 --- a/django/template/loaders/cached.py +++ b/django/template/loaders/cached.py @@ -7,7 +7,7 @@ import hashlib from django.template import TemplateDoesNotExist from django.template.backends.django import copy_exception -from django.utils.encoding import force_bytes, force_text +from django.utils.encoding import force_bytes from .base import Loader as BaseLoader @@ -86,7 +86,7 @@ class Loader(BaseLoader): if matching: skip_prefix = self.generate_hash(matching) - return '-'.join(filter(bool, [force_text(template_name), skip_prefix, dirs_prefix])) + return '-'.join(filter(bool, [str(template_name), skip_prefix, dirs_prefix])) def generate_hash(self, values): return hashlib.sha1(force_bytes('|'.join(values))).hexdigest() diff --git a/django/templatetags/l10n.py b/django/templatetags/l10n.py index 956b2e9019..8760e7e2b8 100644 --- a/django/templatetags/l10n.py +++ b/django/templatetags/l10n.py @@ -1,6 +1,5 @@ from django.template import Library, Node, TemplateSyntaxError from django.utils import formats -from django.utils.encoding import force_text register = Library() @@ -11,7 +10,7 @@ def localize(value): Forces a value to be rendered as a localized value, regardless of the value of ``settings.USE_L10N``. """ - return force_text(formats.localize(value, use_l10n=True)) + return str(formats.localize(value, use_l10n=True)) @register.filter(is_safe=False) @@ -20,7 +19,7 @@ def unlocalize(value): Forces a value to be rendered as a non-localized value, regardless of the value of ``settings.USE_L10N``. """ - return force_text(value) + return str(value) class LocalizeNode(Node): diff --git a/django/utils/tree.py b/django/utils/tree.py index 7f27fc7aac..d4a2930e42 100644 --- a/django/utils/tree.py +++ b/django/utils/tree.py @@ -5,8 +5,6 @@ ORM. import copy -from django.utils.encoding import force_text - class Node: """ @@ -45,7 +43,7 @@ class Node: def __str__(self): template = '(NOT (%s: %s))' if self.negated else '(%s: %s)' - return template % (self.connector, ', '.join(force_text(c) for c in self.children)) + return template % (self.connector, ', '.join(str(c) for c in self.children)) def __repr__(self): return "<%s: %s>" % (self.__class__.__name__, self) diff --git a/tests/signing/tests.py b/tests/signing/tests.py index 01dd55fcfe..5969e3cc1c 100644 --- a/tests/signing/tests.py +++ b/tests/signing/tests.py @@ -18,7 +18,7 @@ class TestSigner(SimpleTestCase): ): self.assertEqual( signer.signature(s), - signing.base64_hmac(signer.salt + 'signer', s, 'predictable-secret').decode() + signing.base64_hmac(signer.salt + 'signer', s, 'predictable-secret') ) self.assertNotEqual(signer.signature(s), signer2.signature(s)) @@ -27,7 +27,7 @@ class TestSigner(SimpleTestCase): signer = signing.Signer('predictable-secret', salt='extra-salt') self.assertEqual( signer.signature('hello'), - signing.base64_hmac('extra-salt' + 'signer', 'hello', 'predictable-secret').decode() + signing.base64_hmac('extra-salt' + 'signer', 'hello', 'predictable-secret') ) self.assertNotEqual( signing.Signer('predictable-secret', salt='one').signature('hello'), diff --git a/tests/template_tests/syntax_tests/i18n/test_blocktrans.py b/tests/template_tests/syntax_tests/i18n/test_blocktrans.py index 9af60e5131..d8b1e807af 100644 --- a/tests/template_tests/syntax_tests/i18n/test_blocktrans.py +++ b/tests/template_tests/syntax_tests/i18n/test_blocktrans.py @@ -17,20 +17,20 @@ class I18nBlockTransTagTests(SimpleTestCase): @setup({'i18n03': '{% load i18n %}{% blocktrans %}{{ anton }}{% endblocktrans %}'}) def test_i18n03(self): """simple translation of a variable""" - output = self.engine.render_to_string('i18n03', {'anton': b'\xc3\x85'}) + output = self.engine.render_to_string('i18n03', {'anton': 'Å'}) self.assertEqual(output, 'Å') @setup({'i18n04': '{% load i18n %}{% blocktrans with berta=anton|lower %}{{ berta }}{% endblocktrans %}'}) def test_i18n04(self): """simple translation of a variable and filter""" - output = self.engine.render_to_string('i18n04', {'anton': b'\xc3\x85'}) + output = self.engine.render_to_string('i18n04', {'anton': 'Å'}) self.assertEqual(output, 'å') @setup({'legacyi18n04': '{% load i18n %}' '{% blocktrans with anton|lower as berta %}{{ berta }}{% endblocktrans %}'}) def test_legacyi18n04(self): """simple translation of a variable and filter""" - output = self.engine.render_to_string('legacyi18n04', {'anton': b'\xc3\x85'}) + output = self.engine.render_to_string('legacyi18n04', {'anton': 'Å'}) self.assertEqual(output, 'å') @setup({'i18n05': '{% load i18n %}{% blocktrans %}xxx{{ anton }}xxx{% endblocktrans %}'}) diff --git a/tests/template_tests/test_unicode.py b/tests/template_tests/test_unicode.py deleted file mode 100644 index 42682166f5..0000000000 --- a/tests/template_tests/test_unicode.py +++ /dev/null @@ -1,31 +0,0 @@ -from unittest import TestCase - -from django.template import Context, Engine -from django.template.base import TemplateEncodingError -from django.utils.safestring import SafeData - - -class UnicodeTests(TestCase): - def test_template(self): - # Templates can be created from strings. - engine = Engine() - t1 = engine.from_string('ŠĐĆŽćžšđ {{ var }}') - # Templates can also be created from bytestrings. These are assumed to - # be encoded using UTF-8. - s = b'\xc5\xa0\xc4\x90\xc4\x86\xc5\xbd\xc4\x87\xc5\xbe\xc5\xa1\xc4\x91 {{ var }}' - t2 = engine.from_string(s) - with self.assertRaises(TemplateEncodingError): - engine.from_string(b'\x80\xc5\xc0') - - # Contexts can be constructed from strings or UTF-8 bytestrings. - Context({b"var": b"foo"}) - Context({"var": b"foo"}) - c3 = Context({b"var": "Đđ"}) - Context({"var": b"\xc4\x90\xc4\x91"}) - - # Since both templates and all four contexts represent the same thing, - # they all render the same (and are returned as strings and - # "safe" objects as well, for auto-escaping purposes). - self.assertEqual(t1.render(c3), t2.render(c3)) - self.assertIsInstance(t1.render(c3), str) - self.assertIsInstance(t1.render(c3), SafeData)