blob: e00dee21623851bed14085f7d49e9d3a3c85d65f [file] [log] [blame]
Armin Ronacher07bc6842008-03-31 14:18:49 +02001# -*- coding: utf-8 -*-
2"""
3 jinja2.utils
4 ~~~~~~~~~~~~
5
6 Utility functions.
7
Armin Ronacher55494e42010-01-22 09:41:48 +01008 :copyright: (c) 2010 by the Jinja Team.
Armin Ronacher07bc6842008-03-31 14:18:49 +02009 :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
Armin Ronacherd416a972009-02-24 22:58:00 +010038# internal code
39internal_code = set()
40
Armin Ronacher7259c762008-04-30 13:03:59 +020041
Armin Ronacher7ceced52008-05-03 10:15:31 +020042# concatenate a list of strings and convert them to unicode.
43# unfortunately there is a bug in python 2.4 and lower that causes
44# unicode.join trash the traceback.
Armin Ronachercda43df2008-05-03 17:10:05 +020045_concat = u''.join
Armin Ronacher7ceced52008-05-03 10:15:31 +020046try:
47 def _test_gen_bug():
48 raise TypeError(_test_gen_bug)
49 yield None
Armin Ronachercda43df2008-05-03 17:10:05 +020050 _concat(_test_gen_bug())
Armin Ronacher7ceced52008-05-03 10:15:31 +020051except TypeError, _error:
Armin Ronachercda43df2008-05-03 17:10:05 +020052 if not _error.args or _error.args[0] is not _test_gen_bug:
Armin Ronacher7ceced52008-05-03 10:15:31 +020053 def concat(gen):
54 try:
Armin Ronachercda43df2008-05-03 17:10:05 +020055 return _concat(list(gen))
Armin Ronacher7ceced52008-05-03 10:15:31 +020056 except:
57 # this hack is needed so that the current frame
58 # does not show up in the traceback.
59 exc_type, exc_value, tb = sys.exc_info()
60 raise exc_type, exc_value, tb.tb_next
Armin Ronachercda43df2008-05-03 17:10:05 +020061 else:
62 concat = _concat
Armin Ronacher7ceced52008-05-03 10:15:31 +020063 del _test_gen_bug, _error
64
65
Armin Ronacherbd357722009-08-05 20:25:06 +020066# for python 2.x we create outselves a next() function that does the
67# basics without exception catching.
68try:
69 next = next
70except NameError:
71 def next(x):
72 return x.next()
73
74
Armin Ronacher0d242be2010-02-10 01:35:13 +010075# if this python version is unable to deal with unicode filenames
76# when passed to encode we let this function encode it properly.
77# This is used in a couple of places. As far as Jinja is concerned
78# filenames are unicode *or* bytestrings in 2.x and unicode only in
79# 3.x because compile cannot handle bytes
80if sys.version_info < (3, 0):
81 def _encode_filename(filename):
82 if isinstance(filename, unicode):
83 return filename.encode('utf-8')
84 return filename
85else:
86 def _encode_filename(filename):
87 assert filename is None or isinstance(filename, str), \
88 'filenames must be strings'
89 return filename
90
91from keyword import iskeyword as is_python_keyword
Armin Ronacher9a0078d2008-08-13 18:24:17 +020092
93
94# common types. These do exist in the special types module too which however
Armin Ronacher0d242be2010-02-10 01:35:13 +010095# does not exist in IronPython out of the box. Also that way we don't have
96# to deal with implementation specific stuff here
Armin Ronacher9a0078d2008-08-13 18:24:17 +020097class _C(object):
98 def method(self): pass
99def _func():
100 yield None
101FunctionType = type(_func)
102GeneratorType = type(_func())
103MethodType = type(_C.method)
104CodeType = type(_C.method.func_code)
105try:
106 raise TypeError()
107except TypeError:
108 _tb = sys.exc_info()[2]
109 TracebackType = type(_tb)
110 FrameType = type(_tb.tb_frame)
111del _C, _tb, _func
112
113
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200114def contextfunction(f):
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200115 """This decorator can be used to mark a function or method context callable.
116 A context callable is passed the active :class:`Context` as first argument when
117 called from the template. This is useful if a function wants to get access
118 to the context or functions provided on the context object. For example
119 a function that returns a sorted list of template variables the current
120 template exports could look like this::
121
Armin Ronacher58f351d2008-05-28 21:30:14 +0200122 @contextfunction
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200123 def get_exported_names(context):
124 return sorted(context.exported_vars)
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200125 """
126 f.contextfunction = True
127 return f
128
129
Armin Ronacher8346bd72010-03-14 19:43:47 +0100130def evalcontextfunction(f):
131 """This decoraotr can be used to mark a function or method as an eval
132 context callable. This is similar to the :func:`contextfunction`
133 but instead of passing the context, an evaluation context object is
134 passed.
135
136 .. versionadded:: 2.4
137 """
138 f.evalcontextfunction = True
139 return f
140
141
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200142def environmentfunction(f):
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200143 """This decorator can be used to mark a function or method as environment
144 callable. This decorator works exactly like the :func:`contextfunction`
145 decorator just that the first argument is the active :class:`Environment`
146 and not context.
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200147 """
148 f.environmentfunction = True
149 return f
150
151
Armin Ronacherd416a972009-02-24 22:58:00 +0100152def internalcode(f):
153 """Marks the function as internally used"""
154 internal_code.add(f.func_code)
155 return f
156
157
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200158def is_undefined(obj):
159 """Check if the object passed is undefined. This does nothing more than
160 performing an instance check against :class:`Undefined` but looks nicer.
161 This can be used for custom filters or tests that want to react to
162 undefined variables. For example a custom default filter can look like
163 this::
164
165 def default(var, default=''):
166 if is_undefined(var):
167 return default
168 return var
169 """
170 from jinja2.runtime import Undefined
171 return isinstance(obj, Undefined)
172
173
Armin Ronacherba6e25a2008-11-02 15:58:14 +0100174def consume(iterable):
175 """Consumes an iterable without doing anything with it."""
176 for event in iterable:
177 pass
178
179
Armin Ronacher187bde12008-05-01 18:19:16 +0200180def clear_caches():
181 """Jinja2 keeps internal caches for environments and lexers. These are
182 used so that Jinja2 doesn't have to recreate environments and lexers all
183 the time. Normally you don't have to care about that but if you are
184 messuring memory consumption you may want to clean the caches.
185 """
186 from jinja2.environment import _spontaneous_environments
187 from jinja2.lexer import _lexer_cache
188 _spontaneous_environments.clear()
189 _lexer_cache.clear()
190
191
Armin Ronacherf59bac22008-04-20 13:11:43 +0200192def import_string(import_name, silent=False):
193 """Imports an object based on a string. This use useful if you want to
194 use import paths as endpoints or something similar. An import path can
195 be specified either in dotted notation (``xml.sax.saxutils.escape``)
196 or with a colon as object delimiter (``xml.sax.saxutils:escape``).
197
198 If the `silent` is True the return value will be `None` if the import
199 fails.
200
201 :return: imported object
Armin Ronacher9a027f42008-04-17 11:13:40 +0200202 """
Armin Ronacherf59bac22008-04-20 13:11:43 +0200203 try:
204 if ':' in import_name:
205 module, obj = import_name.split(':', 1)
206 elif '.' in import_name:
207 items = import_name.split('.')
208 module = '.'.join(items[:-1])
209 obj = items[-1]
210 else:
211 return __import__(import_name)
212 return getattr(__import__(module, None, None, [obj]), obj)
213 except (ImportError, AttributeError):
214 if not silent:
215 raise
Armin Ronacher9a027f42008-04-17 11:13:40 +0200216
217
Armin Ronacher0faa8612010-02-09 15:04:51 +0100218def open_if_exists(filename, mode='rb'):
Armin Ronacherccae0552008-10-05 23:08:58 +0200219 """Returns a file descriptor for the filename if that file exists,
220 otherwise `None`.
221 """
222 try:
Armin Ronacher790b8a82010-02-10 00:05:46 +0100223 return open(filename, mode)
Armin Ronacherccae0552008-10-05 23:08:58 +0200224 except IOError, e:
225 if e.errno not in (errno.ENOENT, errno.EISDIR):
226 raise
227
228
Christoph Hacke9e43bb2008-04-13 23:35:48 +0200229def pformat(obj, verbose=False):
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200230 """Prettyprint an object. Either use the `pretty` library or the
Christoph Hacke9e43bb2008-04-13 23:35:48 +0200231 builtin `pprint`.
232 """
233 try:
234 from pretty import pretty
235 return pretty(obj, verbose=verbose)
236 except ImportError:
237 from pprint import pformat
238 return pformat(obj)
Christoph Hack80909862008-04-14 01:35:10 +0200239
240
Christoph Hack80909862008-04-14 01:35:10 +0200241def urlize(text, trim_url_limit=None, nofollow=False):
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200242 """Converts any URLs in text into clickable links. Works on http://,
Christoph Hack80909862008-04-14 01:35:10 +0200243 https:// and www. links. Links can have trailing punctuation (periods,
244 commas, close-parens) and leading punctuation (opening parens) and
245 it'll still do the right thing.
246
247 If trim_url_limit is not None, the URLs in link text will be limited
248 to trim_url_limit characters.
249
250 If nofollow is True, the URLs in link text will get a rel="nofollow"
251 attribute.
252 """
253 trim_url = lambda x, limit=trim_url_limit: limit is not None \
254 and (x[:limit] + (len(x) >=limit and '...'
255 or '')) or x
Armin Ronacherd9342dc2008-11-17 00:35:30 +0100256 words = _word_split_re.split(unicode(escape(text)))
Christoph Hack80909862008-04-14 01:35:10 +0200257 nofollow_attr = nofollow and ' rel="nofollow"' or ''
258 for i, word in enumerate(words):
259 match = _punctuation_re.match(word)
260 if match:
261 lead, middle, trail = match.groups()
262 if middle.startswith('www.') or (
263 '@' not in middle and
264 not middle.startswith('http://') and
265 len(middle) > 0 and
Armin Ronacher9a0078d2008-08-13 18:24:17 +0200266 middle[0] in _letters + _digits and (
Christoph Hack80909862008-04-14 01:35:10 +0200267 middle.endswith('.org') or
268 middle.endswith('.net') or
269 middle.endswith('.com')
270 )):
271 middle = '<a href="http://%s"%s>%s</a>' % (middle,
272 nofollow_attr, trim_url(middle))
273 if middle.startswith('http://') or \
274 middle.startswith('https://'):
275 middle = '<a href="%s"%s>%s</a>' % (middle,
276 nofollow_attr, trim_url(middle))
277 if '@' in middle and not middle.startswith('www.') and \
278 not ':' in middle and _simple_email_re.match(middle):
279 middle = '<a href="mailto:%s">%s</a>' % (middle, middle)
280 if lead + middle + trail != word:
281 words[i] = lead + middle + trail
282 return u''.join(words)
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200283
284
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200285def generate_lorem_ipsum(n=5, html=True, min=20, max=100):
286 """Generate some lorem impsum for the template."""
287 from jinja2.constants import LOREM_IPSUM_WORDS
Georg Brandl95632c42009-11-22 18:35:18 +0100288 from random import choice, randrange
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200289 words = LOREM_IPSUM_WORDS.split()
290 result = []
291
292 for _ in xrange(n):
293 next_capitalized = True
294 last_comma = last_fullstop = 0
295 word = None
296 last = None
297 p = []
298
299 # each paragraph contains out of 20 to 100 words.
300 for idx, _ in enumerate(xrange(randrange(min, max))):
301 while True:
302 word = choice(words)
303 if word != last:
304 last = word
305 break
306 if next_capitalized:
307 word = word.capitalize()
308 next_capitalized = False
309 # add commas
310 if idx - randrange(3, 8) > last_comma:
311 last_comma = idx
312 last_fullstop += 2
313 word += ','
314 # add end of sentences
315 if idx - randrange(10, 20) > last_fullstop:
316 last_comma = last_fullstop = idx
317 word += '.'
318 next_capitalized = True
319 p.append(word)
320
321 # ensure that the paragraph ends with a dot.
322 p = u' '.join(p)
323 if p.endswith(','):
324 p = p[:-1] + '.'
325 elif not p.endswith('.'):
326 p += '.'
327 result.append(p)
328
329 if not html:
330 return u'\n\n'.join(result)
331 return Markup(u'\n'.join(u'<p>%s</p>' % escape(x) for x in result))
332
333
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200334class Markup(unicode):
Armin Ronacher58f351d2008-05-28 21:30:14 +0200335 r"""Marks a string as being safe for inclusion in HTML/XML output without
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200336 needing to be escaped. This implements the `__html__` interface a couple
Armin Ronacher58f351d2008-05-28 21:30:14 +0200337 of frameworks and web applications use. :class:`Markup` is a direct
338 subclass of `unicode` and provides all the methods of `unicode` just that
339 it escapes arguments passed and always returns `Markup`.
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200340
341 The `escape` function returns markup objects so that double escaping can't
Armin Ronacher88e1cb72008-09-08 23:55:32 +0200342 happen. If you want to use autoescaping in Jinja just enable the
343 autoescaping feature in the environment.
Armin Ronacher58f351d2008-05-28 21:30:14 +0200344
345 The constructor of the :class:`Markup` class can be used for three
346 different things: When passed an unicode object it's assumed to be safe,
347 when passed an object with an HTML representation (has an `__html__`
348 method) that representation is used, otherwise the object passed is
349 converted into a unicode string and then assumed to be safe:
350
351 >>> Markup("Hello <em>World</em>!")
352 Markup(u'Hello <em>World</em>!')
353 >>> class Foo(object):
354 ... def __html__(self):
355 ... return '<a href="#">foo</a>'
356 ...
357 >>> Markup(Foo())
358 Markup(u'<a href="#">foo</a>')
359
360 If you want object passed being always treated as unsafe you can use the
361 :meth:`escape` classmethod to create a :class:`Markup` object:
362
363 >>> Markup.escape("Hello <em>World</em>!")
364 Markup(u'Hello &lt;em&gt;World&lt;/em&gt;!')
365
366 Operations on a markup string are markup aware which means that all
367 arguments are passed through the :func:`escape` function:
368
369 >>> em = Markup("<em>%s</em>")
370 >>> em % "foo & bar"
371 Markup(u'<em>foo &amp; bar</em>')
372 >>> strong = Markup("<strong>%(text)s</strong>")
373 >>> strong % {'text': '<blink>hacker here</blink>'}
374 Markup(u'<strong>&lt;blink&gt;hacker here&lt;/blink&gt;</strong>')
375 >>> Markup("<em>Hello</em> ") + "<foo>"
376 Markup(u'<em>Hello</em> &lt;foo&gt;')
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200377 """
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200378 __slots__ = ()
379
Armin Ronacher3ef20432008-06-09 18:27:19 +0200380 def __new__(cls, base=u'', encoding=None, errors='strict'):
Armin Ronacher4e6f9a22008-05-23 23:57:38 +0200381 if hasattr(base, '__html__'):
382 base = base.__html__()
Armin Ronacher3ef20432008-06-09 18:27:19 +0200383 if encoding is None:
384 return unicode.__new__(cls, base)
385 return unicode.__new__(cls, base, encoding, errors)
Armin Ronacher4e6f9a22008-05-23 23:57:38 +0200386
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200387 def __html__(self):
388 return self
389
390 def __add__(self, other):
391 if hasattr(other, '__html__') or isinstance(other, basestring):
392 return self.__class__(unicode(self) + unicode(escape(other)))
393 return NotImplemented
394
395 def __radd__(self, other):
396 if hasattr(other, '__html__') or isinstance(other, basestring):
397 return self.__class__(unicode(escape(other)) + unicode(self))
398 return NotImplemented
399
400 def __mul__(self, num):
Armin Ronacherd71fff02008-05-26 23:57:07 +0200401 if isinstance(num, (int, long)):
402 return self.__class__(unicode.__mul__(self, num))
403 return NotImplemented
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200404 __rmul__ = __mul__
405
406 def __mod__(self, arg):
407 if isinstance(arg, tuple):
408 arg = tuple(imap(_MarkupEscapeHelper, arg))
409 else:
410 arg = _MarkupEscapeHelper(arg)
411 return self.__class__(unicode.__mod__(self, arg))
412
413 def __repr__(self):
414 return '%s(%s)' % (
415 self.__class__.__name__,
416 unicode.__repr__(self)
417 )
418
419 def join(self, seq):
420 return self.__class__(unicode.join(self, imap(escape, seq)))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200421 join.__doc__ = unicode.join.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200422
423 def split(self, *args, **kwargs):
424 return map(self.__class__, unicode.split(self, *args, **kwargs))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200425 split.__doc__ = unicode.split.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200426
427 def rsplit(self, *args, **kwargs):
428 return map(self.__class__, unicode.rsplit(self, *args, **kwargs))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200429 rsplit.__doc__ = unicode.rsplit.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200430
431 def splitlines(self, *args, **kwargs):
432 return map(self.__class__, unicode.splitlines(self, *args, **kwargs))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200433 splitlines.__doc__ = unicode.splitlines.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200434
Armin Ronacher76c280b2008-05-04 12:31:48 +0200435 def unescape(self):
Armin Ronacher58f351d2008-05-28 21:30:14 +0200436 r"""Unescape markup again into an unicode string. This also resolves
437 known HTML4 and XHTML entities:
438
439 >>> Markup("Main &raquo; <em>About</em>").unescape()
440 u'Main \xbb <em>About</em>'
441 """
Armin Ronacher9a0078d2008-08-13 18:24:17 +0200442 from jinja2.constants import HTML_ENTITIES
Armin Ronacher76c280b2008-05-04 12:31:48 +0200443 def handle_match(m):
444 name = m.group(1)
Armin Ronacher9a0078d2008-08-13 18:24:17 +0200445 if name in HTML_ENTITIES:
446 return unichr(HTML_ENTITIES[name])
Armin Ronacher76c280b2008-05-04 12:31:48 +0200447 try:
448 if name[:2] in ('#x', '#X'):
449 return unichr(int(name[2:], 16))
450 elif name.startswith('#'):
451 return unichr(int(name[1:]))
452 except ValueError:
453 pass
454 return u''
455 return _entity_re.sub(handle_match, unicode(self))
456
457 def striptags(self):
Armin Ronacher58f351d2008-05-28 21:30:14 +0200458 r"""Unescape markup into an unicode string and strip all tags. This
459 also resolves known HTML4 and XHTML entities. Whitespace is
460 normalized to one:
461
462 >>> Markup("Main &raquo; <em>About</em>").striptags()
463 u'Main \xbb About'
464 """
Armin Ronacher76c280b2008-05-04 12:31:48 +0200465 stripped = u' '.join(_striptags_re.sub('', self).split())
466 return Markup(stripped).unescape()
467
Armin Ronacherf35e2812008-05-06 16:04:10 +0200468 @classmethod
469 def escape(cls, s):
Armin Ronacher58f351d2008-05-28 21:30:14 +0200470 """Escape the string. Works like :func:`escape` with the difference
471 that for subclasses of :class:`Markup` this function would return the
472 correct subclass.
473 """
Armin Ronacherf35e2812008-05-06 16:04:10 +0200474 rv = escape(s)
475 if rv.__class__ is not cls:
476 return cls(rv)
477 return rv
478
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200479 def make_wrapper(name):
480 orig = getattr(unicode, name)
481 def func(self, *args, **kwargs):
Armin Ronacherd71fff02008-05-26 23:57:07 +0200482 args = _escape_argspec(list(args), enumerate(args))
483 _escape_argspec(kwargs, kwargs.iteritems())
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200484 return self.__class__(orig(self, *args, **kwargs))
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200485 func.__name__ = orig.__name__
486 func.__doc__ = orig.__doc__
487 return func
Armin Ronacherd71fff02008-05-26 23:57:07 +0200488
Armin Ronacher42a19882009-08-05 18:45:39 +0200489 for method in '__getitem__', 'capitalize', \
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200490 'title', 'lower', 'upper', 'replace', 'ljust', \
Armin Ronacher709f6e52008-04-28 18:18:16 +0200491 'rjust', 'lstrip', 'rstrip', 'center', 'strip', \
Armin Ronacher316157d2008-04-28 18:30:27 +0200492 'translate', 'expandtabs', 'swapcase', 'zfill':
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200493 locals()[method] = make_wrapper(method)
Armin Ronacher709f6e52008-04-28 18:18:16 +0200494
495 # new in python 2.5
496 if hasattr(unicode, 'partition'):
Armin Ronacherd71fff02008-05-26 23:57:07 +0200497 partition = make_wrapper('partition'),
498 rpartition = make_wrapper('rpartition')
499
500 # new in python 2.6
501 if hasattr(unicode, 'format'):
502 format = make_wrapper('format')
503
Armin Ronacher42a19882009-08-05 18:45:39 +0200504 # not in python 3
505 if hasattr(unicode, '__getslice__'):
506 __getslice__ = make_wrapper('__getslice__')
507
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200508 del method, make_wrapper
509
510
Armin Ronacherd71fff02008-05-26 23:57:07 +0200511def _escape_argspec(obj, iterable):
512 """Helper for various string-wrapped functions."""
513 for key, value in iterable:
514 if hasattr(value, '__html__') or isinstance(value, basestring):
515 obj[key] = escape(value)
516 return obj
517
518
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200519class _MarkupEscapeHelper(object):
520 """Helper for Markup.__mod__"""
521
522 def __init__(self, obj):
523 self.obj = obj
524
525 __getitem__ = lambda s, x: _MarkupEscapeHelper(s.obj[x])
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200526 __str__ = lambda s: str(escape(s.obj))
Armin Ronacher790b8a82010-02-10 00:05:46 +0100527 __unicode__ = lambda s: unicode(escape(s.obj))
Armin Ronacher3ef20432008-06-09 18:27:19 +0200528 __repr__ = lambda s: str(escape(repr(s.obj)))
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200529 __int__ = lambda s: int(s.obj)
530 __float__ = lambda s: float(s.obj)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200531
532
533class LRUCache(object):
534 """A simple LRU Cache implementation."""
Armin Ronacher58f351d2008-05-28 21:30:14 +0200535
536 # this is fast for small capacities (something below 1000) but doesn't
537 # scale. But as long as it's only used as storage for templates this
538 # won't do any harm.
Armin Ronacher814f6c22008-04-17 15:52:23 +0200539
540 def __init__(self, capacity):
541 self.capacity = capacity
542 self._mapping = {}
543 self._queue = deque()
Armin Ronacher7962ce72008-05-20 17:52:52 +0200544 self._postinit()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200545
Armin Ronacher7962ce72008-05-20 17:52:52 +0200546 def _postinit(self):
Armin Ronacher814f6c22008-04-17 15:52:23 +0200547 # alias all queue methods for faster lookup
548 self._popleft = self._queue.popleft
549 self._pop = self._queue.pop
550 if hasattr(self._queue, 'remove'):
551 self._remove = self._queue.remove
Armin Ronacher000b4912008-05-01 18:40:15 +0200552 self._wlock = allocate_lock()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200553 self._append = self._queue.append
554
555 def _remove(self, obj):
556 """Python 2.4 compatibility."""
557 for idx, item in enumerate(self._queue):
558 if item == obj:
559 del self._queue[idx]
560 break
561
Armin Ronacher7962ce72008-05-20 17:52:52 +0200562 def __getstate__(self):
563 return {
564 'capacity': self.capacity,
565 '_mapping': self._mapping,
566 '_queue': self._queue
567 }
568
569 def __setstate__(self, d):
570 self.__dict__.update(d)
571 self._postinit()
572
573 def __getnewargs__(self):
574 return (self.capacity,)
575
Armin Ronacher814f6c22008-04-17 15:52:23 +0200576 def copy(self):
577 """Return an shallow copy of the instance."""
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200578 rv = self.__class__(self.capacity)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200579 rv._mapping.update(self._mapping)
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200580 rv._queue = deque(self._queue)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200581 return rv
582
583 def get(self, key, default=None):
584 """Return an item from the cache dict or `default`"""
Armin Ronacher000b4912008-05-01 18:40:15 +0200585 try:
Armin Ronacher814f6c22008-04-17 15:52:23 +0200586 return self[key]
Armin Ronacher000b4912008-05-01 18:40:15 +0200587 except KeyError:
588 return default
Armin Ronacher814f6c22008-04-17 15:52:23 +0200589
590 def setdefault(self, key, default=None):
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200591 """Set `default` if the key is not in the cache otherwise
Armin Ronacher814f6c22008-04-17 15:52:23 +0200592 leave unchanged. Return the value of this key.
593 """
Armin Ronacher000b4912008-05-01 18:40:15 +0200594 try:
Armin Ronacher814f6c22008-04-17 15:52:23 +0200595 return self[key]
Armin Ronacher000b4912008-05-01 18:40:15 +0200596 except KeyError:
597 self[key] = default
598 return default
Armin Ronacher814f6c22008-04-17 15:52:23 +0200599
600 def clear(self):
601 """Clear the cache."""
Armin Ronacher000b4912008-05-01 18:40:15 +0200602 self._wlock.acquire()
603 try:
604 self._mapping.clear()
605 self._queue.clear()
606 finally:
607 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200608
609 def __contains__(self, key):
610 """Check if a key exists in this cache."""
611 return key in self._mapping
612
613 def __len__(self):
614 """Return the current size of the cache."""
615 return len(self._mapping)
616
617 def __repr__(self):
618 return '<%s %r>' % (
619 self.__class__.__name__,
620 self._mapping
621 )
622
623 def __getitem__(self, key):
624 """Get an item from the cache. Moves the item up so that it has the
625 highest priority then.
626
627 Raise an `KeyError` if it does not exist.
628 """
629 rv = self._mapping[key]
630 if self._queue[-1] != key:
Armin Ronacher8de6f182009-01-12 11:08:26 +0100631 try:
632 self._remove(key)
Armin Ronachere7c72bc2009-09-14 12:20:33 -0700633 except ValueError:
Armin Ronacher8de6f182009-01-12 11:08:26 +0100634 # if something removed the key from the container
635 # when we read, ignore the ValueError that we would
636 # get otherwise.
637 pass
Armin Ronacher814f6c22008-04-17 15:52:23 +0200638 self._append(key)
639 return rv
640
641 def __setitem__(self, key, value):
642 """Sets the value for an item. Moves the item up so that it
643 has the highest priority then.
644 """
Armin Ronacher000b4912008-05-01 18:40:15 +0200645 self._wlock.acquire()
646 try:
647 if key in self._mapping:
Armin Ronacher74230e62009-10-25 12:46:31 +0100648 try:
649 self._remove(key)
650 except ValueError:
651 # __getitem__ is not locked, it might happen
652 pass
Armin Ronacher000b4912008-05-01 18:40:15 +0200653 elif len(self._mapping) == self.capacity:
654 del self._mapping[self._popleft()]
655 self._append(key)
656 self._mapping[key] = value
657 finally:
658 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200659
660 def __delitem__(self, key):
661 """Remove an item from the cache dict.
662 Raise an `KeyError` if it does not exist.
663 """
Armin Ronacher000b4912008-05-01 18:40:15 +0200664 self._wlock.acquire()
665 try:
666 del self._mapping[key]
Armin Ronachere7c72bc2009-09-14 12:20:33 -0700667 try:
668 self._remove(key)
669 except ValueError:
670 # __getitem__ is not locked, it might happen
671 pass
Armin Ronacher000b4912008-05-01 18:40:15 +0200672 finally:
673 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200674
Armin Ronachere25f24d2008-05-19 11:20:41 +0200675 def items(self):
676 """Return a list of items."""
677 result = [(key, self._mapping[key]) for key in list(self._queue)]
678 result.reverse()
679 return result
680
681 def iteritems(self):
682 """Iterate over all items."""
683 return iter(self.items())
684
685 def values(self):
686 """Return a list of all values."""
687 return [x[1] for x in self.items()]
688
689 def itervalue(self):
690 """Iterate over all values."""
691 return iter(self.values())
692
693 def keys(self):
694 """Return a list of all keys ordered by most recent usage."""
695 return list(self)
696
697 def iterkeys(self):
698 """Iterate over all keys in the cache dict, ordered by
Armin Ronacher814f6c22008-04-17 15:52:23 +0200699 the most recent usage.
700 """
Armin Ronachere2244882008-05-19 09:25:57 +0200701 return reversed(tuple(self._queue))
Armin Ronacher814f6c22008-04-17 15:52:23 +0200702
Armin Ronachere25f24d2008-05-19 11:20:41 +0200703 __iter__ = iterkeys
704
Armin Ronacher814f6c22008-04-17 15:52:23 +0200705 def __reversed__(self):
706 """Iterate over the values in the cache dict, oldest items
707 coming first.
708 """
Armin Ronachere2244882008-05-19 09:25:57 +0200709 return iter(tuple(self._queue))
Armin Ronacher814f6c22008-04-17 15:52:23 +0200710
711 __copy__ = copy
712
Armin Ronacherbd33f112008-04-18 09:17:32 +0200713
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200714# register the LRU cache as mutable mapping if possible
715try:
716 from collections import MutableMapping
717 MutableMapping.register(LRUCache)
718except ImportError:
719 pass
720
721
Armin Ronacherccae0552008-10-05 23:08:58 +0200722class Cycler(object):
723 """A cycle helper for templates."""
724
725 def __init__(self, *items):
726 if not items:
727 raise RuntimeError('at least one item has to be provided')
728 self.items = items
729 self.reset()
730
731 def reset(self):
732 """Resets the cycle."""
733 self.pos = 0
734
735 @property
736 def current(self):
737 """Returns the current item."""
738 return self.items[self.pos]
739
740 def next(self):
741 """Goes one item ahead and returns it."""
742 rv = self.current
743 self.pos = (self.pos + 1) % len(self.items)
744 return rv
745
746
Armin Ronacherd34eb122008-10-13 23:47:51 +0200747class Joiner(object):
748 """A joining helper for templates."""
749
750 def __init__(self, sep=u', '):
751 self.sep = sep
752 self.used = False
753
754 def __call__(self):
755 if not self.used:
756 self.used = True
757 return u''
758 return self.sep
759
760
Armin Ronacherbd33f112008-04-18 09:17:32 +0200761# we have to import it down here as the speedups module imports the
762# markup type which is define above.
763try:
Armin Ronacherf59bac22008-04-20 13:11:43 +0200764 from jinja2._speedups import escape, soft_unicode
Armin Ronacherbd33f112008-04-18 09:17:32 +0200765except ImportError:
Lukas Meuserad48a2e2008-05-01 18:19:57 +0200766 def escape(s):
Armin Ronacherf35e2812008-05-06 16:04:10 +0200767 """Convert the characters &, <, >, ' and " in string s to HTML-safe
768 sequences. Use this if you need to display text that might contain
Armin Ronacher9a1e33c2008-05-05 22:00:46 +0200769 such characters in HTML. Marks return value as markup string.
Armin Ronacherbd33f112008-04-18 09:17:32 +0200770 """
Lukas Meuserad48a2e2008-05-01 18:19:57 +0200771 if hasattr(s, '__html__'):
772 return s.__html__()
773 return Markup(unicode(s)
Armin Ronacherbd33f112008-04-18 09:17:32 +0200774 .replace('&', '&amp;')
775 .replace('>', '&gt;')
776 .replace('<', '&lt;')
Armin Ronacherf35e2812008-05-06 16:04:10 +0200777 .replace("'", '&#39;')
Armin Ronacher9d42abf2008-05-14 18:10:41 +0200778 .replace('"', '&#34;')
Armin Ronacherbd33f112008-04-18 09:17:32 +0200779 )
Armin Ronacherf59bac22008-04-20 13:11:43 +0200780
781 def soft_unicode(s):
782 """Make a string unicode if it isn't already. That way a markup
783 string is not converted back to unicode.
784 """
785 if not isinstance(s, unicode):
786 s = unicode(s)
787 return s
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200788
789
790# partials
791try:
792 from functools import partial
793except ImportError:
794 class partial(object):
795 def __init__(self, _func, *args, **kwargs):
Benjamin Wiegand228c1832008-04-28 18:09:27 +0200796 self._func = _func
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200797 self._args = args
798 self._kwargs = kwargs
799 def __call__(self, *args, **kwargs):
800 kwargs.update(self._kwargs)
801 return self._func(*(self._args + args), **kwargs)