blob: 480c08662495f709a4a62082f224f27ed62e0646 [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 Ronacherccae0552008-10-05 23:08:58 +020013import errno
Armin Ronacher000b4912008-05-01 18:40:15 +020014try:
15 from thread import allocate_lock
16except ImportError:
17 from dummy_thread import allocate_lock
Armin Ronacher814f6c22008-04-17 15:52:23 +020018from collections import deque
Armin Ronacher18c6ca02008-04-17 10:03:29 +020019from itertools import imap
Armin Ronacher8edbe492008-04-10 20:43:43 +020020
21
Armin Ronacherbe4ae242008-04-18 09:49:08 +020022_word_split_re = re.compile(r'(\s+)')
23_punctuation_re = re.compile(
24 '^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$' % (
25 '|'.join(imap(re.escape, ('(', '<', '&lt;'))),
26 '|'.join(imap(re.escape, ('.', ',', ')', '>', '\n', '&gt;')))
27 )
28)
29_simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$')
Armin Ronacher76c280b2008-05-04 12:31:48 +020030_striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
31_entity_re = re.compile(r'&([^;]+);')
Armin Ronacher9a0078d2008-08-13 18:24:17 +020032_letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
33_digits = '0123456789'
Armin Ronacherbe4ae242008-04-18 09:49:08 +020034
Armin Ronacher7259c762008-04-30 13:03:59 +020035# special singleton representing missing values for the runtime
36missing = type('MissingType', (), {'__repr__': lambda x: 'missing'})()
37
38
Armin Ronacher7ceced52008-05-03 10:15:31 +020039# concatenate a list of strings and convert them to unicode.
40# unfortunately there is a bug in python 2.4 and lower that causes
41# unicode.join trash the traceback.
Armin Ronachercda43df2008-05-03 17:10:05 +020042_concat = u''.join
Armin Ronacher7ceced52008-05-03 10:15:31 +020043try:
44 def _test_gen_bug():
45 raise TypeError(_test_gen_bug)
46 yield None
Armin Ronachercda43df2008-05-03 17:10:05 +020047 _concat(_test_gen_bug())
Armin Ronacher7ceced52008-05-03 10:15:31 +020048except TypeError, _error:
Armin Ronachercda43df2008-05-03 17:10:05 +020049 if not _error.args or _error.args[0] is not _test_gen_bug:
Armin Ronacher7ceced52008-05-03 10:15:31 +020050 def concat(gen):
51 try:
Armin Ronachercda43df2008-05-03 17:10:05 +020052 return _concat(list(gen))
Armin Ronacher7ceced52008-05-03 10:15:31 +020053 except:
54 # this hack is needed so that the current frame
55 # does not show up in the traceback.
56 exc_type, exc_value, tb = sys.exc_info()
57 raise exc_type, exc_value, tb.tb_next
Armin Ronachercda43df2008-05-03 17:10:05 +020058 else:
59 concat = _concat
Armin Ronacher7ceced52008-05-03 10:15:31 +020060 del _test_gen_bug, _error
61
62
Armin Ronacher9a0078d2008-08-13 18:24:17 +020063# ironpython without stdlib doesn't have keyword
64try:
65 from keyword import iskeyword as is_python_keyword
66except ImportError:
67 _py_identifier_re = re.compile(r'^[a-zA-Z_][a-zA-Z0-9]*$')
68 def is_python_keyword(name):
69 if _py_identifier_re.search(name) is None:
70 return False
71 try:
72 exec name + " = 42"
73 except SyntaxError:
74 return False
75 return True
76
77
78# common types. These do exist in the special types module too which however
79# does not exist in IronPython out of the box.
80class _C(object):
81 def method(self): pass
82def _func():
83 yield None
84FunctionType = type(_func)
85GeneratorType = type(_func())
86MethodType = type(_C.method)
87CodeType = type(_C.method.func_code)
88try:
89 raise TypeError()
90except TypeError:
91 _tb = sys.exc_info()[2]
92 TracebackType = type(_tb)
93 FrameType = type(_tb.tb_frame)
94del _C, _tb, _func
95
96
Armin Ronacher4f7d2d52008-04-22 10:40:26 +020097def contextfunction(f):
Armin Ronacher9bb7e472008-05-28 11:26:59 +020098 """This decorator can be used to mark a function or method context callable.
99 A context callable is passed the active :class:`Context` as first argument when
100 called from the template. This is useful if a function wants to get access
101 to the context or functions provided on the context object. For example
102 a function that returns a sorted list of template variables the current
103 template exports could look like this::
104
Armin Ronacher58f351d2008-05-28 21:30:14 +0200105 @contextfunction
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200106 def get_exported_names(context):
107 return sorted(context.exported_vars)
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200108 """
109 f.contextfunction = True
110 return f
111
112
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200113def environmentfunction(f):
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200114 """This decorator can be used to mark a function or method as environment
115 callable. This decorator works exactly like the :func:`contextfunction`
116 decorator just that the first argument is the active :class:`Environment`
117 and not context.
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200118 """
119 f.environmentfunction = True
120 return f
121
122
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200123def is_undefined(obj):
124 """Check if the object passed is undefined. This does nothing more than
125 performing an instance check against :class:`Undefined` but looks nicer.
126 This can be used for custom filters or tests that want to react to
127 undefined variables. For example a custom default filter can look like
128 this::
129
130 def default(var, default=''):
131 if is_undefined(var):
132 return default
133 return var
134 """
135 from jinja2.runtime import Undefined
136 return isinstance(obj, Undefined)
137
138
Armin Ronacherba6e25a2008-11-02 15:58:14 +0100139def consume(iterable):
140 """Consumes an iterable without doing anything with it."""
141 for event in iterable:
142 pass
143
144
Armin Ronacher187bde12008-05-01 18:19:16 +0200145def clear_caches():
146 """Jinja2 keeps internal caches for environments and lexers. These are
147 used so that Jinja2 doesn't have to recreate environments and lexers all
148 the time. Normally you don't have to care about that but if you are
149 messuring memory consumption you may want to clean the caches.
150 """
151 from jinja2.environment import _spontaneous_environments
152 from jinja2.lexer import _lexer_cache
153 _spontaneous_environments.clear()
154 _lexer_cache.clear()
155
156
Armin Ronacherf59bac22008-04-20 13:11:43 +0200157def import_string(import_name, silent=False):
158 """Imports an object based on a string. This use useful if you want to
159 use import paths as endpoints or something similar. An import path can
160 be specified either in dotted notation (``xml.sax.saxutils.escape``)
161 or with a colon as object delimiter (``xml.sax.saxutils:escape``).
162
163 If the `silent` is True the return value will be `None` if the import
164 fails.
165
166 :return: imported object
Armin Ronacher9a027f42008-04-17 11:13:40 +0200167 """
Armin Ronacherf59bac22008-04-20 13:11:43 +0200168 try:
169 if ':' in import_name:
170 module, obj = import_name.split(':', 1)
171 elif '.' in import_name:
172 items = import_name.split('.')
173 module = '.'.join(items[:-1])
174 obj = items[-1]
175 else:
176 return __import__(import_name)
177 return getattr(__import__(module, None, None, [obj]), obj)
178 except (ImportError, AttributeError):
179 if not silent:
180 raise
Armin Ronacher9a027f42008-04-17 11:13:40 +0200181
182
Armin Ronacherccae0552008-10-05 23:08:58 +0200183def open_if_exists(filename, mode='r'):
184 """Returns a file descriptor for the filename if that file exists,
185 otherwise `None`.
186 """
187 try:
188 return file(filename, mode)
189 except IOError, e:
190 if e.errno not in (errno.ENOENT, errno.EISDIR):
191 raise
192
193
Christoph Hacke9e43bb2008-04-13 23:35:48 +0200194def pformat(obj, verbose=False):
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200195 """Prettyprint an object. Either use the `pretty` library or the
Christoph Hacke9e43bb2008-04-13 23:35:48 +0200196 builtin `pprint`.
197 """
198 try:
199 from pretty import pretty
200 return pretty(obj, verbose=verbose)
201 except ImportError:
202 from pprint import pformat
203 return pformat(obj)
Christoph Hack80909862008-04-14 01:35:10 +0200204
205
Christoph Hack80909862008-04-14 01:35:10 +0200206def urlize(text, trim_url_limit=None, nofollow=False):
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200207 """Converts any URLs in text into clickable links. Works on http://,
Christoph Hack80909862008-04-14 01:35:10 +0200208 https:// and www. links. Links can have trailing punctuation (periods,
209 commas, close-parens) and leading punctuation (opening parens) and
210 it'll still do the right thing.
211
212 If trim_url_limit is not None, the URLs in link text will be limited
213 to trim_url_limit characters.
214
215 If nofollow is True, the URLs in link text will get a rel="nofollow"
216 attribute.
217 """
218 trim_url = lambda x, limit=trim_url_limit: limit is not None \
219 and (x[:limit] + (len(x) >=limit and '...'
220 or '')) or x
221 words = _word_split_re.split(text)
222 nofollow_attr = nofollow and ' rel="nofollow"' or ''
223 for i, word in enumerate(words):
224 match = _punctuation_re.match(word)
225 if match:
226 lead, middle, trail = match.groups()
227 if middle.startswith('www.') or (
228 '@' not in middle and
229 not middle.startswith('http://') and
230 len(middle) > 0 and
Armin Ronacher9a0078d2008-08-13 18:24:17 +0200231 middle[0] in _letters + _digits and (
Christoph Hack80909862008-04-14 01:35:10 +0200232 middle.endswith('.org') or
233 middle.endswith('.net') or
234 middle.endswith('.com')
235 )):
236 middle = '<a href="http://%s"%s>%s</a>' % (middle,
237 nofollow_attr, trim_url(middle))
238 if middle.startswith('http://') or \
239 middle.startswith('https://'):
240 middle = '<a href="%s"%s>%s</a>' % (middle,
241 nofollow_attr, trim_url(middle))
242 if '@' in middle and not middle.startswith('www.') and \
243 not ':' in middle and _simple_email_re.match(middle):
244 middle = '<a href="mailto:%s">%s</a>' % (middle, middle)
245 if lead + middle + trail != word:
246 words[i] = lead + middle + trail
247 return u''.join(words)
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200248
249
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200250def generate_lorem_ipsum(n=5, html=True, min=20, max=100):
251 """Generate some lorem impsum for the template."""
252 from jinja2.constants import LOREM_IPSUM_WORDS
253 from random import choice, random, randrange
254 words = LOREM_IPSUM_WORDS.split()
255 result = []
256
257 for _ in xrange(n):
258 next_capitalized = True
259 last_comma = last_fullstop = 0
260 word = None
261 last = None
262 p = []
263
264 # each paragraph contains out of 20 to 100 words.
265 for idx, _ in enumerate(xrange(randrange(min, max))):
266 while True:
267 word = choice(words)
268 if word != last:
269 last = word
270 break
271 if next_capitalized:
272 word = word.capitalize()
273 next_capitalized = False
274 # add commas
275 if idx - randrange(3, 8) > last_comma:
276 last_comma = idx
277 last_fullstop += 2
278 word += ','
279 # add end of sentences
280 if idx - randrange(10, 20) > last_fullstop:
281 last_comma = last_fullstop = idx
282 word += '.'
283 next_capitalized = True
284 p.append(word)
285
286 # ensure that the paragraph ends with a dot.
287 p = u' '.join(p)
288 if p.endswith(','):
289 p = p[:-1] + '.'
290 elif not p.endswith('.'):
291 p += '.'
292 result.append(p)
293
294 if not html:
295 return u'\n\n'.join(result)
296 return Markup(u'\n'.join(u'<p>%s</p>' % escape(x) for x in result))
297
298
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200299class Markup(unicode):
Armin Ronacher58f351d2008-05-28 21:30:14 +0200300 r"""Marks a string as being safe for inclusion in HTML/XML output without
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200301 needing to be escaped. This implements the `__html__` interface a couple
Armin Ronacher58f351d2008-05-28 21:30:14 +0200302 of frameworks and web applications use. :class:`Markup` is a direct
303 subclass of `unicode` and provides all the methods of `unicode` just that
304 it escapes arguments passed and always returns `Markup`.
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200305
306 The `escape` function returns markup objects so that double escaping can't
Armin Ronacher88e1cb72008-09-08 23:55:32 +0200307 happen. If you want to use autoescaping in Jinja just enable the
308 autoescaping feature in the environment.
Armin Ronacher58f351d2008-05-28 21:30:14 +0200309
310 The constructor of the :class:`Markup` class can be used for three
311 different things: When passed an unicode object it's assumed to be safe,
312 when passed an object with an HTML representation (has an `__html__`
313 method) that representation is used, otherwise the object passed is
314 converted into a unicode string and then assumed to be safe:
315
316 >>> Markup("Hello <em>World</em>!")
317 Markup(u'Hello <em>World</em>!')
318 >>> class Foo(object):
319 ... def __html__(self):
320 ... return '<a href="#">foo</a>'
321 ...
322 >>> Markup(Foo())
323 Markup(u'<a href="#">foo</a>')
324
325 If you want object passed being always treated as unsafe you can use the
326 :meth:`escape` classmethod to create a :class:`Markup` object:
327
328 >>> Markup.escape("Hello <em>World</em>!")
329 Markup(u'Hello &lt;em&gt;World&lt;/em&gt;!')
330
331 Operations on a markup string are markup aware which means that all
332 arguments are passed through the :func:`escape` function:
333
334 >>> em = Markup("<em>%s</em>")
335 >>> em % "foo & bar"
336 Markup(u'<em>foo &amp; bar</em>')
337 >>> strong = Markup("<strong>%(text)s</strong>")
338 >>> strong % {'text': '<blink>hacker here</blink>'}
339 Markup(u'<strong>&lt;blink&gt;hacker here&lt;/blink&gt;</strong>')
340 >>> Markup("<em>Hello</em> ") + "<foo>"
341 Markup(u'<em>Hello</em> &lt;foo&gt;')
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200342 """
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200343 __slots__ = ()
344
Armin Ronacher3ef20432008-06-09 18:27:19 +0200345 def __new__(cls, base=u'', encoding=None, errors='strict'):
Armin Ronacher4e6f9a22008-05-23 23:57:38 +0200346 if hasattr(base, '__html__'):
347 base = base.__html__()
Armin Ronacher3ef20432008-06-09 18:27:19 +0200348 if encoding is None:
349 return unicode.__new__(cls, base)
350 return unicode.__new__(cls, base, encoding, errors)
Armin Ronacher4e6f9a22008-05-23 23:57:38 +0200351
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200352 def __html__(self):
353 return self
354
355 def __add__(self, other):
356 if hasattr(other, '__html__') or isinstance(other, basestring):
357 return self.__class__(unicode(self) + unicode(escape(other)))
358 return NotImplemented
359
360 def __radd__(self, other):
361 if hasattr(other, '__html__') or isinstance(other, basestring):
362 return self.__class__(unicode(escape(other)) + unicode(self))
363 return NotImplemented
364
365 def __mul__(self, num):
Armin Ronacherd71fff02008-05-26 23:57:07 +0200366 if isinstance(num, (int, long)):
367 return self.__class__(unicode.__mul__(self, num))
368 return NotImplemented
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200369 __rmul__ = __mul__
370
371 def __mod__(self, arg):
372 if isinstance(arg, tuple):
373 arg = tuple(imap(_MarkupEscapeHelper, arg))
374 else:
375 arg = _MarkupEscapeHelper(arg)
376 return self.__class__(unicode.__mod__(self, arg))
377
378 def __repr__(self):
379 return '%s(%s)' % (
380 self.__class__.__name__,
381 unicode.__repr__(self)
382 )
383
384 def join(self, seq):
385 return self.__class__(unicode.join(self, imap(escape, seq)))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200386 join.__doc__ = unicode.join.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200387
388 def split(self, *args, **kwargs):
389 return map(self.__class__, unicode.split(self, *args, **kwargs))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200390 split.__doc__ = unicode.split.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200391
392 def rsplit(self, *args, **kwargs):
393 return map(self.__class__, unicode.rsplit(self, *args, **kwargs))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200394 rsplit.__doc__ = unicode.rsplit.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200395
396 def splitlines(self, *args, **kwargs):
397 return map(self.__class__, unicode.splitlines(self, *args, **kwargs))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200398 splitlines.__doc__ = unicode.splitlines.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200399
Armin Ronacher76c280b2008-05-04 12:31:48 +0200400 def unescape(self):
Armin Ronacher58f351d2008-05-28 21:30:14 +0200401 r"""Unescape markup again into an unicode string. This also resolves
402 known HTML4 and XHTML entities:
403
404 >>> Markup("Main &raquo; <em>About</em>").unescape()
405 u'Main \xbb <em>About</em>'
406 """
Armin Ronacher9a0078d2008-08-13 18:24:17 +0200407 from jinja2.constants import HTML_ENTITIES
Armin Ronacher76c280b2008-05-04 12:31:48 +0200408 def handle_match(m):
409 name = m.group(1)
Armin Ronacher9a0078d2008-08-13 18:24:17 +0200410 if name in HTML_ENTITIES:
411 return unichr(HTML_ENTITIES[name])
Armin Ronacher76c280b2008-05-04 12:31:48 +0200412 try:
413 if name[:2] in ('#x', '#X'):
414 return unichr(int(name[2:], 16))
415 elif name.startswith('#'):
416 return unichr(int(name[1:]))
417 except ValueError:
418 pass
419 return u''
420 return _entity_re.sub(handle_match, unicode(self))
421
422 def striptags(self):
Armin Ronacher58f351d2008-05-28 21:30:14 +0200423 r"""Unescape markup into an unicode string and strip all tags. This
424 also resolves known HTML4 and XHTML entities. Whitespace is
425 normalized to one:
426
427 >>> Markup("Main &raquo; <em>About</em>").striptags()
428 u'Main \xbb About'
429 """
Armin Ronacher76c280b2008-05-04 12:31:48 +0200430 stripped = u' '.join(_striptags_re.sub('', self).split())
431 return Markup(stripped).unescape()
432
Armin Ronacherf35e2812008-05-06 16:04:10 +0200433 @classmethod
434 def escape(cls, s):
Armin Ronacher58f351d2008-05-28 21:30:14 +0200435 """Escape the string. Works like :func:`escape` with the difference
436 that for subclasses of :class:`Markup` this function would return the
437 correct subclass.
438 """
Armin Ronacherf35e2812008-05-06 16:04:10 +0200439 rv = escape(s)
440 if rv.__class__ is not cls:
441 return cls(rv)
442 return rv
443
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200444 def make_wrapper(name):
445 orig = getattr(unicode, name)
446 def func(self, *args, **kwargs):
Armin Ronacherd71fff02008-05-26 23:57:07 +0200447 args = _escape_argspec(list(args), enumerate(args))
448 _escape_argspec(kwargs, kwargs.iteritems())
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200449 return self.__class__(orig(self, *args, **kwargs))
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200450 func.__name__ = orig.__name__
451 func.__doc__ = orig.__doc__
452 return func
Armin Ronacherd71fff02008-05-26 23:57:07 +0200453
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200454 for method in '__getitem__', '__getslice__', 'capitalize', \
455 'title', 'lower', 'upper', 'replace', 'ljust', \
Armin Ronacher709f6e52008-04-28 18:18:16 +0200456 'rjust', 'lstrip', 'rstrip', 'center', 'strip', \
Armin Ronacher316157d2008-04-28 18:30:27 +0200457 'translate', 'expandtabs', 'swapcase', 'zfill':
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200458 locals()[method] = make_wrapper(method)
Armin Ronacher709f6e52008-04-28 18:18:16 +0200459
460 # new in python 2.5
461 if hasattr(unicode, 'partition'):
Armin Ronacherd71fff02008-05-26 23:57:07 +0200462 partition = make_wrapper('partition'),
463 rpartition = make_wrapper('rpartition')
464
465 # new in python 2.6
466 if hasattr(unicode, 'format'):
467 format = make_wrapper('format')
468
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200469 del method, make_wrapper
470
471
Armin Ronacherd71fff02008-05-26 23:57:07 +0200472def _escape_argspec(obj, iterable):
473 """Helper for various string-wrapped functions."""
474 for key, value in iterable:
475 if hasattr(value, '__html__') or isinstance(value, basestring):
476 obj[key] = escape(value)
477 return obj
478
479
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200480class _MarkupEscapeHelper(object):
481 """Helper for Markup.__mod__"""
482
483 def __init__(self, obj):
484 self.obj = obj
485
486 __getitem__ = lambda s, x: _MarkupEscapeHelper(s.obj[x])
487 __unicode__ = lambda s: unicode(escape(s.obj))
488 __str__ = lambda s: str(escape(s.obj))
Armin Ronacher3ef20432008-06-09 18:27:19 +0200489 __repr__ = lambda s: str(escape(repr(s.obj)))
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200490 __int__ = lambda s: int(s.obj)
491 __float__ = lambda s: float(s.obj)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200492
493
494class LRUCache(object):
495 """A simple LRU Cache implementation."""
Armin Ronacher58f351d2008-05-28 21:30:14 +0200496
497 # this is fast for small capacities (something below 1000) but doesn't
498 # scale. But as long as it's only used as storage for templates this
499 # won't do any harm.
Armin Ronacher814f6c22008-04-17 15:52:23 +0200500
501 def __init__(self, capacity):
502 self.capacity = capacity
503 self._mapping = {}
504 self._queue = deque()
Armin Ronacher7962ce72008-05-20 17:52:52 +0200505 self._postinit()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200506
Armin Ronacher7962ce72008-05-20 17:52:52 +0200507 def _postinit(self):
Armin Ronacher814f6c22008-04-17 15:52:23 +0200508 # alias all queue methods for faster lookup
509 self._popleft = self._queue.popleft
510 self._pop = self._queue.pop
511 if hasattr(self._queue, 'remove'):
512 self._remove = self._queue.remove
Armin Ronacher000b4912008-05-01 18:40:15 +0200513 self._wlock = allocate_lock()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200514 self._append = self._queue.append
515
516 def _remove(self, obj):
517 """Python 2.4 compatibility."""
518 for idx, item in enumerate(self._queue):
519 if item == obj:
520 del self._queue[idx]
521 break
522
Armin Ronacher7962ce72008-05-20 17:52:52 +0200523 def __getstate__(self):
524 return {
525 'capacity': self.capacity,
526 '_mapping': self._mapping,
527 '_queue': self._queue
528 }
529
530 def __setstate__(self, d):
531 self.__dict__.update(d)
532 self._postinit()
533
534 def __getnewargs__(self):
535 return (self.capacity,)
536
Armin Ronacher814f6c22008-04-17 15:52:23 +0200537 def copy(self):
538 """Return an shallow copy of the instance."""
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200539 rv = self.__class__(self.capacity)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200540 rv._mapping.update(self._mapping)
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200541 rv._queue = deque(self._queue)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200542 return rv
543
544 def get(self, key, default=None):
545 """Return an item from the cache dict or `default`"""
Armin Ronacher000b4912008-05-01 18:40:15 +0200546 try:
Armin Ronacher814f6c22008-04-17 15:52:23 +0200547 return self[key]
Armin Ronacher000b4912008-05-01 18:40:15 +0200548 except KeyError:
549 return default
Armin Ronacher814f6c22008-04-17 15:52:23 +0200550
551 def setdefault(self, key, default=None):
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200552 """Set `default` if the key is not in the cache otherwise
Armin Ronacher814f6c22008-04-17 15:52:23 +0200553 leave unchanged. Return the value of this key.
554 """
Armin Ronacher000b4912008-05-01 18:40:15 +0200555 try:
Armin Ronacher814f6c22008-04-17 15:52:23 +0200556 return self[key]
Armin Ronacher000b4912008-05-01 18:40:15 +0200557 except KeyError:
558 self[key] = default
559 return default
Armin Ronacher814f6c22008-04-17 15:52:23 +0200560
561 def clear(self):
562 """Clear the cache."""
Armin Ronacher000b4912008-05-01 18:40:15 +0200563 self._wlock.acquire()
564 try:
565 self._mapping.clear()
566 self._queue.clear()
567 finally:
568 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200569
570 def __contains__(self, key):
571 """Check if a key exists in this cache."""
572 return key in self._mapping
573
574 def __len__(self):
575 """Return the current size of the cache."""
576 return len(self._mapping)
577
578 def __repr__(self):
579 return '<%s %r>' % (
580 self.__class__.__name__,
581 self._mapping
582 )
583
584 def __getitem__(self, key):
585 """Get an item from the cache. Moves the item up so that it has the
586 highest priority then.
587
588 Raise an `KeyError` if it does not exist.
589 """
590 rv = self._mapping[key]
591 if self._queue[-1] != key:
592 self._remove(key)
593 self._append(key)
594 return rv
595
596 def __setitem__(self, key, value):
597 """Sets the value for an item. Moves the item up so that it
598 has the highest priority then.
599 """
Armin Ronacher000b4912008-05-01 18:40:15 +0200600 self._wlock.acquire()
601 try:
602 if key in self._mapping:
603 self._remove(key)
604 elif len(self._mapping) == self.capacity:
605 del self._mapping[self._popleft()]
606 self._append(key)
607 self._mapping[key] = value
608 finally:
609 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200610
611 def __delitem__(self, key):
612 """Remove an item from the cache dict.
613 Raise an `KeyError` if it does not exist.
614 """
Armin Ronacher000b4912008-05-01 18:40:15 +0200615 self._wlock.acquire()
616 try:
617 del self._mapping[key]
618 self._remove(key)
619 finally:
620 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200621
Armin Ronachere25f24d2008-05-19 11:20:41 +0200622 def items(self):
623 """Return a list of items."""
624 result = [(key, self._mapping[key]) for key in list(self._queue)]
625 result.reverse()
626 return result
627
628 def iteritems(self):
629 """Iterate over all items."""
630 return iter(self.items())
631
632 def values(self):
633 """Return a list of all values."""
634 return [x[1] for x in self.items()]
635
636 def itervalue(self):
637 """Iterate over all values."""
638 return iter(self.values())
639
640 def keys(self):
641 """Return a list of all keys ordered by most recent usage."""
642 return list(self)
643
644 def iterkeys(self):
645 """Iterate over all keys in the cache dict, ordered by
Armin Ronacher814f6c22008-04-17 15:52:23 +0200646 the most recent usage.
647 """
Armin Ronachere2244882008-05-19 09:25:57 +0200648 return reversed(tuple(self._queue))
Armin Ronacher814f6c22008-04-17 15:52:23 +0200649
Armin Ronachere25f24d2008-05-19 11:20:41 +0200650 __iter__ = iterkeys
651
Armin Ronacher814f6c22008-04-17 15:52:23 +0200652 def __reversed__(self):
653 """Iterate over the values in the cache dict, oldest items
654 coming first.
655 """
Armin Ronachere2244882008-05-19 09:25:57 +0200656 return iter(tuple(self._queue))
Armin Ronacher814f6c22008-04-17 15:52:23 +0200657
658 __copy__ = copy
659
Armin Ronacherbd33f112008-04-18 09:17:32 +0200660
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200661# register the LRU cache as mutable mapping if possible
662try:
663 from collections import MutableMapping
664 MutableMapping.register(LRUCache)
665except ImportError:
666 pass
667
668
Armin Ronacherccae0552008-10-05 23:08:58 +0200669class Cycler(object):
670 """A cycle helper for templates."""
671
672 def __init__(self, *items):
673 if not items:
674 raise RuntimeError('at least one item has to be provided')
675 self.items = items
676 self.reset()
677
678 def reset(self):
679 """Resets the cycle."""
680 self.pos = 0
681
682 @property
683 def current(self):
684 """Returns the current item."""
685 return self.items[self.pos]
686
687 def next(self):
688 """Goes one item ahead and returns it."""
689 rv = self.current
690 self.pos = (self.pos + 1) % len(self.items)
691 return rv
692
693
Armin Ronacherd34eb122008-10-13 23:47:51 +0200694class Joiner(object):
695 """A joining helper for templates."""
696
697 def __init__(self, sep=u', '):
698 self.sep = sep
699 self.used = False
700
701 def __call__(self):
702 if not self.used:
703 self.used = True
704 return u''
705 return self.sep
706
707
Armin Ronacherbd33f112008-04-18 09:17:32 +0200708# we have to import it down here as the speedups module imports the
709# markup type which is define above.
710try:
Armin Ronacherf59bac22008-04-20 13:11:43 +0200711 from jinja2._speedups import escape, soft_unicode
Armin Ronacherbd33f112008-04-18 09:17:32 +0200712except ImportError:
Lukas Meuserad48a2e2008-05-01 18:19:57 +0200713 def escape(s):
Armin Ronacherf35e2812008-05-06 16:04:10 +0200714 """Convert the characters &, <, >, ' and " in string s to HTML-safe
715 sequences. Use this if you need to display text that might contain
Armin Ronacher9a1e33c2008-05-05 22:00:46 +0200716 such characters in HTML. Marks return value as markup string.
Armin Ronacherbd33f112008-04-18 09:17:32 +0200717 """
Lukas Meuserad48a2e2008-05-01 18:19:57 +0200718 if hasattr(s, '__html__'):
719 return s.__html__()
720 return Markup(unicode(s)
Armin Ronacherbd33f112008-04-18 09:17:32 +0200721 .replace('&', '&amp;')
722 .replace('>', '&gt;')
723 .replace('<', '&lt;')
Armin Ronacherf35e2812008-05-06 16:04:10 +0200724 .replace("'", '&#39;')
Armin Ronacher9d42abf2008-05-14 18:10:41 +0200725 .replace('"', '&#34;')
Armin Ronacherbd33f112008-04-18 09:17:32 +0200726 )
Armin Ronacherf59bac22008-04-20 13:11:43 +0200727
728 def soft_unicode(s):
729 """Make a string unicode if it isn't already. That way a markup
730 string is not converted back to unicode.
731 """
732 if not isinstance(s, unicode):
733 s = unicode(s)
734 return s
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200735
736
737# partials
738try:
739 from functools import partial
740except ImportError:
741 class partial(object):
742 def __init__(self, _func, *args, **kwargs):
Benjamin Wiegand228c1832008-04-28 18:09:27 +0200743 self._func = _func
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200744 self._args = args
745 self._kwargs = kwargs
746 def __call__(self, *args, **kwargs):
747 kwargs.update(self._kwargs)
748 return self._func(*(self._args + args), **kwargs)