Fixed #20223 -- Added keep_lazy() as a replacement for allow_lazy().
Thanks to bmispelon and uruz for the initial patch.
This commit is contained in:
parent
93fc23b2d5
commit
d693074d43
2
AUTHORS
2
AUTHORS
|
@ -32,6 +32,7 @@ answer newbie questions, and generally made Django that much better:
|
||||||
Alex Hill <alex@hill.net.au>
|
Alex Hill <alex@hill.net.au>
|
||||||
Alex Ogier <alex.ogier@gmail.com>
|
Alex Ogier <alex.ogier@gmail.com>
|
||||||
Alex Robbins <alexander.j.robbins@gmail.com>
|
Alex Robbins <alexander.j.robbins@gmail.com>
|
||||||
|
Alexey Boriskin <alex@boriskin.me>
|
||||||
Aljosa Mohorovic <aljosa.mohorovic@gmail.com>
|
Aljosa Mohorovic <aljosa.mohorovic@gmail.com>
|
||||||
Amit Chakradeo <http://amit.chakradeo.net/>
|
Amit Chakradeo <http://amit.chakradeo.net/>
|
||||||
Amit Ramon <amit.ramon@gmail.com>
|
Amit Ramon <amit.ramon@gmail.com>
|
||||||
|
@ -287,6 +288,7 @@ answer newbie questions, and generally made Django that much better:
|
||||||
Honza Král <honza.kral@gmail.com>
|
Honza Král <honza.kral@gmail.com>
|
||||||
Horst Gutmann <zerok@zerokspot.com>
|
Horst Gutmann <zerok@zerokspot.com>
|
||||||
Hyun Mi Ae
|
Hyun Mi Ae
|
||||||
|
Iacopo Spalletti <i.spalletti@nephila.it>
|
||||||
Ian A Wilson <http://ianawilson.com>
|
Ian A Wilson <http://ianawilson.com>
|
||||||
Ian Clelland <clelland@gmail.com>
|
Ian Clelland <clelland@gmail.com>
|
||||||
Ian G. Kelly <ian.g.kelly@gmail.com>
|
Ian G. Kelly <ian.g.kelly@gmail.com>
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import copy
|
import copy
|
||||||
import operator
|
import operator
|
||||||
|
import warnings
|
||||||
from functools import total_ordering, wraps
|
from functools import total_ordering, wraps
|
||||||
|
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
from django.utils.deprecation import RemovedInDjango20Warning
|
||||||
|
|
||||||
|
|
||||||
# You can't trivially replace this with `functools.partial` because this binds
|
# You can't trivially replace this with `functools.partial` because this binds
|
||||||
|
@ -176,24 +178,52 @@ def _lazy_proxy_unpickle(func, args, kwargs, *resultclasses):
|
||||||
return lazy(func, *resultclasses)(*args, **kwargs)
|
return lazy(func, *resultclasses)(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def lazystr(text):
|
||||||
|
"""
|
||||||
|
Shortcut for the common case of a lazy callable that returns str.
|
||||||
|
"""
|
||||||
|
from django.utils.encoding import force_text # Avoid circular import
|
||||||
|
return lazy(force_text, six.text_type)(text)
|
||||||
|
|
||||||
|
|
||||||
def allow_lazy(func, *resultclasses):
|
def allow_lazy(func, *resultclasses):
|
||||||
|
warnings.warn(
|
||||||
|
"django.utils.functional.allow_lazy() is deprecated in favor of "
|
||||||
|
"django.utils.functional.keep_lazy()",
|
||||||
|
RemovedInDjango20Warning, 2)
|
||||||
|
return keep_lazy(*resultclasses)(func)
|
||||||
|
|
||||||
|
|
||||||
|
def keep_lazy(*resultclasses):
|
||||||
"""
|
"""
|
||||||
A decorator that allows a function to be called with one or more lazy
|
A decorator that allows a function to be called with one or more lazy
|
||||||
arguments. If none of the args are lazy, the function is evaluated
|
arguments. If none of the args are lazy, the function is evaluated
|
||||||
immediately, otherwise a __proxy__ is returned that will evaluate the
|
immediately, otherwise a __proxy__ is returned that will evaluate the
|
||||||
function when needed.
|
function when needed.
|
||||||
"""
|
"""
|
||||||
lazy_func = lazy(func, *resultclasses)
|
if not resultclasses:
|
||||||
|
raise TypeError("You must pass at least one argument to keep_lazy().")
|
||||||
|
|
||||||
@wraps(func)
|
def decorator(func):
|
||||||
def wrapper(*args, **kwargs):
|
lazy_func = lazy(func, *resultclasses)
|
||||||
for arg in list(args) + list(kwargs.values()):
|
|
||||||
if isinstance(arg, Promise):
|
@wraps(func)
|
||||||
break
|
def wrapper(*args, **kwargs):
|
||||||
else:
|
for arg in list(args) + list(six.itervalues(kwargs)):
|
||||||
return func(*args, **kwargs)
|
if isinstance(arg, Promise):
|
||||||
return lazy_func(*args, **kwargs)
|
break
|
||||||
return wrapper
|
else:
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
return lazy_func(*args, **kwargs)
|
||||||
|
return wrapper
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
def keep_lazy_text(func):
|
||||||
|
"""
|
||||||
|
A decorator for functions that accept lazy arguments and return text.
|
||||||
|
"""
|
||||||
|
return keep_lazy(six.text_type)(func)
|
||||||
|
|
||||||
empty = object()
|
empty = object()
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import re
|
||||||
|
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
from django.utils.encoding import force_str, force_text
|
from django.utils.encoding import force_str, force_text
|
||||||
from django.utils.functional import allow_lazy
|
from django.utils.functional import keep_lazy, keep_lazy_text
|
||||||
from django.utils.http import RFC3986_GENDELIMS, RFC3986_SUBDELIMS
|
from django.utils.http import RFC3986_GENDELIMS, RFC3986_SUBDELIMS
|
||||||
from django.utils.safestring import SafeData, SafeText, mark_safe
|
from django.utils.safestring import SafeData, SafeText, mark_safe
|
||||||
from django.utils.six.moves.urllib.parse import (
|
from django.utils.six.moves.urllib.parse import (
|
||||||
|
@ -38,6 +38,7 @@ hard_coded_bullets_re = re.compile(
|
||||||
trailing_empty_content_re = re.compile(r'(?:<p>(?: |\s|<br \/>)*?</p>\s*)+\Z')
|
trailing_empty_content_re = re.compile(r'(?:<p>(?: |\s|<br \/>)*?</p>\s*)+\Z')
|
||||||
|
|
||||||
|
|
||||||
|
@keep_lazy(six.text_type, SafeText)
|
||||||
def escape(text):
|
def escape(text):
|
||||||
"""
|
"""
|
||||||
Returns the given text with ampersands, quotes and angle brackets encoded
|
Returns the given text with ampersands, quotes and angle brackets encoded
|
||||||
|
@ -49,7 +50,6 @@ def escape(text):
|
||||||
"""
|
"""
|
||||||
return mark_safe(force_text(text).replace('&', '&').replace('<', '<')
|
return mark_safe(force_text(text).replace('&', '&').replace('<', '<')
|
||||||
.replace('>', '>').replace('"', '"').replace("'", '''))
|
.replace('>', '>').replace('"', '"').replace("'", '''))
|
||||||
escape = allow_lazy(escape, six.text_type, SafeText)
|
|
||||||
|
|
||||||
_js_escapes = {
|
_js_escapes = {
|
||||||
ord('\\'): '\\u005C',
|
ord('\\'): '\\u005C',
|
||||||
|
@ -69,10 +69,10 @@ _js_escapes = {
|
||||||
_js_escapes.update((ord('%c' % z), '\\u%04X' % z) for z in range(32))
|
_js_escapes.update((ord('%c' % z), '\\u%04X' % z) for z in range(32))
|
||||||
|
|
||||||
|
|
||||||
|
@keep_lazy(six.text_type, SafeText)
|
||||||
def escapejs(value):
|
def escapejs(value):
|
||||||
"""Hex encodes characters for use in JavaScript strings."""
|
"""Hex encodes characters for use in JavaScript strings."""
|
||||||
return mark_safe(force_text(value).translate(_js_escapes))
|
return mark_safe(force_text(value).translate(_js_escapes))
|
||||||
escapejs = allow_lazy(escapejs, six.text_type, SafeText)
|
|
||||||
|
|
||||||
|
|
||||||
def conditional_escape(text):
|
def conditional_escape(text):
|
||||||
|
@ -118,16 +118,16 @@ def format_html_join(sep, format_string, args_generator):
|
||||||
for args in args_generator))
|
for args in args_generator))
|
||||||
|
|
||||||
|
|
||||||
|
@keep_lazy_text
|
||||||
def linebreaks(value, autoescape=False):
|
def linebreaks(value, autoescape=False):
|
||||||
"""Converts newlines into <p> and <br />s."""
|
"""Converts newlines into <p> and <br />s."""
|
||||||
value = normalize_newlines(value)
|
value = normalize_newlines(force_text(value))
|
||||||
paras = re.split('\n{2,}', value)
|
paras = re.split('\n{2,}', value)
|
||||||
if autoescape:
|
if autoescape:
|
||||||
paras = ['<p>%s</p>' % escape(p).replace('\n', '<br />') for p in paras]
|
paras = ['<p>%s</p>' % escape(p).replace('\n', '<br />') for p in paras]
|
||||||
else:
|
else:
|
||||||
paras = ['<p>%s</p>' % p.replace('\n', '<br />') for p in paras]
|
paras = ['<p>%s</p>' % p.replace('\n', '<br />') for p in paras]
|
||||||
return '\n\n'.join(paras)
|
return '\n\n'.join(paras)
|
||||||
linebreaks = allow_lazy(linebreaks, six.text_type)
|
|
||||||
|
|
||||||
|
|
||||||
class MLStripper(HTMLParser):
|
class MLStripper(HTMLParser):
|
||||||
|
@ -166,10 +166,12 @@ def _strip_once(value):
|
||||||
return s.get_data()
|
return s.get_data()
|
||||||
|
|
||||||
|
|
||||||
|
@keep_lazy_text
|
||||||
def strip_tags(value):
|
def strip_tags(value):
|
||||||
"""Returns the given HTML with all tags stripped."""
|
"""Returns the given HTML with all tags stripped."""
|
||||||
# Note: in typical case this loop executes _strip_once once. Loop condition
|
# Note: in typical case this loop executes _strip_once once. Loop condition
|
||||||
# is redundant, but helps to reduce number of executions of _strip_once.
|
# is redundant, but helps to reduce number of executions of _strip_once.
|
||||||
|
value = force_text(value)
|
||||||
while '<' in value and '>' in value:
|
while '<' in value and '>' in value:
|
||||||
new_value = _strip_once(value)
|
new_value = _strip_once(value)
|
||||||
if len(new_value) >= len(value):
|
if len(new_value) >= len(value):
|
||||||
|
@ -179,13 +181,12 @@ def strip_tags(value):
|
||||||
break
|
break
|
||||||
value = new_value
|
value = new_value
|
||||||
return value
|
return value
|
||||||
strip_tags = allow_lazy(strip_tags)
|
|
||||||
|
|
||||||
|
|
||||||
|
@keep_lazy_text
|
||||||
def strip_spaces_between_tags(value):
|
def strip_spaces_between_tags(value):
|
||||||
"""Returns the given HTML with spaces between tags removed."""
|
"""Returns the given HTML with spaces between tags removed."""
|
||||||
return re.sub(r'>\s+<', '><', force_text(value))
|
return re.sub(r'>\s+<', '><', force_text(value))
|
||||||
strip_spaces_between_tags = allow_lazy(strip_spaces_between_tags, six.text_type)
|
|
||||||
|
|
||||||
|
|
||||||
def smart_urlquote(url):
|
def smart_urlquote(url):
|
||||||
|
@ -224,6 +225,7 @@ def smart_urlquote(url):
|
||||||
return urlunsplit((scheme, netloc, path, query, fragment))
|
return urlunsplit((scheme, netloc, path, query, fragment))
|
||||||
|
|
||||||
|
|
||||||
|
@keep_lazy_text
|
||||||
def urlize(text, trim_url_limit=None, nofollow=False, autoescape=False):
|
def urlize(text, trim_url_limit=None, nofollow=False, autoescape=False):
|
||||||
"""
|
"""
|
||||||
Converts any URLs in text into clickable links.
|
Converts any URLs in text into clickable links.
|
||||||
|
@ -321,7 +323,6 @@ def urlize(text, trim_url_limit=None, nofollow=False, autoescape=False):
|
||||||
elif autoescape:
|
elif autoescape:
|
||||||
words[i] = escape(word)
|
words[i] = escape(word)
|
||||||
return ''.join(words)
|
return ''.join(words)
|
||||||
urlize = allow_lazy(urlize, six.text_type)
|
|
||||||
|
|
||||||
|
|
||||||
def avoid_wrapping(value):
|
def avoid_wrapping(value):
|
||||||
|
|
|
@ -12,7 +12,7 @@ from email.utils import formatdate
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
from django.utils.datastructures import MultiValueDict
|
from django.utils.datastructures import MultiValueDict
|
||||||
from django.utils.encoding import force_bytes, force_str, force_text
|
from django.utils.encoding import force_bytes, force_str, force_text
|
||||||
from django.utils.functional import allow_lazy
|
from django.utils.functional import keep_lazy_text
|
||||||
from django.utils.six.moves.urllib.parse import (
|
from django.utils.six.moves.urllib.parse import (
|
||||||
quote, quote_plus, unquote, unquote_plus, urlencode as original_urlencode,
|
quote, quote_plus, unquote, unquote_plus, urlencode as original_urlencode,
|
||||||
urlparse,
|
urlparse,
|
||||||
|
@ -40,6 +40,7 @@ PROTOCOL_TO_PORT = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@keep_lazy_text
|
||||||
def urlquote(url, safe='/'):
|
def urlquote(url, safe='/'):
|
||||||
"""
|
"""
|
||||||
A version of Python's urllib.quote() function that can operate on unicode
|
A version of Python's urllib.quote() function that can operate on unicode
|
||||||
|
@ -48,9 +49,9 @@ def urlquote(url, safe='/'):
|
||||||
without double-quoting occurring.
|
without double-quoting occurring.
|
||||||
"""
|
"""
|
||||||
return force_text(quote(force_str(url), force_str(safe)))
|
return force_text(quote(force_str(url), force_str(safe)))
|
||||||
urlquote = allow_lazy(urlquote, six.text_type)
|
|
||||||
|
|
||||||
|
|
||||||
|
@keep_lazy_text
|
||||||
def urlquote_plus(url, safe=''):
|
def urlquote_plus(url, safe=''):
|
||||||
"""
|
"""
|
||||||
A version of Python's urllib.quote_plus() function that can operate on
|
A version of Python's urllib.quote_plus() function that can operate on
|
||||||
|
@ -59,25 +60,24 @@ def urlquote_plus(url, safe=''):
|
||||||
iri_to_uri() call without double-quoting occurring.
|
iri_to_uri() call without double-quoting occurring.
|
||||||
"""
|
"""
|
||||||
return force_text(quote_plus(force_str(url), force_str(safe)))
|
return force_text(quote_plus(force_str(url), force_str(safe)))
|
||||||
urlquote_plus = allow_lazy(urlquote_plus, six.text_type)
|
|
||||||
|
|
||||||
|
|
||||||
|
@keep_lazy_text
|
||||||
def urlunquote(quoted_url):
|
def urlunquote(quoted_url):
|
||||||
"""
|
"""
|
||||||
A wrapper for Python's urllib.unquote() function that can operate on
|
A wrapper for Python's urllib.unquote() function that can operate on
|
||||||
the result of django.utils.http.urlquote().
|
the result of django.utils.http.urlquote().
|
||||||
"""
|
"""
|
||||||
return force_text(unquote(force_str(quoted_url)))
|
return force_text(unquote(force_str(quoted_url)))
|
||||||
urlunquote = allow_lazy(urlunquote, six.text_type)
|
|
||||||
|
|
||||||
|
|
||||||
|
@keep_lazy_text
|
||||||
def urlunquote_plus(quoted_url):
|
def urlunquote_plus(quoted_url):
|
||||||
"""
|
"""
|
||||||
A wrapper for Python's urllib.unquote_plus() function that can operate on
|
A wrapper for Python's urllib.unquote_plus() function that can operate on
|
||||||
the result of django.utils.http.urlquote_plus().
|
the result of django.utils.http.urlquote_plus().
|
||||||
"""
|
"""
|
||||||
return force_text(unquote_plus(force_str(quoted_url)))
|
return force_text(unquote_plus(force_str(quoted_url)))
|
||||||
urlunquote_plus = allow_lazy(urlunquote_plus, six.text_type)
|
|
||||||
|
|
||||||
|
|
||||||
def urlencode(query, doseq=0):
|
def urlencode(query, doseq=0):
|
||||||
|
|
|
@ -7,7 +7,7 @@ from io import BytesIO
|
||||||
|
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils.functional import SimpleLazyObject, allow_lazy
|
from django.utils.functional import SimpleLazyObject, keep_lazy, keep_lazy_text
|
||||||
from django.utils.safestring import SafeText, mark_safe
|
from django.utils.safestring import SafeText, mark_safe
|
||||||
from django.utils.six.moves import html_entities
|
from django.utils.six.moves import html_entities
|
||||||
from django.utils.translation import pgettext, ugettext as _, ugettext_lazy
|
from django.utils.translation import pgettext, ugettext as _, ugettext_lazy
|
||||||
|
@ -20,7 +20,7 @@ if six.PY2:
|
||||||
|
|
||||||
# Capitalizes the first letter of a string.
|
# Capitalizes the first letter of a string.
|
||||||
capfirst = lambda x: x and force_text(x)[0].upper() + force_text(x)[1:]
|
capfirst = lambda x: x and force_text(x)[0].upper() + force_text(x)[1:]
|
||||||
capfirst = allow_lazy(capfirst, six.text_type)
|
capfirst = keep_lazy_text(capfirst)
|
||||||
|
|
||||||
# Set up regular expressions
|
# Set up regular expressions
|
||||||
re_words = re.compile(r'<.*?>|((?:\w[-\w]*|&.*?;)+)', re.U | re.S)
|
re_words = re.compile(r'<.*?>|((?:\w[-\w]*|&.*?;)+)', re.U | re.S)
|
||||||
|
@ -30,6 +30,7 @@ re_newlines = re.compile(r'\r\n|\r') # Used in normalize_newlines
|
||||||
re_camel_case = re.compile(r'(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))')
|
re_camel_case = re.compile(r'(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))')
|
||||||
|
|
||||||
|
|
||||||
|
@keep_lazy_text
|
||||||
def wrap(text, width):
|
def wrap(text, width):
|
||||||
"""
|
"""
|
||||||
A word-wrap function that preserves existing line breaks. Expects that
|
A word-wrap function that preserves existing line breaks. Expects that
|
||||||
|
@ -60,7 +61,6 @@ def wrap(text, width):
|
||||||
if line:
|
if line:
|
||||||
yield line
|
yield line
|
||||||
return ''.join(_generator())
|
return ''.join(_generator())
|
||||||
wrap = allow_lazy(wrap, six.text_type)
|
|
||||||
|
|
||||||
|
|
||||||
class Truncator(SimpleLazyObject):
|
class Truncator(SimpleLazyObject):
|
||||||
|
@ -95,6 +95,7 @@ class Truncator(SimpleLazyObject):
|
||||||
string has been truncated, defaulting to a translatable string of an
|
string has been truncated, defaulting to a translatable string of an
|
||||||
ellipsis (...).
|
ellipsis (...).
|
||||||
"""
|
"""
|
||||||
|
self._setup()
|
||||||
length = int(num)
|
length = int(num)
|
||||||
text = unicodedata.normalize('NFC', self._wrapped)
|
text = unicodedata.normalize('NFC', self._wrapped)
|
||||||
|
|
||||||
|
@ -108,7 +109,6 @@ class Truncator(SimpleLazyObject):
|
||||||
if html:
|
if html:
|
||||||
return self._truncate_html(length, truncate, text, truncate_len, False)
|
return self._truncate_html(length, truncate, text, truncate_len, False)
|
||||||
return self._text_chars(length, truncate, text, truncate_len)
|
return self._text_chars(length, truncate, text, truncate_len)
|
||||||
chars = allow_lazy(chars)
|
|
||||||
|
|
||||||
def _text_chars(self, length, truncate, text, truncate_len):
|
def _text_chars(self, length, truncate, text, truncate_len):
|
||||||
"""
|
"""
|
||||||
|
@ -138,11 +138,11 @@ class Truncator(SimpleLazyObject):
|
||||||
argument of what should be used to notify that the string has been
|
argument of what should be used to notify that the string has been
|
||||||
truncated, defaulting to ellipsis (...).
|
truncated, defaulting to ellipsis (...).
|
||||||
"""
|
"""
|
||||||
|
self._setup()
|
||||||
length = int(num)
|
length = int(num)
|
||||||
if html:
|
if html:
|
||||||
return self._truncate_html(length, truncate, self._wrapped, length, True)
|
return self._truncate_html(length, truncate, self._wrapped, length, True)
|
||||||
return self._text_words(length, truncate)
|
return self._text_words(length, truncate)
|
||||||
words = allow_lazy(words)
|
|
||||||
|
|
||||||
def _text_words(self, length, truncate):
|
def _text_words(self, length, truncate):
|
||||||
"""
|
"""
|
||||||
|
@ -229,6 +229,7 @@ class Truncator(SimpleLazyObject):
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
@keep_lazy_text
|
||||||
def get_valid_filename(s):
|
def get_valid_filename(s):
|
||||||
"""
|
"""
|
||||||
Returns the given string converted to a string that can be used for a clean
|
Returns the given string converted to a string that can be used for a clean
|
||||||
|
@ -240,9 +241,9 @@ def get_valid_filename(s):
|
||||||
"""
|
"""
|
||||||
s = force_text(s).strip().replace(' ', '_')
|
s = force_text(s).strip().replace(' ', '_')
|
||||||
return re.sub(r'(?u)[^-\w.]', '', s)
|
return re.sub(r'(?u)[^-\w.]', '', s)
|
||||||
get_valid_filename = allow_lazy(get_valid_filename, six.text_type)
|
|
||||||
|
|
||||||
|
|
||||||
|
@keep_lazy_text
|
||||||
def get_text_list(list_, last_word=ugettext_lazy('or')):
|
def get_text_list(list_, last_word=ugettext_lazy('or')):
|
||||||
"""
|
"""
|
||||||
>>> get_text_list(['a', 'b', 'c', 'd'])
|
>>> get_text_list(['a', 'b', 'c', 'd'])
|
||||||
|
@ -264,16 +265,16 @@ def get_text_list(list_, last_word=ugettext_lazy('or')):
|
||||||
# Translators: This string is used as a separator between list elements
|
# Translators: This string is used as a separator between list elements
|
||||||
_(', ').join(force_text(i) for i in list_[:-1]),
|
_(', ').join(force_text(i) for i in list_[:-1]),
|
||||||
force_text(last_word), force_text(list_[-1]))
|
force_text(last_word), force_text(list_[-1]))
|
||||||
get_text_list = allow_lazy(get_text_list, six.text_type)
|
|
||||||
|
|
||||||
|
|
||||||
|
@keep_lazy_text
|
||||||
def normalize_newlines(text):
|
def normalize_newlines(text):
|
||||||
"""Normalizes CRLF and CR newlines to just LF."""
|
"""Normalizes CRLF and CR newlines to just LF."""
|
||||||
text = force_text(text)
|
text = force_text(text)
|
||||||
return re_newlines.sub('\n', text)
|
return re_newlines.sub('\n', text)
|
||||||
normalize_newlines = allow_lazy(normalize_newlines, six.text_type)
|
|
||||||
|
|
||||||
|
|
||||||
|
@keep_lazy_text
|
||||||
def phone2numeric(phone):
|
def phone2numeric(phone):
|
||||||
"""Converts a phone number with letters into its numeric equivalent."""
|
"""Converts a phone number with letters into its numeric equivalent."""
|
||||||
char2number = {'a': '2', 'b': '2', 'c': '2', 'd': '3', 'e': '3', 'f': '3',
|
char2number = {'a': '2', 'b': '2', 'c': '2', 'd': '3', 'e': '3', 'f': '3',
|
||||||
|
@ -281,7 +282,6 @@ def phone2numeric(phone):
|
||||||
'n': '6', 'o': '6', 'p': '7', 'q': '7', 'r': '7', 's': '7', 't': '8',
|
'n': '6', 'o': '6', 'p': '7', 'q': '7', 'r': '7', 's': '7', 't': '8',
|
||||||
'u': '8', 'v': '8', 'w': '9', 'x': '9', 'y': '9', 'z': '9'}
|
'u': '8', 'v': '8', 'w': '9', 'x': '9', 'y': '9', 'z': '9'}
|
||||||
return ''.join(char2number.get(c, c) for c in phone.lower())
|
return ''.join(char2number.get(c, c) for c in phone.lower())
|
||||||
phone2numeric = allow_lazy(phone2numeric)
|
|
||||||
|
|
||||||
|
|
||||||
# From http://www.xhaus.com/alan/python/httpcomp.html#gzip
|
# From http://www.xhaus.com/alan/python/httpcomp.html#gzip
|
||||||
|
@ -384,11 +384,12 @@ def _replace_entity(match):
|
||||||
_entity_re = re.compile(r"&(#?[xX]?(?:[0-9a-fA-F]+|\w{1,8}));")
|
_entity_re = re.compile(r"&(#?[xX]?(?:[0-9a-fA-F]+|\w{1,8}));")
|
||||||
|
|
||||||
|
|
||||||
|
@keep_lazy_text
|
||||||
def unescape_entities(text):
|
def unescape_entities(text):
|
||||||
return _entity_re.sub(_replace_entity, text)
|
return _entity_re.sub(_replace_entity, force_text(text))
|
||||||
unescape_entities = allow_lazy(unescape_entities, six.text_type)
|
|
||||||
|
|
||||||
|
|
||||||
|
@keep_lazy_text
|
||||||
def unescape_string_literal(s):
|
def unescape_string_literal(s):
|
||||||
r"""
|
r"""
|
||||||
Convert quoted string literals to unquoted strings with escaped quotes and
|
Convert quoted string literals to unquoted strings with escaped quotes and
|
||||||
|
@ -407,9 +408,9 @@ def unescape_string_literal(s):
|
||||||
raise ValueError("Not a string literal: %r" % s)
|
raise ValueError("Not a string literal: %r" % s)
|
||||||
quote = s[0]
|
quote = s[0]
|
||||||
return s[1:-1].replace(r'\%s' % quote, quote).replace(r'\\', '\\')
|
return s[1:-1].replace(r'\%s' % quote, quote).replace(r'\\', '\\')
|
||||||
unescape_string_literal = allow_lazy(unescape_string_literal)
|
|
||||||
|
|
||||||
|
|
||||||
|
@keep_lazy(six.text_type, SafeText)
|
||||||
def slugify(value, allow_unicode=False):
|
def slugify(value, allow_unicode=False):
|
||||||
"""
|
"""
|
||||||
Convert to ASCII if 'allow_unicode' is False. Convert spaces to hyphens.
|
Convert to ASCII if 'allow_unicode' is False. Convert spaces to hyphens.
|
||||||
|
@ -424,7 +425,6 @@ def slugify(value, allow_unicode=False):
|
||||||
value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore').decode('ascii')
|
value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore').decode('ascii')
|
||||||
value = re.sub('[^\w\s-]', '', value).strip().lower()
|
value = re.sub('[^\w\s-]', '', value).strip().lower()
|
||||||
return mark_safe(re.sub('[-\s]+', '-', value))
|
return mark_safe(re.sub('[-\s]+', '-', value))
|
||||||
slugify = allow_lazy(slugify, six.text_type, SafeText)
|
|
||||||
|
|
||||||
|
|
||||||
def camel_case_to_spaces(value):
|
def camel_case_to_spaces(value):
|
||||||
|
|
|
@ -124,6 +124,8 @@ details on these changes.
|
||||||
* The ``cascaded_union`` property of ``django.contrib.gis.geos.MultiPolygon``
|
* The ``cascaded_union`` property of ``django.contrib.gis.geos.MultiPolygon``
|
||||||
will be removed.
|
will be removed.
|
||||||
|
|
||||||
|
* ``django.utils.functional.allow_lazy()`` will be removed.
|
||||||
|
|
||||||
.. _deprecation-removed-in-1.10:
|
.. _deprecation-removed-in-1.10:
|
||||||
|
|
||||||
1.10
|
1.10
|
||||||
|
|
|
@ -522,6 +522,15 @@ Atom1Feed
|
||||||
|
|
||||||
.. function:: allow_lazy(func, *resultclasses)
|
.. function:: allow_lazy(func, *resultclasses)
|
||||||
|
|
||||||
|
.. deprecated:: 1.10
|
||||||
|
|
||||||
|
Works like :meth:`~django.utils.functional.keep_lazy` except that it can't
|
||||||
|
be used as a decorator.
|
||||||
|
|
||||||
|
.. function:: keep_lazy(func, *resultclasses)
|
||||||
|
|
||||||
|
.. versionadded:: 1.10
|
||||||
|
|
||||||
Django offers many utility functions (particularly in ``django.utils``)
|
Django offers many utility functions (particularly in ``django.utils``)
|
||||||
that take a string as their first argument and do something to that string.
|
that take a string as their first argument and do something to that string.
|
||||||
These functions are used by template filters as well as directly in other
|
These functions are used by template filters as well as directly in other
|
||||||
|
@ -533,31 +542,58 @@ Atom1Feed
|
||||||
because you might be using this function outside of a view (and hence the
|
because you might be using this function outside of a view (and hence the
|
||||||
current thread's locale setting will not be correct).
|
current thread's locale setting will not be correct).
|
||||||
|
|
||||||
For cases like this, use the ``django.utils.functional.allow_lazy()``
|
For cases like this, use the ``django.utils.functional.keep_lazy()``
|
||||||
decorator. It modifies the function so that *if* it's called with a lazy
|
decorator. It modifies the function so that *if* it's called with a lazy
|
||||||
translation as one of its arguments, the function evaluation is delayed
|
translation as one of its arguments, the function evaluation is delayed
|
||||||
until it needs to be converted to a string.
|
until it needs to be converted to a string.
|
||||||
|
|
||||||
For example::
|
For example::
|
||||||
|
|
||||||
from django.utils.functional import allow_lazy
|
from django.utils import six
|
||||||
|
from django.utils.functional import keep_lazy, keep_lazy_text
|
||||||
|
|
||||||
def fancy_utility_function(s, ...):
|
def fancy_utility_function(s, ...):
|
||||||
# Do some conversion on string 's'
|
# Do some conversion on string 's'
|
||||||
...
|
...
|
||||||
# Replace unicode by str on Python 3
|
fancy_utility_function = keep_lazy(six.text_type)(fancy_utility_function)
|
||||||
fancy_utility_function = allow_lazy(fancy_utility_function, unicode)
|
|
||||||
|
|
||||||
The ``allow_lazy()`` decorator takes, in addition to the function to
|
# Or more succinctly:
|
||||||
decorate, a number of extra arguments (``*args``) specifying the type(s)
|
@keep_lazy(six.text_type)
|
||||||
that the original function can return. Usually, it's enough to include
|
def fancy_utility_function(s, ...):
|
||||||
``unicode`` (or ``str`` on Python 3) here and ensure that your function
|
...
|
||||||
returns only Unicode strings.
|
|
||||||
|
The ``keep_lazy()`` decorator takes a number of extra arguments (``*args``)
|
||||||
|
specifying the type(s) that the original function can return. A common
|
||||||
|
use case is to have functions that return text. For these, you can just
|
||||||
|
pass the ``six.text_type`` type to ``keep_lazy`` (or even simpler, use the
|
||||||
|
:func:`keep_lazy_text` decorator described in the next section).
|
||||||
|
|
||||||
Using this decorator means you can write your function and assume that the
|
Using this decorator means you can write your function and assume that the
|
||||||
input is a proper string, then add support for lazy translation objects at
|
input is a proper string, then add support for lazy translation objects at
|
||||||
the end.
|
the end.
|
||||||
|
|
||||||
|
.. function:: keep_lazy_text(func)
|
||||||
|
|
||||||
|
.. versionadded:: 1.10
|
||||||
|
|
||||||
|
A shortcut for ``keep_lazy(six.text_type)(func)``.
|
||||||
|
|
||||||
|
If you have a function that returns text and you want to be able to take
|
||||||
|
lazy arguments while delaying their evaluation, simply use this decorator::
|
||||||
|
|
||||||
|
from django.utils import six
|
||||||
|
from django.utils.functional import keep_lazy, keep_lazy_text
|
||||||
|
|
||||||
|
# Our previous example was:
|
||||||
|
@keep_lazy(six.text_type)
|
||||||
|
def fancy_utility_function(s, ...):
|
||||||
|
...
|
||||||
|
|
||||||
|
# Which can be rewritten as:
|
||||||
|
@keep_lazy_text
|
||||||
|
def fancy_utility_function(s, ...):
|
||||||
|
...
|
||||||
|
|
||||||
``django.utils.html``
|
``django.utils.html``
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
|
|
|
@ -402,6 +402,10 @@ Miscellaneous
|
||||||
* The ``makemigrations --exit`` option is deprecated in favor of the
|
* The ``makemigrations --exit`` option is deprecated in favor of the
|
||||||
:djadminopt:`--check` option.
|
:djadminopt:`--check` option.
|
||||||
|
|
||||||
|
* ``django.utils.functional.allow_lazy()`` is deprecated in favor of the new
|
||||||
|
:func:`~django.utils.functional.keep_lazy` function which can be used with a
|
||||||
|
more natural decorator syntax.
|
||||||
|
|
||||||
.. _removed-features-1.10:
|
.. _removed-features-1.10:
|
||||||
|
|
||||||
Features removed in 1.10
|
Features removed in 1.10
|
||||||
|
|
|
@ -223,7 +223,7 @@ QuerySet <when-querysets-are-evaluated>`. Avoiding the premature evaluation of
|
||||||
a ``QuerySet`` can save making an expensive and unnecessary trip to the
|
a ``QuerySet`` can save making an expensive and unnecessary trip to the
|
||||||
database.
|
database.
|
||||||
|
|
||||||
Django also offers an :meth:`~django.utils.functional.allow_lazy` decorator.
|
Django also offers a :meth:`~django.utils.functional.keep_lazy` decorator.
|
||||||
This allows a function that has been called with a lazy argument to behave
|
This allows a function that has been called with a lazy argument to behave
|
||||||
lazily itself, only being evaluated when it needs to be. Thus the lazy argument
|
lazily itself, only being evaluated when it needs to be. Thus the lazy argument
|
||||||
- which could be an expensive one - will not be called upon for evaluation
|
- which could be an expensive one - will not be called upon for evaluation
|
||||||
|
|
|
@ -8,8 +8,12 @@ from django.contrib.auth.decorators import (
|
||||||
from django.http import HttpRequest, HttpResponse, HttpResponseNotAllowed
|
from django.http import HttpRequest, HttpResponse, HttpResponseNotAllowed
|
||||||
from django.middleware.clickjacking import XFrameOptionsMiddleware
|
from django.middleware.clickjacking import XFrameOptionsMiddleware
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase
|
||||||
|
from django.utils import six
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.utils.functional import allow_lazy, lazy
|
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.translation import ugettext_lazy
|
||||||
from django.views.decorators.cache import (
|
from django.views.decorators.cache import (
|
||||||
cache_control, cache_page, never_cache,
|
cache_control, cache_page, never_cache,
|
||||||
)
|
)
|
||||||
|
@ -67,7 +71,8 @@ full_decorator = compose(
|
||||||
staff_member_required,
|
staff_member_required,
|
||||||
|
|
||||||
# django.utils.functional
|
# django.utils.functional
|
||||||
allow_lazy,
|
keep_lazy(HttpResponse),
|
||||||
|
keep_lazy_text,
|
||||||
lazy,
|
lazy,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -149,6 +154,15 @@ class DecoratorsTest(TestCase):
|
||||||
request.method = 'DELETE'
|
request.method = 'DELETE'
|
||||||
self.assertIsInstance(my_safe_view(request), HttpResponseNotAllowed)
|
self.assertIsInstance(my_safe_view(request), HttpResponseNotAllowed)
|
||||||
|
|
||||||
|
def test_deprecated_allow_lazy(self):
|
||||||
|
with self.assertRaises(RemovedInDjango20Warning):
|
||||||
|
def noop_text(text):
|
||||||
|
return force_text(text)
|
||||||
|
noop_text = allow_lazy(noop_text, six.text_type)
|
||||||
|
rendered = noop_text(ugettext_lazy("I am a text"))
|
||||||
|
self.assertEqual(type(rendered), six.text_type)
|
||||||
|
self.assertEqual(rendered, "I am a text")
|
||||||
|
|
||||||
|
|
||||||
# For testing method_decorator, a decorator that assumes a single argument.
|
# For testing method_decorator, a decorator that assumes a single argument.
|
||||||
# We will get type arguments if there is a mismatch in the number of arguments.
|
# We will get type arguments if there is a mismatch in the number of arguments.
|
||||||
|
|
|
@ -21,10 +21,8 @@ from django.http import (
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
from django.utils._os import upath
|
from django.utils._os import upath
|
||||||
from django.utils.encoding import force_text, smart_str
|
from django.utils.encoding import smart_str
|
||||||
from django.utils.functional import lazy
|
from django.utils.functional import lazystr
|
||||||
|
|
||||||
lazystr = lazy(force_text, six.text_type)
|
|
||||||
|
|
||||||
|
|
||||||
class QueryDictTests(unittest.TestCase):
|
class QueryDictTests(unittest.TestCase):
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
from django.template.defaultfilters import escape
|
from django.template.defaultfilters import escape
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase
|
||||||
|
from django.utils import six
|
||||||
|
from django.utils.functional import Promise, lazy
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
from ..utils import setup
|
from ..utils import setup
|
||||||
|
@ -33,6 +35,12 @@ class EscapeTests(SimpleTestCase):
|
||||||
output = self.engine.render_to_string('escape04', {"a": "x&y"})
|
output = self.engine.render_to_string('escape04', {"a": "x&y"})
|
||||||
self.assertEqual(output, "x&y")
|
self.assertEqual(output, "x&y")
|
||||||
|
|
||||||
|
def test_escape_lazy_string(self):
|
||||||
|
add_html = lazy(lambda string: string + 'special characters > here', six.text_type)
|
||||||
|
escaped = escape(add_html('<some html & '))
|
||||||
|
self.assertIsInstance(escaped, Promise)
|
||||||
|
self.assertEqual(escaped, '<some html & special characters > here')
|
||||||
|
|
||||||
|
|
||||||
class FunctionTests(SimpleTestCase):
|
class FunctionTests(SimpleTestCase):
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,8 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.template.defaultfilters import escapejs_filter
|
from django.template.defaultfilters import escapejs_filter
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase
|
||||||
|
from django.utils import six
|
||||||
|
from django.utils.functional import lazy
|
||||||
|
|
||||||
from ..utils import setup
|
from ..utils import setup
|
||||||
|
|
||||||
|
@ -51,3 +53,11 @@ class FunctionTests(SimpleTestCase):
|
||||||
escapejs_filter('paragraph separator:\u2029and line separator:\u2028'),
|
escapejs_filter('paragraph separator:\u2029and line separator:\u2028'),
|
||||||
'paragraph separator:\\u2029and line separator:\\u2028',
|
'paragraph separator:\\u2029and line separator:\\u2028',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_lazy_string(self):
|
||||||
|
append_script = lazy(lambda string: r'<script>this</script>' + string, six.text_type)
|
||||||
|
self.assertEqual(
|
||||||
|
escapejs_filter(append_script('whitespace: \r\n\t\v\f\b')),
|
||||||
|
'\\u003Cscript\\u003Ethis\\u003C/script\\u003E'
|
||||||
|
'whitespace: \\u000D\\u000A\\u0009\\u000B\\u000C\\u0008'
|
||||||
|
)
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
from django.template.defaultfilters import linebreaks_filter
|
from django.template.defaultfilters import linebreaks_filter
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase
|
||||||
|
from django.utils import six
|
||||||
|
from django.utils.functional import lazy
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
from ..utils import setup
|
from ..utils import setup
|
||||||
|
@ -51,3 +53,10 @@ class FunctionTests(SimpleTestCase):
|
||||||
linebreaks_filter('foo\n<a>bar</a>\nbuz', autoescape=False),
|
linebreaks_filter('foo\n<a>bar</a>\nbuz', autoescape=False),
|
||||||
'<p>foo<br /><a>bar</a><br />buz</p>',
|
'<p>foo<br /><a>bar</a><br />buz</p>',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_lazy_string_input(self):
|
||||||
|
add_header = lazy(lambda string: 'Header\n\n' + string, six.text_type)
|
||||||
|
self.assertEqual(
|
||||||
|
linebreaks_filter(add_header('line 1\r\nline2')),
|
||||||
|
'<p>Header</p>\n\n<p>line 1<br />line2</p>'
|
||||||
|
)
|
||||||
|
|
|
@ -3,6 +3,9 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.template.defaultfilters import slugify
|
from django.template.defaultfilters import slugify
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase
|
||||||
|
from django.utils import six
|
||||||
|
from django.utils.encoding import force_text
|
||||||
|
from django.utils.functional import lazy
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
from ..utils import setup
|
from ..utils import setup
|
||||||
|
@ -41,3 +44,10 @@ class FunctionTests(SimpleTestCase):
|
||||||
|
|
||||||
def test_non_string_input(self):
|
def test_non_string_input(self):
|
||||||
self.assertEqual(slugify(123), '123')
|
self.assertEqual(slugify(123), '123')
|
||||||
|
|
||||||
|
def test_slugify_lazy_string(self):
|
||||||
|
lazy_str = lazy(lambda string: force_text(string), six.text_type)
|
||||||
|
self.assertEqual(
|
||||||
|
slugify(lazy_str(' Jack & Jill like numbers 1,2,3 and 4 and silly characters ?%.$!/')),
|
||||||
|
'jack-jill-like-numbers-123-and-4-and-silly-characters',
|
||||||
|
)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from django.template.defaultfilters import striptags
|
from django.template.defaultfilters import striptags
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase
|
||||||
|
from django.utils.functional import lazystr
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
from ..utils import setup
|
from ..utils import setup
|
||||||
|
@ -40,3 +41,9 @@ class FunctionTests(SimpleTestCase):
|
||||||
|
|
||||||
def test_non_string_input(self):
|
def test_non_string_input(self):
|
||||||
self.assertEqual(striptags(123), '123')
|
self.assertEqual(striptags(123), '123')
|
||||||
|
|
||||||
|
def test_strip_lazy_string(self):
|
||||||
|
self.assertEqual(
|
||||||
|
striptags(lazystr('some <b>html</b> with <script>alert("Hello")</script> disallowed <img /> tags')),
|
||||||
|
'some html with alert("Hello") disallowed tags',
|
||||||
|
)
|
||||||
|
|
|
@ -3,6 +3,8 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.template.defaultfilters import urlize
|
from django.template.defaultfilters import urlize
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase
|
||||||
|
from django.utils import six
|
||||||
|
from django.utils.functional import lazy
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
from ..utils import setup
|
from ..utils import setup
|
||||||
|
@ -348,3 +350,10 @@ class FunctionTests(SimpleTestCase):
|
||||||
urlize('foo<a href=" google.com ">bar</a>buz', autoescape=False),
|
urlize('foo<a href=" google.com ">bar</a>buz', autoescape=False),
|
||||||
'foo<a href=" <a href="http://google.com" rel="nofollow">google.com</a> ">bar</a>buz',
|
'foo<a href=" <a href="http://google.com" rel="nofollow">google.com</a> ">bar</a>buz',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_lazystring(self):
|
||||||
|
prepend_www = lazy(lambda url: 'www.' + url, six.text_type)
|
||||||
|
self.assertEqual(
|
||||||
|
urlize(prepend_www('google.com')),
|
||||||
|
'<a href="http://www.google.com" rel="nofollow">www.google.com</a>',
|
||||||
|
)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from django.template.defaultfilters import wordwrap
|
from django.template.defaultfilters import wordwrap
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase
|
||||||
|
from django.utils.functional import lazystr
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
from ..utils import setup
|
from ..utils import setup
|
||||||
|
@ -41,3 +42,11 @@ class FunctionTests(SimpleTestCase):
|
||||||
|
|
||||||
def test_non_string_input(self):
|
def test_non_string_input(self):
|
||||||
self.assertEqual(wordwrap(123, 2), '123')
|
self.assertEqual(wordwrap(123, 2), '123')
|
||||||
|
|
||||||
|
def test_wrap_lazy_string(self):
|
||||||
|
self.assertEqual(
|
||||||
|
wordwrap(lazystr(
|
||||||
|
'this is a long paragraph of text that really needs to be wrapped I\'m afraid'
|
||||||
|
), 14),
|
||||||
|
'this is a long\nparagraph of\ntext that\nreally needs\nto be wrapped\nI\'m afraid',
|
||||||
|
)
|
||||||
|
|
|
@ -8,6 +8,7 @@ from django.test import SimpleTestCase
|
||||||
from django.utils import html, safestring, six
|
from django.utils import html, safestring, six
|
||||||
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
|
||||||
|
from django.utils.functional import lazystr
|
||||||
|
|
||||||
|
|
||||||
class TestUtilsHtml(SimpleTestCase):
|
class TestUtilsHtml(SimpleTestCase):
|
||||||
|
@ -35,6 +36,7 @@ class TestUtilsHtml(SimpleTestCase):
|
||||||
for value, output in items:
|
for value, output in items:
|
||||||
for pattern in patterns:
|
for pattern in patterns:
|
||||||
self.check_output(f, pattern % value, pattern % output)
|
self.check_output(f, pattern % value, pattern % output)
|
||||||
|
self.check_output(f, lazystr(pattern % value), pattern % output)
|
||||||
# Check repeated values.
|
# Check repeated values.
|
||||||
self.check_output(f, value * 2, output * 2)
|
self.check_output(f, value * 2, output * 2)
|
||||||
# Verify it doesn't double replace &.
|
# Verify it doesn't double replace &.
|
||||||
|
@ -61,6 +63,7 @@ class TestUtilsHtml(SimpleTestCase):
|
||||||
)
|
)
|
||||||
for value, output in items:
|
for value, output in items:
|
||||||
self.check_output(f, value, output)
|
self.check_output(f, value, output)
|
||||||
|
self.check_output(f, lazystr(value), output)
|
||||||
|
|
||||||
def test_strip_tags(self):
|
def test_strip_tags(self):
|
||||||
f = html.strip_tags
|
f = html.strip_tags
|
||||||
|
@ -86,6 +89,7 @@ class TestUtilsHtml(SimpleTestCase):
|
||||||
)
|
)
|
||||||
for value, output in items:
|
for value, output in items:
|
||||||
self.check_output(f, value, output)
|
self.check_output(f, value, output)
|
||||||
|
self.check_output(f, lazystr(value), output)
|
||||||
|
|
||||||
# Some convoluted syntax for which parsing may differ between python versions
|
# Some convoluted syntax for which parsing may differ between python versions
|
||||||
output = html.strip_tags('<sc<!-- -->ript>test<<!-- -->/script>')
|
output = html.strip_tags('<sc<!-- -->ript>test<<!-- -->/script>')
|
||||||
|
@ -113,6 +117,7 @@ class TestUtilsHtml(SimpleTestCase):
|
||||||
items = (' <adf>', '<adf> ', ' </adf> ', ' <f> x</f>')
|
items = (' <adf>', '<adf> ', ' </adf> ', ' <f> x</f>')
|
||||||
for value in items:
|
for value in items:
|
||||||
self.check_output(f, value)
|
self.check_output(f, value)
|
||||||
|
self.check_output(f, lazystr(value))
|
||||||
# Strings that have spaces to strip.
|
# Strings that have spaces to strip.
|
||||||
items = (
|
items = (
|
||||||
('<d> </d>', '<d></d>'),
|
('<d> </d>', '<d></d>'),
|
||||||
|
@ -121,6 +126,7 @@ class TestUtilsHtml(SimpleTestCase):
|
||||||
)
|
)
|
||||||
for value, output in items:
|
for value, output in items:
|
||||||
self.check_output(f, value, output)
|
self.check_output(f, value, output)
|
||||||
|
self.check_output(f, lazystr(value), output)
|
||||||
|
|
||||||
def test_escapejs(self):
|
def test_escapejs(self):
|
||||||
f = html.escapejs
|
f = html.escapejs
|
||||||
|
@ -139,6 +145,7 @@ class TestUtilsHtml(SimpleTestCase):
|
||||||
)
|
)
|
||||||
for value, output in items:
|
for value, output in items:
|
||||||
self.check_output(f, value, output)
|
self.check_output(f, value, output)
|
||||||
|
self.check_output(f, lazystr(value), output)
|
||||||
|
|
||||||
def test_smart_urlquote(self):
|
def test_smart_urlquote(self):
|
||||||
quote = html.smart_urlquote
|
quote = html.smart_urlquote
|
||||||
|
|
|
@ -3,13 +3,12 @@ from __future__ import unicode_literals
|
||||||
from django.template import Context, Template
|
from django.template import Context, Template
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase
|
||||||
from django.utils import html, six, text
|
from django.utils import html, six, text
|
||||||
from django.utils.encoding import force_bytes, force_text
|
from django.utils.encoding import force_bytes
|
||||||
from django.utils.functional import lazy
|
from django.utils.functional import lazy, lazystr
|
||||||
from django.utils.safestring import (
|
from django.utils.safestring import (
|
||||||
EscapeData, SafeData, mark_for_escaping, mark_safe,
|
EscapeData, SafeData, mark_for_escaping, mark_safe,
|
||||||
)
|
)
|
||||||
|
|
||||||
lazystr = lazy(force_text, six.text_type)
|
|
||||||
lazybytes = lazy(force_bytes, bytes)
|
lazybytes = lazy(force_bytes, bytes)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,12 +5,9 @@ import json
|
||||||
|
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase
|
||||||
from django.utils import six, text
|
from django.utils import six, text
|
||||||
from django.utils.encoding import force_text
|
from django.utils.functional import lazystr
|
||||||
from django.utils.functional import lazy
|
|
||||||
from django.utils.translation import override
|
from django.utils.translation import override
|
||||||
|
|
||||||
lazystr = lazy(force_text, six.text_type)
|
|
||||||
|
|
||||||
IS_WIDE_BUILD = (len('\U0001F4A9') == 1)
|
IS_WIDE_BUILD = (len('\U0001F4A9') == 1)
|
||||||
|
|
||||||
|
|
||||||
|
@ -93,6 +90,8 @@ class TestUtilsText(SimpleTestCase):
|
||||||
# Make a best effort to shorten to the desired length, but requesting
|
# Make a best effort to shorten to the desired length, but requesting
|
||||||
# a length shorter than the ellipsis shouldn't break
|
# a length shorter than the ellipsis shouldn't break
|
||||||
self.assertEqual('...', text.Truncator('asdf').chars(1))
|
self.assertEqual('...', text.Truncator('asdf').chars(1))
|
||||||
|
# Ensure that lazy strings are handled correctly
|
||||||
|
self.assertEqual(text.Truncator(lazystr('The quick brown fox')).chars(12), 'The quick...')
|
||||||
|
|
||||||
def test_truncate_words(self):
|
def test_truncate_words(self):
|
||||||
truncator = text.Truncator('The quick brown fox jumped over the lazy '
|
truncator = text.Truncator('The quick brown fox jumped over the lazy '
|
||||||
|
@ -102,6 +101,9 @@ class TestUtilsText(SimpleTestCase):
|
||||||
self.assertEqual('The quick brown fox...', truncator.words(4))
|
self.assertEqual('The quick brown fox...', truncator.words(4))
|
||||||
self.assertEqual('The quick brown fox[snip]',
|
self.assertEqual('The quick brown fox[snip]',
|
||||||
truncator.words(4, '[snip]'))
|
truncator.words(4, '[snip]'))
|
||||||
|
# Ensure that lazy strings are handled correctly
|
||||||
|
truncator = text.Truncator(lazystr('The quick brown fox jumped over the lazy dog.'))
|
||||||
|
self.assertEqual('The quick brown fox...', truncator.words(4))
|
||||||
|
|
||||||
def test_truncate_html_words(self):
|
def test_truncate_html_words(self):
|
||||||
truncator = text.Truncator('<p id="par"><strong><em>The quick brown fox'
|
truncator = text.Truncator('<p id="par"><strong><em>The quick brown fox'
|
||||||
|
@ -156,6 +158,7 @@ class TestUtilsText(SimpleTestCase):
|
||||||
self.assertEqual(text.wrap(long_word, 20), long_word)
|
self.assertEqual(text.wrap(long_word, 20), long_word)
|
||||||
self.assertEqual(text.wrap('a %s word' % long_word, 10),
|
self.assertEqual(text.wrap('a %s word' % long_word, 10),
|
||||||
'a\n%s\nword' % long_word)
|
'a\n%s\nword' % long_word)
|
||||||
|
self.assertEqual(text.wrap(lazystr(digits), 100), '1234 67 9')
|
||||||
|
|
||||||
def test_normalize_newlines(self):
|
def test_normalize_newlines(self):
|
||||||
self.assertEqual(text.normalize_newlines("abc\ndef\rghi\r\n"),
|
self.assertEqual(text.normalize_newlines("abc\ndef\rghi\r\n"),
|
||||||
|
@ -163,6 +166,7 @@ class TestUtilsText(SimpleTestCase):
|
||||||
self.assertEqual(text.normalize_newlines("\n\r\r\n\r"), "\n\n\n\n")
|
self.assertEqual(text.normalize_newlines("\n\r\r\n\r"), "\n\n\n\n")
|
||||||
self.assertEqual(text.normalize_newlines("abcdefghi"), "abcdefghi")
|
self.assertEqual(text.normalize_newlines("abcdefghi"), "abcdefghi")
|
||||||
self.assertEqual(text.normalize_newlines(""), "")
|
self.assertEqual(text.normalize_newlines(""), "")
|
||||||
|
self.assertEqual(text.normalize_newlines(lazystr("abc\ndef\rghi\r\n")), "abc\ndef\nghi\n")
|
||||||
|
|
||||||
def test_normalize_newlines_bytes(self):
|
def test_normalize_newlines_bytes(self):
|
||||||
"""normalize_newlines should be able to handle bytes too"""
|
"""normalize_newlines should be able to handle bytes too"""
|
||||||
|
@ -170,6 +174,12 @@ class TestUtilsText(SimpleTestCase):
|
||||||
self.assertEqual(normalized, "abc\ndef\nghi\n")
|
self.assertEqual(normalized, "abc\ndef\nghi\n")
|
||||||
self.assertIsInstance(normalized, six.text_type)
|
self.assertIsInstance(normalized, six.text_type)
|
||||||
|
|
||||||
|
def test_phone2numeric(self):
|
||||||
|
numeric = text.phone2numeric('0800 flowers')
|
||||||
|
self.assertEqual(numeric, '0800 3569377')
|
||||||
|
lazy_numeric = lazystr(text.phone2numeric('0800 flowers'))
|
||||||
|
self.assertEqual(lazy_numeric, '0800 3569377')
|
||||||
|
|
||||||
def test_slugify(self):
|
def test_slugify(self):
|
||||||
items = (
|
items = (
|
||||||
# given - expected - unicode?
|
# given - expected - unicode?
|
||||||
|
@ -195,10 +205,23 @@ class TestUtilsText(SimpleTestCase):
|
||||||
]
|
]
|
||||||
for value, output in items:
|
for value, output in items:
|
||||||
self.assertEqual(text.unescape_entities(value), output)
|
self.assertEqual(text.unescape_entities(value), output)
|
||||||
|
self.assertEqual(text.unescape_entities(lazystr(value)), output)
|
||||||
|
|
||||||
|
def test_unescape_string_literal(self):
|
||||||
|
items = [
|
||||||
|
('"abc"', 'abc'),
|
||||||
|
("'abc'", 'abc'),
|
||||||
|
('"a \"bc\""', 'a "bc"'),
|
||||||
|
("'\'ab\' c'", "'ab' c"),
|
||||||
|
]
|
||||||
|
for value, output in items:
|
||||||
|
self.assertEqual(text.unescape_string_literal(value), output)
|
||||||
|
self.assertEqual(text.unescape_string_literal(lazystr(value)), output)
|
||||||
|
|
||||||
def test_get_valid_filename(self):
|
def test_get_valid_filename(self):
|
||||||
filename = "^&'@{}[],$=!-#()%+~_123.txt"
|
filename = "^&'@{}[],$=!-#()%+~_123.txt"
|
||||||
self.assertEqual(text.get_valid_filename(filename), "-_123.txt")
|
self.assertEqual(text.get_valid_filename(filename), "-_123.txt")
|
||||||
|
self.assertEqual(text.get_valid_filename(lazystr(filename)), "-_123.txt")
|
||||||
|
|
||||||
def test_compress_sequence(self):
|
def test_compress_sequence(self):
|
||||||
data = [{'key': i} for i in range(10)]
|
data = [{'key': i} for i in range(10)]
|
||||||
|
|
Loading…
Reference in New Issue