blob: 6d1c9580ac66f8aa18ac2d9c57ea66bd7ca66f74 [file] [log] [blame]
Armin Ronacher07bc6842008-03-31 14:18:49 +02001# -*- coding: utf-8 -*-
2"""
3 jinja2.utils
4 ~~~~~~~~~~~~
5
6 Utility functions.
7
8 :copyright: 2008 by Armin Ronacher.
9 :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
Christoph Hack80909862008-04-14 01:35:10 +020013import string
Armin Ronacher000b4912008-05-01 18:40:15 +020014try:
15 from thread import allocate_lock
16except ImportError:
17 from dummy_thread import allocate_lock
Armin Ronacher76c280b2008-05-04 12:31:48 +020018from htmlentitydefs import name2codepoint
Armin Ronacher814f6c22008-04-17 15:52:23 +020019from collections import deque
20from copy import deepcopy
Armin Ronacher18c6ca02008-04-17 10:03:29 +020021from itertools import imap
Armin Ronacher8edbe492008-04-10 20:43:43 +020022
23
Armin Ronacherbe4ae242008-04-18 09:49:08 +020024_word_split_re = re.compile(r'(\s+)')
25_punctuation_re = re.compile(
26 '^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$' % (
27 '|'.join(imap(re.escape, ('(', '<', '&lt;'))),
28 '|'.join(imap(re.escape, ('.', ',', ')', '>', '\n', '&gt;')))
29 )
30)
31_simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$')
Armin Ronacher76c280b2008-05-04 12:31:48 +020032_striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
33_entity_re = re.compile(r'&([^;]+);')
34_entities = name2codepoint.copy()
35_entities['apos'] = 39
Armin Ronacherbe4ae242008-04-18 09:49:08 +020036
Armin Ronacher7259c762008-04-30 13:03:59 +020037# special singleton representing missing values for the runtime
38missing = type('MissingType', (), {'__repr__': lambda x: 'missing'})()
39
40
Armin Ronacher7ceced52008-05-03 10:15:31 +020041# concatenate a list of strings and convert them to unicode.
42# unfortunately there is a bug in python 2.4 and lower that causes
43# unicode.join trash the traceback.
Armin Ronachercda43df2008-05-03 17:10:05 +020044_concat = u''.join
Armin Ronacher7ceced52008-05-03 10:15:31 +020045try:
46 def _test_gen_bug():
47 raise TypeError(_test_gen_bug)
48 yield None
Armin Ronachercda43df2008-05-03 17:10:05 +020049 _concat(_test_gen_bug())
Armin Ronacher7ceced52008-05-03 10:15:31 +020050except TypeError, _error:
Armin Ronachercda43df2008-05-03 17:10:05 +020051 if not _error.args or _error.args[0] is not _test_gen_bug:
Armin Ronacher7ceced52008-05-03 10:15:31 +020052 def concat(gen):
53 try:
Armin Ronachercda43df2008-05-03 17:10:05 +020054 return _concat(list(gen))
Armin Ronacher7ceced52008-05-03 10:15:31 +020055 except:
56 # this hack is needed so that the current frame
57 # does not show up in the traceback.
58 exc_type, exc_value, tb = sys.exc_info()
59 raise exc_type, exc_value, tb.tb_next
Armin Ronachercda43df2008-05-03 17:10:05 +020060 else:
61 concat = _concat
Armin Ronacher7ceced52008-05-03 10:15:31 +020062 del _test_gen_bug, _error
63
64
Armin Ronacher4f7d2d52008-04-22 10:40:26 +020065def contextfunction(f):
Armin Ronacher9bb7e472008-05-28 11:26:59 +020066 """This decorator can be used to mark a function or method context callable.
67 A context callable is passed the active :class:`Context` as first argument when
68 called from the template. This is useful if a function wants to get access
69 to the context or functions provided on the context object. For example
70 a function that returns a sorted list of template variables the current
71 template exports could look like this::
72
73 @contextcallable
74 def get_exported_names(context):
75 return sorted(context.exported_vars)
Armin Ronacher4f7d2d52008-04-22 10:40:26 +020076 """
77 f.contextfunction = True
78 return f
79
80
Armin Ronacher203bfcb2008-04-24 21:54:44 +020081def environmentfunction(f):
Armin Ronacher9bb7e472008-05-28 11:26:59 +020082 """This decorator can be used to mark a function or method as environment
83 callable. This decorator works exactly like the :func:`contextfunction`
84 decorator just that the first argument is the active :class:`Environment`
85 and not context.
Armin Ronacher203bfcb2008-04-24 21:54:44 +020086 """
87 f.environmentfunction = True
88 return f
89
90
Armin Ronacher9bb7e472008-05-28 11:26:59 +020091def is_undefined(obj):
92 """Check if the object passed is undefined. This does nothing more than
93 performing an instance check against :class:`Undefined` but looks nicer.
94 This can be used for custom filters or tests that want to react to
95 undefined variables. For example a custom default filter can look like
96 this::
97
98 def default(var, default=''):
99 if is_undefined(var):
100 return default
101 return var
102 """
103 from jinja2.runtime import Undefined
104 return isinstance(obj, Undefined)
105
106
Armin Ronacher187bde12008-05-01 18:19:16 +0200107def clear_caches():
108 """Jinja2 keeps internal caches for environments and lexers. These are
109 used so that Jinja2 doesn't have to recreate environments and lexers all
110 the time. Normally you don't have to care about that but if you are
111 messuring memory consumption you may want to clean the caches.
112 """
113 from jinja2.environment import _spontaneous_environments
114 from jinja2.lexer import _lexer_cache
115 _spontaneous_environments.clear()
116 _lexer_cache.clear()
117
118
Armin Ronacherf59bac22008-04-20 13:11:43 +0200119def import_string(import_name, silent=False):
120 """Imports an object based on a string. This use useful if you want to
121 use import paths as endpoints or something similar. An import path can
122 be specified either in dotted notation (``xml.sax.saxutils.escape``)
123 or with a colon as object delimiter (``xml.sax.saxutils:escape``).
124
125 If the `silent` is True the return value will be `None` if the import
126 fails.
127
128 :return: imported object
Armin Ronacher9a027f42008-04-17 11:13:40 +0200129 """
Armin Ronacherf59bac22008-04-20 13:11:43 +0200130 try:
131 if ':' in import_name:
132 module, obj = import_name.split(':', 1)
133 elif '.' in import_name:
134 items = import_name.split('.')
135 module = '.'.join(items[:-1])
136 obj = items[-1]
137 else:
138 return __import__(import_name)
139 return getattr(__import__(module, None, None, [obj]), obj)
140 except (ImportError, AttributeError):
141 if not silent:
142 raise
Armin Ronacher9a027f42008-04-17 11:13:40 +0200143
144
Christoph Hacke9e43bb2008-04-13 23:35:48 +0200145def pformat(obj, verbose=False):
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200146 """Prettyprint an object. Either use the `pretty` library or the
Christoph Hacke9e43bb2008-04-13 23:35:48 +0200147 builtin `pprint`.
148 """
149 try:
150 from pretty import pretty
151 return pretty(obj, verbose=verbose)
152 except ImportError:
153 from pprint import pformat
154 return pformat(obj)
Christoph Hack80909862008-04-14 01:35:10 +0200155
156
Christoph Hack80909862008-04-14 01:35:10 +0200157def urlize(text, trim_url_limit=None, nofollow=False):
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200158 """Converts any URLs in text into clickable links. Works on http://,
Christoph Hack80909862008-04-14 01:35:10 +0200159 https:// and www. links. Links can have trailing punctuation (periods,
160 commas, close-parens) and leading punctuation (opening parens) and
161 it'll still do the right thing.
162
163 If trim_url_limit is not None, the URLs in link text will be limited
164 to trim_url_limit characters.
165
166 If nofollow is True, the URLs in link text will get a rel="nofollow"
167 attribute.
168 """
169 trim_url = lambda x, limit=trim_url_limit: limit is not None \
170 and (x[:limit] + (len(x) >=limit and '...'
171 or '')) or x
172 words = _word_split_re.split(text)
173 nofollow_attr = nofollow and ' rel="nofollow"' or ''
174 for i, word in enumerate(words):
175 match = _punctuation_re.match(word)
176 if match:
177 lead, middle, trail = match.groups()
178 if middle.startswith('www.') or (
179 '@' not in middle and
180 not middle.startswith('http://') and
181 len(middle) > 0 and
182 middle[0] in string.letters + string.digits and (
183 middle.endswith('.org') or
184 middle.endswith('.net') or
185 middle.endswith('.com')
186 )):
187 middle = '<a href="http://%s"%s>%s</a>' % (middle,
188 nofollow_attr, trim_url(middle))
189 if middle.startswith('http://') or \
190 middle.startswith('https://'):
191 middle = '<a href="%s"%s>%s</a>' % (middle,
192 nofollow_attr, trim_url(middle))
193 if '@' in middle and not middle.startswith('www.') and \
194 not ':' in middle and _simple_email_re.match(middle):
195 middle = '<a href="mailto:%s">%s</a>' % (middle, middle)
196 if lead + middle + trail != word:
197 words[i] = lead + middle + trail
198 return u''.join(words)
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200199
200
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200201def generate_lorem_ipsum(n=5, html=True, min=20, max=100):
202 """Generate some lorem impsum for the template."""
203 from jinja2.constants import LOREM_IPSUM_WORDS
204 from random import choice, random, randrange
205 words = LOREM_IPSUM_WORDS.split()
206 result = []
207
208 for _ in xrange(n):
209 next_capitalized = True
210 last_comma = last_fullstop = 0
211 word = None
212 last = None
213 p = []
214
215 # each paragraph contains out of 20 to 100 words.
216 for idx, _ in enumerate(xrange(randrange(min, max))):
217 while True:
218 word = choice(words)
219 if word != last:
220 last = word
221 break
222 if next_capitalized:
223 word = word.capitalize()
224 next_capitalized = False
225 # add commas
226 if idx - randrange(3, 8) > last_comma:
227 last_comma = idx
228 last_fullstop += 2
229 word += ','
230 # add end of sentences
231 if idx - randrange(10, 20) > last_fullstop:
232 last_comma = last_fullstop = idx
233 word += '.'
234 next_capitalized = True
235 p.append(word)
236
237 # ensure that the paragraph ends with a dot.
238 p = u' '.join(p)
239 if p.endswith(','):
240 p = p[:-1] + '.'
241 elif not p.endswith('.'):
242 p += '.'
243 result.append(p)
244
245 if not html:
246 return u'\n\n'.join(result)
247 return Markup(u'\n'.join(u'<p>%s</p>' % escape(x) for x in result))
248
249
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200250class Markup(unicode):
251 """Marks a string as being safe for inclusion in HTML/XML output without
252 needing to be escaped. This implements the `__html__` interface a couple
253 of frameworks and web applications use.
254
255 The `escape` function returns markup objects so that double escaping can't
256 happen. If you want to use autoescaping in Jinja just set the finalizer
257 of the environment to `escape`.
258 """
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200259 __slots__ = ()
260
Armin Ronacher4e6f9a22008-05-23 23:57:38 +0200261 def __new__(cls, base=u''):
262 if hasattr(base, '__html__'):
263 base = base.__html__()
264 return unicode.__new__(cls, base)
265
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200266 def __html__(self):
267 return self
268
269 def __add__(self, other):
270 if hasattr(other, '__html__') or isinstance(other, basestring):
271 return self.__class__(unicode(self) + unicode(escape(other)))
272 return NotImplemented
273
274 def __radd__(self, other):
275 if hasattr(other, '__html__') or isinstance(other, basestring):
276 return self.__class__(unicode(escape(other)) + unicode(self))
277 return NotImplemented
278
279 def __mul__(self, num):
Armin Ronacherd71fff02008-05-26 23:57:07 +0200280 if isinstance(num, (int, long)):
281 return self.__class__(unicode.__mul__(self, num))
282 return NotImplemented
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200283 __rmul__ = __mul__
284
285 def __mod__(self, arg):
286 if isinstance(arg, tuple):
287 arg = tuple(imap(_MarkupEscapeHelper, arg))
288 else:
289 arg = _MarkupEscapeHelper(arg)
290 return self.__class__(unicode.__mod__(self, arg))
291
292 def __repr__(self):
293 return '%s(%s)' % (
294 self.__class__.__name__,
295 unicode.__repr__(self)
296 )
297
298 def join(self, seq):
299 return self.__class__(unicode.join(self, imap(escape, seq)))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200300 join.__doc__ = unicode.join.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200301
302 def split(self, *args, **kwargs):
303 return map(self.__class__, unicode.split(self, *args, **kwargs))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200304 split.__doc__ = unicode.split.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200305
306 def rsplit(self, *args, **kwargs):
307 return map(self.__class__, unicode.rsplit(self, *args, **kwargs))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200308 rsplit.__doc__ = unicode.rsplit.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200309
310 def splitlines(self, *args, **kwargs):
311 return map(self.__class__, unicode.splitlines(self, *args, **kwargs))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200312 splitlines.__doc__ = unicode.splitlines.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200313
Armin Ronacher76c280b2008-05-04 12:31:48 +0200314 def unescape(self):
315 """Unescape markup."""
316 def handle_match(m):
317 name = m.group(1)
318 if name in _entities:
319 return unichr(_entities[name])
320 try:
321 if name[:2] in ('#x', '#X'):
322 return unichr(int(name[2:], 16))
323 elif name.startswith('#'):
324 return unichr(int(name[1:]))
325 except ValueError:
326 pass
327 return u''
328 return _entity_re.sub(handle_match, unicode(self))
329
330 def striptags(self):
331 """Strip tags and resolve enities."""
332 stripped = u' '.join(_striptags_re.sub('', self).split())
333 return Markup(stripped).unescape()
334
Armin Ronacherf35e2812008-05-06 16:04:10 +0200335 @classmethod
336 def escape(cls, s):
337 """Escape the string. Works like :func:`escape`."""
338 rv = escape(s)
339 if rv.__class__ is not cls:
340 return cls(rv)
341 return rv
342
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200343 def make_wrapper(name):
344 orig = getattr(unicode, name)
345 def func(self, *args, **kwargs):
Armin Ronacherd71fff02008-05-26 23:57:07 +0200346 args = _escape_argspec(list(args), enumerate(args))
347 _escape_argspec(kwargs, kwargs.iteritems())
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200348 return self.__class__(orig(self, *args, **kwargs))
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200349 func.__name__ = orig.__name__
350 func.__doc__ = orig.__doc__
351 return func
Armin Ronacherd71fff02008-05-26 23:57:07 +0200352
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200353 for method in '__getitem__', '__getslice__', 'capitalize', \
354 'title', 'lower', 'upper', 'replace', 'ljust', \
Armin Ronacher709f6e52008-04-28 18:18:16 +0200355 'rjust', 'lstrip', 'rstrip', 'center', 'strip', \
Armin Ronacher316157d2008-04-28 18:30:27 +0200356 'translate', 'expandtabs', 'swapcase', 'zfill':
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200357 locals()[method] = make_wrapper(method)
Armin Ronacher709f6e52008-04-28 18:18:16 +0200358
359 # new in python 2.5
360 if hasattr(unicode, 'partition'):
Armin Ronacherd71fff02008-05-26 23:57:07 +0200361 partition = make_wrapper('partition'),
362 rpartition = make_wrapper('rpartition')
363
364 # new in python 2.6
365 if hasattr(unicode, 'format'):
366 format = make_wrapper('format')
367
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200368 del method, make_wrapper
369
370
Armin Ronacherd71fff02008-05-26 23:57:07 +0200371def _escape_argspec(obj, iterable):
372 """Helper for various string-wrapped functions."""
373 for key, value in iterable:
374 if hasattr(value, '__html__') or isinstance(value, basestring):
375 obj[key] = escape(value)
376 return obj
377
378
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200379class _MarkupEscapeHelper(object):
380 """Helper for Markup.__mod__"""
381
382 def __init__(self, obj):
383 self.obj = obj
384
385 __getitem__ = lambda s, x: _MarkupEscapeHelper(s.obj[x])
386 __unicode__ = lambda s: unicode(escape(s.obj))
387 __str__ = lambda s: str(escape(s.obj))
388 __repr__ = lambda s: str(repr(escape(s.obj)))
389 __int__ = lambda s: int(s.obj)
390 __float__ = lambda s: float(s.obj)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200391
392
393class LRUCache(object):
394 """A simple LRU Cache implementation."""
395 # this is fast for small capacities (something around 200) but doesn't
396 # scale. But as long as it's only used for the database connections in
397 # a non request fallback it's fine.
398
399 def __init__(self, capacity):
400 self.capacity = capacity
401 self._mapping = {}
402 self._queue = deque()
Armin Ronacher7962ce72008-05-20 17:52:52 +0200403 self._postinit()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200404
Armin Ronacher7962ce72008-05-20 17:52:52 +0200405 def _postinit(self):
Armin Ronacher814f6c22008-04-17 15:52:23 +0200406 # alias all queue methods for faster lookup
407 self._popleft = self._queue.popleft
408 self._pop = self._queue.pop
409 if hasattr(self._queue, 'remove'):
410 self._remove = self._queue.remove
Armin Ronacher000b4912008-05-01 18:40:15 +0200411 self._wlock = allocate_lock()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200412 self._append = self._queue.append
413
414 def _remove(self, obj):
415 """Python 2.4 compatibility."""
416 for idx, item in enumerate(self._queue):
417 if item == obj:
418 del self._queue[idx]
419 break
420
Armin Ronacher7962ce72008-05-20 17:52:52 +0200421 def __getstate__(self):
422 return {
423 'capacity': self.capacity,
424 '_mapping': self._mapping,
425 '_queue': self._queue
426 }
427
428 def __setstate__(self, d):
429 self.__dict__.update(d)
430 self._postinit()
431
432 def __getnewargs__(self):
433 return (self.capacity,)
434
Armin Ronacher814f6c22008-04-17 15:52:23 +0200435 def copy(self):
436 """Return an shallow copy of the instance."""
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200437 rv = self.__class__(self.capacity)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200438 rv._mapping.update(self._mapping)
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200439 rv._queue = deque(self._queue)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200440 return rv
441
442 def get(self, key, default=None):
443 """Return an item from the cache dict or `default`"""
Armin Ronacher000b4912008-05-01 18:40:15 +0200444 try:
Armin Ronacher814f6c22008-04-17 15:52:23 +0200445 return self[key]
Armin Ronacher000b4912008-05-01 18:40:15 +0200446 except KeyError:
447 return default
Armin Ronacher814f6c22008-04-17 15:52:23 +0200448
449 def setdefault(self, key, default=None):
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200450 """Set `default` if the key is not in the cache otherwise
Armin Ronacher814f6c22008-04-17 15:52:23 +0200451 leave unchanged. Return the value of this key.
452 """
Armin Ronacher000b4912008-05-01 18:40:15 +0200453 try:
Armin Ronacher814f6c22008-04-17 15:52:23 +0200454 return self[key]
Armin Ronacher000b4912008-05-01 18:40:15 +0200455 except KeyError:
456 self[key] = default
457 return default
Armin Ronacher814f6c22008-04-17 15:52:23 +0200458
459 def clear(self):
460 """Clear the cache."""
Armin Ronacher000b4912008-05-01 18:40:15 +0200461 self._wlock.acquire()
462 try:
463 self._mapping.clear()
464 self._queue.clear()
465 finally:
466 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200467
468 def __contains__(self, key):
469 """Check if a key exists in this cache."""
470 return key in self._mapping
471
472 def __len__(self):
473 """Return the current size of the cache."""
474 return len(self._mapping)
475
476 def __repr__(self):
477 return '<%s %r>' % (
478 self.__class__.__name__,
479 self._mapping
480 )
481
482 def __getitem__(self, key):
483 """Get an item from the cache. Moves the item up so that it has the
484 highest priority then.
485
486 Raise an `KeyError` if it does not exist.
487 """
488 rv = self._mapping[key]
489 if self._queue[-1] != key:
490 self._remove(key)
491 self._append(key)
492 return rv
493
494 def __setitem__(self, key, value):
495 """Sets the value for an item. Moves the item up so that it
496 has the highest priority then.
497 """
Armin Ronacher000b4912008-05-01 18:40:15 +0200498 self._wlock.acquire()
499 try:
500 if key in self._mapping:
501 self._remove(key)
502 elif len(self._mapping) == self.capacity:
503 del self._mapping[self._popleft()]
504 self._append(key)
505 self._mapping[key] = value
506 finally:
507 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200508
509 def __delitem__(self, key):
510 """Remove an item from the cache dict.
511 Raise an `KeyError` if it does not exist.
512 """
Armin Ronacher000b4912008-05-01 18:40:15 +0200513 self._wlock.acquire()
514 try:
515 del self._mapping[key]
516 self._remove(key)
517 finally:
518 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200519
Armin Ronachere25f24d2008-05-19 11:20:41 +0200520 def items(self):
521 """Return a list of items."""
522 result = [(key, self._mapping[key]) for key in list(self._queue)]
523 result.reverse()
524 return result
525
526 def iteritems(self):
527 """Iterate over all items."""
528 return iter(self.items())
529
530 def values(self):
531 """Return a list of all values."""
532 return [x[1] for x in self.items()]
533
534 def itervalue(self):
535 """Iterate over all values."""
536 return iter(self.values())
537
538 def keys(self):
539 """Return a list of all keys ordered by most recent usage."""
540 return list(self)
541
542 def iterkeys(self):
543 """Iterate over all keys in the cache dict, ordered by
Armin Ronacher814f6c22008-04-17 15:52:23 +0200544 the most recent usage.
545 """
Armin Ronachere2244882008-05-19 09:25:57 +0200546 return reversed(tuple(self._queue))
Armin Ronacher814f6c22008-04-17 15:52:23 +0200547
Armin Ronachere25f24d2008-05-19 11:20:41 +0200548 __iter__ = iterkeys
549
Armin Ronacher814f6c22008-04-17 15:52:23 +0200550 def __reversed__(self):
551 """Iterate over the values in the cache dict, oldest items
552 coming first.
553 """
Armin Ronachere2244882008-05-19 09:25:57 +0200554 return iter(tuple(self._queue))
Armin Ronacher814f6c22008-04-17 15:52:23 +0200555
556 __copy__ = copy
557
Armin Ronacherbd33f112008-04-18 09:17:32 +0200558
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200559# register the LRU cache as mutable mapping if possible
560try:
561 from collections import MutableMapping
562 MutableMapping.register(LRUCache)
563except ImportError:
564 pass
565
566
Armin Ronacherbd33f112008-04-18 09:17:32 +0200567# we have to import it down here as the speedups module imports the
568# markup type which is define above.
569try:
Armin Ronacherf59bac22008-04-20 13:11:43 +0200570 from jinja2._speedups import escape, soft_unicode
Armin Ronacherbd33f112008-04-18 09:17:32 +0200571except ImportError:
Lukas Meuserad48a2e2008-05-01 18:19:57 +0200572 def escape(s):
Armin Ronacherf35e2812008-05-06 16:04:10 +0200573 """Convert the characters &, <, >, ' and " in string s to HTML-safe
574 sequences. Use this if you need to display text that might contain
Armin Ronacher9a1e33c2008-05-05 22:00:46 +0200575 such characters in HTML. Marks return value as markup string.
Armin Ronacherbd33f112008-04-18 09:17:32 +0200576 """
Lukas Meuserad48a2e2008-05-01 18:19:57 +0200577 if hasattr(s, '__html__'):
578 return s.__html__()
579 return Markup(unicode(s)
Armin Ronacherbd33f112008-04-18 09:17:32 +0200580 .replace('&', '&amp;')
581 .replace('>', '&gt;')
582 .replace('<', '&lt;')
Armin Ronacherf35e2812008-05-06 16:04:10 +0200583 .replace("'", '&#39;')
Armin Ronacher9d42abf2008-05-14 18:10:41 +0200584 .replace('"', '&#34;')
Armin Ronacherbd33f112008-04-18 09:17:32 +0200585 )
Armin Ronacherf59bac22008-04-20 13:11:43 +0200586
587 def soft_unicode(s):
588 """Make a string unicode if it isn't already. That way a markup
589 string is not converted back to unicode.
590 """
591 if not isinstance(s, unicode):
592 s = unicode(s)
593 return s
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200594
595
596# partials
597try:
598 from functools import partial
599except ImportError:
600 class partial(object):
601 def __init__(self, _func, *args, **kwargs):
Benjamin Wiegand228c1832008-04-28 18:09:27 +0200602 self._func = _func
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200603 self._args = args
604 self._kwargs = kwargs
605 def __call__(self, *args, **kwargs):
606 kwargs.update(self._kwargs)
607 return self._func(*(self._args + args), **kwargs)