blob: 1ed653668bce549de384808b13a8f2fb5aa8edec [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
Armin Ronacher000b4912008-05-01 18:40:15 +020013try:
14 from thread import allocate_lock
15except ImportError:
16 from dummy_thread import allocate_lock
Armin Ronacher814f6c22008-04-17 15:52:23 +020017from collections import deque
Armin Ronacher18c6ca02008-04-17 10:03:29 +020018from itertools import imap
Armin Ronacher8edbe492008-04-10 20:43:43 +020019
20
Armin Ronacherbe4ae242008-04-18 09:49:08 +020021_word_split_re = re.compile(r'(\s+)')
22_punctuation_re = re.compile(
23 '^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$' % (
24 '|'.join(imap(re.escape, ('(', '<', '&lt;'))),
25 '|'.join(imap(re.escape, ('.', ',', ')', '>', '\n', '&gt;')))
26 )
27)
28_simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$')
Armin Ronacher76c280b2008-05-04 12:31:48 +020029_striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
30_entity_re = re.compile(r'&([^;]+);')
Armin Ronacher9a0078d2008-08-13 18:24:17 +020031_letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
32_digits = '0123456789'
Armin Ronacherbe4ae242008-04-18 09:49:08 +020033
Armin Ronacher7259c762008-04-30 13:03:59 +020034# special singleton representing missing values for the runtime
35missing = type('MissingType', (), {'__repr__': lambda x: 'missing'})()
36
37
Armin Ronacher7ceced52008-05-03 10:15:31 +020038# concatenate a list of strings and convert them to unicode.
39# unfortunately there is a bug in python 2.4 and lower that causes
40# unicode.join trash the traceback.
Armin Ronachercda43df2008-05-03 17:10:05 +020041_concat = u''.join
Armin Ronacher7ceced52008-05-03 10:15:31 +020042try:
43 def _test_gen_bug():
44 raise TypeError(_test_gen_bug)
45 yield None
Armin Ronachercda43df2008-05-03 17:10:05 +020046 _concat(_test_gen_bug())
Armin Ronacher7ceced52008-05-03 10:15:31 +020047except TypeError, _error:
Armin Ronachercda43df2008-05-03 17:10:05 +020048 if not _error.args or _error.args[0] is not _test_gen_bug:
Armin Ronacher7ceced52008-05-03 10:15:31 +020049 def concat(gen):
50 try:
Armin Ronachercda43df2008-05-03 17:10:05 +020051 return _concat(list(gen))
Armin Ronacher7ceced52008-05-03 10:15:31 +020052 except:
53 # this hack is needed so that the current frame
54 # does not show up in the traceback.
55 exc_type, exc_value, tb = sys.exc_info()
56 raise exc_type, exc_value, tb.tb_next
Armin Ronachercda43df2008-05-03 17:10:05 +020057 else:
58 concat = _concat
Armin Ronacher7ceced52008-05-03 10:15:31 +020059 del _test_gen_bug, _error
60
61
Armin Ronacher9a0078d2008-08-13 18:24:17 +020062# ironpython without stdlib doesn't have keyword
63try:
64 from keyword import iskeyword as is_python_keyword
65except ImportError:
66 _py_identifier_re = re.compile(r'^[a-zA-Z_][a-zA-Z0-9]*$')
67 def is_python_keyword(name):
68 if _py_identifier_re.search(name) is None:
69 return False
70 try:
71 exec name + " = 42"
72 except SyntaxError:
73 return False
74 return True
75
76
77# common types. These do exist in the special types module too which however
78# does not exist in IronPython out of the box.
79class _C(object):
80 def method(self): pass
81def _func():
82 yield None
83FunctionType = type(_func)
84GeneratorType = type(_func())
85MethodType = type(_C.method)
86CodeType = type(_C.method.func_code)
87try:
88 raise TypeError()
89except TypeError:
90 _tb = sys.exc_info()[2]
91 TracebackType = type(_tb)
92 FrameType = type(_tb.tb_frame)
93del _C, _tb, _func
94
95
Armin Ronacher4f7d2d52008-04-22 10:40:26 +020096def contextfunction(f):
Armin Ronacher9bb7e472008-05-28 11:26:59 +020097 """This decorator can be used to mark a function or method context callable.
98 A context callable is passed the active :class:`Context` as first argument when
99 called from the template. This is useful if a function wants to get access
100 to the context or functions provided on the context object. For example
101 a function that returns a sorted list of template variables the current
102 template exports could look like this::
103
Armin Ronacher58f351d2008-05-28 21:30:14 +0200104 @contextfunction
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200105 def get_exported_names(context):
106 return sorted(context.exported_vars)
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200107 """
108 f.contextfunction = True
109 return f
110
111
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200112def environmentfunction(f):
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200113 """This decorator can be used to mark a function or method as environment
114 callable. This decorator works exactly like the :func:`contextfunction`
115 decorator just that the first argument is the active :class:`Environment`
116 and not context.
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200117 """
118 f.environmentfunction = True
119 return f
120
121
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200122def is_undefined(obj):
123 """Check if the object passed is undefined. This does nothing more than
124 performing an instance check against :class:`Undefined` but looks nicer.
125 This can be used for custom filters or tests that want to react to
126 undefined variables. For example a custom default filter can look like
127 this::
128
129 def default(var, default=''):
130 if is_undefined(var):
131 return default
132 return var
133 """
134 from jinja2.runtime import Undefined
135 return isinstance(obj, Undefined)
136
137
Armin Ronacher187bde12008-05-01 18:19:16 +0200138def clear_caches():
139 """Jinja2 keeps internal caches for environments and lexers. These are
140 used so that Jinja2 doesn't have to recreate environments and lexers all
141 the time. Normally you don't have to care about that but if you are
142 messuring memory consumption you may want to clean the caches.
143 """
144 from jinja2.environment import _spontaneous_environments
145 from jinja2.lexer import _lexer_cache
146 _spontaneous_environments.clear()
147 _lexer_cache.clear()
148
149
Armin Ronacherf59bac22008-04-20 13:11:43 +0200150def import_string(import_name, silent=False):
151 """Imports an object based on a string. This use useful if you want to
152 use import paths as endpoints or something similar. An import path can
153 be specified either in dotted notation (``xml.sax.saxutils.escape``)
154 or with a colon as object delimiter (``xml.sax.saxutils:escape``).
155
156 If the `silent` is True the return value will be `None` if the import
157 fails.
158
159 :return: imported object
Armin Ronacher9a027f42008-04-17 11:13:40 +0200160 """
Armin Ronacherf59bac22008-04-20 13:11:43 +0200161 try:
162 if ':' in import_name:
163 module, obj = import_name.split(':', 1)
164 elif '.' in import_name:
165 items = import_name.split('.')
166 module = '.'.join(items[:-1])
167 obj = items[-1]
168 else:
169 return __import__(import_name)
170 return getattr(__import__(module, None, None, [obj]), obj)
171 except (ImportError, AttributeError):
172 if not silent:
173 raise
Armin Ronacher9a027f42008-04-17 11:13:40 +0200174
175
Christoph Hacke9e43bb2008-04-13 23:35:48 +0200176def pformat(obj, verbose=False):
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200177 """Prettyprint an object. Either use the `pretty` library or the
Christoph Hacke9e43bb2008-04-13 23:35:48 +0200178 builtin `pprint`.
179 """
180 try:
181 from pretty import pretty
182 return pretty(obj, verbose=verbose)
183 except ImportError:
184 from pprint import pformat
185 return pformat(obj)
Christoph Hack80909862008-04-14 01:35:10 +0200186
187
Christoph Hack80909862008-04-14 01:35:10 +0200188def urlize(text, trim_url_limit=None, nofollow=False):
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200189 """Converts any URLs in text into clickable links. Works on http://,
Christoph Hack80909862008-04-14 01:35:10 +0200190 https:// and www. links. Links can have trailing punctuation (periods,
191 commas, close-parens) and leading punctuation (opening parens) and
192 it'll still do the right thing.
193
194 If trim_url_limit is not None, the URLs in link text will be limited
195 to trim_url_limit characters.
196
197 If nofollow is True, the URLs in link text will get a rel="nofollow"
198 attribute.
199 """
200 trim_url = lambda x, limit=trim_url_limit: limit is not None \
201 and (x[:limit] + (len(x) >=limit and '...'
202 or '')) or x
203 words = _word_split_re.split(text)
204 nofollow_attr = nofollow and ' rel="nofollow"' or ''
205 for i, word in enumerate(words):
206 match = _punctuation_re.match(word)
207 if match:
208 lead, middle, trail = match.groups()
209 if middle.startswith('www.') or (
210 '@' not in middle and
211 not middle.startswith('http://') and
212 len(middle) > 0 and
Armin Ronacher9a0078d2008-08-13 18:24:17 +0200213 middle[0] in _letters + _digits and (
Christoph Hack80909862008-04-14 01:35:10 +0200214 middle.endswith('.org') or
215 middle.endswith('.net') or
216 middle.endswith('.com')
217 )):
218 middle = '<a href="http://%s"%s>%s</a>' % (middle,
219 nofollow_attr, trim_url(middle))
220 if middle.startswith('http://') or \
221 middle.startswith('https://'):
222 middle = '<a href="%s"%s>%s</a>' % (middle,
223 nofollow_attr, trim_url(middle))
224 if '@' in middle and not middle.startswith('www.') and \
225 not ':' in middle and _simple_email_re.match(middle):
226 middle = '<a href="mailto:%s">%s</a>' % (middle, middle)
227 if lead + middle + trail != word:
228 words[i] = lead + middle + trail
229 return u''.join(words)
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200230
231
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200232def generate_lorem_ipsum(n=5, html=True, min=20, max=100):
233 """Generate some lorem impsum for the template."""
234 from jinja2.constants import LOREM_IPSUM_WORDS
235 from random import choice, random, randrange
236 words = LOREM_IPSUM_WORDS.split()
237 result = []
238
239 for _ in xrange(n):
240 next_capitalized = True
241 last_comma = last_fullstop = 0
242 word = None
243 last = None
244 p = []
245
246 # each paragraph contains out of 20 to 100 words.
247 for idx, _ in enumerate(xrange(randrange(min, max))):
248 while True:
249 word = choice(words)
250 if word != last:
251 last = word
252 break
253 if next_capitalized:
254 word = word.capitalize()
255 next_capitalized = False
256 # add commas
257 if idx - randrange(3, 8) > last_comma:
258 last_comma = idx
259 last_fullstop += 2
260 word += ','
261 # add end of sentences
262 if idx - randrange(10, 20) > last_fullstop:
263 last_comma = last_fullstop = idx
264 word += '.'
265 next_capitalized = True
266 p.append(word)
267
268 # ensure that the paragraph ends with a dot.
269 p = u' '.join(p)
270 if p.endswith(','):
271 p = p[:-1] + '.'
272 elif not p.endswith('.'):
273 p += '.'
274 result.append(p)
275
276 if not html:
277 return u'\n\n'.join(result)
278 return Markup(u'\n'.join(u'<p>%s</p>' % escape(x) for x in result))
279
280
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200281class Markup(unicode):
Armin Ronacher58f351d2008-05-28 21:30:14 +0200282 r"""Marks a string as being safe for inclusion in HTML/XML output without
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200283 needing to be escaped. This implements the `__html__` interface a couple
Armin Ronacher58f351d2008-05-28 21:30:14 +0200284 of frameworks and web applications use. :class:`Markup` is a direct
285 subclass of `unicode` and provides all the methods of `unicode` just that
286 it escapes arguments passed and always returns `Markup`.
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200287
288 The `escape` function returns markup objects so that double escaping can't
289 happen. If you want to use autoescaping in Jinja just set the finalizer
290 of the environment to `escape`.
Armin Ronacher58f351d2008-05-28 21:30:14 +0200291
292 The constructor of the :class:`Markup` class can be used for three
293 different things: When passed an unicode object it's assumed to be safe,
294 when passed an object with an HTML representation (has an `__html__`
295 method) that representation is used, otherwise the object passed is
296 converted into a unicode string and then assumed to be safe:
297
298 >>> Markup("Hello <em>World</em>!")
299 Markup(u'Hello <em>World</em>!')
300 >>> class Foo(object):
301 ... def __html__(self):
302 ... return '<a href="#">foo</a>'
303 ...
304 >>> Markup(Foo())
305 Markup(u'<a href="#">foo</a>')
306
307 If you want object passed being always treated as unsafe you can use the
308 :meth:`escape` classmethod to create a :class:`Markup` object:
309
310 >>> Markup.escape("Hello <em>World</em>!")
311 Markup(u'Hello &lt;em&gt;World&lt;/em&gt;!')
312
313 Operations on a markup string are markup aware which means that all
314 arguments are passed through the :func:`escape` function:
315
316 >>> em = Markup("<em>%s</em>")
317 >>> em % "foo & bar"
318 Markup(u'<em>foo &amp; bar</em>')
319 >>> strong = Markup("<strong>%(text)s</strong>")
320 >>> strong % {'text': '<blink>hacker here</blink>'}
321 Markup(u'<strong>&lt;blink&gt;hacker here&lt;/blink&gt;</strong>')
322 >>> Markup("<em>Hello</em> ") + "<foo>"
323 Markup(u'<em>Hello</em> &lt;foo&gt;')
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200324 """
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200325 __slots__ = ()
326
Armin Ronacher3ef20432008-06-09 18:27:19 +0200327 def __new__(cls, base=u'', encoding=None, errors='strict'):
Armin Ronacher4e6f9a22008-05-23 23:57:38 +0200328 if hasattr(base, '__html__'):
329 base = base.__html__()
Armin Ronacher3ef20432008-06-09 18:27:19 +0200330 if encoding is None:
331 return unicode.__new__(cls, base)
332 return unicode.__new__(cls, base, encoding, errors)
Armin Ronacher4e6f9a22008-05-23 23:57:38 +0200333
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200334 def __html__(self):
335 return self
336
337 def __add__(self, other):
338 if hasattr(other, '__html__') or isinstance(other, basestring):
339 return self.__class__(unicode(self) + unicode(escape(other)))
340 return NotImplemented
341
342 def __radd__(self, other):
343 if hasattr(other, '__html__') or isinstance(other, basestring):
344 return self.__class__(unicode(escape(other)) + unicode(self))
345 return NotImplemented
346
347 def __mul__(self, num):
Armin Ronacherd71fff02008-05-26 23:57:07 +0200348 if isinstance(num, (int, long)):
349 return self.__class__(unicode.__mul__(self, num))
350 return NotImplemented
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200351 __rmul__ = __mul__
352
353 def __mod__(self, arg):
354 if isinstance(arg, tuple):
355 arg = tuple(imap(_MarkupEscapeHelper, arg))
356 else:
357 arg = _MarkupEscapeHelper(arg)
358 return self.__class__(unicode.__mod__(self, arg))
359
360 def __repr__(self):
361 return '%s(%s)' % (
362 self.__class__.__name__,
363 unicode.__repr__(self)
364 )
365
366 def join(self, seq):
367 return self.__class__(unicode.join(self, imap(escape, seq)))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200368 join.__doc__ = unicode.join.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200369
370 def split(self, *args, **kwargs):
371 return map(self.__class__, unicode.split(self, *args, **kwargs))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200372 split.__doc__ = unicode.split.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200373
374 def rsplit(self, *args, **kwargs):
375 return map(self.__class__, unicode.rsplit(self, *args, **kwargs))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200376 rsplit.__doc__ = unicode.rsplit.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200377
378 def splitlines(self, *args, **kwargs):
379 return map(self.__class__, unicode.splitlines(self, *args, **kwargs))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200380 splitlines.__doc__ = unicode.splitlines.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200381
Armin Ronacher76c280b2008-05-04 12:31:48 +0200382 def unescape(self):
Armin Ronacher58f351d2008-05-28 21:30:14 +0200383 r"""Unescape markup again into an unicode string. This also resolves
384 known HTML4 and XHTML entities:
385
386 >>> Markup("Main &raquo; <em>About</em>").unescape()
387 u'Main \xbb <em>About</em>'
388 """
Armin Ronacher9a0078d2008-08-13 18:24:17 +0200389 from jinja2.constants import HTML_ENTITIES
Armin Ronacher76c280b2008-05-04 12:31:48 +0200390 def handle_match(m):
391 name = m.group(1)
Armin Ronacher9a0078d2008-08-13 18:24:17 +0200392 if name in HTML_ENTITIES:
393 return unichr(HTML_ENTITIES[name])
Armin Ronacher76c280b2008-05-04 12:31:48 +0200394 try:
395 if name[:2] in ('#x', '#X'):
396 return unichr(int(name[2:], 16))
397 elif name.startswith('#'):
398 return unichr(int(name[1:]))
399 except ValueError:
400 pass
401 return u''
402 return _entity_re.sub(handle_match, unicode(self))
403
404 def striptags(self):
Armin Ronacher58f351d2008-05-28 21:30:14 +0200405 r"""Unescape markup into an unicode string and strip all tags. This
406 also resolves known HTML4 and XHTML entities. Whitespace is
407 normalized to one:
408
409 >>> Markup("Main &raquo; <em>About</em>").striptags()
410 u'Main \xbb About'
411 """
Armin Ronacher76c280b2008-05-04 12:31:48 +0200412 stripped = u' '.join(_striptags_re.sub('', self).split())
413 return Markup(stripped).unescape()
414
Armin Ronacherf35e2812008-05-06 16:04:10 +0200415 @classmethod
416 def escape(cls, s):
Armin Ronacher58f351d2008-05-28 21:30:14 +0200417 """Escape the string. Works like :func:`escape` with the difference
418 that for subclasses of :class:`Markup` this function would return the
419 correct subclass.
420 """
Armin Ronacherf35e2812008-05-06 16:04:10 +0200421 rv = escape(s)
422 if rv.__class__ is not cls:
423 return cls(rv)
424 return rv
425
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200426 def make_wrapper(name):
427 orig = getattr(unicode, name)
428 def func(self, *args, **kwargs):
Armin Ronacherd71fff02008-05-26 23:57:07 +0200429 args = _escape_argspec(list(args), enumerate(args))
430 _escape_argspec(kwargs, kwargs.iteritems())
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200431 return self.__class__(orig(self, *args, **kwargs))
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200432 func.__name__ = orig.__name__
433 func.__doc__ = orig.__doc__
434 return func
Armin Ronacherd71fff02008-05-26 23:57:07 +0200435
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200436 for method in '__getitem__', '__getslice__', 'capitalize', \
437 'title', 'lower', 'upper', 'replace', 'ljust', \
Armin Ronacher709f6e52008-04-28 18:18:16 +0200438 'rjust', 'lstrip', 'rstrip', 'center', 'strip', \
Armin Ronacher316157d2008-04-28 18:30:27 +0200439 'translate', 'expandtabs', 'swapcase', 'zfill':
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200440 locals()[method] = make_wrapper(method)
Armin Ronacher709f6e52008-04-28 18:18:16 +0200441
442 # new in python 2.5
443 if hasattr(unicode, 'partition'):
Armin Ronacherd71fff02008-05-26 23:57:07 +0200444 partition = make_wrapper('partition'),
445 rpartition = make_wrapper('rpartition')
446
447 # new in python 2.6
448 if hasattr(unicode, 'format'):
449 format = make_wrapper('format')
450
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200451 del method, make_wrapper
452
453
Armin Ronacherd71fff02008-05-26 23:57:07 +0200454def _escape_argspec(obj, iterable):
455 """Helper for various string-wrapped functions."""
456 for key, value in iterable:
457 if hasattr(value, '__html__') or isinstance(value, basestring):
458 obj[key] = escape(value)
459 return obj
460
461
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200462class _MarkupEscapeHelper(object):
463 """Helper for Markup.__mod__"""
464
465 def __init__(self, obj):
466 self.obj = obj
467
468 __getitem__ = lambda s, x: _MarkupEscapeHelper(s.obj[x])
469 __unicode__ = lambda s: unicode(escape(s.obj))
470 __str__ = lambda s: str(escape(s.obj))
Armin Ronacher3ef20432008-06-09 18:27:19 +0200471 __repr__ = lambda s: str(escape(repr(s.obj)))
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200472 __int__ = lambda s: int(s.obj)
473 __float__ = lambda s: float(s.obj)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200474
475
476class LRUCache(object):
477 """A simple LRU Cache implementation."""
Armin Ronacher58f351d2008-05-28 21:30:14 +0200478
479 # this is fast for small capacities (something below 1000) but doesn't
480 # scale. But as long as it's only used as storage for templates this
481 # won't do any harm.
Armin Ronacher814f6c22008-04-17 15:52:23 +0200482
483 def __init__(self, capacity):
484 self.capacity = capacity
485 self._mapping = {}
486 self._queue = deque()
Armin Ronacher7962ce72008-05-20 17:52:52 +0200487 self._postinit()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200488
Armin Ronacher7962ce72008-05-20 17:52:52 +0200489 def _postinit(self):
Armin Ronacher814f6c22008-04-17 15:52:23 +0200490 # alias all queue methods for faster lookup
491 self._popleft = self._queue.popleft
492 self._pop = self._queue.pop
493 if hasattr(self._queue, 'remove'):
494 self._remove = self._queue.remove
Armin Ronacher000b4912008-05-01 18:40:15 +0200495 self._wlock = allocate_lock()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200496 self._append = self._queue.append
497
498 def _remove(self, obj):
499 """Python 2.4 compatibility."""
500 for idx, item in enumerate(self._queue):
501 if item == obj:
502 del self._queue[idx]
503 break
504
Armin Ronacher7962ce72008-05-20 17:52:52 +0200505 def __getstate__(self):
506 return {
507 'capacity': self.capacity,
508 '_mapping': self._mapping,
509 '_queue': self._queue
510 }
511
512 def __setstate__(self, d):
513 self.__dict__.update(d)
514 self._postinit()
515
516 def __getnewargs__(self):
517 return (self.capacity,)
518
Armin Ronacher814f6c22008-04-17 15:52:23 +0200519 def copy(self):
520 """Return an shallow copy of the instance."""
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200521 rv = self.__class__(self.capacity)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200522 rv._mapping.update(self._mapping)
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200523 rv._queue = deque(self._queue)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200524 return rv
525
526 def get(self, key, default=None):
527 """Return an item from the cache dict or `default`"""
Armin Ronacher000b4912008-05-01 18:40:15 +0200528 try:
Armin Ronacher814f6c22008-04-17 15:52:23 +0200529 return self[key]
Armin Ronacher000b4912008-05-01 18:40:15 +0200530 except KeyError:
531 return default
Armin Ronacher814f6c22008-04-17 15:52:23 +0200532
533 def setdefault(self, key, default=None):
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200534 """Set `default` if the key is not in the cache otherwise
Armin Ronacher814f6c22008-04-17 15:52:23 +0200535 leave unchanged. Return the value of this key.
536 """
Armin Ronacher000b4912008-05-01 18:40:15 +0200537 try:
Armin Ronacher814f6c22008-04-17 15:52:23 +0200538 return self[key]
Armin Ronacher000b4912008-05-01 18:40:15 +0200539 except KeyError:
540 self[key] = default
541 return default
Armin Ronacher814f6c22008-04-17 15:52:23 +0200542
543 def clear(self):
544 """Clear the cache."""
Armin Ronacher000b4912008-05-01 18:40:15 +0200545 self._wlock.acquire()
546 try:
547 self._mapping.clear()
548 self._queue.clear()
549 finally:
550 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200551
552 def __contains__(self, key):
553 """Check if a key exists in this cache."""
554 return key in self._mapping
555
556 def __len__(self):
557 """Return the current size of the cache."""
558 return len(self._mapping)
559
560 def __repr__(self):
561 return '<%s %r>' % (
562 self.__class__.__name__,
563 self._mapping
564 )
565
566 def __getitem__(self, key):
567 """Get an item from the cache. Moves the item up so that it has the
568 highest priority then.
569
570 Raise an `KeyError` if it does not exist.
571 """
572 rv = self._mapping[key]
573 if self._queue[-1] != key:
574 self._remove(key)
575 self._append(key)
576 return rv
577
578 def __setitem__(self, key, value):
579 """Sets the value for an item. Moves the item up so that it
580 has the highest priority then.
581 """
Armin Ronacher000b4912008-05-01 18:40:15 +0200582 self._wlock.acquire()
583 try:
584 if key in self._mapping:
585 self._remove(key)
586 elif len(self._mapping) == self.capacity:
587 del self._mapping[self._popleft()]
588 self._append(key)
589 self._mapping[key] = value
590 finally:
591 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200592
593 def __delitem__(self, key):
594 """Remove an item from the cache dict.
595 Raise an `KeyError` if it does not exist.
596 """
Armin Ronacher000b4912008-05-01 18:40:15 +0200597 self._wlock.acquire()
598 try:
599 del self._mapping[key]
600 self._remove(key)
601 finally:
602 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200603
Armin Ronachere25f24d2008-05-19 11:20:41 +0200604 def items(self):
605 """Return a list of items."""
606 result = [(key, self._mapping[key]) for key in list(self._queue)]
607 result.reverse()
608 return result
609
610 def iteritems(self):
611 """Iterate over all items."""
612 return iter(self.items())
613
614 def values(self):
615 """Return a list of all values."""
616 return [x[1] for x in self.items()]
617
618 def itervalue(self):
619 """Iterate over all values."""
620 return iter(self.values())
621
622 def keys(self):
623 """Return a list of all keys ordered by most recent usage."""
624 return list(self)
625
626 def iterkeys(self):
627 """Iterate over all keys in the cache dict, ordered by
Armin Ronacher814f6c22008-04-17 15:52:23 +0200628 the most recent usage.
629 """
Armin Ronachere2244882008-05-19 09:25:57 +0200630 return reversed(tuple(self._queue))
Armin Ronacher814f6c22008-04-17 15:52:23 +0200631
Armin Ronachere25f24d2008-05-19 11:20:41 +0200632 __iter__ = iterkeys
633
Armin Ronacher814f6c22008-04-17 15:52:23 +0200634 def __reversed__(self):
635 """Iterate over the values in the cache dict, oldest items
636 coming first.
637 """
Armin Ronachere2244882008-05-19 09:25:57 +0200638 return iter(tuple(self._queue))
Armin Ronacher814f6c22008-04-17 15:52:23 +0200639
640 __copy__ = copy
641
Armin Ronacherbd33f112008-04-18 09:17:32 +0200642
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200643# register the LRU cache as mutable mapping if possible
644try:
645 from collections import MutableMapping
646 MutableMapping.register(LRUCache)
647except ImportError:
648 pass
649
650
Armin Ronacherbd33f112008-04-18 09:17:32 +0200651# we have to import it down here as the speedups module imports the
652# markup type which is define above.
653try:
Armin Ronacherf59bac22008-04-20 13:11:43 +0200654 from jinja2._speedups import escape, soft_unicode
Armin Ronacherbd33f112008-04-18 09:17:32 +0200655except ImportError:
Lukas Meuserad48a2e2008-05-01 18:19:57 +0200656 def escape(s):
Armin Ronacherf35e2812008-05-06 16:04:10 +0200657 """Convert the characters &, <, >, ' and " in string s to HTML-safe
658 sequences. Use this if you need to display text that might contain
Armin Ronacher9a1e33c2008-05-05 22:00:46 +0200659 such characters in HTML. Marks return value as markup string.
Armin Ronacherbd33f112008-04-18 09:17:32 +0200660 """
Lukas Meuserad48a2e2008-05-01 18:19:57 +0200661 if hasattr(s, '__html__'):
662 return s.__html__()
663 return Markup(unicode(s)
Armin Ronacherbd33f112008-04-18 09:17:32 +0200664 .replace('&', '&amp;')
665 .replace('>', '&gt;')
666 .replace('<', '&lt;')
Armin Ronacherf35e2812008-05-06 16:04:10 +0200667 .replace("'", '&#39;')
Armin Ronacher9d42abf2008-05-14 18:10:41 +0200668 .replace('"', '&#34;')
Armin Ronacherbd33f112008-04-18 09:17:32 +0200669 )
Armin Ronacherf59bac22008-04-20 13:11:43 +0200670
671 def soft_unicode(s):
672 """Make a string unicode if it isn't already. That way a markup
673 string is not converted back to unicode.
674 """
675 if not isinstance(s, unicode):
676 s = unicode(s)
677 return s
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200678
679
680# partials
681try:
682 from functools import partial
683except ImportError:
684 class partial(object):
685 def __init__(self, _func, *args, **kwargs):
Benjamin Wiegand228c1832008-04-28 18:09:27 +0200686 self._func = _func
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200687 self._args = args
688 self._kwargs = kwargs
689 def __call__(self, *args, **kwargs):
690 kwargs.update(self._kwargs)
691 return self._func(*(self._args + args), **kwargs)