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.deprecation import RemovedInDjango20Warning
from django.utils.functional import Promise, curry
from django.utils.functional import Promise, curry, wraps
class EscapeData(object):
@ -117,11 +117,20 @@ else:
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):
"""
Explicitly mark a string as safe for (HTML) output purposes. The returned
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.
"""
if hasattr(s, '__html__'):
@ -130,6 +139,8 @@ def mark_safe(s):
return SafeBytes(s)
if isinstance(s, (six.text_type, Promise)):
return SafeText(s)
if callable(s):
return _safety_decorator(mark_safe, s)
return SafeString(str(s))

View File

@ -832,6 +832,8 @@ appropriate entities.
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
:func:`django.utils.html.format_html` instead.
@ -846,6 +848,10 @@ appropriate entities.
>>> type(mystr)
<type 'str'>
.. versionchanged:: 1.11
Added support for decorator usage.
.. function:: mark_for_escaping(s)
.. deprecated:: 1.10

View File

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

View File

@ -13,6 +13,7 @@ from django.utils.decorators import method_decorator
from django.utils.deprecation import RemovedInDjango20Warning
from django.utils.encoding import force_text
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.views.decorators.cache import (
cache_control, cache_page, never_cache,
@ -74,6 +75,9 @@ full_decorator = compose(
keep_lazy(HttpResponse),
keep_lazy_text,
lazy,
# django.utils.safestring
mark_safe,
)
fully_decorated = full_decorator(fully_decorated)

View File

@ -109,3 +109,33 @@ class SafeStringTest(SimpleTestCase):
s = text.slugify(lazystr('a'))
s += mark_safe('&b')
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())