blob: 249e36309d3552b1789ad2011b92f5082c18d3de [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 Ronacher187bde12008-05-01 18:19:16 +0200139def clear_caches():
140 """Jinja2 keeps internal caches for environments and lexers. These are
141 used so that Jinja2 doesn't have to recreate environments and lexers all
142 the time. Normally you don't have to care about that but if you are
143 messuring memory consumption you may want to clean the caches.
144 """
145 from jinja2.environment import _spontaneous_environments
146 from jinja2.lexer import _lexer_cache
147 _spontaneous_environments.clear()
148 _lexer_cache.clear()
149
150
Armin Ronacherf59bac22008-04-20 13:11:43 +0200151def import_string(import_name, silent=False):
152 """Imports an object based on a string. This use useful if you want to
153 use import paths as endpoints or something similar. An import path can
154 be specified either in dotted notation (``xml.sax.saxutils.escape``)
155 or with a colon as object delimiter (``xml.sax.saxutils:escape``).
156
157 If the `silent` is True the return value will be `None` if the import
158 fails.
159
160 :return: imported object
Armin Ronacher9a027f42008-04-17 11:13:40 +0200161 """
Armin Ronacherf59bac22008-04-20 13:11:43 +0200162 try:
163 if ':' in import_name:
164 module, obj = import_name.split(':', 1)
165 elif '.' in import_name:
166 items = import_name.split('.')
167 module = '.'.join(items[:-1])
168 obj = items[-1]
169 else:
170 return __import__(import_name)
171 return getattr(__import__(module, None, None, [obj]), obj)
172 except (ImportError, AttributeError):
173 if not silent:
174 raise
Armin Ronacher9a027f42008-04-17 11:13:40 +0200175
176
Armin Ronacherccae0552008-10-05 23:08:58 +0200177def open_if_exists(filename, mode='r'):
178 """Returns a file descriptor for the filename if that file exists,
179 otherwise `None`.
180 """
181 try:
182 return file(filename, mode)
183 except IOError, e:
184 if e.errno not in (errno.ENOENT, errno.EISDIR):
185 raise
186
187
Christoph Hacke9e43bb2008-04-13 23:35:48 +0200188def pformat(obj, verbose=False):
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200189 """Prettyprint an object. Either use the `pretty` library or the
Christoph Hacke9e43bb2008-04-13 23:35:48 +0200190 builtin `pprint`.
191 """
192 try:
193 from pretty import pretty
194 return pretty(obj, verbose=verbose)
195 except ImportError:
196 from pprint import pformat
197 return pformat(obj)
Christoph Hack80909862008-04-14 01:35:10 +0200198
199
Christoph Hack80909862008-04-14 01:35:10 +0200200def urlize(text, trim_url_limit=None, nofollow=False):
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200201 """Converts any URLs in text into clickable links. Works on http://,
Christoph Hack80909862008-04-14 01:35:10 +0200202 https:// and www. links. Links can have trailing punctuation (periods,
203 commas, close-parens) and leading punctuation (opening parens) and
204 it'll still do the right thing.
205
206 If trim_url_limit is not None, the URLs in link text will be limited
207 to trim_url_limit characters.
208
209 If nofollow is True, the URLs in link text will get a rel="nofollow"
210 attribute.
211 """
212 trim_url = lambda x, limit=trim_url_limit: limit is not None \
213 and (x[:limit] + (len(x) >=limit and '...'
214 or '')) or x
215 words = _word_split_re.split(text)
216 nofollow_attr = nofollow and ' rel="nofollow"' or ''
217 for i, word in enumerate(words):
218 match = _punctuation_re.match(word)
219 if match:
220 lead, middle, trail = match.groups()
221 if middle.startswith('www.') or (
222 '@' not in middle and
223 not middle.startswith('http://') and
224 len(middle) > 0 and
Armin Ronacher9a0078d2008-08-13 18:24:17 +0200225 middle[0] in _letters + _digits and (
Christoph Hack80909862008-04-14 01:35:10 +0200226 middle.endswith('.org') or
227 middle.endswith('.net') or
228 middle.endswith('.com')
229 )):
230 middle = '<a href="http://%s"%s>%s</a>' % (middle,
231 nofollow_attr, trim_url(middle))
232 if middle.startswith('http://') or \
233 middle.startswith('https://'):
234 middle = '<a href="%s"%s>%s</a>' % (middle,
235 nofollow_attr, trim_url(middle))
236 if '@' in middle and not middle.startswith('www.') and \
237 not ':' in middle and _simple_email_re.match(middle):
238 middle = '<a href="mailto:%s">%s</a>' % (middle, middle)
239 if lead + middle + trail != word:
240 words[i] = lead + middle + trail
241 return u''.join(words)
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200242
243
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200244def generate_lorem_ipsum(n=5, html=True, min=20, max=100):
245 """Generate some lorem impsum for the template."""
246 from jinja2.constants import LOREM_IPSUM_WORDS
247 from random import choice, random, randrange
248 words = LOREM_IPSUM_WORDS.split()
249 result = []
250
251 for _ in xrange(n):
252 next_capitalized = True
253 last_comma = last_fullstop = 0
254 word = None
255 last = None
256 p = []
257
258 # each paragraph contains out of 20 to 100 words.
259 for idx, _ in enumerate(xrange(randrange(min, max))):
260 while True:
261 word = choice(words)
262 if word != last:
263 last = word
264 break
265 if next_capitalized:
266 word = word.capitalize()
267 next_capitalized = False
268 # add commas
269 if idx - randrange(3, 8) > last_comma:
270 last_comma = idx
271 last_fullstop += 2
272 word += ','
273 # add end of sentences
274 if idx - randrange(10, 20) > last_fullstop:
275 last_comma = last_fullstop = idx
276 word += '.'
277 next_capitalized = True
278 p.append(word)
279
280 # ensure that the paragraph ends with a dot.
281 p = u' '.join(p)
282 if p.endswith(','):
283 p = p[:-1] + '.'
284 elif not p.endswith('.'):
285 p += '.'
286 result.append(p)
287
288 if not html:
289 return u'\n\n'.join(result)
290 return Markup(u'\n'.join(u'<p>%s</p>' % escape(x) for x in result))
291
292
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200293class Markup(unicode):
Armin Ronacher58f351d2008-05-28 21:30:14 +0200294 r"""Marks a string as being safe for inclusion in HTML/XML output without
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200295 needing to be escaped. This implements the `__html__` interface a couple
Armin Ronacher58f351d2008-05-28 21:30:14 +0200296 of frameworks and web applications use. :class:`Markup` is a direct
297 subclass of `unicode` and provides all the methods of `unicode` just that
298 it escapes arguments passed and always returns `Markup`.
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200299
300 The `escape` function returns markup objects so that double escaping can't
Armin Ronacher88e1cb72008-09-08 23:55:32 +0200301 happen. If you want to use autoescaping in Jinja just enable the
302 autoescaping feature in the environment.
Armin Ronacher58f351d2008-05-28 21:30:14 +0200303
304 The constructor of the :class:`Markup` class can be used for three
305 different things: When passed an unicode object it's assumed to be safe,
306 when passed an object with an HTML representation (has an `__html__`
307 method) that representation is used, otherwise the object passed is
308 converted into a unicode string and then assumed to be safe:
309
310 >>> Markup("Hello <em>World</em>!")
311 Markup(u'Hello <em>World</em>!')
312 >>> class Foo(object):
313 ... def __html__(self):
314 ... return '<a href="#">foo</a>'
315 ...
316 >>> Markup(Foo())
317 Markup(u'<a href="#">foo</a>')
318
319 If you want object passed being always treated as unsafe you can use the
320 :meth:`escape` classmethod to create a :class:`Markup` object:
321
322 >>> Markup.escape("Hello <em>World</em>!")
323 Markup(u'Hello &lt;em&gt;World&lt;/em&gt;!')
324
325 Operations on a markup string are markup aware which means that all
326 arguments are passed through the :func:`escape` function:
327
328 >>> em = Markup("<em>%s</em>")
329 >>> em % "foo & bar"
330 Markup(u'<em>foo &amp; bar</em>')
331 >>> strong = Markup("<strong>%(text)s</strong>")
332 >>> strong % {'text': '<blink>hacker here</blink>'}
333 Markup(u'<strong>&lt;blink&gt;hacker here&lt;/blink&gt;</strong>')
334 >>> Markup("<em>Hello</em> ") + "<foo>"
335 Markup(u'<em>Hello</em> &lt;foo&gt;')
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200336 """
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200337 __slots__ = ()
338
Armin Ronacher3ef20432008-06-09 18:27:19 +0200339 def __new__(cls, base=u'', encoding=None, errors='strict'):
Armin Ronacher4e6f9a22008-05-23 23:57:38 +0200340 if hasattr(base, '__html__'):
341 base = base.__html__()
Armin Ronacher3ef20432008-06-09 18:27:19 +0200342 if encoding is None:
343 return unicode.__new__(cls, base)
344 return unicode.__new__(cls, base, encoding, errors)
Armin Ronacher4e6f9a22008-05-23 23:57:38 +0200345
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200346 def __html__(self):
347 return self
348
349 def __add__(self, other):
350 if hasattr(other, '__html__') or isinstance(other, basestring):
351 return self.__class__(unicode(self) + unicode(escape(other)))
352 return NotImplemented
353
354 def __radd__(self, other):
355 if hasattr(other, '__html__') or isinstance(other, basestring):
356 return self.__class__(unicode(escape(other)) + unicode(self))
357 return NotImplemented
358
359 def __mul__(self, num):
Armin Ronacherd71fff02008-05-26 23:57:07 +0200360 if isinstance(num, (int, long)):
361 return self.__class__(unicode.__mul__(self, num))
362 return NotImplemented
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200363 __rmul__ = __mul__
364
365 def __mod__(self, arg):
366 if isinstance(arg, tuple):
367 arg = tuple(imap(_MarkupEscapeHelper, arg))
368 else:
369 arg = _MarkupEscapeHelper(arg)
370 return self.__class__(unicode.__mod__(self, arg))
371
372 def __repr__(self):
373 return '%s(%s)' % (
374 self.__class__.__name__,
375 unicode.__repr__(self)
376 )
377
378 def join(self, seq):
379 return self.__class__(unicode.join(self, imap(escape, seq)))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200380 join.__doc__ = unicode.join.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200381
382 def split(self, *args, **kwargs):
383 return map(self.__class__, unicode.split(self, *args, **kwargs))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200384 split.__doc__ = unicode.split.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200385
386 def rsplit(self, *args, **kwargs):
387 return map(self.__class__, unicode.rsplit(self, *args, **kwargs))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200388 rsplit.__doc__ = unicode.rsplit.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200389
390 def splitlines(self, *args, **kwargs):
391 return map(self.__class__, unicode.splitlines(self, *args, **kwargs))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200392 splitlines.__doc__ = unicode.splitlines.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200393
Armin Ronacher76c280b2008-05-04 12:31:48 +0200394 def unescape(self):
Armin Ronacher58f351d2008-05-28 21:30:14 +0200395 r"""Unescape markup again into an unicode string. This also resolves
396 known HTML4 and XHTML entities:
397
398 >>> Markup("Main &raquo; <em>About</em>").unescape()
399 u'Main \xbb <em>About</em>'
400 """
Armin Ronacher9a0078d2008-08-13 18:24:17 +0200401 from jinja2.constants import HTML_ENTITIES
Armin Ronacher76c280b2008-05-04 12:31:48 +0200402 def handle_match(m):
403 name = m.group(1)
Armin Ronacher9a0078d2008-08-13 18:24:17 +0200404 if name in HTML_ENTITIES:
405 return unichr(HTML_ENTITIES[name])
Armin Ronacher76c280b2008-05-04 12:31:48 +0200406 try:
407 if name[:2] in ('#x', '#X'):
408 return unichr(int(name[2:], 16))
409 elif name.startswith('#'):
410 return unichr(int(name[1:]))
411 except ValueError:
412 pass
413 return u''
414 return _entity_re.sub(handle_match, unicode(self))
415
416 def striptags(self):
Armin Ronacher58f351d2008-05-28 21:30:14 +0200417 r"""Unescape markup into an unicode string and strip all tags. This
418 also resolves known HTML4 and XHTML entities. Whitespace is
419 normalized to one:
420
421 >>> Markup("Main &raquo; <em>About</em>").striptags()
422 u'Main \xbb About'
423 """
Armin Ronacher76c280b2008-05-04 12:31:48 +0200424 stripped = u' '.join(_striptags_re.sub('', self).split())
425 return Markup(stripped).unescape()
426
Armin Ronacherf35e2812008-05-06 16:04:10 +0200427 @classmethod
428 def escape(cls, s):
Armin Ronacher58f351d2008-05-28 21:30:14 +0200429 """Escape the string. Works like :func:`escape` with the difference
430 that for subclasses of :class:`Markup` this function would return the
431 correct subclass.
432 """
Armin Ronacherf35e2812008-05-06 16:04:10 +0200433 rv = escape(s)
434 if rv.__class__ is not cls:
435 return cls(rv)
436 return rv
437
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200438 def make_wrapper(name):
439 orig = getattr(unicode, name)
440 def func(self, *args, **kwargs):
Armin Ronacherd71fff02008-05-26 23:57:07 +0200441 args = _escape_argspec(list(args), enumerate(args))
442 _escape_argspec(kwargs, kwargs.iteritems())
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200443 return self.__class__(orig(self, *args, **kwargs))
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200444 func.__name__ = orig.__name__
445 func.__doc__ = orig.__doc__
446 return func
Armin Ronacherd71fff02008-05-26 23:57:07 +0200447
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200448 for method in '__getitem__', '__getslice__', 'capitalize', \
449 'title', 'lower', 'upper', 'replace', 'ljust', \
Armin Ronacher709f6e52008-04-28 18:18:16 +0200450 'rjust', 'lstrip', 'rstrip', 'center', 'strip', \
Armin Ronacher316157d2008-04-28 18:30:27 +0200451 'translate', 'expandtabs', 'swapcase', 'zfill':
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200452 locals()[method] = make_wrapper(method)
Armin Ronacher709f6e52008-04-28 18:18:16 +0200453
454 # new in python 2.5
455 if hasattr(unicode, 'partition'):
Armin Ronacherd71fff02008-05-26 23:57:07 +0200456 partition = make_wrapper('partition'),
457 rpartition = make_wrapper('rpartition')
458
459 # new in python 2.6
460 if hasattr(unicode, 'format'):
461 format = make_wrapper('format')
462
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200463 del method, make_wrapper
464
465
Armin Ronacherd71fff02008-05-26 23:57:07 +0200466def _escape_argspec(obj, iterable):
467 """Helper for various string-wrapped functions."""
468 for key, value in iterable:
469 if hasattr(value, '__html__') or isinstance(value, basestring):
470 obj[key] = escape(value)
471 return obj
472
473
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200474class _MarkupEscapeHelper(object):
475 """Helper for Markup.__mod__"""
476
477 def __init__(self, obj):
478 self.obj = obj
479
480 __getitem__ = lambda s, x: _MarkupEscapeHelper(s.obj[x])
481 __unicode__ = lambda s: unicode(escape(s.obj))
482 __str__ = lambda s: str(escape(s.obj))
Armin Ronacher3ef20432008-06-09 18:27:19 +0200483 __repr__ = lambda s: str(escape(repr(s.obj)))
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200484 __int__ = lambda s: int(s.obj)
485 __float__ = lambda s: float(s.obj)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200486
487
488class LRUCache(object):
489 """A simple LRU Cache implementation."""
Armin Ronacher58f351d2008-05-28 21:30:14 +0200490
491 # this is fast for small capacities (something below 1000) but doesn't
492 # scale. But as long as it's only used as storage for templates this
493 # won't do any harm.
Armin Ronacher814f6c22008-04-17 15:52:23 +0200494
495 def __init__(self, capacity):
496 self.capacity = capacity
497 self._mapping = {}
498 self._queue = deque()
Armin Ronacher7962ce72008-05-20 17:52:52 +0200499 self._postinit()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200500
Armin Ronacher7962ce72008-05-20 17:52:52 +0200501 def _postinit(self):
Armin Ronacher814f6c22008-04-17 15:52:23 +0200502 # alias all queue methods for faster lookup
503 self._popleft = self._queue.popleft
504 self._pop = self._queue.pop
505 if hasattr(self._queue, 'remove'):
506 self._remove = self._queue.remove
Armin Ronacher000b4912008-05-01 18:40:15 +0200507 self._wlock = allocate_lock()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200508 self._append = self._queue.append
509
510 def _remove(self, obj):
511 """Python 2.4 compatibility."""
512 for idx, item in enumerate(self._queue):
513 if item == obj:
514 del self._queue[idx]
515 break
516
Armin Ronacher7962ce72008-05-20 17:52:52 +0200517 def __getstate__(self):
518 return {
519 'capacity': self.capacity,
520 '_mapping': self._mapping,
521 '_queue': self._queue
522 }
523
524 def __setstate__(self, d):
525 self.__dict__.update(d)
526 self._postinit()
527
528 def __getnewargs__(self):
529 return (self.capacity,)
530
Armin Ronacher814f6c22008-04-17 15:52:23 +0200531 def copy(self):
532 """Return an shallow copy of the instance."""
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200533 rv = self.__class__(self.capacity)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200534 rv._mapping.update(self._mapping)
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200535 rv._queue = deque(self._queue)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200536 return rv
537
538 def get(self, key, default=None):
539 """Return an item from the cache dict or `default`"""
Armin Ronacher000b4912008-05-01 18:40:15 +0200540 try:
Armin Ronacher814f6c22008-04-17 15:52:23 +0200541 return self[key]
Armin Ronacher000b4912008-05-01 18:40:15 +0200542 except KeyError:
543 return default
Armin Ronacher814f6c22008-04-17 15:52:23 +0200544
545 def setdefault(self, key, default=None):
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200546 """Set `default` if the key is not in the cache otherwise
Armin Ronacher814f6c22008-04-17 15:52:23 +0200547 leave unchanged. Return the value of this key.
548 """
Armin Ronacher000b4912008-05-01 18:40:15 +0200549 try:
Armin Ronacher814f6c22008-04-17 15:52:23 +0200550 return self[key]
Armin Ronacher000b4912008-05-01 18:40:15 +0200551 except KeyError:
552 self[key] = default
553 return default
Armin Ronacher814f6c22008-04-17 15:52:23 +0200554
555 def clear(self):
556 """Clear the cache."""
Armin Ronacher000b4912008-05-01 18:40:15 +0200557 self._wlock.acquire()
558 try:
559 self._mapping.clear()
560 self._queue.clear()
561 finally:
562 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200563
564 def __contains__(self, key):
565 """Check if a key exists in this cache."""
566 return key in self._mapping
567
568 def __len__(self):
569 """Return the current size of the cache."""
570 return len(self._mapping)
571
572 def __repr__(self):
573 return '<%s %r>' % (
574 self.__class__.__name__,
575 self._mapping
576 )
577
578 def __getitem__(self, key):
579 """Get an item from the cache. Moves the item up so that it has the
580 highest priority then.
581
582 Raise an `KeyError` if it does not exist.
583 """
584 rv = self._mapping[key]
585 if self._queue[-1] != key:
586 self._remove(key)
587 self._append(key)
588 return rv
589
590 def __setitem__(self, key, value):
591 """Sets the value for an item. Moves the item up so that it
592 has the highest priority then.
593 """
Armin Ronacher000b4912008-05-01 18:40:15 +0200594 self._wlock.acquire()
595 try:
596 if key in self._mapping:
597 self._remove(key)
598 elif len(self._mapping) == self.capacity:
599 del self._mapping[self._popleft()]
600 self._append(key)
601 self._mapping[key] = value
602 finally:
603 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200604
605 def __delitem__(self, key):
606 """Remove an item from the cache dict.
607 Raise an `KeyError` if it does not exist.
608 """
Armin Ronacher000b4912008-05-01 18:40:15 +0200609 self._wlock.acquire()
610 try:
611 del self._mapping[key]
612 self._remove(key)
613 finally:
614 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200615
Armin Ronachere25f24d2008-05-19 11:20:41 +0200616 def items(self):
617 """Return a list of items."""
618 result = [(key, self._mapping[key]) for key in list(self._queue)]
619 result.reverse()
620 return result
621
622 def iteritems(self):
623 """Iterate over all items."""
624 return iter(self.items())
625
626 def values(self):
627 """Return a list of all values."""
628 return [x[1] for x in self.items()]
629
630 def itervalue(self):
631 """Iterate over all values."""
632 return iter(self.values())
633
634 def keys(self):
635 """Return a list of all keys ordered by most recent usage."""
636 return list(self)
637
638 def iterkeys(self):
639 """Iterate over all keys in the cache dict, ordered by
Armin Ronacher814f6c22008-04-17 15:52:23 +0200640 the most recent usage.
641 """
Armin Ronachere2244882008-05-19 09:25:57 +0200642 return reversed(tuple(self._queue))
Armin Ronacher814f6c22008-04-17 15:52:23 +0200643
Armin Ronachere25f24d2008-05-19 11:20:41 +0200644 __iter__ = iterkeys
645
Armin Ronacher814f6c22008-04-17 15:52:23 +0200646 def __reversed__(self):
647 """Iterate over the values in the cache dict, oldest items
648 coming first.
649 """
Armin Ronachere2244882008-05-19 09:25:57 +0200650 return iter(tuple(self._queue))
Armin Ronacher814f6c22008-04-17 15:52:23 +0200651
652 __copy__ = copy
653
Armin Ronacherbd33f112008-04-18 09:17:32 +0200654
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200655# register the LRU cache as mutable mapping if possible
656try:
657 from collections import MutableMapping
658 MutableMapping.register(LRUCache)
659except ImportError:
660 pass
661
662
Armin Ronacherccae0552008-10-05 23:08:58 +0200663class Cycler(object):
664 """A cycle helper for templates."""
665
666 def __init__(self, *items):
667 if not items:
668 raise RuntimeError('at least one item has to be provided')
669 self.items = items
670 self.reset()
671
672 def reset(self):
673 """Resets the cycle."""
674 self.pos = 0
675
676 @property
677 def current(self):
678 """Returns the current item."""
679 return self.items[self.pos]
680
681 def next(self):
682 """Goes one item ahead and returns it."""
683 rv = self.current
684 self.pos = (self.pos + 1) % len(self.items)
685 return rv
686
687
Armin Ronacherd34eb122008-10-13 23:47:51 +0200688class Joiner(object):
689 """A joining helper for templates."""
690
691 def __init__(self, sep=u', '):
692 self.sep = sep
693 self.used = False
694
695 def __call__(self):
696 if not self.used:
697 self.used = True
698 return u''
699 return self.sep
700
701
Armin Ronacherbd33f112008-04-18 09:17:32 +0200702# we have to import it down here as the speedups module imports the
703# markup type which is define above.
704try:
Armin Ronacherf59bac22008-04-20 13:11:43 +0200705 from jinja2._speedups import escape, soft_unicode
Armin Ronacherbd33f112008-04-18 09:17:32 +0200706except ImportError:
Lukas Meuserad48a2e2008-05-01 18:19:57 +0200707 def escape(s):
Armin Ronacherf35e2812008-05-06 16:04:10 +0200708 """Convert the characters &, <, >, ' and " in string s to HTML-safe
709 sequences. Use this if you need to display text that might contain
Armin Ronacher9a1e33c2008-05-05 22:00:46 +0200710 such characters in HTML. Marks return value as markup string.
Armin Ronacherbd33f112008-04-18 09:17:32 +0200711 """
Lukas Meuserad48a2e2008-05-01 18:19:57 +0200712 if hasattr(s, '__html__'):
713 return s.__html__()
714 return Markup(unicode(s)
Armin Ronacherbd33f112008-04-18 09:17:32 +0200715 .replace('&', '&amp;')
716 .replace('>', '&gt;')
717 .replace('<', '&lt;')
Armin Ronacherf35e2812008-05-06 16:04:10 +0200718 .replace("'", '&#39;')
Armin Ronacher9d42abf2008-05-14 18:10:41 +0200719 .replace('"', '&#34;')
Armin Ronacherbd33f112008-04-18 09:17:32 +0200720 )
Armin Ronacherf59bac22008-04-20 13:11:43 +0200721
722 def soft_unicode(s):
723 """Make a string unicode if it isn't already. That way a markup
724 string is not converted back to unicode.
725 """
726 if not isinstance(s, unicode):
727 s = unicode(s)
728 return s
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200729
730
731# partials
732try:
733 from functools import partial
734except ImportError:
735 class partial(object):
736 def __init__(self, _func, *args, **kwargs):
Benjamin Wiegand228c1832008-04-28 18:09:27 +0200737 self._func = _func
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200738 self._args = args
739 self._kwargs = kwargs
740 def __call__(self, *args, **kwargs):
741 kwargs.update(self._kwargs)
742 return self._func(*(self._args + args), **kwargs)