Fixed #24046 -- Deprecated the "escape" half of utils.safestring.
This commit is contained in:
parent
c3e1086949
commit
2f0e0eee45
|
@ -54,6 +54,7 @@ from __future__ import unicode_literals
|
||||||
import inspect
|
import inspect
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
import warnings
|
||||||
|
|
||||||
from django.template.context import ( # NOQA: imported for backwards compatibility
|
from django.template.context import ( # NOQA: imported for backwards compatibility
|
||||||
BaseContext, Context, ContextPopException, RequestContext,
|
BaseContext, Context, ContextPopException, RequestContext,
|
||||||
|
@ -722,6 +723,7 @@ class FilterExpression(object):
|
||||||
obj = string_if_invalid
|
obj = string_if_invalid
|
||||||
else:
|
else:
|
||||||
obj = self.var
|
obj = self.var
|
||||||
|
escape_isnt_last_filter = True
|
||||||
for func, args in self.filters:
|
for func, args in self.filters:
|
||||||
arg_vals = []
|
arg_vals = []
|
||||||
for lookup, arg in args:
|
for lookup, arg in args:
|
||||||
|
@ -738,9 +740,21 @@ class FilterExpression(object):
|
||||||
if getattr(func, 'is_safe', False) and isinstance(obj, SafeData):
|
if getattr(func, 'is_safe', False) and isinstance(obj, SafeData):
|
||||||
obj = mark_safe(new_obj)
|
obj = mark_safe(new_obj)
|
||||||
elif isinstance(obj, EscapeData):
|
elif isinstance(obj, EscapeData):
|
||||||
obj = mark_for_escaping(new_obj)
|
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:
|
else:
|
||||||
obj = new_obj
|
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
|
return obj
|
||||||
|
|
||||||
def args_check(name, func, provided):
|
def args_check(name, func, provided):
|
||||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
import random as random_module
|
import random as random_module
|
||||||
import re
|
import re
|
||||||
|
import warnings
|
||||||
from decimal import ROUND_HALF_UP, Context, Decimal, InvalidOperation
|
from decimal import ROUND_HALF_UP, Context, Decimal, InvalidOperation
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
|
@ -10,6 +11,7 @@ from pprint import pformat
|
||||||
|
|
||||||
from django.utils import formats, six
|
from django.utils import formats, six
|
||||||
from django.utils.dateformat import format, time_format
|
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.encoding import force_text, iri_to_uri
|
||||||
from django.utils.html import (
|
from django.utils.html import (
|
||||||
avoid_wrapping, conditional_escape, escape, escapejs, linebreaks,
|
avoid_wrapping, conditional_escape, escape, escapejs, linebreaks,
|
||||||
|
@ -439,7 +441,11 @@ def escape_filter(value):
|
||||||
"""
|
"""
|
||||||
Marks the value as a string that should be auto-escaped.
|
Marks the value as a string that should be auto-escaped.
|
||||||
"""
|
"""
|
||||||
return mark_for_escaping(value)
|
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)
|
||||||
|
|
||||||
|
|
||||||
@register.filter(is_safe=True)
|
@register.filter(is_safe=True)
|
||||||
|
|
|
@ -4,7 +4,10 @@ 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
|
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.
|
be interpreted by the HTML engine (e.g. '<') into the appropriate entities.
|
||||||
"""
|
"""
|
||||||
|
import warnings
|
||||||
|
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
from django.utils.deprecation import RemovedInDjango20Warning
|
||||||
from django.utils.functional import Promise, curry
|
from django.utils.functional import Promise, curry
|
||||||
|
|
||||||
|
|
||||||
|
@ -138,6 +141,7 @@ def mark_for_escaping(s):
|
||||||
Can be called multiple times on a single string (the resulting escaping is
|
Can be called multiple times on a single string (the resulting escaping is
|
||||||
only applied once).
|
only applied once).
|
||||||
"""
|
"""
|
||||||
|
warnings.warn('mark_for_escaping() is deprecated.', RemovedInDjango20Warning)
|
||||||
if hasattr(s, '__html__') or isinstance(s, EscapeData):
|
if hasattr(s, '__html__') or isinstance(s, EscapeData):
|
||||||
return s
|
return s
|
||||||
if isinstance(s, bytes) or (isinstance(s, Promise) and s._delegate_bytes):
|
if isinstance(s, bytes) or (isinstance(s, Promise) and s._delegate_bytes):
|
||||||
|
|
|
@ -210,15 +210,6 @@ passed around inside the template code:
|
||||||
# Do something with the "safe" string.
|
# Do something with the "safe" string.
|
||||||
...
|
...
|
||||||
|
|
||||||
* **Strings marked as "needing escaping"** are *always* escaped on
|
|
||||||
output, regardless of whether they are in an :ttag:`autoescape` block or
|
|
||||||
not. These strings are only escaped once, however, even if auto-escaping
|
|
||||||
applies.
|
|
||||||
|
|
||||||
Internally, these strings are of type ``EscapeBytes`` or
|
|
||||||
``EscapeText``. Generally you don't have to worry about these; they
|
|
||||||
exist for the implementation of the :tfilter:`escape` filter.
|
|
||||||
|
|
||||||
Template filter code falls into one of two situations:
|
Template filter code falls into one of two situations:
|
||||||
|
|
||||||
1. Your filter does not introduce any HTML-unsafe characters (``<``, ``>``,
|
1. Your filter does not introduce any HTML-unsafe characters (``<``, ``>``,
|
||||||
|
|
|
@ -168,6 +168,13 @@ details on these changes.
|
||||||
* ``FileField`` methods ``get_directory_name()`` and ``get_filename()`` will be
|
* ``FileField`` methods ``get_directory_name()`` and ``get_filename()`` will be
|
||||||
removed.
|
removed.
|
||||||
|
|
||||||
|
* The ``mark_for_escaping()`` function and the classes it uses: ``EscapeData``,
|
||||||
|
``EscapeBytes``, ``EscapeText``, ``EscapeString``, and ``EscapeUnicode`` will
|
||||||
|
be removed.
|
||||||
|
|
||||||
|
* The ``escape`` filter will change to use
|
||||||
|
``django.utils.html.conditional_escape()``.
|
||||||
|
|
||||||
.. _deprecation-removed-in-1.10:
|
.. _deprecation-removed-in-1.10:
|
||||||
|
|
||||||
1.10
|
1.10
|
||||||
|
|
|
@ -1578,6 +1578,12 @@ For example, you can apply ``escape`` to fields when :ttag:`autoescape` is off::
|
||||||
{{ title|escape }}
|
{{ title|escape }}
|
||||||
{% endautoescape %}
|
{% 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
|
.. templatefilter:: escapejs
|
||||||
|
|
||||||
``escapejs``
|
``escapejs``
|
||||||
|
|
|
@ -839,6 +839,8 @@ appropriate entities.
|
||||||
|
|
||||||
.. function:: mark_for_escaping(s)
|
.. function:: mark_for_escaping(s)
|
||||||
|
|
||||||
|
.. deprecated:: 1.10
|
||||||
|
|
||||||
Explicitly mark a string as requiring HTML escaping upon output. Has no
|
Explicitly mark a string as requiring HTML escaping upon output. Has no
|
||||||
effect on ``SafeData`` subclasses.
|
effect on ``SafeData`` subclasses.
|
||||||
|
|
||||||
|
|
|
@ -1012,6 +1012,18 @@ This method must accept a :class:`~django.db.models.query.QuerySet` instance
|
||||||
as its single argument and return a filtered version of the queryset for the
|
as its single argument and return a filtered version of the queryset for the
|
||||||
model instance the manager is bound to.
|
model instance the manager is bound to.
|
||||||
|
|
||||||
|
The "escape" half of ``django.utils.safestring``
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
The ``mark_for_escaping()`` function and the classes it uses: ``EscapeData``,
|
||||||
|
``EscapeBytes``, ``EscapeText``, ``EscapeString``, and ``EscapeUnicode`` are
|
||||||
|
deprecated.
|
||||||
|
|
||||||
|
As a result, the "lazy" behavior of the ``escape`` filter (where it would
|
||||||
|
always be applied as the last filter no matter where in the filter chain it
|
||||||
|
appeared) is deprecated. The filter will change to immediately apply
|
||||||
|
:func:`~django.utils.html.conditional_escape` in Django 2.0.
|
||||||
|
|
||||||
Miscellaneous
|
Miscellaneous
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
|
|
@ -177,7 +177,7 @@ Bugfixes
|
||||||
setup (:ticket:`24000`).
|
setup (:ticket:`24000`).
|
||||||
|
|
||||||
* Restored support for objects that aren't :class:`str` or :class:`bytes` in
|
* Restored support for objects that aren't :class:`str` or :class:`bytes` in
|
||||||
:func:`~django.utils.safestring.mark_for_escaping` on Python 3.
|
``django.utils.safestring.mark_for_escaping()`` on Python 3.
|
||||||
|
|
||||||
* Supported strings escaped by third-party libraries with the ``__html__``
|
* Supported strings escaped by third-party libraries with the ``__html__``
|
||||||
convention in the template engine (:ticket:`23831`).
|
convention in the template engine (:ticket:`23831`).
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
from django.test import SimpleTestCase
|
import warnings
|
||||||
|
|
||||||
|
from django.test import SimpleTestCase, ignore_warnings
|
||||||
|
from django.utils.deprecation import RemovedInDjango20Warning
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
from ..utils import setup
|
from ..utils import setup
|
||||||
|
@ -38,9 +41,19 @@ class ChainingTests(SimpleTestCase):
|
||||||
# Using a filter that forces safeness does not lead to double-escaping
|
# Using a filter that forces safeness does not lead to double-escaping
|
||||||
@setup({'chaining05': '{{ a|escape|capfirst }}'})
|
@setup({'chaining05': '{{ a|escape|capfirst }}'})
|
||||||
def test_chaining05(self):
|
def test_chaining05(self):
|
||||||
output = self.engine.render_to_string('chaining05', {'a': 'a < b'})
|
with warnings.catch_warnings(record=True) as warns:
|
||||||
self.assertEqual(output, 'A < b')
|
warnings.simplefilter('always')
|
||||||
|
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 %}'})
|
@setup({'chaining06': '{% autoescape off %}{{ a|escape|capfirst }}{% endautoescape %}'})
|
||||||
def test_chaining06(self):
|
def test_chaining06(self):
|
||||||
output = self.engine.render_to_string('chaining06', {'a': 'a < b'})
|
output = self.engine.render_to_string('chaining06', {'a': 'a < b'})
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from django.template.defaultfilters import escape
|
from django.template.defaultfilters import escape
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase, ignore_warnings
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
from django.utils.deprecation import RemovedInDjango20Warning
|
||||||
from django.utils.functional import Promise, lazy
|
from django.utils.functional import Promise, lazy
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
|
@ -24,12 +25,14 @@ class EscapeTests(SimpleTestCase):
|
||||||
self.assertEqual(output, "x&y x&y")
|
self.assertEqual(output, "x&y x&y")
|
||||||
|
|
||||||
# It is only applied once, regardless of the number of times it
|
# It is only applied once, regardless of the number of times it
|
||||||
# appears in a chain.
|
# appears in a chain (to be changed in Django 2.0).
|
||||||
|
@ignore_warnings(category=RemovedInDjango20Warning)
|
||||||
@setup({'escape03': '{% autoescape off %}{{ a|escape|escape }}{% endautoescape %}'})
|
@setup({'escape03': '{% autoescape off %}{{ a|escape|escape }}{% endautoescape %}'})
|
||||||
def test_escape03(self):
|
def test_escape03(self):
|
||||||
output = self.engine.render_to_string('escape03', {"a": "x&y"})
|
output = self.engine.render_to_string('escape03', {"a": "x&y"})
|
||||||
self.assertEqual(output, "x&y")
|
self.assertEqual(output, "x&y")
|
||||||
|
|
||||||
|
@ignore_warnings(category=RemovedInDjango20Warning)
|
||||||
@setup({'escape04': '{{ a|escape|escape }}'})
|
@setup({'escape04': '{{ a|escape|escape }}'})
|
||||||
def test_escape04(self):
|
def test_escape04(self):
|
||||||
output = self.engine.render_to_string('escape04', {"a": "x&y"})
|
output = self.engine.render_to_string('escape04', {"a": "x&y"})
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.template.defaultfilters import force_escape
|
from django.template.defaultfilters import force_escape
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase, ignore_warnings
|
||||||
|
from django.utils.deprecation import RemovedInDjango20Warning
|
||||||
from django.utils.safestring import SafeData
|
from django.utils.safestring import SafeData
|
||||||
|
|
||||||
from ..utils import setup
|
from ..utils import setup
|
||||||
|
@ -35,7 +36,8 @@ class ForceEscapeTests(SimpleTestCase):
|
||||||
self.assertEqual(output, "x&amp;y")
|
self.assertEqual(output, "x&amp;y")
|
||||||
|
|
||||||
# Because the result of force_escape is "safe", an additional
|
# Because the result of force_escape is "safe", an additional
|
||||||
# escape filter has no effect.
|
# escape filter has no effect (to be changed in Django 2.0).
|
||||||
|
@ignore_warnings(category=RemovedInDjango20Warning)
|
||||||
@setup({'force-escape05': '{% autoescape off %}{{ a|force_escape|escape }}{% endautoescape %}'})
|
@setup({'force-escape05': '{% autoescape off %}{{ a|force_escape|escape }}{% endautoescape %}'})
|
||||||
def test_force_escape05(self):
|
def test_force_escape05(self):
|
||||||
output = self.engine.render_to_string('force-escape05', {"a": "x&y"})
|
output = self.engine.render_to_string('force-escape05', {"a": "x&y"})
|
||||||
|
@ -46,11 +48,13 @@ class ForceEscapeTests(SimpleTestCase):
|
||||||
output = self.engine.render_to_string('force-escape06', {"a": "x&y"})
|
output = self.engine.render_to_string('force-escape06', {"a": "x&y"})
|
||||||
self.assertEqual(output, "x&y")
|
self.assertEqual(output, "x&y")
|
||||||
|
|
||||||
|
@ignore_warnings(category=RemovedInDjango20Warning)
|
||||||
@setup({'force-escape07': '{% autoescape off %}{{ a|escape|force_escape }}{% endautoescape %}'})
|
@setup({'force-escape07': '{% autoescape off %}{{ a|escape|force_escape }}{% endautoescape %}'})
|
||||||
def test_force_escape07(self):
|
def test_force_escape07(self):
|
||||||
output = self.engine.render_to_string('force-escape07', {"a": "x&y"})
|
output = self.engine.render_to_string('force-escape07', {"a": "x&y"})
|
||||||
self.assertEqual(output, "x&y")
|
self.assertEqual(output, "x&y")
|
||||||
|
|
||||||
|
@ignore_warnings(category=RemovedInDjango20Warning)
|
||||||
@setup({'force-escape08': '{{ a|escape|force_escape }}'})
|
@setup({'force-escape08': '{{ a|escape|force_escape }}'})
|
||||||
def test_force_escape08(self):
|
def test_force_escape08(self):
|
||||||
output = self.engine.render_to_string('force-escape08', {"a": "x&y"})
|
output = self.engine.render_to_string('force-escape08', {"a": "x&y"})
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.template import Context, Template
|
from django.template import Context, Template
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase, ignore_warnings
|
||||||
from django.utils import html, six, text
|
from django.utils import html, six, text
|
||||||
|
from django.utils.deprecation import RemovedInDjango20Warning
|
||||||
from django.utils.encoding import force_bytes
|
from django.utils.encoding import force_bytes
|
||||||
from django.utils.functional import lazy, lazystr
|
from django.utils.functional import lazy, lazystr
|
||||||
from django.utils.safestring import (
|
from django.utils.safestring import (
|
||||||
|
@ -62,11 +63,13 @@ class SafeStringTest(SimpleTestCase):
|
||||||
def test_mark_safe_lazy_result_implements_dunder_html(self):
|
def test_mark_safe_lazy_result_implements_dunder_html(self):
|
||||||
self.assertEqual(mark_safe(lazystr('a&b')).__html__(), 'a&b')
|
self.assertEqual(mark_safe(lazystr('a&b')).__html__(), 'a&b')
|
||||||
|
|
||||||
|
@ignore_warnings(category=RemovedInDjango20Warning)
|
||||||
def test_mark_for_escaping(self):
|
def test_mark_for_escaping(self):
|
||||||
s = mark_for_escaping('a&b')
|
s = mark_for_escaping('a&b')
|
||||||
self.assertRenderEqual('{{ s }}', 'a&b', s=s)
|
self.assertRenderEqual('{{ s }}', 'a&b', s=s)
|
||||||
self.assertRenderEqual('{{ s }}', 'a&b', s=mark_for_escaping(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):
|
def test_mark_for_escaping_object_implementing_dunder_html(self):
|
||||||
e = customescape('<a&b>')
|
e = customescape('<a&b>')
|
||||||
s = mark_for_escaping(e)
|
s = mark_for_escaping(e)
|
||||||
|
@ -75,6 +78,7 @@ class SafeStringTest(SimpleTestCase):
|
||||||
self.assertRenderEqual('{{ s }}', '<<a&b>>', s=s)
|
self.assertRenderEqual('{{ s }}', '<<a&b>>', s=s)
|
||||||
self.assertRenderEqual('{{ s|force_escape }}', '<a&b>', s=s)
|
self.assertRenderEqual('{{ s|force_escape }}', '<a&b>', s=s)
|
||||||
|
|
||||||
|
@ignore_warnings(category=RemovedInDjango20Warning)
|
||||||
def test_mark_for_escaping_lazy(self):
|
def test_mark_for_escaping_lazy(self):
|
||||||
s = lazystr('a&b')
|
s = lazystr('a&b')
|
||||||
b = lazybytes(b'a&b')
|
b = lazybytes(b'a&b')
|
||||||
|
@ -83,6 +87,7 @@ class SafeStringTest(SimpleTestCase):
|
||||||
self.assertIsInstance(mark_for_escaping(b), EscapeData)
|
self.assertIsInstance(mark_for_escaping(b), EscapeData)
|
||||||
self.assertRenderEqual('{% autoescape off %}{{ s }}{% endautoescape %}', 'a&b', s=mark_for_escaping(s))
|
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):
|
def test_mark_for_escaping_object_implementing_dunder_str(self):
|
||||||
class Obj(object):
|
class Obj(object):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|
Loading…
Reference in New Issue