blob: f402704661bf8ff9e3d95e00babe695767a88895 [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
Armin Ronacher58f351d2008-05-28 21:30:14 +020073 @contextfunction
Armin Ronacher9bb7e472008-05-28 11:26:59 +020074 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):
Armin Ronacher58f351d2008-05-28 21:30:14 +0200251 r"""Marks a string as being safe for inclusion in HTML/XML output without
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200252 needing to be escaped. This implements the `__html__` interface a couple
Armin Ronacher58f351d2008-05-28 21:30:14 +0200253 of frameworks and web applications use. :class:`Markup` is a direct
254 subclass of `unicode` and provides all the methods of `unicode` just that
255 it escapes arguments passed and always returns `Markup`.
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200256
257 The `escape` function returns markup objects so that double escaping can't
258 happen. If you want to use autoescaping in Jinja just set the finalizer
259 of the environment to `escape`.
Armin Ronacher58f351d2008-05-28 21:30:14 +0200260
261 The constructor of the :class:`Markup` class can be used for three
262 different things: When passed an unicode object it's assumed to be safe,
263 when passed an object with an HTML representation (has an `__html__`
264 method) that representation is used, otherwise the object passed is
265 converted into a unicode string and then assumed to be safe:
266
267 >>> Markup("Hello <em>World</em>!")
268 Markup(u'Hello <em>World</em>!')
269 >>> class Foo(object):
270 ... def __html__(self):
271 ... return '<a href="#">foo</a>'
272 ...
273 >>> Markup(Foo())
274 Markup(u'<a href="#">foo</a>')
275
276 If you want object passed being always treated as unsafe you can use the
277 :meth:`escape` classmethod to create a :class:`Markup` object:
278
279 >>> Markup.escape("Hello <em>World</em>!")
280 Markup(u'Hello &lt;em&gt;World&lt;/em&gt;!')
281
282 Operations on a markup string are markup aware which means that all
283 arguments are passed through the :func:`escape` function:
284
285 >>> em = Markup("<em>%s</em>")
286 >>> em % "foo & bar"
287 Markup(u'<em>foo &amp; bar</em>')
288 >>> strong = Markup("<strong>%(text)s</strong>")
289 >>> strong % {'text': '<blink>hacker here</blink>'}
290 Markup(u'<strong>&lt;blink&gt;hacker here&lt;/blink&gt;</strong>')
291 >>> Markup("<em>Hello</em> ") + "<foo>"
292 Markup(u'<em>Hello</em> &lt;foo&gt;')
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200293 """
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200294 __slots__ = ()
295
Armin Ronacher4e6f9a22008-05-23 23:57:38 +0200296 def __new__(cls, base=u''):
297 if hasattr(base, '__html__'):
298 base = base.__html__()
299 return unicode.__new__(cls, base)
300
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200301 def __html__(self):
302 return self
303
304 def __add__(self, other):
305 if hasattr(other, '__html__') or isinstance(other, basestring):
306 return self.__class__(unicode(self) + unicode(escape(other)))
307 return NotImplemented
308
309 def __radd__(self, other):
310 if hasattr(other, '__html__') or isinstance(other, basestring):
311 return self.__class__(unicode(escape(other)) + unicode(self))
312 return NotImplemented
313
314 def __mul__(self, num):
Armin Ronacherd71fff02008-05-26 23:57:07 +0200315 if isinstance(num, (int, long)):
316 return self.__class__(unicode.__mul__(self, num))
317 return NotImplemented
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200318 __rmul__ = __mul__
319
320 def __mod__(self, arg):
321 if isinstance(arg, tuple):
322 arg = tuple(imap(_MarkupEscapeHelper, arg))
323 else:
324 arg = _MarkupEscapeHelper(arg)
325 return self.__class__(unicode.__mod__(self, arg))
326
327 def __repr__(self):
328 return '%s(%s)' % (
329 self.__class__.__name__,
330 unicode.__repr__(self)
331 )
332
333 def join(self, seq):
334 return self.__class__(unicode.join(self, imap(escape, seq)))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200335 join.__doc__ = unicode.join.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200336
337 def split(self, *args, **kwargs):
338 return map(self.__class__, unicode.split(self, *args, **kwargs))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200339 split.__doc__ = unicode.split.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200340
341 def rsplit(self, *args, **kwargs):
342 return map(self.__class__, unicode.rsplit(self, *args, **kwargs))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200343 rsplit.__doc__ = unicode.rsplit.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200344
345 def splitlines(self, *args, **kwargs):
346 return map(self.__class__, unicode.splitlines(self, *args, **kwargs))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200347 splitlines.__doc__ = unicode.splitlines.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200348
Armin Ronacher76c280b2008-05-04 12:31:48 +0200349 def unescape(self):
Armin Ronacher58f351d2008-05-28 21:30:14 +0200350 r"""Unescape markup again into an unicode string. This also resolves
351 known HTML4 and XHTML entities:
352
353 >>> Markup("Main &raquo; <em>About</em>").unescape()
354 u'Main \xbb <em>About</em>'
355 """
Armin Ronacher76c280b2008-05-04 12:31:48 +0200356 def handle_match(m):
357 name = m.group(1)
358 if name in _entities:
359 return unichr(_entities[name])
360 try:
361 if name[:2] in ('#x', '#X'):
362 return unichr(int(name[2:], 16))
363 elif name.startswith('#'):
364 return unichr(int(name[1:]))
365 except ValueError:
366 pass
367 return u''
368 return _entity_re.sub(handle_match, unicode(self))
369
370 def striptags(self):
Armin Ronacher58f351d2008-05-28 21:30:14 +0200371 r"""Unescape markup into an unicode string and strip all tags. This
372 also resolves known HTML4 and XHTML entities. Whitespace is
373 normalized to one:
374
375 >>> Markup("Main &raquo; <em>About</em>").striptags()
376 u'Main \xbb About'
377 """
Armin Ronacher76c280b2008-05-04 12:31:48 +0200378 stripped = u' '.join(_striptags_re.sub('', self).split())
379 return Markup(stripped).unescape()
380
Armin Ronacherf35e2812008-05-06 16:04:10 +0200381 @classmethod
382 def escape(cls, s):
Armin Ronacher58f351d2008-05-28 21:30:14 +0200383 """Escape the string. Works like :func:`escape` with the difference
384 that for subclasses of :class:`Markup` this function would return the
385 correct subclass.
386 """
Armin Ronacherf35e2812008-05-06 16:04:10 +0200387 rv = escape(s)
388 if rv.__class__ is not cls:
389 return cls(rv)
390 return rv
391
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200392 def make_wrapper(name):
393 orig = getattr(unicode, name)
394 def func(self, *args, **kwargs):
Armin Ronacherd71fff02008-05-26 23:57:07 +0200395 args = _escape_argspec(list(args), enumerate(args))
396 _escape_argspec(kwargs, kwargs.iteritems())
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200397 return self.__class__(orig(self, *args, **kwargs))
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200398 func.__name__ = orig.__name__
399 func.__doc__ = orig.__doc__
400 return func
Armin Ronacherd71fff02008-05-26 23:57:07 +0200401
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200402 for method in '__getitem__', '__getslice__', 'capitalize', \
403 'title', 'lower', 'upper', 'replace', 'ljust', \
Armin Ronacher709f6e52008-04-28 18:18:16 +0200404 'rjust', 'lstrip', 'rstrip', 'center', 'strip', \
Armin Ronacher316157d2008-04-28 18:30:27 +0200405 'translate', 'expandtabs', 'swapcase', 'zfill':
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200406 locals()[method] = make_wrapper(method)
Armin Ronacher709f6e52008-04-28 18:18:16 +0200407
408 # new in python 2.5
409 if hasattr(unicode, 'partition'):
Armin Ronacherd71fff02008-05-26 23:57:07 +0200410 partition = make_wrapper('partition'),
411 rpartition = make_wrapper('rpartition')
412
413 # new in python 2.6
414 if hasattr(unicode, 'format'):
415 format = make_wrapper('format')
416
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200417 del method, make_wrapper
418
419
Armin Ronacherd71fff02008-05-26 23:57:07 +0200420def _escape_argspec(obj, iterable):
421 """Helper for various string-wrapped functions."""
422 for key, value in iterable:
423 if hasattr(value, '__html__') or isinstance(value, basestring):
424 obj[key] = escape(value)
425 return obj
426
427
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200428class _MarkupEscapeHelper(object):
429 """Helper for Markup.__mod__"""
430
431 def __init__(self, obj):
432 self.obj = obj
433
434 __getitem__ = lambda s, x: _MarkupEscapeHelper(s.obj[x])
435 __unicode__ = lambda s: unicode(escape(s.obj))
436 __str__ = lambda s: str(escape(s.obj))
437 __repr__ = lambda s: str(repr(escape(s.obj)))
438 __int__ = lambda s: int(s.obj)
439 __float__ = lambda s: float(s.obj)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200440
441
442class LRUCache(object):
443 """A simple LRU Cache implementation."""
Armin Ronacher58f351d2008-05-28 21:30:14 +0200444
445 # this is fast for small capacities (something below 1000) but doesn't
446 # scale. But as long as it's only used as storage for templates this
447 # won't do any harm.
Armin Ronacher814f6c22008-04-17 15:52:23 +0200448
449 def __init__(self, capacity):
450 self.capacity = capacity
451 self._mapping = {}
452 self._queue = deque()
Armin Ronacher7962ce72008-05-20 17:52:52 +0200453 self._postinit()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200454
Armin Ronacher7962ce72008-05-20 17:52:52 +0200455 def _postinit(self):
Armin Ronacher814f6c22008-04-17 15:52:23 +0200456 # alias all queue methods for faster lookup
457 self._popleft = self._queue.popleft
458 self._pop = self._queue.pop
459 if hasattr(self._queue, 'remove'):
460 self._remove = self._queue.remove
Armin Ronacher000b4912008-05-01 18:40:15 +0200461 self._wlock = allocate_lock()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200462 self._append = self._queue.append
463
464 def _remove(self, obj):
465 """Python 2.4 compatibility."""
466 for idx, item in enumerate(self._queue):
467 if item == obj:
468 del self._queue[idx]
469 break
470
Armin Ronacher7962ce72008-05-20 17:52:52 +0200471 def __getstate__(self):
472 return {
473 'capacity': self.capacity,
474 '_mapping': self._mapping,
475 '_queue': self._queue
476 }
477
478 def __setstate__(self, d):
479 self.__dict__.update(d)
480 self._postinit()
481
482 def __getnewargs__(self):
483 return (self.capacity,)
484
Armin Ronacher814f6c22008-04-17 15:52:23 +0200485 def copy(self):
486 """Return an shallow copy of the instance."""
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200487 rv = self.__class__(self.capacity)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200488 rv._mapping.update(self._mapping)
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200489 rv._queue = deque(self._queue)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200490 return rv
491
492 def get(self, key, default=None):
493 """Return an item from the cache dict or `default`"""
Armin Ronacher000b4912008-05-01 18:40:15 +0200494 try:
Armin Ronacher814f6c22008-04-17 15:52:23 +0200495 return self[key]
Armin Ronacher000b4912008-05-01 18:40:15 +0200496 except KeyError:
497 return default
Armin Ronacher814f6c22008-04-17 15:52:23 +0200498
499 def setdefault(self, key, default=None):
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200500 """Set `default` if the key is not in the cache otherwise
Armin Ronacher814f6c22008-04-17 15:52:23 +0200501 leave unchanged. Return the value of this key.
502 """
Armin Ronacher000b4912008-05-01 18:40:15 +0200503 try:
Armin Ronacher814f6c22008-04-17 15:52:23 +0200504 return self[key]
Armin Ronacher000b4912008-05-01 18:40:15 +0200505 except KeyError:
506 self[key] = default
507 return default
Armin Ronacher814f6c22008-04-17 15:52:23 +0200508
509 def clear(self):
510 """Clear the cache."""
Armin Ronacher000b4912008-05-01 18:40:15 +0200511 self._wlock.acquire()
512 try:
513 self._mapping.clear()
514 self._queue.clear()
515 finally:
516 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200517
518 def __contains__(self, key):
519 """Check if a key exists in this cache."""
520 return key in self._mapping
521
522 def __len__(self):
523 """Return the current size of the cache."""
524 return len(self._mapping)
525
526 def __repr__(self):
527 return '<%s %r>' % (
528 self.__class__.__name__,
529 self._mapping
530 )
531
532 def __getitem__(self, key):
533 """Get an item from the cache. Moves the item up so that it has the
534 highest priority then.
535
536 Raise an `KeyError` if it does not exist.
537 """
538 rv = self._mapping[key]
539 if self._queue[-1] != key:
540 self._remove(key)
541 self._append(key)
542 return rv
543
544 def __setitem__(self, key, value):
545 """Sets the value for an item. Moves the item up so that it
546 has the highest priority then.
547 """
Armin Ronacher000b4912008-05-01 18:40:15 +0200548 self._wlock.acquire()
549 try:
550 if key in self._mapping:
551 self._remove(key)
552 elif len(self._mapping) == self.capacity:
553 del self._mapping[self._popleft()]
554 self._append(key)
555 self._mapping[key] = value
556 finally:
557 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200558
559 def __delitem__(self, key):
560 """Remove an item from the cache dict.
561 Raise an `KeyError` if it does not exist.
562 """
Armin Ronacher000b4912008-05-01 18:40:15 +0200563 self._wlock.acquire()
564 try:
565 del self._mapping[key]
566 self._remove(key)
567 finally:
568 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200569
Armin Ronachere25f24d2008-05-19 11:20:41 +0200570 def items(self):
571 """Return a list of items."""
572 result = [(key, self._mapping[key]) for key in list(self._queue)]
573 result.reverse()
574 return result
575
576 def iteritems(self):
577 """Iterate over all items."""
578 return iter(self.items())
579
580 def values(self):
581 """Return a list of all values."""
582 return [x[1] for x in self.items()]
583
584 def itervalue(self):
585 """Iterate over all values."""
586 return iter(self.values())
587
588 def keys(self):
589 """Return a list of all keys ordered by most recent usage."""
590 return list(self)
591
592 def iterkeys(self):
593 """Iterate over all keys in the cache dict, ordered by
Armin Ronacher814f6c22008-04-17 15:52:23 +0200594 the most recent usage.
595 """
Armin Ronachere2244882008-05-19 09:25:57 +0200596 return reversed(tuple(self._queue))
Armin Ronacher814f6c22008-04-17 15:52:23 +0200597
Armin Ronachere25f24d2008-05-19 11:20:41 +0200598 __iter__ = iterkeys
599
Armin Ronacher814f6c22008-04-17 15:52:23 +0200600 def __reversed__(self):
601 """Iterate over the values in the cache dict, oldest items
602 coming first.
603 """
Armin Ronachere2244882008-05-19 09:25:57 +0200604 return iter(tuple(self._queue))
Armin Ronacher814f6c22008-04-17 15:52:23 +0200605
606 __copy__ = copy
607
Armin Ronacherbd33f112008-04-18 09:17:32 +0200608
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200609# register the LRU cache as mutable mapping if possible
610try:
611 from collections import MutableMapping
612 MutableMapping.register(LRUCache)
613except ImportError:
614 pass
615
616
Armin Ronacherbd33f112008-04-18 09:17:32 +0200617# we have to import it down here as the speedups module imports the
618# markup type which is define above.
619try:
Armin Ronacherf59bac22008-04-20 13:11:43 +0200620 from jinja2._speedups import escape, soft_unicode
Armin Ronacherbd33f112008-04-18 09:17:32 +0200621except ImportError:
Lukas Meuserad48a2e2008-05-01 18:19:57 +0200622 def escape(s):
Armin Ronacherf35e2812008-05-06 16:04:10 +0200623 """Convert the characters &, <, >, ' and " in string s to HTML-safe
624 sequences. Use this if you need to display text that might contain
Armin Ronacher9a1e33c2008-05-05 22:00:46 +0200625 such characters in HTML. Marks return value as markup string.
Armin Ronacherbd33f112008-04-18 09:17:32 +0200626 """
Lukas Meuserad48a2e2008-05-01 18:19:57 +0200627 if hasattr(s, '__html__'):
628 return s.__html__()
629 return Markup(unicode(s)
Armin Ronacherbd33f112008-04-18 09:17:32 +0200630 .replace('&', '&amp;')
631 .replace('>', '&gt;')
632 .replace('<', '&lt;')
Armin Ronacherf35e2812008-05-06 16:04:10 +0200633 .replace("'", '&#39;')
Armin Ronacher9d42abf2008-05-14 18:10:41 +0200634 .replace('"', '&#34;')
Armin Ronacherbd33f112008-04-18 09:17:32 +0200635 )
Armin Ronacherf59bac22008-04-20 13:11:43 +0200636
637 def soft_unicode(s):
638 """Make a string unicode if it isn't already. That way a markup
639 string is not converted back to unicode.
640 """
641 if not isinstance(s, unicode):
642 s = unicode(s)
643 return s
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200644
645
646# partials
647try:
648 from functools import partial
649except ImportError:
650 class partial(object):
651 def __init__(self, _func, *args, **kwargs):
Benjamin Wiegand228c1832008-04-28 18:09:27 +0200652 self._func = _func
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200653 self._args = args
654 self._kwargs = kwargs
655 def __call__(self, *args, **kwargs):
656 kwargs.update(self._kwargs)
657 return self._func(*(self._args + args), **kwargs)