Fixed #7261 -- support for __html__ for library interoperability

The idea is that if an object implements __html__ which returns a string this is
used as HTML representation (eg: on escaping). If the object is a str or unicode
subclass and returns itself the object is a safe string type.

This is an updated patch based on jbalogh and ivank patches.
This commit is contained in:
Unai Zalakain 2013-10-15 00:40:52 +02:00
parent ef22d512b5
commit af64429b99
4 changed files with 21 additions and 5 deletions

View File

@ -65,8 +65,8 @@ def conditional_escape(text):
""" """
Similar to escape(), except that it doesn't operate on pre-escaped strings. Similar to escape(), except that it doesn't operate on pre-escaped strings.
""" """
if isinstance(text, SafeData): if hasattr(text, '__html__'):
return text return text.__html__()
else: else:
return escape(text) return escape(text)

View File

@ -30,7 +30,13 @@ else:
EscapeUnicode = EscapeText EscapeUnicode = EscapeText
class SafeData(object): class SafeData(object):
pass def __html__(self):
"""
Returns the html representation of a string.
Allows interoperability with other template engines.
"""
return self
class SafeBytes(bytes, SafeData): class SafeBytes(bytes, SafeData):
""" """

View File

@ -5,7 +5,7 @@ from datetime import datetime
import os import os
from unittest import TestCase from unittest import TestCase
from django.utils import html from django.utils import html, safestring
from django.utils._os import upath from django.utils._os import upath
from django.utils.encoding import force_text from django.utils.encoding import force_text
@ -192,3 +192,9 @@ class TestUtilsHtml(TestCase):
self.assertEqual(quote('http://example.com/path/öäü/'), 'http://example.com/path/%C3%B6%C3%A4%C3%BC/') self.assertEqual(quote('http://example.com/path/öäü/'), 'http://example.com/path/%C3%B6%C3%A4%C3%BC/')
self.assertEqual(quote('http://example.com/%C3%B6/ä/'), 'http://example.com/%C3%B6/%C3%A4/') self.assertEqual(quote('http://example.com/%C3%B6/ä/'), 'http://example.com/%C3%B6/%C3%A4/')
self.assertEqual(quote('http://example.com/?x=1&y=2'), 'http://example.com/?x=1&y=2') self.assertEqual(quote('http://example.com/?x=1&y=2'), 'http://example.com/?x=1&y=2')
def test_conditional_escape(self):
s = '<h1>interop</h1>'
self.assertEqual(html.conditional_escape(s),
'&lt;h1&gt;interop&lt;/h1&gt;')
self.assertEqual(html.conditional_escape(safestring.mark_safe(s)), s)

View File

@ -4,7 +4,7 @@ from django.template import Template, Context
from django.test import TestCase from django.test import TestCase
from django.utils.encoding import force_text, force_bytes from django.utils.encoding import force_text, force_bytes
from django.utils.functional import lazy, Promise from django.utils.functional import lazy, Promise
from django.utils.html import escape, conditional_escape from django.utils.html import escape
from django.utils.safestring import mark_safe, mark_for_escaping from django.utils.safestring import mark_safe, mark_for_escaping
from django.utils import six from django.utils import six
from django.utils import translation from django.utils import translation
@ -50,3 +50,7 @@ class SafeStringTest(TestCase):
s = mark_safe(translation.ugettext_lazy("username")) s = mark_safe(translation.ugettext_lazy("username"))
with translation.override('fr'): with translation.override('fr'):
self.assertRenderEqual('{{ s }}', "nom d'utilisateur", s=s) self.assertRenderEqual('{{ s }}', "nom d'utilisateur", s=s)
def test_html(self):
s = '<h1>interop</h1>'
self.assertEqual(s, mark_safe(s).__html__())