blob: 7bddcfe984bb375e144694737b4dc3a17ead5b2d [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
12import string
Armin Ronacher000b4912008-05-01 18:40:15 +020013try:
14 from thread import allocate_lock
15except ImportError:
16 from dummy_thread import allocate_lock
Armin Ronacher814f6c22008-04-17 15:52:23 +020017from collections import deque
18from copy import deepcopy
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._-]+$')
30
31
Armin Ronacher7259c762008-04-30 13:03:59 +020032# special singleton representing missing values for the runtime
33missing = type('MissingType', (), {'__repr__': lambda x: 'missing'})()
34
35
Armin Ronacher4f7d2d52008-04-22 10:40:26 +020036def contextfunction(f):
Armin Ronacherd84ec462008-04-29 13:43:16 +020037 """This decorator can be used to mark a callable as context callable. A
38 context callable is passed the active context as first argument if it
39 was directly stored in the context.
Armin Ronacher4f7d2d52008-04-22 10:40:26 +020040 """
41 f.contextfunction = True
42 return f
43
44
Armin Ronacher203bfcb2008-04-24 21:54:44 +020045def environmentfunction(f):
Armin Ronacherd84ec462008-04-29 13:43:16 +020046 """This decorator can be used to mark a callable as environment callable.
47 A environment callable is passed the current environment as first argument
48 if it was directly stored in the context.
Armin Ronacher203bfcb2008-04-24 21:54:44 +020049 """
50 f.environmentfunction = True
51 return f
52
53
Armin Ronacher187bde12008-05-01 18:19:16 +020054def clear_caches():
55 """Jinja2 keeps internal caches for environments and lexers. These are
56 used so that Jinja2 doesn't have to recreate environments and lexers all
57 the time. Normally you don't have to care about that but if you are
58 messuring memory consumption you may want to clean the caches.
59 """
60 from jinja2.environment import _spontaneous_environments
61 from jinja2.lexer import _lexer_cache
62 _spontaneous_environments.clear()
63 _lexer_cache.clear()
64
65
Armin Ronacherf59bac22008-04-20 13:11:43 +020066def import_string(import_name, silent=False):
67 """Imports an object based on a string. This use useful if you want to
68 use import paths as endpoints or something similar. An import path can
69 be specified either in dotted notation (``xml.sax.saxutils.escape``)
70 or with a colon as object delimiter (``xml.sax.saxutils:escape``).
71
72 If the `silent` is True the return value will be `None` if the import
73 fails.
74
75 :return: imported object
Armin Ronacher9a027f42008-04-17 11:13:40 +020076 """
Armin Ronacherf59bac22008-04-20 13:11:43 +020077 try:
78 if ':' in import_name:
79 module, obj = import_name.split(':', 1)
80 elif '.' in import_name:
81 items = import_name.split('.')
82 module = '.'.join(items[:-1])
83 obj = items[-1]
84 else:
85 return __import__(import_name)
86 return getattr(__import__(module, None, None, [obj]), obj)
87 except (ImportError, AttributeError):
88 if not silent:
89 raise
Armin Ronacher9a027f42008-04-17 11:13:40 +020090
91
Christoph Hacke9e43bb2008-04-13 23:35:48 +020092def pformat(obj, verbose=False):
Armin Ronacherbe4ae242008-04-18 09:49:08 +020093 """Prettyprint an object. Either use the `pretty` library or the
Christoph Hacke9e43bb2008-04-13 23:35:48 +020094 builtin `pprint`.
95 """
96 try:
97 from pretty import pretty
98 return pretty(obj, verbose=verbose)
99 except ImportError:
100 from pprint import pformat
101 return pformat(obj)
Christoph Hack80909862008-04-14 01:35:10 +0200102
103
Christoph Hack80909862008-04-14 01:35:10 +0200104def urlize(text, trim_url_limit=None, nofollow=False):
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200105 """Converts any URLs in text into clickable links. Works on http://,
Christoph Hack80909862008-04-14 01:35:10 +0200106 https:// and www. links. Links can have trailing punctuation (periods,
107 commas, close-parens) and leading punctuation (opening parens) and
108 it'll still do the right thing.
109
110 If trim_url_limit is not None, the URLs in link text will be limited
111 to trim_url_limit characters.
112
113 If nofollow is True, the URLs in link text will get a rel="nofollow"
114 attribute.
115 """
116 trim_url = lambda x, limit=trim_url_limit: limit is not None \
117 and (x[:limit] + (len(x) >=limit and '...'
118 or '')) or x
119 words = _word_split_re.split(text)
120 nofollow_attr = nofollow and ' rel="nofollow"' or ''
121 for i, word in enumerate(words):
122 match = _punctuation_re.match(word)
123 if match:
124 lead, middle, trail = match.groups()
125 if middle.startswith('www.') or (
126 '@' not in middle and
127 not middle.startswith('http://') and
128 len(middle) > 0 and
129 middle[0] in string.letters + string.digits and (
130 middle.endswith('.org') or
131 middle.endswith('.net') or
132 middle.endswith('.com')
133 )):
134 middle = '<a href="http://%s"%s>%s</a>' % (middle,
135 nofollow_attr, trim_url(middle))
136 if middle.startswith('http://') or \
137 middle.startswith('https://'):
138 middle = '<a href="%s"%s>%s</a>' % (middle,
139 nofollow_attr, trim_url(middle))
140 if '@' in middle and not middle.startswith('www.') and \
141 not ':' in middle and _simple_email_re.match(middle):
142 middle = '<a href="mailto:%s">%s</a>' % (middle, middle)
143 if lead + middle + trail != word:
144 words[i] = lead + middle + trail
145 return u''.join(words)
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200146
147
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200148def generate_lorem_ipsum(n=5, html=True, min=20, max=100):
149 """Generate some lorem impsum for the template."""
150 from jinja2.constants import LOREM_IPSUM_WORDS
151 from random import choice, random, randrange
152 words = LOREM_IPSUM_WORDS.split()
153 result = []
154
155 for _ in xrange(n):
156 next_capitalized = True
157 last_comma = last_fullstop = 0
158 word = None
159 last = None
160 p = []
161
162 # each paragraph contains out of 20 to 100 words.
163 for idx, _ in enumerate(xrange(randrange(min, max))):
164 while True:
165 word = choice(words)
166 if word != last:
167 last = word
168 break
169 if next_capitalized:
170 word = word.capitalize()
171 next_capitalized = False
172 # add commas
173 if idx - randrange(3, 8) > last_comma:
174 last_comma = idx
175 last_fullstop += 2
176 word += ','
177 # add end of sentences
178 if idx - randrange(10, 20) > last_fullstop:
179 last_comma = last_fullstop = idx
180 word += '.'
181 next_capitalized = True
182 p.append(word)
183
184 # ensure that the paragraph ends with a dot.
185 p = u' '.join(p)
186 if p.endswith(','):
187 p = p[:-1] + '.'
188 elif not p.endswith('.'):
189 p += '.'
190 result.append(p)
191
192 if not html:
193 return u'\n\n'.join(result)
194 return Markup(u'\n'.join(u'<p>%s</p>' % escape(x) for x in result))
195
196
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200197class Markup(unicode):
198 """Marks a string as being safe for inclusion in HTML/XML output without
199 needing to be escaped. This implements the `__html__` interface a couple
200 of frameworks and web applications use.
201
202 The `escape` function returns markup objects so that double escaping can't
203 happen. If you want to use autoescaping in Jinja just set the finalizer
204 of the environment to `escape`.
205 """
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200206 __slots__ = ()
207
208 def __html__(self):
209 return self
210
211 def __add__(self, other):
212 if hasattr(other, '__html__') or isinstance(other, basestring):
213 return self.__class__(unicode(self) + unicode(escape(other)))
214 return NotImplemented
215
216 def __radd__(self, other):
217 if hasattr(other, '__html__') or isinstance(other, basestring):
218 return self.__class__(unicode(escape(other)) + unicode(self))
219 return NotImplemented
220
221 def __mul__(self, num):
222 if not isinstance(num, (int, long)):
223 return NotImplemented
224 return self.__class__(unicode.__mul__(self, num))
225 __rmul__ = __mul__
226
227 def __mod__(self, arg):
228 if isinstance(arg, tuple):
229 arg = tuple(imap(_MarkupEscapeHelper, arg))
230 else:
231 arg = _MarkupEscapeHelper(arg)
232 return self.__class__(unicode.__mod__(self, arg))
233
234 def __repr__(self):
235 return '%s(%s)' % (
236 self.__class__.__name__,
237 unicode.__repr__(self)
238 )
239
240 def join(self, seq):
241 return self.__class__(unicode.join(self, imap(escape, seq)))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200242 join.__doc__ = unicode.join.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200243
244 def split(self, *args, **kwargs):
245 return map(self.__class__, unicode.split(self, *args, **kwargs))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200246 split.__doc__ = unicode.split.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200247
248 def rsplit(self, *args, **kwargs):
249 return map(self.__class__, unicode.rsplit(self, *args, **kwargs))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200250 rsplit.__doc__ = unicode.rsplit.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200251
252 def splitlines(self, *args, **kwargs):
253 return map(self.__class__, unicode.splitlines(self, *args, **kwargs))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200254 splitlines.__doc__ = unicode.splitlines.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200255
256 def make_wrapper(name):
257 orig = getattr(unicode, name)
258 def func(self, *args, **kwargs):
259 args = list(args)
260 for idx, arg in enumerate(args):
261 if hasattr(arg, '__html__') or isinstance(arg, basestring):
262 args[idx] = escape(arg)
263 for name, arg in kwargs.iteritems():
264 if hasattr(arg, '__html__') or isinstance(arg, basestring):
265 kwargs[name] = escape(arg)
266 return self.__class__(orig(self, *args, **kwargs))
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200267 func.__name__ = orig.__name__
268 func.__doc__ = orig.__doc__
269 return func
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200270 for method in '__getitem__', '__getslice__', 'capitalize', \
271 'title', 'lower', 'upper', 'replace', 'ljust', \
Armin Ronacher709f6e52008-04-28 18:18:16 +0200272 'rjust', 'lstrip', 'rstrip', 'center', 'strip', \
Armin Ronacher316157d2008-04-28 18:30:27 +0200273 'translate', 'expandtabs', 'swapcase', 'zfill':
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200274 locals()[method] = make_wrapper(method)
Armin Ronacher709f6e52008-04-28 18:18:16 +0200275
276 # new in python 2.5
277 if hasattr(unicode, 'partition'):
Armin Ronacher316157d2008-04-28 18:30:27 +0200278 locals().update(
279 partition=make_wrapper('partition'),
280 rpartition=make_wrapper('rpartition')
281 )
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200282 del method, make_wrapper
283
284
285class _MarkupEscapeHelper(object):
286 """Helper for Markup.__mod__"""
287
288 def __init__(self, obj):
289 self.obj = obj
290
291 __getitem__ = lambda s, x: _MarkupEscapeHelper(s.obj[x])
292 __unicode__ = lambda s: unicode(escape(s.obj))
293 __str__ = lambda s: str(escape(s.obj))
294 __repr__ = lambda s: str(repr(escape(s.obj)))
295 __int__ = lambda s: int(s.obj)
296 __float__ = lambda s: float(s.obj)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200297
298
299class LRUCache(object):
300 """A simple LRU Cache implementation."""
301 # this is fast for small capacities (something around 200) but doesn't
302 # scale. But as long as it's only used for the database connections in
303 # a non request fallback it's fine.
304
305 def __init__(self, capacity):
306 self.capacity = capacity
307 self._mapping = {}
308 self._queue = deque()
309
310 # alias all queue methods for faster lookup
311 self._popleft = self._queue.popleft
312 self._pop = self._queue.pop
313 if hasattr(self._queue, 'remove'):
314 self._remove = self._queue.remove
Armin Ronacher000b4912008-05-01 18:40:15 +0200315 self._wlock = allocate_lock()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200316 self._append = self._queue.append
317
318 def _remove(self, obj):
319 """Python 2.4 compatibility."""
320 for idx, item in enumerate(self._queue):
321 if item == obj:
322 del self._queue[idx]
323 break
324
325 def copy(self):
326 """Return an shallow copy of the instance."""
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200327 rv = self.__class__(self.capacity)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200328 rv._mapping.update(self._mapping)
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200329 rv._queue = deque(self._queue)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200330 return rv
331
332 def get(self, key, default=None):
333 """Return an item from the cache dict or `default`"""
Armin Ronacher000b4912008-05-01 18:40:15 +0200334 try:
Armin Ronacher814f6c22008-04-17 15:52:23 +0200335 return self[key]
Armin Ronacher000b4912008-05-01 18:40:15 +0200336 except KeyError:
337 return default
Armin Ronacher814f6c22008-04-17 15:52:23 +0200338
339 def setdefault(self, key, default=None):
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200340 """Set `default` if the key is not in the cache otherwise
Armin Ronacher814f6c22008-04-17 15:52:23 +0200341 leave unchanged. Return the value of this key.
342 """
Armin Ronacher000b4912008-05-01 18:40:15 +0200343 try:
Armin Ronacher814f6c22008-04-17 15:52:23 +0200344 return self[key]
Armin Ronacher000b4912008-05-01 18:40:15 +0200345 except KeyError:
346 self[key] = default
347 return default
Armin Ronacher814f6c22008-04-17 15:52:23 +0200348
349 def clear(self):
350 """Clear the cache."""
Armin Ronacher000b4912008-05-01 18:40:15 +0200351 self._wlock.acquire()
352 try:
353 self._mapping.clear()
354 self._queue.clear()
355 finally:
356 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200357
358 def __contains__(self, key):
359 """Check if a key exists in this cache."""
360 return key in self._mapping
361
362 def __len__(self):
363 """Return the current size of the cache."""
364 return len(self._mapping)
365
366 def __repr__(self):
367 return '<%s %r>' % (
368 self.__class__.__name__,
369 self._mapping
370 )
371
372 def __getitem__(self, key):
373 """Get an item from the cache. Moves the item up so that it has the
374 highest priority then.
375
376 Raise an `KeyError` if it does not exist.
377 """
378 rv = self._mapping[key]
379 if self._queue[-1] != key:
380 self._remove(key)
381 self._append(key)
382 return rv
383
384 def __setitem__(self, key, value):
385 """Sets the value for an item. Moves the item up so that it
386 has the highest priority then.
387 """
Armin Ronacher000b4912008-05-01 18:40:15 +0200388 self._wlock.acquire()
389 try:
390 if key in self._mapping:
391 self._remove(key)
392 elif len(self._mapping) == self.capacity:
393 del self._mapping[self._popleft()]
394 self._append(key)
395 self._mapping[key] = value
396 finally:
397 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200398
399 def __delitem__(self, key):
400 """Remove an item from the cache dict.
401 Raise an `KeyError` if it does not exist.
402 """
Armin Ronacher000b4912008-05-01 18:40:15 +0200403 self._wlock.acquire()
404 try:
405 del self._mapping[key]
406 self._remove(key)
407 finally:
408 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200409
410 def __iter__(self):
411 """Iterate over all values in the cache dict, ordered by
412 the most recent usage.
413 """
414 return reversed(self._queue)
415
416 def __reversed__(self):
417 """Iterate over the values in the cache dict, oldest items
418 coming first.
419 """
420 return iter(self._queue)
421
422 __copy__ = copy
423
Armin Ronacherbd33f112008-04-18 09:17:32 +0200424
425# we have to import it down here as the speedups module imports the
426# markup type which is define above.
427try:
Armin Ronacherf59bac22008-04-20 13:11:43 +0200428 from jinja2._speedups import escape, soft_unicode
Armin Ronacherbd33f112008-04-18 09:17:32 +0200429except ImportError:
Lukas Meuserad48a2e2008-05-01 18:19:57 +0200430 def escape(s):
Armin Ronacherbd33f112008-04-18 09:17:32 +0200431 """Convert the characters &, <, >, and " in string s to HTML-safe
432 sequences. Use this if you need to display text that might contain
433 such characters in HTML.
434 """
Lukas Meuserad48a2e2008-05-01 18:19:57 +0200435 if hasattr(s, '__html__'):
436 return s.__html__()
437 return Markup(unicode(s)
Armin Ronacherbd33f112008-04-18 09:17:32 +0200438 .replace('&', '&amp;')
439 .replace('>', '&gt;')
440 .replace('<', '&lt;')
441 .replace('"', '&quot;')
442 )
Armin Ronacherf59bac22008-04-20 13:11:43 +0200443
444 def soft_unicode(s):
445 """Make a string unicode if it isn't already. That way a markup
446 string is not converted back to unicode.
447 """
448 if not isinstance(s, unicode):
449 s = unicode(s)
450 return s
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200451
452
453# partials
454try:
455 from functools import partial
456except ImportError:
457 class partial(object):
458 def __init__(self, _func, *args, **kwargs):
Benjamin Wiegand228c1832008-04-28 18:09:27 +0200459 self._func = _func
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200460 self._args = args
461 self._kwargs = kwargs
462 def __call__(self, *args, **kwargs):
463 kwargs.update(self._kwargs)
464 return self._func(*(self._args + args), **kwargs)