ソースを参照

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

Thanks ArcTanSusan for the initial patch.
Scott Vitale 8 年 前
コミット
be729b6120

+ 12 - 1
django/utils/safestring.py

@@ -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
docs/ref/utils.txt

@@ -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
docs/releases/1.11.txt

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

+ 4 - 0
tests/decorators/tests.py

@@ -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
tests/utils_tests/test_safestring.py

@@ -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())