From 60ca37d2e56e435521a4aa5ba56b1b11cb2a78e5 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Sat, 31 Dec 2016 12:43:30 -0500 Subject: [PATCH] Refs #24046 -- Removed mark_for_escaping() per deprecation timeline. --- django/template/base.py | 23 +--------- django/template/defaultfilters.py | 10 +--- django/utils/safestring.py | 46 ------------------- docs/ref/templates/builtins.txt | 11 ----- docs/ref/utils.txt | 10 ---- docs/releases/2.0.txt | 6 +++ docs/topics/python3.txt | 12 ++--- .../filter_tests/test_chaining.py | 21 ++------- .../filter_tests/test_escape.py | 7 +-- .../filter_tests/test_force_escape.py | 12 ++--- tests/utils_tests/test_safestring.py | 41 +---------------- 11 files changed, 24 insertions(+), 175 deletions(-) diff --git a/django/template/base.py b/django/template/base.py index dd604db235..af3b6c5371 100644 --- a/django/template/base.py +++ b/django/template/base.py @@ -54,22 +54,18 @@ from __future__ import unicode_literals import inspect import logging import re -import warnings from django.template.context import ( # NOQA: imported for backwards compatibility BaseContext, Context, ContextPopException, RequestContext, ) from django.utils import six -from django.utils.deprecation import RemovedInDjango20Warning from django.utils.encoding import ( force_str, force_text, python_2_unicode_compatible, ) from django.utils.formats import localize from django.utils.html import conditional_escape, escape from django.utils.inspect import getargspec -from django.utils.safestring import ( - EscapeData, SafeData, mark_for_escaping, mark_safe, -) +from django.utils.safestring import SafeData, mark_safe from django.utils.text import ( get_text_list, smart_split, unescape_string_literal, ) @@ -713,7 +709,6 @@ class FilterExpression(object): obj = string_if_invalid else: obj = self.var - escape_isnt_last_filter = True for func, args in self.filters: arg_vals = [] for lookup, arg in args: @@ -729,22 +724,8 @@ class FilterExpression(object): new_obj = func(obj, *arg_vals) if getattr(func, 'is_safe', False) and isinstance(obj, SafeData): obj = mark_safe(new_obj) - elif isinstance(obj, EscapeData): - with warnings.catch_warnings(): - # Ignore mark_for_escaping deprecation as this will be - # removed in Django 2.0. - warnings.simplefilter('ignore', category=RemovedInDjango20Warning) - obj = mark_for_escaping(new_obj) - escape_isnt_last_filter = False else: obj = new_obj - if not escape_isnt_last_filter: - warnings.warn( - "escape isn't the last filter in %s and will be applied " - "immediately in Django 2.0 so the output may change." - % [func.__name__ for func, _ in self.filters], - RemovedInDjango20Warning, stacklevel=2 - ) return obj def args_check(name, func, provided): @@ -1015,7 +996,7 @@ 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 or isinstance(value, EscapeData): + if context.autoescape: return conditional_escape(value) else: return value diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py index a1f96f5e2e..e35415bdf7 100644 --- a/django/template/defaultfilters.py +++ b/django/template/defaultfilters.py @@ -3,7 +3,6 @@ from __future__ import unicode_literals import random as random_module import re -import warnings from decimal import ROUND_HALF_UP, Context, Decimal, InvalidOperation from functools import wraps from operator import itemgetter @@ -11,14 +10,13 @@ from pprint import pformat from django.utils import formats, six from django.utils.dateformat import format, time_format -from django.utils.deprecation import RemovedInDjango20Warning from django.utils.encoding import force_text, iri_to_uri from django.utils.html import ( avoid_wrapping, conditional_escape, escape, escapejs, linebreaks, strip_tags, urlize as _urlize, ) from django.utils.http import urlquote -from django.utils.safestring import SafeData, mark_for_escaping, mark_safe +from django.utils.safestring import SafeData, mark_safe from django.utils.text import ( Truncator, normalize_newlines, phone2numeric, slugify as _slugify, wrap, ) @@ -442,11 +440,7 @@ def escape_filter(value): """ Marks the value as a string that should be auto-escaped. """ - with warnings.catch_warnings(): - # Ignore mark_for_escaping deprecation -- this will use - # conditional_escape() in Django 2.0. - warnings.simplefilter('ignore', category=RemovedInDjango20Warning) - return mark_for_escaping(value) + return conditional_escape(value) @register.filter(is_safe=True) diff --git a/django/utils/safestring.py b/django/utils/safestring.py index 76136d0b0c..4355a050a0 100644 --- a/django/utils/safestring.py +++ b/django/utils/safestring.py @@ -4,39 +4,11 @@ without further escaping in HTML. Marking something as a "safe string" means that the producer of the string has already turned characters that should not be interpreted by the HTML engine (e.g. '<') into the appropriate entities. """ -import warnings from django.utils import six -from django.utils.deprecation import RemovedInDjango20Warning from django.utils.functional import Promise, curry, wraps -class EscapeData(object): - pass - - -class EscapeBytes(bytes, EscapeData): - """ - A byte string that should be HTML-escaped when output. - """ - pass - - -class EscapeText(six.text_type, EscapeData): - """ - A unicode string object that should be HTML-escaped when output. - """ - pass - - -if six.PY3: - EscapeString = EscapeText -else: - EscapeString = EscapeBytes - # backwards compatibility for Python 2 - EscapeUnicode = EscapeText - - class SafeData(object): def __html__(self): """ @@ -144,21 +116,3 @@ def mark_safe(s): if callable(s): return _safety_decorator(mark_safe, s) return SafeString(str(s)) - - -def mark_for_escaping(s): - """ - Explicitly mark a string as requiring HTML escaping upon output. Has no - effect on SafeData subclasses. - - Can be called multiple times on a single string (the resulting escaping is - only applied once). - """ - warnings.warn('mark_for_escaping() is deprecated.', RemovedInDjango20Warning) - if hasattr(s, '__html__') or isinstance(s, EscapeData): - return s - if isinstance(s, bytes) or (isinstance(s, Promise) and s._delegate_bytes): - return EscapeBytes(s) - if isinstance(s, (six.text_type, Promise)): - return EscapeText(s) - return EscapeString(str(s)) diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt index 6395f97607..5925370843 100644 --- a/docs/ref/templates/builtins.txt +++ b/docs/ref/templates/builtins.txt @@ -1636,11 +1636,6 @@ Escapes a string's HTML. Specifically, it makes these replacements: * ``"`` (double quote) is converted to ``"`` * ``&`` is converted to ``&`` -The escaping is only applied when the string is output, so it does not matter -where in a chained sequence of filters you put ``escape``: it will always be -applied as though it were the last filter. If you want escaping to be applied -immediately, use the :tfilter:`force_escape` filter. - Applying ``escape`` to a variable that would normally have auto-escaping applied to the result will only result in one round of escaping being done. So it is safe to use this function even in auto-escaping environments. If you want @@ -1652,12 +1647,6 @@ For example, you can apply ``escape`` to fields when :ttag:`autoescape` is off:: {{ title|escape }} {% endautoescape %} -.. deprecated:: 1.10 - - The "lazy" behavior of the ``escape`` filter is deprecated. It will change - to immediately apply :func:`~django.utils.html.conditional_escape` in - Django 2.0. - .. templatefilter:: escapejs ``escapejs`` diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt index 5870c955f6..8beafb8268 100644 --- a/docs/ref/utils.txt +++ b/docs/ref/utils.txt @@ -841,16 +841,6 @@ appropriate entities. Added support for decorator usage. -.. function:: mark_for_escaping(s) - - .. deprecated:: 1.10 - - Explicitly mark a string as requiring HTML escaping upon output. Has no - effect on ``SafeData`` subclasses. - - Can be called multiple times on a single string (the resulting escaping is - only applied once). - ``django.utils.text`` ===================== diff --git a/docs/releases/2.0.txt b/docs/releases/2.0.txt index dade7e0bda..7e595e9cbf 100644 --- a/docs/releases/2.0.txt +++ b/docs/releases/2.0.txt @@ -378,3 +378,9 @@ these features. * ``FileField`` methods ``get_directory_name()`` and ``get_filename()`` are removed. + +* The ``mark_for_escaping()`` function and the classes it uses: ``EscapeData``, + ``EscapeBytes``, ``EscapeText``, ``EscapeString``, and ``EscapeUnicode`` are + removed. + +* The ``escape`` filter now uses ``django.utils.html.conditional_escape()``. diff --git a/docs/topics/python3.txt b/docs/topics/python3.txt index 04a975d8f2..9dd4d83732 100644 --- a/docs/topics/python3.txt +++ b/docs/topics/python3.txt @@ -112,22 +112,18 @@ For forwards compatibility, the new names work as of Django 1.4.2. information. :mod:`django.utils.safestring` is mostly used via the -:func:`~django.utils.safestring.mark_safe` and -:func:`~django.utils.safestring.mark_for_escaping` functions, which didn't -change. In case you're using the internals, here are the name changes: +:func:`~django.utils.safestring.mark_safe` function, which didn't change. In +case you're using the internals, here are the name changes: ================== ================== Old name New name ================== ================== -``EscapeString`` ``EscapeBytes`` -``EscapeUnicode`` ``EscapeText`` ``SafeString`` ``SafeBytes`` ``SafeUnicode`` ``SafeText`` ================== ================== -For backwards compatibility, the old names still work on Python 2. Under -Python 3, ``EscapeString`` and ``SafeString`` are aliases for ``EscapeText`` -and ``SafeText`` respectively. +For backwards compatibility, the old names still work on Python 2. On Python 3, +``SafeString`` is an alias for ``SafeText``. For forwards compatibility, the new names work as of Django 1.4.2. diff --git a/tests/template_tests/filter_tests/test_chaining.py b/tests/template_tests/filter_tests/test_chaining.py index 1c12e252a0..9bc3976f37 100644 --- a/tests/template_tests/filter_tests/test_chaining.py +++ b/tests/template_tests/filter_tests/test_chaining.py @@ -1,8 +1,4 @@ -import warnings - -from django.test import SimpleTestCase, ignore_warnings -from django.test.utils import reset_warning_registry -from django.utils.deprecation import RemovedInDjango20Warning +from django.test import SimpleTestCase from django.utils.safestring import mark_safe from ..utils import setup @@ -42,20 +38,9 @@ class ChainingTests(SimpleTestCase): # Using a filter that forces safeness does not lead to double-escaping @setup({'chaining05': '{{ a|escape|capfirst }}'}) def test_chaining05(self): - reset_warning_registry() - with warnings.catch_warnings(record=True) as warns: - warnings.simplefilter('always') - output = self.engine.render_to_string('chaining05', {'a': 'a < b'}) - self.assertEqual(output, 'A < b') + output = self.engine.render_to_string('chaining05', {'a': 'a < b'}) + self.assertEqual(output, 'A < b') - self.assertEqual(len(warns), 1) - self.assertEqual( - str(warns[0].message), - "escape isn't the last filter in ['escape_filter', 'capfirst'] and " - "will be applied immediately in Django 2.0 so the output may change." - ) - - @ignore_warnings(category=RemovedInDjango20Warning) @setup({'chaining06': '{% autoescape off %}{{ a|escape|capfirst }}{% endautoescape %}'}) def test_chaining06(self): output = self.engine.render_to_string('chaining06', {'a': 'a < b'}) diff --git a/tests/template_tests/filter_tests/test_escape.py b/tests/template_tests/filter_tests/test_escape.py index 644ed7ac9e..6f28b972a2 100644 --- a/tests/template_tests/filter_tests/test_escape.py +++ b/tests/template_tests/filter_tests/test_escape.py @@ -1,7 +1,6 @@ from django.template.defaultfilters import escape -from django.test import SimpleTestCase, ignore_warnings +from django.test import SimpleTestCase from django.utils import six -from django.utils.deprecation import RemovedInDjango20Warning from django.utils.functional import Promise, lazy from django.utils.safestring import mark_safe @@ -24,15 +23,11 @@ class EscapeTests(SimpleTestCase): output = self.engine.render_to_string('escape02', {"a": "x&y", "b": mark_safe("x&y")}) self.assertEqual(output, "x&y x&y") - # It is only applied once, regardless of the number of times it - # appears in a chain (to be changed in Django 2.0). - @ignore_warnings(category=RemovedInDjango20Warning) @setup({'escape03': '{% autoescape off %}{{ a|escape|escape }}{% endautoescape %}'}) def test_escape03(self): output = self.engine.render_to_string('escape03', {"a": "x&y"}) self.assertEqual(output, "x&y") - @ignore_warnings(category=RemovedInDjango20Warning) @setup({'escape04': '{{ a|escape|escape }}'}) def test_escape04(self): output = self.engine.render_to_string('escape04', {"a": "x&y"}) diff --git a/tests/template_tests/filter_tests/test_force_escape.py b/tests/template_tests/filter_tests/test_force_escape.py index f163f2cd75..45f4efde62 100644 --- a/tests/template_tests/filter_tests/test_force_escape.py +++ b/tests/template_tests/filter_tests/test_force_escape.py @@ -2,8 +2,7 @@ from __future__ import unicode_literals from django.template.defaultfilters import force_escape -from django.test import SimpleTestCase, ignore_warnings -from django.utils.deprecation import RemovedInDjango20Warning +from django.test import SimpleTestCase from django.utils.safestring import SafeData from ..utils import setup @@ -36,8 +35,7 @@ class ForceEscapeTests(SimpleTestCase): self.assertEqual(output, "x&amp;y") # Because the result of force_escape is "safe", an additional - # escape filter has no effect (to be changed in Django 2.0). - @ignore_warnings(category=RemovedInDjango20Warning) + # escape filter has no effect. @setup({'force-escape05': '{% autoescape off %}{{ a|force_escape|escape }}{% endautoescape %}'}) def test_force_escape05(self): output = self.engine.render_to_string('force-escape05', {"a": "x&y"}) @@ -48,17 +46,15 @@ class ForceEscapeTests(SimpleTestCase): output = self.engine.render_to_string('force-escape06', {"a": "x&y"}) self.assertEqual(output, "x&y") - @ignore_warnings(category=RemovedInDjango20Warning) @setup({'force-escape07': '{% autoescape off %}{{ a|escape|force_escape }}{% endautoescape %}'}) def test_force_escape07(self): output = self.engine.render_to_string('force-escape07', {"a": "x&y"}) - self.assertEqual(output, "x&y") + self.assertEqual(output, "x&amp;y") - @ignore_warnings(category=RemovedInDjango20Warning) @setup({'force-escape08': '{{ a|escape|force_escape }}'}) def test_force_escape08(self): output = self.engine.render_to_string('force-escape08', {"a": "x&y"}) - self.assertEqual(output, "x&y") + self.assertEqual(output, "x&amp;y") class FunctionTests(SimpleTestCase): diff --git a/tests/utils_tests/test_safestring.py b/tests/utils_tests/test_safestring.py index 6afcb3b1f7..abaf6a0596 100644 --- a/tests/utils_tests/test_safestring.py +++ b/tests/utils_tests/test_safestring.py @@ -1,14 +1,11 @@ from __future__ import unicode_literals from django.template import Context, Template -from django.test import SimpleTestCase, ignore_warnings +from django.test import SimpleTestCase from django.utils import html, six, text -from django.utils.deprecation import RemovedInDjango20Warning from django.utils.encoding import force_bytes from django.utils.functional import lazy, lazystr -from django.utils.safestring import ( - EscapeData, SafeData, mark_for_escaping, mark_safe, -) +from django.utils.safestring import SafeData, mark_safe lazybytes = lazy(force_bytes, bytes) @@ -63,40 +60,6 @@ class SafeStringTest(SimpleTestCase): def test_mark_safe_lazy_result_implements_dunder_html(self): self.assertEqual(mark_safe(lazystr('a&b')).__html__(), 'a&b') - @ignore_warnings(category=RemovedInDjango20Warning) - def test_mark_for_escaping(self): - s = mark_for_escaping('a&b') - self.assertRenderEqual('{{ s }}', 'a&b', s=s) - self.assertRenderEqual('{{ s }}', 'a&b', s=mark_for_escaping(s)) - - @ignore_warnings(category=RemovedInDjango20Warning) - def test_mark_for_escaping_object_implementing_dunder_html(self): - e = customescape('') - s = mark_for_escaping(e) - self.assertIs(s, e) - - self.assertRenderEqual('{{ s }}', '<>', s=s) - self.assertRenderEqual('{{ s|force_escape }}', '<a&b>', s=s) - - @ignore_warnings(category=RemovedInDjango20Warning) - def test_mark_for_escaping_lazy(self): - s = lazystr('a&b') - b = lazybytes(b'a&b') - - self.assertIsInstance(mark_for_escaping(s), EscapeData) - self.assertIsInstance(mark_for_escaping(b), EscapeData) - self.assertRenderEqual('{% autoescape off %}{{ s }}{% endautoescape %}', 'a&b', s=mark_for_escaping(s)) - - @ignore_warnings(category=RemovedInDjango20Warning) - def test_mark_for_escaping_object_implementing_dunder_str(self): - class Obj(object): - def __str__(self): - return '' - - s = mark_for_escaping(Obj()) - - self.assertRenderEqual('{{ s }}', '<obj>', s=s) - def test_add_lazy_safe_text_and_safe_text(self): s = html.escape(lazystr('a')) s += mark_safe('&b')