Fixed #10107 -- Allowed using mark_safe() as a decorator.

Thanks ArcTanSusan for the initial patch.
This commit is contained in:
Scott Vitale 2016-06-02 15:11:43 -06:00 committed by Tim Graham
parent 5e3f4c2e53
commit be729b6120
5 changed files with 53 additions and 2 deletions

View File

@ -8,7 +8,7 @@ import warnings
from django.utils import six from django.utils import six
from django.utils.deprecation import RemovedInDjango20Warning from django.utils.deprecation import RemovedInDjango20Warning
from django.utils.functional import Promise, curry from django.utils.functional import Promise, curry, wraps
class EscapeData(object): class EscapeData(object):
@ -117,11 +117,20 @@ else:
SafeUnicode = SafeText SafeUnicode = SafeText
def _safety_decorator(safety_marker, func):
@wraps(func)
def wrapped(*args, **kwargs):
return safety_marker(func(*args, **kwargs))
return wrapped
def mark_safe(s): def mark_safe(s):
""" """
Explicitly mark a string as safe for (HTML) output purposes. The returned Explicitly mark a string as safe for (HTML) output purposes. The returned
object can be used everywhere a string or unicode object is appropriate. object can be used everywhere a string or unicode object is appropriate.
If used on a method as a decorator, mark the returned data as safe.
Can be called multiple times on a single string. Can be called multiple times on a single string.
""" """
if hasattr(s, '__html__'): if hasattr(s, '__html__'):
@ -130,6 +139,8 @@ def mark_safe(s):
return SafeBytes(s) return SafeBytes(s)
if isinstance(s, (six.text_type, Promise)): if isinstance(s, (six.text_type, Promise)):
return SafeText(s) return SafeText(s)
if callable(s):
return _safety_decorator(mark_safe, s)
return SafeString(str(s)) return SafeString(str(s))

View File

@ -832,6 +832,8 @@ appropriate entities.
Can be called multiple times on a single string. Can be called multiple times on a single string.
Can also be used as a decorator.
For building up fragments of HTML, you should normally be using For building up fragments of HTML, you should normally be using
:func:`django.utils.html.format_html` instead. :func:`django.utils.html.format_html` instead.
@ -846,6 +848,10 @@ appropriate entities.
>>> type(mystr) >>> type(mystr)
<type 'str'> <type 'str'>
.. versionchanged:: 1.11
Added support for decorator usage.
.. function:: mark_for_escaping(s) .. function:: mark_for_escaping(s)
.. deprecated:: 1.10 .. deprecated:: 1.10

View File

@ -202,7 +202,7 @@ Signals
Templates Templates
~~~~~~~~~ ~~~~~~~~~
* ... * :meth:`~django.utils.safestring.mark_safe` can now be used as a decorator.
Tests Tests
~~~~~ ~~~~~

View File

@ -13,6 +13,7 @@ from django.utils.decorators import method_decorator
from django.utils.deprecation import RemovedInDjango20Warning from django.utils.deprecation import RemovedInDjango20Warning
from django.utils.encoding import force_text from django.utils.encoding import force_text
from django.utils.functional import allow_lazy, keep_lazy, keep_lazy_text, lazy from django.utils.functional import allow_lazy, keep_lazy, keep_lazy_text, lazy
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy from django.utils.translation import ugettext_lazy
from django.views.decorators.cache import ( from django.views.decorators.cache import (
cache_control, cache_page, never_cache, cache_control, cache_page, never_cache,
@ -74,6 +75,9 @@ full_decorator = compose(
keep_lazy(HttpResponse), keep_lazy(HttpResponse),
keep_lazy_text, keep_lazy_text,
lazy, lazy,
# django.utils.safestring
mark_safe,
) )
fully_decorated = full_decorator(fully_decorated) fully_decorated = full_decorator(fully_decorated)

View File

@ -109,3 +109,33 @@ class SafeStringTest(SimpleTestCase):
s = text.slugify(lazystr('a')) s = text.slugify(lazystr('a'))
s += mark_safe('&b') s += mark_safe('&b')
self.assertRenderEqual('{{ s }}', 'a&b', s=s) self.assertRenderEqual('{{ s }}', 'a&b', s=s)
def test_mark_safe_as_decorator(self):
"""
mark_safe used as a decorator leaves the result of a function
unchanged.
"""
def clean_string_provider():
return '<html><body>dummy</body></html>'
self.assertEqual(mark_safe(clean_string_provider)(), clean_string_provider())
def test_mark_safe_decorator_does_not_affect_dunder_html(self):
"""
mark_safe doesn't affect a callable that has an __html__() method.
"""
class SafeStringContainer:
def __html__(self):
return '<html></html>'
self.assertIs(mark_safe(SafeStringContainer), SafeStringContainer)
def test_mark_safe_decorator_does_not_affect_promises(self):
"""
mark_safe doesn't affect lazy strings (Promise objects).
"""
def html_str():
return '<html></html>'
lazy_str = lazy(html_str, str)()
self.assertEqual(mark_safe(lazy_str), html_str())