blob: b56c749e48c20fe17727757d3736048e5f8d66be [file] [log] [blame]
Armin Ronacher07bc6842008-03-31 14:18:49 +02001# -*- coding: utf-8 -*-
2"""
3 jinja2.utils
4 ~~~~~~~~~~~~
5
6 Utility functions.
7
Armin Ronacher55494e42010-01-22 09:41:48 +01008 :copyright: (c) 2010 by the Jinja Team.
Armin Ronacher07bc6842008-03-31 14:18:49 +02009 :license: BSD, see LICENSE for more details.
10"""
Christoph Hack80909862008-04-14 01:35:10 +020011import re
Benjamin Wiegand96828552008-05-03 22:27:29 +020012import sys
Armin Ronacherccae0552008-10-05 23:08:58 +020013import errno
Thomas Waldmanne0003552013-05-17 23:52:14 +020014import six
Thomas Waldmann7d295622013-05-18 00:06:22 +020015from six.moves import map
Armin Ronacher000b4912008-05-01 18:40:15 +020016try:
Armin Ronacher1d4c6382012-01-07 17:46:40 +010017 from urllib.parse import quote_from_bytes as url_quote
18except ImportError:
19 from urllib import quote as url_quote
20try:
Armin Ronacher000b4912008-05-01 18:40:15 +020021 from thread import allocate_lock
22except ImportError:
Thomas Waldmann7d295622013-05-18 00:06:22 +020023 try:
24 from _thread import allocate_lock # py 3
25 except ImportError:
26 from dummy_thread import allocate_lock
Armin Ronacher814f6c22008-04-17 15:52:23 +020027from collections import deque
Armin Ronacher8edbe492008-04-10 20:43:43 +020028
Armin Ronacherbe4ae242008-04-18 09:49:08 +020029_word_split_re = re.compile(r'(\s+)')
30_punctuation_re = re.compile(
31 '^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$' % (
Thomas Waldmann7d295622013-05-18 00:06:22 +020032 '|'.join(map(re.escape, ('(', '<', '&lt;'))),
33 '|'.join(map(re.escape, ('.', ',', ')', '>', '\n', '&gt;')))
Armin Ronacherbe4ae242008-04-18 09:49:08 +020034 )
35)
36_simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$')
Armin Ronacher76c280b2008-05-04 12:31:48 +020037_striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
38_entity_re = re.compile(r'&([^;]+);')
Armin Ronacher9a0078d2008-08-13 18:24:17 +020039_letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
40_digits = '0123456789'
Armin Ronacherbe4ae242008-04-18 09:49:08 +020041
Armin Ronacher7259c762008-04-30 13:03:59 +020042# special singleton representing missing values for the runtime
43missing = type('MissingType', (), {'__repr__': lambda x: 'missing'})()
44
Armin Ronacherd416a972009-02-24 22:58:00 +010045# internal code
46internal_code = set()
47
Thomas Waldmann07a7d552013-05-18 00:47:41 +020048concat = u''.join
Armin Ronacher7ceced52008-05-03 10:15:31 +020049
50
Armin Ronacher0d242be2010-02-10 01:35:13 +010051# if this python version is unable to deal with unicode filenames
52# when passed to encode we let this function encode it properly.
53# This is used in a couple of places. As far as Jinja is concerned
54# filenames are unicode *or* bytestrings in 2.x and unicode only in
55# 3.x because compile cannot handle bytes
56if sys.version_info < (3, 0):
57 def _encode_filename(filename):
58 if isinstance(filename, unicode):
59 return filename.encode('utf-8')
60 return filename
61else:
62 def _encode_filename(filename):
63 assert filename is None or isinstance(filename, str), \
64 'filenames must be strings'
65 return filename
66
67from keyword import iskeyword as is_python_keyword
Armin Ronacher9a0078d2008-08-13 18:24:17 +020068
69
70# common types. These do exist in the special types module too which however
Armin Ronacher0d242be2010-02-10 01:35:13 +010071# does not exist in IronPython out of the box. Also that way we don't have
72# to deal with implementation specific stuff here
Armin Ronacher9a0078d2008-08-13 18:24:17 +020073class _C(object):
74 def method(self): pass
75def _func():
76 yield None
77FunctionType = type(_func)
78GeneratorType = type(_func())
79MethodType = type(_C.method)
Thomas Waldmanne0003552013-05-17 23:52:14 +020080CodeType = type(_C.method.__code__)
Armin Ronacher9a0078d2008-08-13 18:24:17 +020081try:
82 raise TypeError()
83except TypeError:
84 _tb = sys.exc_info()[2]
85 TracebackType = type(_tb)
86 FrameType = type(_tb.tb_frame)
87del _C, _tb, _func
88
89
Armin Ronacher4f7d2d52008-04-22 10:40:26 +020090def contextfunction(f):
Armin Ronacher9bb7e472008-05-28 11:26:59 +020091 """This decorator can be used to mark a function or method context callable.
92 A context callable is passed the active :class:`Context` as first argument when
93 called from the template. This is useful if a function wants to get access
94 to the context or functions provided on the context object. For example
95 a function that returns a sorted list of template variables the current
96 template exports could look like this::
97
Armin Ronacher58f351d2008-05-28 21:30:14 +020098 @contextfunction
Armin Ronacher9bb7e472008-05-28 11:26:59 +020099 def get_exported_names(context):
100 return sorted(context.exported_vars)
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200101 """
102 f.contextfunction = True
103 return f
104
105
Armin Ronacher8346bd72010-03-14 19:43:47 +0100106def evalcontextfunction(f):
Florent Xicluna0ec4f762012-02-05 13:09:15 +0100107 """This decorator can be used to mark a function or method as an eval
Armin Ronacher8346bd72010-03-14 19:43:47 +0100108 context callable. This is similar to the :func:`contextfunction`
109 but instead of passing the context, an evaluation context object is
Armin Ronacherfe150f32010-03-15 02:42:41 +0100110 passed. For more information about the eval context, see
111 :ref:`eval-context`.
Armin Ronacher8346bd72010-03-14 19:43:47 +0100112
113 .. versionadded:: 2.4
114 """
115 f.evalcontextfunction = True
116 return f
117
118
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200119def environmentfunction(f):
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200120 """This decorator can be used to mark a function or method as environment
121 callable. This decorator works exactly like the :func:`contextfunction`
122 decorator just that the first argument is the active :class:`Environment`
123 and not context.
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200124 """
125 f.environmentfunction = True
126 return f
127
128
Armin Ronacherd416a972009-02-24 22:58:00 +0100129def internalcode(f):
130 """Marks the function as internally used"""
Thomas Waldmanne0003552013-05-17 23:52:14 +0200131 internal_code.add(f.__code__)
Armin Ronacherd416a972009-02-24 22:58:00 +0100132 return f
133
134
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200135def is_undefined(obj):
136 """Check if the object passed is undefined. This does nothing more than
137 performing an instance check against :class:`Undefined` but looks nicer.
138 This can be used for custom filters or tests that want to react to
139 undefined variables. For example a custom default filter can look like
140 this::
141
142 def default(var, default=''):
143 if is_undefined(var):
144 return default
145 return var
146 """
147 from jinja2.runtime import Undefined
148 return isinstance(obj, Undefined)
149
150
Armin Ronacherba6e25a2008-11-02 15:58:14 +0100151def consume(iterable):
152 """Consumes an iterable without doing anything with it."""
153 for event in iterable:
154 pass
155
156
Armin Ronacher187bde12008-05-01 18:19:16 +0200157def clear_caches():
158 """Jinja2 keeps internal caches for environments and lexers. These are
159 used so that Jinja2 doesn't have to recreate environments and lexers all
160 the time. Normally you don't have to care about that but if you are
161 messuring memory consumption you may want to clean the caches.
162 """
163 from jinja2.environment import _spontaneous_environments
164 from jinja2.lexer import _lexer_cache
165 _spontaneous_environments.clear()
166 _lexer_cache.clear()
167
168
Armin Ronacherf59bac22008-04-20 13:11:43 +0200169def import_string(import_name, silent=False):
Florent Xicluna0ec4f762012-02-05 13:09:15 +0100170 """Imports an object based on a string. This is useful if you want to
Armin Ronacherf59bac22008-04-20 13:11:43 +0200171 use import paths as endpoints or something similar. An import path can
172 be specified either in dotted notation (``xml.sax.saxutils.escape``)
173 or with a colon as object delimiter (``xml.sax.saxutils:escape``).
174
175 If the `silent` is True the return value will be `None` if the import
176 fails.
177
178 :return: imported object
Armin Ronacher9a027f42008-04-17 11:13:40 +0200179 """
Armin Ronacherf59bac22008-04-20 13:11:43 +0200180 try:
181 if ':' in import_name:
182 module, obj = import_name.split(':', 1)
183 elif '.' in import_name:
184 items = import_name.split('.')
185 module = '.'.join(items[:-1])
186 obj = items[-1]
187 else:
188 return __import__(import_name)
189 return getattr(__import__(module, None, None, [obj]), obj)
190 except (ImportError, AttributeError):
191 if not silent:
192 raise
Armin Ronacher9a027f42008-04-17 11:13:40 +0200193
194
Armin Ronacher0faa8612010-02-09 15:04:51 +0100195def open_if_exists(filename, mode='rb'):
Armin Ronacherccae0552008-10-05 23:08:58 +0200196 """Returns a file descriptor for the filename if that file exists,
197 otherwise `None`.
198 """
199 try:
Armin Ronacher790b8a82010-02-10 00:05:46 +0100200 return open(filename, mode)
Thomas Waldmanne0003552013-05-17 23:52:14 +0200201 except IOError as e:
Armin Ronacherccae0552008-10-05 23:08:58 +0200202 if e.errno not in (errno.ENOENT, errno.EISDIR):
203 raise
204
205
Armin Ronacher98dbf5f2010-04-12 15:49:59 +0200206def object_type_repr(obj):
207 """Returns the name of the object's type. For some recognized
208 singletons the name of the object is returned instead. (For
209 example for `None` and `Ellipsis`).
210 """
211 if obj is None:
212 return 'None'
213 elif obj is Ellipsis:
214 return 'Ellipsis'
Armin Ronacher802f4722010-04-20 19:48:46 +0200215 # __builtin__ in 2.x, builtins in 3.x
216 if obj.__class__.__module__ in ('__builtin__', 'builtins'):
Armin Ronacher800ac7f2010-04-20 13:45:11 +0200217 name = obj.__class__.__name__
Armin Ronacher98dbf5f2010-04-12 15:49:59 +0200218 else:
Armin Ronacher800ac7f2010-04-20 13:45:11 +0200219 name = obj.__class__.__module__ + '.' + obj.__class__.__name__
Armin Ronacher98dbf5f2010-04-12 15:49:59 +0200220 return '%s object' % name
221
222
Christoph Hacke9e43bb2008-04-13 23:35:48 +0200223def pformat(obj, verbose=False):
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200224 """Prettyprint an object. Either use the `pretty` library or the
Christoph Hacke9e43bb2008-04-13 23:35:48 +0200225 builtin `pprint`.
226 """
227 try:
228 from pretty import pretty
229 return pretty(obj, verbose=verbose)
230 except ImportError:
231 from pprint import pformat
232 return pformat(obj)
Christoph Hack80909862008-04-14 01:35:10 +0200233
234
Christoph Hack80909862008-04-14 01:35:10 +0200235def urlize(text, trim_url_limit=None, nofollow=False):
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200236 """Converts any URLs in text into clickable links. Works on http://,
Christoph Hack80909862008-04-14 01:35:10 +0200237 https:// and www. links. Links can have trailing punctuation (periods,
238 commas, close-parens) and leading punctuation (opening parens) and
239 it'll still do the right thing.
240
241 If trim_url_limit is not None, the URLs in link text will be limited
242 to trim_url_limit characters.
243
244 If nofollow is True, the URLs in link text will get a rel="nofollow"
245 attribute.
246 """
247 trim_url = lambda x, limit=trim_url_limit: limit is not None \
248 and (x[:limit] + (len(x) >=limit and '...'
249 or '')) or x
Thomas Waldmanne0003552013-05-17 23:52:14 +0200250 words = _word_split_re.split(six.text_type(escape(text)))
Christoph Hack80909862008-04-14 01:35:10 +0200251 nofollow_attr = nofollow and ' rel="nofollow"' or ''
252 for i, word in enumerate(words):
253 match = _punctuation_re.match(word)
254 if match:
255 lead, middle, trail = match.groups()
256 if middle.startswith('www.') or (
257 '@' not in middle and
258 not middle.startswith('http://') and
mozillazg66448932013-03-18 14:27:54 +0800259 not middle.startswith('https://') and
Christoph Hack80909862008-04-14 01:35:10 +0200260 len(middle) > 0 and
Armin Ronacher9a0078d2008-08-13 18:24:17 +0200261 middle[0] in _letters + _digits and (
Christoph Hack80909862008-04-14 01:35:10 +0200262 middle.endswith('.org') or
263 middle.endswith('.net') or
264 middle.endswith('.com')
265 )):
266 middle = '<a href="http://%s"%s>%s</a>' % (middle,
267 nofollow_attr, trim_url(middle))
268 if middle.startswith('http://') or \
269 middle.startswith('https://'):
270 middle = '<a href="%s"%s>%s</a>' % (middle,
271 nofollow_attr, trim_url(middle))
272 if '@' in middle and not middle.startswith('www.') and \
273 not ':' in middle and _simple_email_re.match(middle):
274 middle = '<a href="mailto:%s">%s</a>' % (middle, middle)
275 if lead + middle + trail != word:
276 words[i] = lead + middle + trail
277 return u''.join(words)
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200278
279
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200280def generate_lorem_ipsum(n=5, html=True, min=20, max=100):
281 """Generate some lorem impsum for the template."""
282 from jinja2.constants import LOREM_IPSUM_WORDS
Georg Brandl95632c42009-11-22 18:35:18 +0100283 from random import choice, randrange
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200284 words = LOREM_IPSUM_WORDS.split()
285 result = []
286
Thomas Waldmanne0003552013-05-17 23:52:14 +0200287 for _ in range(n):
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200288 next_capitalized = True
289 last_comma = last_fullstop = 0
290 word = None
291 last = None
292 p = []
293
294 # each paragraph contains out of 20 to 100 words.
Thomas Waldmanne0003552013-05-17 23:52:14 +0200295 for idx, _ in enumerate(range(randrange(min, max))):
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200296 while True:
297 word = choice(words)
298 if word != last:
299 last = word
300 break
301 if next_capitalized:
302 word = word.capitalize()
303 next_capitalized = False
304 # add commas
305 if idx - randrange(3, 8) > last_comma:
306 last_comma = idx
307 last_fullstop += 2
308 word += ','
309 # add end of sentences
310 if idx - randrange(10, 20) > last_fullstop:
311 last_comma = last_fullstop = idx
312 word += '.'
313 next_capitalized = True
314 p.append(word)
315
316 # ensure that the paragraph ends with a dot.
317 p = u' '.join(p)
318 if p.endswith(','):
319 p = p[:-1] + '.'
320 elif not p.endswith('.'):
321 p += '.'
322 result.append(p)
323
324 if not html:
325 return u'\n\n'.join(result)
326 return Markup(u'\n'.join(u'<p>%s</p>' % escape(x) for x in result))
327
328
Armin Ronacher51454012012-01-07 17:47:56 +0100329def unicode_urlencode(obj, charset='utf-8'):
Armin Ronacher1d4c6382012-01-07 17:46:40 +0100330 """URL escapes a single bytestring or unicode string with the
331 given charset if applicable to URL safe quoting under all rules
332 that need to be considered under all supported Python versions.
333
334 If non strings are provided they are converted to their unicode
335 representation first.
336 """
Thomas Waldmann7d295622013-05-18 00:06:22 +0200337 if not isinstance(obj, six.string_types):
Thomas Waldmanne0003552013-05-17 23:52:14 +0200338 obj = six.text_type(obj)
Thomas Waldmann7d295622013-05-18 00:06:22 +0200339 if isinstance(obj, six.text_type):
Armin Ronacher1d4c6382012-01-07 17:46:40 +0100340 obj = obj.encode(charset)
Thomas Waldmanne0003552013-05-17 23:52:14 +0200341 return six.text_type(url_quote(obj))
Armin Ronacher1d4c6382012-01-07 17:46:40 +0100342
343
Armin Ronacher814f6c22008-04-17 15:52:23 +0200344class LRUCache(object):
345 """A simple LRU Cache implementation."""
Armin Ronacher58f351d2008-05-28 21:30:14 +0200346
347 # this is fast for small capacities (something below 1000) but doesn't
348 # scale. But as long as it's only used as storage for templates this
349 # won't do any harm.
Armin Ronacher814f6c22008-04-17 15:52:23 +0200350
351 def __init__(self, capacity):
352 self.capacity = capacity
353 self._mapping = {}
354 self._queue = deque()
Armin Ronacher7962ce72008-05-20 17:52:52 +0200355 self._postinit()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200356
Armin Ronacher7962ce72008-05-20 17:52:52 +0200357 def _postinit(self):
Armin Ronacher814f6c22008-04-17 15:52:23 +0200358 # alias all queue methods for faster lookup
359 self._popleft = self._queue.popleft
360 self._pop = self._queue.pop
Thomas Waldmann07a7d552013-05-18 00:47:41 +0200361 self._remove = self._queue.remove
Armin Ronacher000b4912008-05-01 18:40:15 +0200362 self._wlock = allocate_lock()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200363 self._append = self._queue.append
364
Armin Ronacher7962ce72008-05-20 17:52:52 +0200365 def __getstate__(self):
366 return {
367 'capacity': self.capacity,
368 '_mapping': self._mapping,
369 '_queue': self._queue
370 }
371
372 def __setstate__(self, d):
373 self.__dict__.update(d)
374 self._postinit()
375
376 def __getnewargs__(self):
377 return (self.capacity,)
378
Armin Ronacher814f6c22008-04-17 15:52:23 +0200379 def copy(self):
Florent Xicluna0ec4f762012-02-05 13:09:15 +0100380 """Return a shallow copy of the instance."""
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200381 rv = self.__class__(self.capacity)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200382 rv._mapping.update(self._mapping)
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200383 rv._queue = deque(self._queue)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200384 return rv
385
386 def get(self, key, default=None):
387 """Return an item from the cache dict or `default`"""
Armin Ronacher000b4912008-05-01 18:40:15 +0200388 try:
Armin Ronacher814f6c22008-04-17 15:52:23 +0200389 return self[key]
Armin Ronacher000b4912008-05-01 18:40:15 +0200390 except KeyError:
391 return default
Armin Ronacher814f6c22008-04-17 15:52:23 +0200392
393 def setdefault(self, key, default=None):
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200394 """Set `default` if the key is not in the cache otherwise
Armin Ronacher814f6c22008-04-17 15:52:23 +0200395 leave unchanged. Return the value of this key.
396 """
Armin Ronacherd4e54382013-04-13 00:38:27 +0100397 self._wlock.acquire()
Armin Ronacher000b4912008-05-01 18:40:15 +0200398 try:
Armin Ronacherd4e54382013-04-13 00:38:27 +0100399 try:
400 return self[key]
401 except KeyError:
402 self[key] = default
403 return default
404 finally:
405 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200406
407 def clear(self):
408 """Clear the cache."""
Armin Ronacher000b4912008-05-01 18:40:15 +0200409 self._wlock.acquire()
410 try:
411 self._mapping.clear()
412 self._queue.clear()
413 finally:
414 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200415
416 def __contains__(self, key):
417 """Check if a key exists in this cache."""
418 return key in self._mapping
419
420 def __len__(self):
421 """Return the current size of the cache."""
422 return len(self._mapping)
423
424 def __repr__(self):
425 return '<%s %r>' % (
426 self.__class__.__name__,
427 self._mapping
428 )
429
430 def __getitem__(self, key):
431 """Get an item from the cache. Moves the item up so that it has the
432 highest priority then.
433
Florent Xicluna0ec4f762012-02-05 13:09:15 +0100434 Raise a `KeyError` if it does not exist.
Armin Ronacher814f6c22008-04-17 15:52:23 +0200435 """
Armin Ronacherd4e54382013-04-13 00:38:27 +0100436 self._wlock.acquire()
437 try:
438 rv = self._mapping[key]
439 if self._queue[-1] != key:
440 try:
441 self._remove(key)
442 except ValueError:
443 # if something removed the key from the container
444 # when we read, ignore the ValueError that we would
445 # get otherwise.
446 pass
447 self._append(key)
448 return rv
449 finally:
450 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200451
452 def __setitem__(self, key, value):
453 """Sets the value for an item. Moves the item up so that it
454 has the highest priority then.
455 """
Armin Ronacher000b4912008-05-01 18:40:15 +0200456 self._wlock.acquire()
457 try:
458 if key in self._mapping:
Armin Ronacherd4e54382013-04-13 00:38:27 +0100459 self._remove(key)
Armin Ronacher000b4912008-05-01 18:40:15 +0200460 elif len(self._mapping) == self.capacity:
461 del self._mapping[self._popleft()]
462 self._append(key)
463 self._mapping[key] = value
464 finally:
465 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200466
467 def __delitem__(self, key):
468 """Remove an item from the cache dict.
Florent Xicluna0ec4f762012-02-05 13:09:15 +0100469 Raise a `KeyError` if it does not exist.
Armin Ronacher814f6c22008-04-17 15:52:23 +0200470 """
Armin Ronacher000b4912008-05-01 18:40:15 +0200471 self._wlock.acquire()
472 try:
473 del self._mapping[key]
Armin Ronachere7c72bc2009-09-14 12:20:33 -0700474 try:
475 self._remove(key)
476 except ValueError:
477 # __getitem__ is not locked, it might happen
478 pass
Armin Ronacher000b4912008-05-01 18:40:15 +0200479 finally:
480 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200481
Armin Ronachere25f24d2008-05-19 11:20:41 +0200482 def items(self):
483 """Return a list of items."""
484 result = [(key, self._mapping[key]) for key in list(self._queue)]
485 result.reverse()
486 return result
487
488 def iteritems(self):
489 """Iterate over all items."""
490 return iter(self.items())
491
492 def values(self):
493 """Return a list of all values."""
494 return [x[1] for x in self.items()]
495
496 def itervalue(self):
497 """Iterate over all values."""
498 return iter(self.values())
499
500 def keys(self):
501 """Return a list of all keys ordered by most recent usage."""
502 return list(self)
503
504 def iterkeys(self):
505 """Iterate over all keys in the cache dict, ordered by
Armin Ronacher814f6c22008-04-17 15:52:23 +0200506 the most recent usage.
507 """
Armin Ronachere2244882008-05-19 09:25:57 +0200508 return reversed(tuple(self._queue))
Armin Ronacher814f6c22008-04-17 15:52:23 +0200509
Armin Ronachere25f24d2008-05-19 11:20:41 +0200510 __iter__ = iterkeys
511
Armin Ronacher814f6c22008-04-17 15:52:23 +0200512 def __reversed__(self):
513 """Iterate over the values in the cache dict, oldest items
514 coming first.
515 """
Armin Ronachere2244882008-05-19 09:25:57 +0200516 return iter(tuple(self._queue))
Armin Ronacher814f6c22008-04-17 15:52:23 +0200517
518 __copy__ = copy
519
Armin Ronacherbd33f112008-04-18 09:17:32 +0200520
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200521# register the LRU cache as mutable mapping if possible
522try:
523 from collections import MutableMapping
524 MutableMapping.register(LRUCache)
525except ImportError:
526 pass
527
528
Thomas Waldmann7d295622013-05-18 00:06:22 +0200529class Cycler(six.Iterator):
Armin Ronacherccae0552008-10-05 23:08:58 +0200530 """A cycle helper for templates."""
531
532 def __init__(self, *items):
533 if not items:
534 raise RuntimeError('at least one item has to be provided')
535 self.items = items
536 self.reset()
537
538 def reset(self):
539 """Resets the cycle."""
540 self.pos = 0
541
542 @property
543 def current(self):
544 """Returns the current item."""
545 return self.items[self.pos]
546
Thomas Waldmann7d295622013-05-18 00:06:22 +0200547 def __next__(self):
Armin Ronacherccae0552008-10-05 23:08:58 +0200548 """Goes one item ahead and returns it."""
549 rv = self.current
550 self.pos = (self.pos + 1) % len(self.items)
551 return rv
552
553
Armin Ronacherd34eb122008-10-13 23:47:51 +0200554class Joiner(object):
555 """A joining helper for templates."""
556
557 def __init__(self, sep=u', '):
558 self.sep = sep
559 self.used = False
560
561 def __call__(self):
562 if not self.used:
563 self.used = True
564 return u''
565 return self.sep
566
567
Armin Ronacherf9f5f262010-08-17 11:57:07 +0200568# try markupsafe first, if that fails go with Jinja2's bundled version
569# of markupsafe. Markupsafe was previously Jinja2's implementation of
Florent Xicluna0ec4f762012-02-05 13:09:15 +0100570# the Markup object but was moved into a separate package in a patchlevel
Armin Ronacherf9f5f262010-08-17 11:57:07 +0200571# release
Armin Ronacherbd33f112008-04-18 09:17:32 +0200572try:
Armin Ronacherf9f5f262010-08-17 11:57:07 +0200573 from markupsafe import Markup, escape, soft_unicode
Armin Ronacherbd33f112008-04-18 09:17:32 +0200574except ImportError:
Armin Ronacherf9f5f262010-08-17 11:57:07 +0200575 from jinja2._markupsafe import Markup, escape, soft_unicode
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200576
577
Thomas Waldmann07a7d552013-05-18 00:47:41 +0200578from functools import partial