Commit be729b61 authored by Scott Vitale's avatar Scott Vitale Committed by Tim Graham
Browse files

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

Thanks ArcTanSusan for the initial patch.
parent 5e3f4c2e
Loading
Loading
Loading
Loading
+12 −1
Original line number Diff line number Diff line
@@ -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))


+6 −0
Original line number Diff line number Diff line
@@ -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
+1 −1
Original line number Diff line number Diff line
@@ -202,7 +202,7 @@ Signals
Templates
~~~~~~~~~

* ...
* :meth:`~django.utils.safestring.mark_safe` can now be used as a decorator.

Tests
~~~~~
+4 −0
Original line number Diff line number Diff line
@@ -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)
+30 −0
Original line number Diff line number Diff line
@@ -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())