blob: 0ba86e768909a99798ceee69fbba9fb0d6d599e1 [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
Armin Ronacherfe150f32010-03-15 02:42:41 +0100134 passed. For more information about the eval context, see
135 :ref:`eval-context`.
Armin Ronacher8346bd72010-03-14 19:43:47 +0100136
137 .. versionadded:: 2.4
138 """
139 f.evalcontextfunction = True
140 return f
141
142
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200143def environmentfunction(f):
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200144 """This decorator can be used to mark a function or method as environment
145 callable. This decorator works exactly like the :func:`contextfunction`
146 decorator just that the first argument is the active :class:`Environment`
147 and not context.
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200148 """
149 f.environmentfunction = True
150 return f
151
152
Armin Ronacherd416a972009-02-24 22:58:00 +0100153def internalcode(f):
154 """Marks the function as internally used"""
155 internal_code.add(f.func_code)
156 return f
157
158
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200159def is_undefined(obj):
160 """Check if the object passed is undefined. This does nothing more than
161 performing an instance check against :class:`Undefined` but looks nicer.
162 This can be used for custom filters or tests that want to react to
163 undefined variables. For example a custom default filter can look like
164 this::
165
166 def default(var, default=''):
167 if is_undefined(var):
168 return default
169 return var
170 """
171 from jinja2.runtime import Undefined
172 return isinstance(obj, Undefined)
173
174
Armin Ronacherba6e25a2008-11-02 15:58:14 +0100175def consume(iterable):
176 """Consumes an iterable without doing anything with it."""
177 for event in iterable:
178 pass
179
180
Armin Ronacher187bde12008-05-01 18:19:16 +0200181def clear_caches():
182 """Jinja2 keeps internal caches for environments and lexers. These are
183 used so that Jinja2 doesn't have to recreate environments and lexers all
184 the time. Normally you don't have to care about that but if you are
185 messuring memory consumption you may want to clean the caches.
186 """
187 from jinja2.environment import _spontaneous_environments
188 from jinja2.lexer import _lexer_cache
189 _spontaneous_environments.clear()
190 _lexer_cache.clear()
191
192
Armin Ronacherf59bac22008-04-20 13:11:43 +0200193def import_string(import_name, silent=False):
194 """Imports an object based on a string. This use useful if you want to
195 use import paths as endpoints or something similar. An import path can
196 be specified either in dotted notation (``xml.sax.saxutils.escape``)
197 or with a colon as object delimiter (``xml.sax.saxutils:escape``).
198
199 If the `silent` is True the return value will be `None` if the import
200 fails.
201
202 :return: imported object
Armin Ronacher9a027f42008-04-17 11:13:40 +0200203 """
Armin Ronacherf59bac22008-04-20 13:11:43 +0200204 try:
205 if ':' in import_name:
206 module, obj = import_name.split(':', 1)
207 elif '.' in import_name:
208 items = import_name.split('.')
209 module = '.'.join(items[:-1])
210 obj = items[-1]
211 else:
212 return __import__(import_name)
213 return getattr(__import__(module, None, None, [obj]), obj)
214 except (ImportError, AttributeError):
215 if not silent:
216 raise
Armin Ronacher9a027f42008-04-17 11:13:40 +0200217
218
Armin Ronacher0faa8612010-02-09 15:04:51 +0100219def open_if_exists(filename, mode='rb'):
Armin Ronacherccae0552008-10-05 23:08:58 +0200220 """Returns a file descriptor for the filename if that file exists,
221 otherwise `None`.
222 """
223 try:
Armin Ronacher790b8a82010-02-10 00:05:46 +0100224 return open(filename, mode)
Armin Ronacherccae0552008-10-05 23:08:58 +0200225 except IOError, e:
226 if e.errno not in (errno.ENOENT, errno.EISDIR):
227 raise
228
229
Armin Ronacher98dbf5f2010-04-12 15:49:59 +0200230def object_type_repr(obj):
231 """Returns the name of the object's type. For some recognized
232 singletons the name of the object is returned instead. (For
233 example for `None` and `Ellipsis`).
234 """
235 if obj is None:
236 return 'None'
237 elif obj is Ellipsis:
238 return 'Ellipsis'
239 if obj.__class__.__module__ == '__builtin__':
240 name = obj.__name__
241 else:
242 name = obj.__class__.module__ + '.' + obj.__name__
243 return '%s object' % name
244
245
Christoph Hacke9e43bb2008-04-13 23:35:48 +0200246def pformat(obj, verbose=False):
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200247 """Prettyprint an object. Either use the `pretty` library or the
Christoph Hacke9e43bb2008-04-13 23:35:48 +0200248 builtin `pprint`.
249 """
250 try:
251 from pretty import pretty
252 return pretty(obj, verbose=verbose)
253 except ImportError:
254 from pprint import pformat
255 return pformat(obj)
Christoph Hack80909862008-04-14 01:35:10 +0200256
257
Christoph Hack80909862008-04-14 01:35:10 +0200258def urlize(text, trim_url_limit=None, nofollow=False):
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200259 """Converts any URLs in text into clickable links. Works on http://,
Christoph Hack80909862008-04-14 01:35:10 +0200260 https:// and www. links. Links can have trailing punctuation (periods,
261 commas, close-parens) and leading punctuation (opening parens) and
262 it'll still do the right thing.
263
264 If trim_url_limit is not None, the URLs in link text will be limited
265 to trim_url_limit characters.
266
267 If nofollow is True, the URLs in link text will get a rel="nofollow"
268 attribute.
269 """
270 trim_url = lambda x, limit=trim_url_limit: limit is not None \
271 and (x[:limit] + (len(x) >=limit and '...'
272 or '')) or x
Armin Ronacherd9342dc2008-11-17 00:35:30 +0100273 words = _word_split_re.split(unicode(escape(text)))
Christoph Hack80909862008-04-14 01:35:10 +0200274 nofollow_attr = nofollow and ' rel="nofollow"' or ''
275 for i, word in enumerate(words):
276 match = _punctuation_re.match(word)
277 if match:
278 lead, middle, trail = match.groups()
279 if middle.startswith('www.') or (
280 '@' not in middle and
281 not middle.startswith('http://') and
282 len(middle) > 0 and
Armin Ronacher9a0078d2008-08-13 18:24:17 +0200283 middle[0] in _letters + _digits and (
Christoph Hack80909862008-04-14 01:35:10 +0200284 middle.endswith('.org') or
285 middle.endswith('.net') or
286 middle.endswith('.com')
287 )):
288 middle = '<a href="http://%s"%s>%s</a>' % (middle,
289 nofollow_attr, trim_url(middle))
290 if middle.startswith('http://') or \
291 middle.startswith('https://'):
292 middle = '<a href="%s"%s>%s</a>' % (middle,
293 nofollow_attr, trim_url(middle))
294 if '@' in middle and not middle.startswith('www.') and \
295 not ':' in middle and _simple_email_re.match(middle):
296 middle = '<a href="mailto:%s">%s</a>' % (middle, middle)
297 if lead + middle + trail != word:
298 words[i] = lead + middle + trail
299 return u''.join(words)
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200300
301
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200302def generate_lorem_ipsum(n=5, html=True, min=20, max=100):
303 """Generate some lorem impsum for the template."""
304 from jinja2.constants import LOREM_IPSUM_WORDS
Georg Brandl95632c42009-11-22 18:35:18 +0100305 from random import choice, randrange
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200306 words = LOREM_IPSUM_WORDS.split()
307 result = []
308
309 for _ in xrange(n):
310 next_capitalized = True
311 last_comma = last_fullstop = 0
312 word = None
313 last = None
314 p = []
315
316 # each paragraph contains out of 20 to 100 words.
317 for idx, _ in enumerate(xrange(randrange(min, max))):
318 while True:
319 word = choice(words)
320 if word != last:
321 last = word
322 break
323 if next_capitalized:
324 word = word.capitalize()
325 next_capitalized = False
326 # add commas
327 if idx - randrange(3, 8) > last_comma:
328 last_comma = idx
329 last_fullstop += 2
330 word += ','
331 # add end of sentences
332 if idx - randrange(10, 20) > last_fullstop:
333 last_comma = last_fullstop = idx
334 word += '.'
335 next_capitalized = True
336 p.append(word)
337
338 # ensure that the paragraph ends with a dot.
339 p = u' '.join(p)
340 if p.endswith(','):
341 p = p[:-1] + '.'
342 elif not p.endswith('.'):
343 p += '.'
344 result.append(p)
345
346 if not html:
347 return u'\n\n'.join(result)
348 return Markup(u'\n'.join(u'<p>%s</p>' % escape(x) for x in result))
349
350
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200351class Markup(unicode):
Armin Ronacher58f351d2008-05-28 21:30:14 +0200352 r"""Marks a string as being safe for inclusion in HTML/XML output without
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200353 needing to be escaped. This implements the `__html__` interface a couple
Armin Ronacher58f351d2008-05-28 21:30:14 +0200354 of frameworks and web applications use. :class:`Markup` is a direct
355 subclass of `unicode` and provides all the methods of `unicode` just that
356 it escapes arguments passed and always returns `Markup`.
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200357
358 The `escape` function returns markup objects so that double escaping can't
Armin Ronacher88e1cb72008-09-08 23:55:32 +0200359 happen. If you want to use autoescaping in Jinja just enable the
360 autoescaping feature in the environment.
Armin Ronacher58f351d2008-05-28 21:30:14 +0200361
362 The constructor of the :class:`Markup` class can be used for three
363 different things: When passed an unicode object it's assumed to be safe,
364 when passed an object with an HTML representation (has an `__html__`
365 method) that representation is used, otherwise the object passed is
366 converted into a unicode string and then assumed to be safe:
367
368 >>> Markup("Hello <em>World</em>!")
369 Markup(u'Hello <em>World</em>!')
370 >>> class Foo(object):
371 ... def __html__(self):
372 ... return '<a href="#">foo</a>'
373 ...
374 >>> Markup(Foo())
375 Markup(u'<a href="#">foo</a>')
376
377 If you want object passed being always treated as unsafe you can use the
378 :meth:`escape` classmethod to create a :class:`Markup` object:
379
380 >>> Markup.escape("Hello <em>World</em>!")
381 Markup(u'Hello &lt;em&gt;World&lt;/em&gt;!')
382
383 Operations on a markup string are markup aware which means that all
384 arguments are passed through the :func:`escape` function:
385
386 >>> em = Markup("<em>%s</em>")
387 >>> em % "foo & bar"
388 Markup(u'<em>foo &amp; bar</em>')
389 >>> strong = Markup("<strong>%(text)s</strong>")
390 >>> strong % {'text': '<blink>hacker here</blink>'}
391 Markup(u'<strong>&lt;blink&gt;hacker here&lt;/blink&gt;</strong>')
392 >>> Markup("<em>Hello</em> ") + "<foo>"
393 Markup(u'<em>Hello</em> &lt;foo&gt;')
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200394 """
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200395 __slots__ = ()
396
Armin Ronacher3ef20432008-06-09 18:27:19 +0200397 def __new__(cls, base=u'', encoding=None, errors='strict'):
Armin Ronacher4e6f9a22008-05-23 23:57:38 +0200398 if hasattr(base, '__html__'):
399 base = base.__html__()
Armin Ronacher3ef20432008-06-09 18:27:19 +0200400 if encoding is None:
401 return unicode.__new__(cls, base)
402 return unicode.__new__(cls, base, encoding, errors)
Armin Ronacher4e6f9a22008-05-23 23:57:38 +0200403
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200404 def __html__(self):
405 return self
406
407 def __add__(self, other):
408 if hasattr(other, '__html__') or isinstance(other, basestring):
409 return self.__class__(unicode(self) + unicode(escape(other)))
410 return NotImplemented
411
412 def __radd__(self, other):
413 if hasattr(other, '__html__') or isinstance(other, basestring):
414 return self.__class__(unicode(escape(other)) + unicode(self))
415 return NotImplemented
416
417 def __mul__(self, num):
Armin Ronacherd71fff02008-05-26 23:57:07 +0200418 if isinstance(num, (int, long)):
419 return self.__class__(unicode.__mul__(self, num))
420 return NotImplemented
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200421 __rmul__ = __mul__
422
423 def __mod__(self, arg):
424 if isinstance(arg, tuple):
425 arg = tuple(imap(_MarkupEscapeHelper, arg))
426 else:
427 arg = _MarkupEscapeHelper(arg)
428 return self.__class__(unicode.__mod__(self, arg))
429
430 def __repr__(self):
431 return '%s(%s)' % (
432 self.__class__.__name__,
433 unicode.__repr__(self)
434 )
435
436 def join(self, seq):
437 return self.__class__(unicode.join(self, imap(escape, seq)))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200438 join.__doc__ = unicode.join.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200439
440 def split(self, *args, **kwargs):
441 return map(self.__class__, unicode.split(self, *args, **kwargs))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200442 split.__doc__ = unicode.split.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200443
444 def rsplit(self, *args, **kwargs):
445 return map(self.__class__, unicode.rsplit(self, *args, **kwargs))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200446 rsplit.__doc__ = unicode.rsplit.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200447
448 def splitlines(self, *args, **kwargs):
449 return map(self.__class__, unicode.splitlines(self, *args, **kwargs))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200450 splitlines.__doc__ = unicode.splitlines.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200451
Armin Ronacher76c280b2008-05-04 12:31:48 +0200452 def unescape(self):
Armin Ronacher58f351d2008-05-28 21:30:14 +0200453 r"""Unescape markup again into an unicode string. This also resolves
454 known HTML4 and XHTML entities:
455
456 >>> Markup("Main &raquo; <em>About</em>").unescape()
457 u'Main \xbb <em>About</em>'
458 """
Armin Ronacher9a0078d2008-08-13 18:24:17 +0200459 from jinja2.constants import HTML_ENTITIES
Armin Ronacher76c280b2008-05-04 12:31:48 +0200460 def handle_match(m):
461 name = m.group(1)
Armin Ronacher9a0078d2008-08-13 18:24:17 +0200462 if name in HTML_ENTITIES:
463 return unichr(HTML_ENTITIES[name])
Armin Ronacher76c280b2008-05-04 12:31:48 +0200464 try:
465 if name[:2] in ('#x', '#X'):
466 return unichr(int(name[2:], 16))
467 elif name.startswith('#'):
468 return unichr(int(name[1:]))
469 except ValueError:
470 pass
471 return u''
472 return _entity_re.sub(handle_match, unicode(self))
473
474 def striptags(self):
Armin Ronacher58f351d2008-05-28 21:30:14 +0200475 r"""Unescape markup into an unicode string and strip all tags. This
476 also resolves known HTML4 and XHTML entities. Whitespace is
477 normalized to one:
478
479 >>> Markup("Main &raquo; <em>About</em>").striptags()
480 u'Main \xbb About'
481 """
Armin Ronacher76c280b2008-05-04 12:31:48 +0200482 stripped = u' '.join(_striptags_re.sub('', self).split())
483 return Markup(stripped).unescape()
484
Armin Ronacherf35e2812008-05-06 16:04:10 +0200485 @classmethod
486 def escape(cls, s):
Armin Ronacher58f351d2008-05-28 21:30:14 +0200487 """Escape the string. Works like :func:`escape` with the difference
488 that for subclasses of :class:`Markup` this function would return the
489 correct subclass.
490 """
Armin Ronacherf35e2812008-05-06 16:04:10 +0200491 rv = escape(s)
492 if rv.__class__ is not cls:
493 return cls(rv)
494 return rv
495
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200496 def make_wrapper(name):
497 orig = getattr(unicode, name)
498 def func(self, *args, **kwargs):
Armin Ronacherd71fff02008-05-26 23:57:07 +0200499 args = _escape_argspec(list(args), enumerate(args))
500 _escape_argspec(kwargs, kwargs.iteritems())
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200501 return self.__class__(orig(self, *args, **kwargs))
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200502 func.__name__ = orig.__name__
503 func.__doc__ = orig.__doc__
504 return func
Armin Ronacherd71fff02008-05-26 23:57:07 +0200505
Armin Ronacher42a19882009-08-05 18:45:39 +0200506 for method in '__getitem__', 'capitalize', \
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200507 'title', 'lower', 'upper', 'replace', 'ljust', \
Armin Ronacher709f6e52008-04-28 18:18:16 +0200508 'rjust', 'lstrip', 'rstrip', 'center', 'strip', \
Armin Ronacher316157d2008-04-28 18:30:27 +0200509 'translate', 'expandtabs', 'swapcase', 'zfill':
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200510 locals()[method] = make_wrapper(method)
Armin Ronacher709f6e52008-04-28 18:18:16 +0200511
512 # new in python 2.5
513 if hasattr(unicode, 'partition'):
Armin Ronacherd71fff02008-05-26 23:57:07 +0200514 partition = make_wrapper('partition'),
515 rpartition = make_wrapper('rpartition')
516
517 # new in python 2.6
518 if hasattr(unicode, 'format'):
519 format = make_wrapper('format')
520
Armin Ronacher42a19882009-08-05 18:45:39 +0200521 # not in python 3
522 if hasattr(unicode, '__getslice__'):
523 __getslice__ = make_wrapper('__getslice__')
524
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200525 del method, make_wrapper
526
527
Armin Ronacherd71fff02008-05-26 23:57:07 +0200528def _escape_argspec(obj, iterable):
529 """Helper for various string-wrapped functions."""
530 for key, value in iterable:
531 if hasattr(value, '__html__') or isinstance(value, basestring):
532 obj[key] = escape(value)
533 return obj
534
535
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200536class _MarkupEscapeHelper(object):
537 """Helper for Markup.__mod__"""
538
539 def __init__(self, obj):
540 self.obj = obj
541
542 __getitem__ = lambda s, x: _MarkupEscapeHelper(s.obj[x])
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200543 __str__ = lambda s: str(escape(s.obj))
Armin Ronacher790b8a82010-02-10 00:05:46 +0100544 __unicode__ = lambda s: unicode(escape(s.obj))
Armin Ronacher3ef20432008-06-09 18:27:19 +0200545 __repr__ = lambda s: str(escape(repr(s.obj)))
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200546 __int__ = lambda s: int(s.obj)
547 __float__ = lambda s: float(s.obj)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200548
549
550class LRUCache(object):
551 """A simple LRU Cache implementation."""
Armin Ronacher58f351d2008-05-28 21:30:14 +0200552
553 # this is fast for small capacities (something below 1000) but doesn't
554 # scale. But as long as it's only used as storage for templates this
555 # won't do any harm.
Armin Ronacher814f6c22008-04-17 15:52:23 +0200556
557 def __init__(self, capacity):
558 self.capacity = capacity
559 self._mapping = {}
560 self._queue = deque()
Armin Ronacher7962ce72008-05-20 17:52:52 +0200561 self._postinit()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200562
Armin Ronacher7962ce72008-05-20 17:52:52 +0200563 def _postinit(self):
Armin Ronacher814f6c22008-04-17 15:52:23 +0200564 # alias all queue methods for faster lookup
565 self._popleft = self._queue.popleft
566 self._pop = self._queue.pop
567 if hasattr(self._queue, 'remove'):
568 self._remove = self._queue.remove
Armin Ronacher000b4912008-05-01 18:40:15 +0200569 self._wlock = allocate_lock()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200570 self._append = self._queue.append
571
572 def _remove(self, obj):
573 """Python 2.4 compatibility."""
574 for idx, item in enumerate(self._queue):
575 if item == obj:
576 del self._queue[idx]
577 break
578
Armin Ronacher7962ce72008-05-20 17:52:52 +0200579 def __getstate__(self):
580 return {
581 'capacity': self.capacity,
582 '_mapping': self._mapping,
583 '_queue': self._queue
584 }
585
586 def __setstate__(self, d):
587 self.__dict__.update(d)
588 self._postinit()
589
590 def __getnewargs__(self):
591 return (self.capacity,)
592
Armin Ronacher814f6c22008-04-17 15:52:23 +0200593 def copy(self):
594 """Return an shallow copy of the instance."""
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200595 rv = self.__class__(self.capacity)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200596 rv._mapping.update(self._mapping)
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200597 rv._queue = deque(self._queue)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200598 return rv
599
600 def get(self, key, default=None):
601 """Return an item from the cache dict or `default`"""
Armin Ronacher000b4912008-05-01 18:40:15 +0200602 try:
Armin Ronacher814f6c22008-04-17 15:52:23 +0200603 return self[key]
Armin Ronacher000b4912008-05-01 18:40:15 +0200604 except KeyError:
605 return default
Armin Ronacher814f6c22008-04-17 15:52:23 +0200606
607 def setdefault(self, key, default=None):
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200608 """Set `default` if the key is not in the cache otherwise
Armin Ronacher814f6c22008-04-17 15:52:23 +0200609 leave unchanged. Return the value of this key.
610 """
Armin Ronacher000b4912008-05-01 18:40:15 +0200611 try:
Armin Ronacher814f6c22008-04-17 15:52:23 +0200612 return self[key]
Armin Ronacher000b4912008-05-01 18:40:15 +0200613 except KeyError:
614 self[key] = default
615 return default
Armin Ronacher814f6c22008-04-17 15:52:23 +0200616
617 def clear(self):
618 """Clear the cache."""
Armin Ronacher000b4912008-05-01 18:40:15 +0200619 self._wlock.acquire()
620 try:
621 self._mapping.clear()
622 self._queue.clear()
623 finally:
624 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200625
626 def __contains__(self, key):
627 """Check if a key exists in this cache."""
628 return key in self._mapping
629
630 def __len__(self):
631 """Return the current size of the cache."""
632 return len(self._mapping)
633
634 def __repr__(self):
635 return '<%s %r>' % (
636 self.__class__.__name__,
637 self._mapping
638 )
639
640 def __getitem__(self, key):
641 """Get an item from the cache. Moves the item up so that it has the
642 highest priority then.
643
644 Raise an `KeyError` if it does not exist.
645 """
646 rv = self._mapping[key]
647 if self._queue[-1] != key:
Armin Ronacher8de6f182009-01-12 11:08:26 +0100648 try:
649 self._remove(key)
Armin Ronachere7c72bc2009-09-14 12:20:33 -0700650 except ValueError:
Armin Ronacher8de6f182009-01-12 11:08:26 +0100651 # if something removed the key from the container
652 # when we read, ignore the ValueError that we would
653 # get otherwise.
654 pass
Armin Ronacher814f6c22008-04-17 15:52:23 +0200655 self._append(key)
656 return rv
657
658 def __setitem__(self, key, value):
659 """Sets the value for an item. Moves the item up so that it
660 has the highest priority then.
661 """
Armin Ronacher000b4912008-05-01 18:40:15 +0200662 self._wlock.acquire()
663 try:
664 if key in self._mapping:
Armin Ronacher74230e62009-10-25 12:46:31 +0100665 try:
666 self._remove(key)
667 except ValueError:
668 # __getitem__ is not locked, it might happen
669 pass
Armin Ronacher000b4912008-05-01 18:40:15 +0200670 elif len(self._mapping) == self.capacity:
671 del self._mapping[self._popleft()]
672 self._append(key)
673 self._mapping[key] = value
674 finally:
675 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200676
677 def __delitem__(self, key):
678 """Remove an item from the cache dict.
679 Raise an `KeyError` if it does not exist.
680 """
Armin Ronacher000b4912008-05-01 18:40:15 +0200681 self._wlock.acquire()
682 try:
683 del self._mapping[key]
Armin Ronachere7c72bc2009-09-14 12:20:33 -0700684 try:
685 self._remove(key)
686 except ValueError:
687 # __getitem__ is not locked, it might happen
688 pass
Armin Ronacher000b4912008-05-01 18:40:15 +0200689 finally:
690 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200691
Armin Ronachere25f24d2008-05-19 11:20:41 +0200692 def items(self):
693 """Return a list of items."""
694 result = [(key, self._mapping[key]) for key in list(self._queue)]
695 result.reverse()
696 return result
697
698 def iteritems(self):
699 """Iterate over all items."""
700 return iter(self.items())
701
702 def values(self):
703 """Return a list of all values."""
704 return [x[1] for x in self.items()]
705
706 def itervalue(self):
707 """Iterate over all values."""
708 return iter(self.values())
709
710 def keys(self):
711 """Return a list of all keys ordered by most recent usage."""
712 return list(self)
713
714 def iterkeys(self):
715 """Iterate over all keys in the cache dict, ordered by
Armin Ronacher814f6c22008-04-17 15:52:23 +0200716 the most recent usage.
717 """
Armin Ronachere2244882008-05-19 09:25:57 +0200718 return reversed(tuple(self._queue))
Armin Ronacher814f6c22008-04-17 15:52:23 +0200719
Armin Ronachere25f24d2008-05-19 11:20:41 +0200720 __iter__ = iterkeys
721
Armin Ronacher814f6c22008-04-17 15:52:23 +0200722 def __reversed__(self):
723 """Iterate over the values in the cache dict, oldest items
724 coming first.
725 """
Armin Ronachere2244882008-05-19 09:25:57 +0200726 return iter(tuple(self._queue))
Armin Ronacher814f6c22008-04-17 15:52:23 +0200727
728 __copy__ = copy
729
Armin Ronacherbd33f112008-04-18 09:17:32 +0200730
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200731# register the LRU cache as mutable mapping if possible
732try:
733 from collections import MutableMapping
734 MutableMapping.register(LRUCache)
735except ImportError:
736 pass
737
738
Armin Ronacherccae0552008-10-05 23:08:58 +0200739class Cycler(object):
740 """A cycle helper for templates."""
741
742 def __init__(self, *items):
743 if not items:
744 raise RuntimeError('at least one item has to be provided')
745 self.items = items
746 self.reset()
747
748 def reset(self):
749 """Resets the cycle."""
750 self.pos = 0
751
752 @property
753 def current(self):
754 """Returns the current item."""
755 return self.items[self.pos]
756
757 def next(self):
758 """Goes one item ahead and returns it."""
759 rv = self.current
760 self.pos = (self.pos + 1) % len(self.items)
761 return rv
762
763
Armin Ronacherd34eb122008-10-13 23:47:51 +0200764class Joiner(object):
765 """A joining helper for templates."""
766
767 def __init__(self, sep=u', '):
768 self.sep = sep
769 self.used = False
770
771 def __call__(self):
772 if not self.used:
773 self.used = True
774 return u''
775 return self.sep
776
777
Armin Ronacherbd33f112008-04-18 09:17:32 +0200778# we have to import it down here as the speedups module imports the
779# markup type which is define above.
780try:
Armin Ronacherf59bac22008-04-20 13:11:43 +0200781 from jinja2._speedups import escape, soft_unicode
Armin Ronacherbd33f112008-04-18 09:17:32 +0200782except ImportError:
Lukas Meuserad48a2e2008-05-01 18:19:57 +0200783 def escape(s):
Armin Ronacherf35e2812008-05-06 16:04:10 +0200784 """Convert the characters &, <, >, ' and " in string s to HTML-safe
785 sequences. Use this if you need to display text that might contain
Armin Ronacher9a1e33c2008-05-05 22:00:46 +0200786 such characters in HTML. Marks return value as markup string.
Armin Ronacherbd33f112008-04-18 09:17:32 +0200787 """
Lukas Meuserad48a2e2008-05-01 18:19:57 +0200788 if hasattr(s, '__html__'):
789 return s.__html__()
790 return Markup(unicode(s)
Armin Ronacherbd33f112008-04-18 09:17:32 +0200791 .replace('&', '&amp;')
792 .replace('>', '&gt;')
793 .replace('<', '&lt;')
Armin Ronacherf35e2812008-05-06 16:04:10 +0200794 .replace("'", '&#39;')
Armin Ronacher9d42abf2008-05-14 18:10:41 +0200795 .replace('"', '&#34;')
Armin Ronacherbd33f112008-04-18 09:17:32 +0200796 )
Armin Ronacherf59bac22008-04-20 13:11:43 +0200797
798 def soft_unicode(s):
799 """Make a string unicode if it isn't already. That way a markup
800 string is not converted back to unicode.
801 """
802 if not isinstance(s, unicode):
803 s = unicode(s)
804 return s
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200805
806
807# partials
808try:
809 from functools import partial
810except ImportError:
811 class partial(object):
812 def __init__(self, _func, *args, **kwargs):
Benjamin Wiegand228c1832008-04-28 18:09:27 +0200813 self._func = _func
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200814 self._args = args
815 self._kwargs = kwargs
816 def __call__(self, *args, **kwargs):
817 kwargs.update(self._kwargs)
818 return self._func(*(self._args + args), **kwargs)