mirror of
https://github.com/django/django.git
synced 2024-12-22 09:05:43 +00:00
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 Ogier <alex.ogier@gmail.com>
|
||||
Alex Robbins <alexander.j.robbins@gmail.com>
|
||||
Alexey Boriskin <alex@boriskin.me>
|
||||
Aljosa Mohorovic <aljosa.mohorovic@gmail.com>
|
||||
Amit Chakradeo <http://amit.chakradeo.net/>
|
||||
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>
|
||||
Horst Gutmann <zerok@zerokspot.com>
|
||||
Hyun Mi Ae
|
||||
Iacopo Spalletti <i.spalletti@nephila.it>
|
||||
Ian A Wilson <http://ianawilson.com>
|
||||
Ian Clelland <clelland@gmail.com>
|
||||
Ian G. Kelly <ian.g.kelly@gmail.com>
|
||||
|
@ -1,8 +1,10 @@
|
||||
import copy
|
||||
import operator
|
||||
import warnings
|
||||
from functools import total_ordering, wraps
|
||||
|
||||
from django.utils import six
|
||||
from django.utils.deprecation import RemovedInDjango20Warning
|
||||
|
||||
|
||||
# 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)
|
||||
|
||||
|
||||
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):
|
||||
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
|
||||
arguments. If none of the args are lazy, the function is evaluated
|
||||
immediately, otherwise a __proxy__ is returned that will evaluate the
|
||||
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 wrapper(*args, **kwargs):
|
||||
for arg in list(args) + list(kwargs.values()):
|
||||
if isinstance(arg, Promise):
|
||||
break
|
||||
else:
|
||||
return func(*args, **kwargs)
|
||||
return lazy_func(*args, **kwargs)
|
||||
return wrapper
|
||||
def decorator(func):
|
||||
lazy_func = lazy(func, *resultclasses)
|
||||
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
for arg in list(args) + list(six.itervalues(kwargs)):
|
||||
if isinstance(arg, Promise):
|
||||
break
|
||||
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()
|
||||
|
||||
|
@ -6,7 +6,7 @@ import re
|
||||
|
||||
from django.utils import six
|
||||
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.safestring import SafeData, SafeText, mark_safe
|
||||
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')
|
||||
|
||||
|
||||
@keep_lazy(six.text_type, SafeText)
|
||||
def escape(text):
|
||||
"""
|
||||
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('<', '<')
|
||||
.replace('>', '>').replace('"', '"').replace("'", '''))
|
||||
escape = allow_lazy(escape, six.text_type, SafeText)
|
||||
|
||||
_js_escapes = {
|
||||
ord('\\'): '\\u005C',
|
||||
@ -69,10 +69,10 @@ _js_escapes = {
|
||||
_js_escapes.update((ord('%c' % z), '\\u%04X' % z) for z in range(32))
|
||||
|
||||
|
||||
@keep_lazy(six.text_type, SafeText)
|
||||
def escapejs(value):
|
||||
"""Hex encodes characters for use in JavaScript strings."""
|
||||
return mark_safe(force_text(value).translate(_js_escapes))
|
||||
escapejs = allow_lazy(escapejs, six.text_type, SafeText)
|
||||
|
||||
|
||||
def conditional_escape(text):
|
||||
@ -118,16 +118,16 @@ def format_html_join(sep, format_string, args_generator):
|
||||
for args in args_generator))
|
||||
|
||||
|
||||
@keep_lazy_text
|
||||
def linebreaks(value, autoescape=False):
|
||||
"""Converts newlines into <p> and <br />s."""
|
||||
value = normalize_newlines(value)
|
||||
value = normalize_newlines(force_text(value))
|
||||
paras = re.split('\n{2,}', value)
|
||||
if autoescape:
|
||||
paras = ['<p>%s</p>' % escape(p).replace('\n', '<br />') for p in paras]
|
||||
else:
|
||||
paras = ['<p>%s</p>' % p.replace('\n', '<br />') for p in paras]
|
||||
return '\n\n'.join(paras)
|
||||
linebreaks = allow_lazy(linebreaks, six.text_type)
|
||||
|
||||
|
||||
class MLStripper(HTMLParser):
|
||||
@ -166,10 +166,12 @@ def _strip_once(value):
|
||||
return s.get_data()
|
||||
|
||||
|
||||
@keep_lazy_text
|
||||
def strip_tags(value):
|
||||
"""Returns the given HTML with all tags stripped."""
|
||||
# Note: in typical case this loop executes _strip_once once. Loop condition
|
||||
# is redundant, but helps to reduce number of executions of _strip_once.
|
||||
value = force_text(value)
|
||||
while '<' in value and '>' in value:
|
||||
new_value = _strip_once(value)
|
||||
if len(new_value) >= len(value):
|
||||
@ -179,13 +181,12 @@ def strip_tags(value):
|
||||
break
|
||||
value = new_value
|
||||
return value
|
||||
strip_tags = allow_lazy(strip_tags)
|
||||
|
||||
|
||||
@keep_lazy_text
|
||||
def strip_spaces_between_tags(value):
|
||||
"""Returns the given HTML with spaces between tags removed."""
|
||||
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):
|
||||
@ -224,6 +225,7 @@ def smart_urlquote(url):
|
||||
return urlunsplit((scheme, netloc, path, query, fragment))
|
||||
|
||||
|
||||
@keep_lazy_text
|
||||
def urlize(text, trim_url_limit=None, nofollow=False, autoescape=False):
|
||||
"""
|
||||
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:
|
||||
words[i] = escape(word)
|
||||
return ''.join(words)
|
||||
urlize = allow_lazy(urlize, six.text_type)
|
||||
|
||||
|
||||
def avoid_wrapping(value):
|
||||
|
@ -12,7 +12,7 @@ from email.utils import formatdate
|
||||
from django.utils import six
|
||||
from django.utils.datastructures import MultiValueDict
|
||||
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 (
|
||||
quote, quote_plus, unquote, unquote_plus, urlencode as original_urlencode,
|
||||
urlparse,
|
||||
@ -40,6 +40,7 @@ PROTOCOL_TO_PORT = {
|
||||
}
|
||||
|
||||
|
||||
@keep_lazy_text
|
||||
def urlquote(url, safe='/'):
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
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=''):
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
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):
|
||||
"""
|
||||
A wrapper for Python's urllib.unquote() function that can operate on
|
||||
the result of django.utils.http.urlquote().
|
||||
"""
|
||||
return force_text(unquote(force_str(quoted_url)))
|
||||
urlunquote = allow_lazy(urlunquote, six.text_type)
|
||||
|
||||
|
||||
@keep_lazy_text
|
||||
def urlunquote_plus(quoted_url):
|
||||
"""
|
||||
A wrapper for Python's urllib.unquote_plus() function that can operate on
|
||||
the result of django.utils.http.urlquote_plus().
|
||||
"""
|
||||
return force_text(unquote_plus(force_str(quoted_url)))
|
||||
urlunquote_plus = allow_lazy(urlunquote_plus, six.text_type)
|
||||
|
||||
|
||||
def urlencode(query, doseq=0):
|
||||
|
@ -7,7 +7,7 @@ from io import BytesIO
|
||||
|
||||
from django.utils import six
|
||||
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.six.moves import html_entities
|
||||
from django.utils.translation import pgettext, ugettext as _, ugettext_lazy
|
||||
@ -20,7 +20,7 @@ if six.PY2:
|
||||
|
||||
# Capitalizes the first letter of a string.
|
||||
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
|
||||
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]|$)))')
|
||||
|
||||
|
||||
@keep_lazy_text
|
||||
def wrap(text, width):
|
||||
"""
|
||||
A word-wrap function that preserves existing line breaks. Expects that
|
||||
@ -60,7 +61,6 @@ def wrap(text, width):
|
||||
if line:
|
||||
yield line
|
||||
return ''.join(_generator())
|
||||
wrap = allow_lazy(wrap, six.text_type)
|
||||
|
||||
|
||||
class Truncator(SimpleLazyObject):
|
||||
@ -95,6 +95,7 @@ class Truncator(SimpleLazyObject):
|
||||
string has been truncated, defaulting to a translatable string of an
|
||||
ellipsis (...).
|
||||
"""
|
||||
self._setup()
|
||||
length = int(num)
|
||||
text = unicodedata.normalize('NFC', self._wrapped)
|
||||
|
||||
@ -108,7 +109,6 @@ class Truncator(SimpleLazyObject):
|
||||
if html:
|
||||
return self._truncate_html(length, truncate, text, truncate_len, False)
|
||||
return self._text_chars(length, truncate, text, truncate_len)
|
||||
chars = allow_lazy(chars)
|
||||
|
||||
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
|
||||
truncated, defaulting to ellipsis (...).
|
||||
"""
|
||||
self._setup()
|
||||
length = int(num)
|
||||
if html:
|
||||
return self._truncate_html(length, truncate, self._wrapped, length, True)
|
||||
return self._text_words(length, truncate)
|
||||
words = allow_lazy(words)
|
||||
|
||||
def _text_words(self, length, truncate):
|
||||
"""
|
||||
@ -229,6 +229,7 @@ class Truncator(SimpleLazyObject):
|
||||
return out
|
||||
|
||||
|
||||
@keep_lazy_text
|
||||
def get_valid_filename(s):
|
||||
"""
|
||||
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(' ', '_')
|
||||
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')):
|
||||
"""
|
||||
>>> 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
|
||||
_(', ').join(force_text(i) for i in 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):
|
||||
"""Normalizes CRLF and CR newlines to just LF."""
|
||||
text = force_text(text)
|
||||
return re_newlines.sub('\n', text)
|
||||
normalize_newlines = allow_lazy(normalize_newlines, six.text_type)
|
||||
|
||||
|
||||
@keep_lazy_text
|
||||
def phone2numeric(phone):
|
||||
"""Converts a phone number with letters into its numeric equivalent."""
|
||||
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',
|
||||
'u': '8', 'v': '8', 'w': '9', 'x': '9', 'y': '9', 'z': '9'}
|
||||
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
|
||||
@ -384,11 +384,12 @@ def _replace_entity(match):
|
||||
_entity_re = re.compile(r"&(#?[xX]?(?:[0-9a-fA-F]+|\w{1,8}));")
|
||||
|
||||
|
||||
@keep_lazy_text
|
||||
def unescape_entities(text):
|
||||
return _entity_re.sub(_replace_entity, text)
|
||||
unescape_entities = allow_lazy(unescape_entities, six.text_type)
|
||||
return _entity_re.sub(_replace_entity, force_text(text))
|
||||
|
||||
|
||||
@keep_lazy_text
|
||||
def unescape_string_literal(s):
|
||||
r"""
|
||||
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)
|
||||
quote = s[0]
|
||||
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):
|
||||
"""
|
||||
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 = re.sub('[^\w\s-]', '', value).strip().lower()
|
||||
return mark_safe(re.sub('[-\s]+', '-', value))
|
||||
slugify = allow_lazy(slugify, six.text_type, SafeText)
|
||||
|
||||
|
||||
def camel_case_to_spaces(value):
|
||||
|
@ -124,6 +124,8 @@ details on these changes.
|
||||
* The ``cascaded_union`` property of ``django.contrib.gis.geos.MultiPolygon``
|
||||
will be removed.
|
||||
|
||||
* ``django.utils.functional.allow_lazy()`` will be removed.
|
||||
|
||||
.. _deprecation-removed-in-1.10:
|
||||
|
||||
1.10
|
||||
|
@ -522,6 +522,15 @@ Atom1Feed
|
||||
|
||||
.. 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``)
|
||||
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
|
||||
@ -533,31 +542,58 @@ Atom1Feed
|
||||
because you might be using this function outside of a view (and hence the
|
||||
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
|
||||
translation as one of its arguments, the function evaluation is delayed
|
||||
until it needs to be converted to a string.
|
||||
|
||||
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, ...):
|
||||
# Do some conversion on string 's'
|
||||
...
|
||||
# Replace unicode by str on Python 3
|
||||
fancy_utility_function = allow_lazy(fancy_utility_function, unicode)
|
||||
fancy_utility_function = keep_lazy(six.text_type)(fancy_utility_function)
|
||||
|
||||
The ``allow_lazy()`` decorator takes, in addition to the function to
|
||||
decorate, a number of extra arguments (``*args``) specifying the type(s)
|
||||
that the original function can return. Usually, it's enough to include
|
||||
``unicode`` (or ``str`` on Python 3) here and ensure that your function
|
||||
returns only Unicode strings.
|
||||
# Or more succinctly:
|
||||
@keep_lazy(six.text_type)
|
||||
def fancy_utility_function(s, ...):
|
||||
...
|
||||
|
||||
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
|
||||
input is a proper string, then add support for lazy translation objects at
|
||||
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``
|
||||
=====================
|
||||
|
||||
|
@ -402,6 +402,10 @@ Miscellaneous
|
||||
* The ``makemigrations --exit`` option is deprecated in favor of the
|
||||
: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:
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
|
@ -8,8 +8,12 @@ from django.contrib.auth.decorators import (
|
||||
from django.http import HttpRequest, HttpResponse, HttpResponseNotAllowed
|
||||
from django.middleware.clickjacking import XFrameOptionsMiddleware
|
||||
from django.test import SimpleTestCase
|
||||
from django.utils import six
|
||||
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 (
|
||||
cache_control, cache_page, never_cache,
|
||||
)
|
||||
@ -67,7 +71,8 @@ full_decorator = compose(
|
||||
staff_member_required,
|
||||
|
||||
# django.utils.functional
|
||||
allow_lazy,
|
||||
keep_lazy(HttpResponse),
|
||||
keep_lazy_text,
|
||||
lazy,
|
||||
)
|
||||
|
||||
@ -149,6 +154,15 @@ class DecoratorsTest(TestCase):
|
||||
request.method = 'DELETE'
|
||||
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.
|
||||
# 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.utils import six
|
||||
from django.utils._os import upath
|
||||
from django.utils.encoding import force_text, smart_str
|
||||
from django.utils.functional import lazy
|
||||
|
||||
lazystr = lazy(force_text, six.text_type)
|
||||
from django.utils.encoding import smart_str
|
||||
from django.utils.functional import lazystr
|
||||
|
||||
|
||||
class QueryDictTests(unittest.TestCase):
|
||||
|
@ -1,5 +1,7 @@
|
||||
from django.template.defaultfilters import escape
|
||||
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 ..utils import setup
|
||||
@ -33,6 +35,12 @@ class EscapeTests(SimpleTestCase):
|
||||
output = self.engine.render_to_string('escape04', {"a": "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):
|
||||
|
||||
|
@ -2,6 +2,8 @@ from __future__ import unicode_literals
|
||||
|
||||
from django.template.defaultfilters import escapejs_filter
|
||||
from django.test import SimpleTestCase
|
||||
from django.utils import six
|
||||
from django.utils.functional import lazy
|
||||
|
||||
from ..utils import setup
|
||||
|
||||
@ -51,3 +53,11 @@ class FunctionTests(SimpleTestCase):
|
||||
escapejs_filter('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.test import SimpleTestCase
|
||||
from django.utils import six
|
||||
from django.utils.functional import lazy
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
from ..utils import setup
|
||||
@ -51,3 +53,10 @@ class FunctionTests(SimpleTestCase):
|
||||
linebreaks_filter('foo\n<a>bar</a>\nbuz', autoescape=False),
|
||||
'<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.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 ..utils import setup
|
||||
@ -41,3 +44,10 @@ class FunctionTests(SimpleTestCase):
|
||||
|
||||
def test_non_string_input(self):
|
||||
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.test import SimpleTestCase
|
||||
from django.utils.functional import lazystr
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
from ..utils import setup
|
||||
@ -40,3 +41,9 @@ class FunctionTests(SimpleTestCase):
|
||||
|
||||
def test_non_string_input(self):
|
||||
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.test import SimpleTestCase
|
||||
from django.utils import six
|
||||
from django.utils.functional import lazy
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
from ..utils import setup
|
||||
@ -348,3 +350,10 @@ class FunctionTests(SimpleTestCase):
|
||||
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',
|
||||
)
|
||||
|
||||
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.test import SimpleTestCase
|
||||
from django.utils.functional import lazystr
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
from ..utils import setup
|
||||
@ -41,3 +42,11 @@ class FunctionTests(SimpleTestCase):
|
||||
|
||||
def test_non_string_input(self):
|
||||
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._os import upath
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.functional import lazystr
|
||||
|
||||
|
||||
class TestUtilsHtml(SimpleTestCase):
|
||||
@ -35,6 +36,7 @@ class TestUtilsHtml(SimpleTestCase):
|
||||
for value, output in items:
|
||||
for pattern in patterns:
|
||||
self.check_output(f, pattern % value, pattern % output)
|
||||
self.check_output(f, lazystr(pattern % value), pattern % output)
|
||||
# Check repeated values.
|
||||
self.check_output(f, value * 2, output * 2)
|
||||
# Verify it doesn't double replace &.
|
||||
@ -61,6 +63,7 @@ class TestUtilsHtml(SimpleTestCase):
|
||||
)
|
||||
for value, output in items:
|
||||
self.check_output(f, value, output)
|
||||
self.check_output(f, lazystr(value), output)
|
||||
|
||||
def test_strip_tags(self):
|
||||
f = html.strip_tags
|
||||
@ -86,6 +89,7 @@ class TestUtilsHtml(SimpleTestCase):
|
||||
)
|
||||
for value, output in items:
|
||||
self.check_output(f, value, output)
|
||||
self.check_output(f, lazystr(value), output)
|
||||
|
||||
# Some convoluted syntax for which parsing may differ between python versions
|
||||
output = html.strip_tags('<sc<!-- -->ript>test<<!-- -->/script>')
|
||||
@ -113,6 +117,7 @@ class TestUtilsHtml(SimpleTestCase):
|
||||
items = (' <adf>', '<adf> ', ' </adf> ', ' <f> x</f>')
|
||||
for value in items:
|
||||
self.check_output(f, value)
|
||||
self.check_output(f, lazystr(value))
|
||||
# Strings that have spaces to strip.
|
||||
items = (
|
||||
('<d> </d>', '<d></d>'),
|
||||
@ -121,6 +126,7 @@ class TestUtilsHtml(SimpleTestCase):
|
||||
)
|
||||
for value, output in items:
|
||||
self.check_output(f, value, output)
|
||||
self.check_output(f, lazystr(value), output)
|
||||
|
||||
def test_escapejs(self):
|
||||
f = html.escapejs
|
||||
@ -139,6 +145,7 @@ class TestUtilsHtml(SimpleTestCase):
|
||||
)
|
||||
for value, output in items:
|
||||
self.check_output(f, value, output)
|
||||
self.check_output(f, lazystr(value), output)
|
||||
|
||||
def test_smart_urlquote(self):
|
||||
quote = html.smart_urlquote
|
||||
|
@ -3,13 +3,12 @@ from __future__ import unicode_literals
|
||||
from django.template import Context, Template
|
||||
from django.test import SimpleTestCase
|
||||
from django.utils import html, six, text
|
||||
from django.utils.encoding import force_bytes, force_text
|
||||
from django.utils.functional import lazy
|
||||
from django.utils.encoding import force_bytes
|
||||
from django.utils.functional import lazy, lazystr
|
||||
from django.utils.safestring import (
|
||||
EscapeData, SafeData, mark_for_escaping, mark_safe,
|
||||
)
|
||||
|
||||
lazystr = lazy(force_text, six.text_type)
|
||||
lazybytes = lazy(force_bytes, bytes)
|
||||
|
||||
|
||||
|
@ -5,12 +5,9 @@ import json
|
||||
|
||||
from django.test import SimpleTestCase
|
||||
from django.utils import six, text
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.functional import lazy
|
||||
from django.utils.functional import lazystr
|
||||
from django.utils.translation import override
|
||||
|
||||
lazystr = lazy(force_text, six.text_type)
|
||||
|
||||
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
|
||||
# a length shorter than the ellipsis shouldn't break
|
||||
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):
|
||||
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[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):
|
||||
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('a %s word' % long_word, 10),
|
||||
'a\n%s\nword' % long_word)
|
||||
self.assertEqual(text.wrap(lazystr(digits), 100), '1234 67 9')
|
||||
|
||||
def test_normalize_newlines(self):
|
||||
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("abcdefghi"), "abcdefghi")
|
||||
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):
|
||||
"""normalize_newlines should be able to handle bytes too"""
|
||||
@ -170,6 +174,12 @@ class TestUtilsText(SimpleTestCase):
|
||||
self.assertEqual(normalized, "abc\ndef\nghi\n")
|
||||
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):
|
||||
items = (
|
||||
# given - expected - unicode?
|
||||
@ -195,10 +205,23 @@ class TestUtilsText(SimpleTestCase):
|
||||
]
|
||||
for value, output in items:
|
||||
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):
|
||||
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):
|
||||
data = [{'key': i} for i in range(10)]
|
||||
|
Loading…
Reference in New Issue
Block a user