120 lines
3.7 KiB
Python
120 lines
3.7 KiB
Python
|
"""
|
||
|
Functions for working with "safe strings": strings that can be displayed safely
|
||
|
without further escaping in HTML. Marking something as a "safe string" means
|
||
|
that the producer of the string has already turned characters that should not
|
||
|
be interpreted by the HTML engine (e.g. '<') into the appropriate entities.
|
||
|
"""
|
||
|
from django.utils.functional import curry, Promise
|
||
|
|
||
|
class EscapeData(object):
|
||
|
pass
|
||
|
|
||
|
class EscapeString(str, EscapeData):
|
||
|
"""
|
||
|
A string that should be HTML-escaped when output.
|
||
|
"""
|
||
|
pass
|
||
|
|
||
|
class EscapeUnicode(unicode, EscapeData):
|
||
|
"""
|
||
|
A unicode object that should be HTML-escaped when output.
|
||
|
"""
|
||
|
pass
|
||
|
|
||
|
class SafeData(object):
|
||
|
pass
|
||
|
|
||
|
class SafeString(str, SafeData):
|
||
|
"""
|
||
|
A string subclass that has been specifically marked as "safe" (requires no
|
||
|
further escaping) for HTML output purposes.
|
||
|
"""
|
||
|
def __add__(self, rhs):
|
||
|
"""
|
||
|
Concatenating a safe string with another safe string or safe unicode
|
||
|
object is safe. Otherwise, the result is no longer safe.
|
||
|
"""
|
||
|
if isinstance(rhs, SafeUnicode):
|
||
|
return SafeUnicode(self + rhs)
|
||
|
elif isinstance(rhs, SafeString):
|
||
|
return SafeString(self + rhs)
|
||
|
else:
|
||
|
return super(SafeString, self).__add__(rhs)
|
||
|
|
||
|
def _proxy_method(self, *args, **kwargs):
|
||
|
"""
|
||
|
Wrap a call to a normal unicode method up so that we return safe
|
||
|
results. The method that is being wrapped is passed in the 'method'
|
||
|
argument.
|
||
|
"""
|
||
|
method = kwargs.pop('method')
|
||
|
data = method(self, *args, **kwargs)
|
||
|
if isinstance(data, str):
|
||
|
return SafeString(data)
|
||
|
else:
|
||
|
return SafeUnicode(data)
|
||
|
|
||
|
decode = curry(_proxy_method, method = str.decode)
|
||
|
|
||
|
class SafeUnicode(unicode, SafeData):
|
||
|
"""
|
||
|
A unicode subclass that has been specifically marked as "safe" for HTML
|
||
|
output purposes.
|
||
|
"""
|
||
|
def __add__(self, rhs):
|
||
|
"""
|
||
|
Concatenating a safe unicode object with another safe string or safe
|
||
|
unicode object is safe. Otherwise, the result is no longer safe.
|
||
|
"""
|
||
|
if isinstance(rhs, SafeData):
|
||
|
return SafeUnicode(self + rhs)
|
||
|
else:
|
||
|
return super(SafeUnicode, self).__add__(rhs)
|
||
|
|
||
|
def _proxy_method(self, *args, **kwargs):
|
||
|
"""
|
||
|
Wrap a call to a normal unicode method up so that we return safe
|
||
|
results. The method that is being wrapped is passed in the 'method'
|
||
|
argument.
|
||
|
"""
|
||
|
method = kwargs.pop('method')
|
||
|
data = method(self, *args, **kwargs)
|
||
|
if isinstance(data, str):
|
||
|
return SafeString(data)
|
||
|
else:
|
||
|
return SafeUnicode(data)
|
||
|
|
||
|
encode = curry(_proxy_method, method = unicode.encode)
|
||
|
|
||
|
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.
|
||
|
|
||
|
Can be called multiple times on a single string.
|
||
|
"""
|
||
|
if isinstance(s, SafeData):
|
||
|
return s
|
||
|
if isinstance(s, str) or (isinstance(s, Promise) and s._delegate_str):
|
||
|
return SafeString(s)
|
||
|
if isinstance(s, (unicode, Promise)):
|
||
|
return SafeUnicode(s)
|
||
|
return SafeString(str(s))
|
||
|
|
||
|
def mark_for_escaping(s):
|
||
|
"""
|
||
|
Explicitly mark a string as requiring HTML escaping upon output. Has no
|
||
|
effect on SafeData subclasses.
|
||
|
|
||
|
Can be called multiple times on a single string (the resulting escaping is
|
||
|
only applied once).
|
||
|
"""
|
||
|
if isinstance(s, (SafeData, EscapeData)):
|
||
|
return s
|
||
|
if isinstance(s, str) or (isinstance(s, Promise) and s._delegate_str):
|
||
|
return EscapeString(s)
|
||
|
if isinstance(s, (unicode, Promise)):
|
||
|
return EscapeUnicode(s)
|
||
|
return EscapeString(str(s))
|
||
|
|