blob: 1c717899b7d4d0dd2bc0d5e23ba33eb84dcc61a9 [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
Armin Ronacherccae0552008-10-05 23:08:58 +020012import errno
Armin Ronacher814f6c22008-04-17 15:52:23 +020013from collections import deque
Armin Ronacherf15b8142013-05-20 17:06:41 +010014from threading import Lock
Armin Ronacherce779a52013-05-20 02:11:16 +010015from jinja2._compat import text_type, string_types, implements_iterator, \
Armin Ronacherf15b8142013-05-20 17:06:41 +010016 url_quote
Armin Ronachere9098672013-05-19 14:16:13 +010017
Armin Ronacher8edbe492008-04-10 20:43:43 +020018
Armin Ronacherbe4ae242008-04-18 09:49:08 +020019_word_split_re = re.compile(r'(\s+)')
20_punctuation_re = re.compile(
21 '^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$' % (
Thomas Waldmann7d295622013-05-18 00:06:22 +020022 '|'.join(map(re.escape, ('(', '<', '&lt;'))),
23 '|'.join(map(re.escape, ('.', ',', ')', '>', '\n', '&gt;')))
Armin Ronacherbe4ae242008-04-18 09:49:08 +020024 )
25)
26_simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$')
Armin Ronacher76c280b2008-05-04 12:31:48 +020027_striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
28_entity_re = re.compile(r'&([^;]+);')
Armin Ronacher9a0078d2008-08-13 18:24:17 +020029_letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
30_digits = '0123456789'
Armin Ronacherbe4ae242008-04-18 09:49:08 +020031
Armin Ronacher7259c762008-04-30 13:03:59 +020032# special singleton representing missing values for the runtime
33missing = type('MissingType', (), {'__repr__': lambda x: 'missing'})()
34
Armin Ronacherd416a972009-02-24 22:58:00 +010035# internal code
36internal_code = set()
37
Thomas Waldmann07a7d552013-05-18 00:47:41 +020038concat = u''.join
Armin Ronacher7ceced52008-05-03 10:15:31 +020039
40
Armin Ronacher4f7d2d52008-04-22 10:40:26 +020041def contextfunction(f):
Armin Ronacher9bb7e472008-05-28 11:26:59 +020042 """This decorator can be used to mark a function or method context callable.
43 A context callable is passed the active :class:`Context` as first argument when
44 called from the template. This is useful if a function wants to get access
45 to the context or functions provided on the context object. For example
46 a function that returns a sorted list of template variables the current
47 template exports could look like this::
48
Armin Ronacher58f351d2008-05-28 21:30:14 +020049 @contextfunction
Armin Ronacher9bb7e472008-05-28 11:26:59 +020050 def get_exported_names(context):
51 return sorted(context.exported_vars)
Armin Ronacher4f7d2d52008-04-22 10:40:26 +020052 """
53 f.contextfunction = True
54 return f
55
56
Armin Ronacher8346bd72010-03-14 19:43:47 +010057def evalcontextfunction(f):
Florent Xicluna0ec4f762012-02-05 13:09:15 +010058 """This decorator can be used to mark a function or method as an eval
Armin Ronacher8346bd72010-03-14 19:43:47 +010059 context callable. This is similar to the :func:`contextfunction`
60 but instead of passing the context, an evaluation context object is
Armin Ronacherfe150f32010-03-15 02:42:41 +010061 passed. For more information about the eval context, see
62 :ref:`eval-context`.
Armin Ronacher8346bd72010-03-14 19:43:47 +010063
64 .. versionadded:: 2.4
65 """
66 f.evalcontextfunction = True
67 return f
68
69
Armin Ronacher203bfcb2008-04-24 21:54:44 +020070def environmentfunction(f):
Armin Ronacher9bb7e472008-05-28 11:26:59 +020071 """This decorator can be used to mark a function or method as environment
72 callable. This decorator works exactly like the :func:`contextfunction`
73 decorator just that the first argument is the active :class:`Environment`
74 and not context.
Armin Ronacher203bfcb2008-04-24 21:54:44 +020075 """
76 f.environmentfunction = True
77 return f
78
79
Armin Ronacherd416a972009-02-24 22:58:00 +010080def internalcode(f):
81 """Marks the function as internally used"""
Thomas Waldmanne0003552013-05-17 23:52:14 +020082 internal_code.add(f.__code__)
Armin Ronacherd416a972009-02-24 22:58:00 +010083 return f
84
85
Armin Ronacher9bb7e472008-05-28 11:26:59 +020086def is_undefined(obj):
87 """Check if the object passed is undefined. This does nothing more than
88 performing an instance check against :class:`Undefined` but looks nicer.
89 This can be used for custom filters or tests that want to react to
90 undefined variables. For example a custom default filter can look like
91 this::
92
93 def default(var, default=''):
94 if is_undefined(var):
95 return default
96 return var
97 """
98 from jinja2.runtime import Undefined
99 return isinstance(obj, Undefined)
100
101
Armin Ronacherba6e25a2008-11-02 15:58:14 +0100102def consume(iterable):
103 """Consumes an iterable without doing anything with it."""
104 for event in iterable:
105 pass
106
107
Armin Ronacher187bde12008-05-01 18:19:16 +0200108def clear_caches():
109 """Jinja2 keeps internal caches for environments and lexers. These are
110 used so that Jinja2 doesn't have to recreate environments and lexers all
111 the time. Normally you don't have to care about that but if you are
112 messuring memory consumption you may want to clean the caches.
113 """
114 from jinja2.environment import _spontaneous_environments
115 from jinja2.lexer import _lexer_cache
116 _spontaneous_environments.clear()
117 _lexer_cache.clear()
118
119
Armin Ronacherf59bac22008-04-20 13:11:43 +0200120def import_string(import_name, silent=False):
Florent Xicluna0ec4f762012-02-05 13:09:15 +0100121 """Imports an object based on a string. This is useful if you want to
Armin Ronacherf59bac22008-04-20 13:11:43 +0200122 use import paths as endpoints or something similar. An import path can
123 be specified either in dotted notation (``xml.sax.saxutils.escape``)
124 or with a colon as object delimiter (``xml.sax.saxutils:escape``).
125
126 If the `silent` is True the return value will be `None` if the import
127 fails.
128
129 :return: imported object
Armin Ronacher9a027f42008-04-17 11:13:40 +0200130 """
Armin Ronacherf59bac22008-04-20 13:11:43 +0200131 try:
132 if ':' in import_name:
133 module, obj = import_name.split(':', 1)
134 elif '.' in import_name:
135 items = import_name.split('.')
136 module = '.'.join(items[:-1])
137 obj = items[-1]
138 else:
139 return __import__(import_name)
140 return getattr(__import__(module, None, None, [obj]), obj)
141 except (ImportError, AttributeError):
142 if not silent:
143 raise
Armin Ronacher9a027f42008-04-17 11:13:40 +0200144
145
Armin Ronacher0faa8612010-02-09 15:04:51 +0100146def open_if_exists(filename, mode='rb'):
Armin Ronacherccae0552008-10-05 23:08:58 +0200147 """Returns a file descriptor for the filename if that file exists,
148 otherwise `None`.
149 """
150 try:
Armin Ronacher790b8a82010-02-10 00:05:46 +0100151 return open(filename, mode)
Thomas Waldmanne0003552013-05-17 23:52:14 +0200152 except IOError as e:
Armin Ronacherccae0552008-10-05 23:08:58 +0200153 if e.errno not in (errno.ENOENT, errno.EISDIR):
154 raise
155
156
Armin Ronacher98dbf5f2010-04-12 15:49:59 +0200157def object_type_repr(obj):
158 """Returns the name of the object's type. For some recognized
159 singletons the name of the object is returned instead. (For
160 example for `None` and `Ellipsis`).
161 """
162 if obj is None:
163 return 'None'
164 elif obj is Ellipsis:
165 return 'Ellipsis'
Armin Ronacher802f4722010-04-20 19:48:46 +0200166 # __builtin__ in 2.x, builtins in 3.x
167 if obj.__class__.__module__ in ('__builtin__', 'builtins'):
Armin Ronacher800ac7f2010-04-20 13:45:11 +0200168 name = obj.__class__.__name__
Armin Ronacher98dbf5f2010-04-12 15:49:59 +0200169 else:
Armin Ronacher800ac7f2010-04-20 13:45:11 +0200170 name = obj.__class__.__module__ + '.' + obj.__class__.__name__
Armin Ronacher98dbf5f2010-04-12 15:49:59 +0200171 return '%s object' % name
172
173
Christoph Hacke9e43bb2008-04-13 23:35:48 +0200174def pformat(obj, verbose=False):
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200175 """Prettyprint an object. Either use the `pretty` library or the
Christoph Hacke9e43bb2008-04-13 23:35:48 +0200176 builtin `pprint`.
177 """
178 try:
179 from pretty import pretty
180 return pretty(obj, verbose=verbose)
181 except ImportError:
182 from pprint import pformat
183 return pformat(obj)
Christoph Hack80909862008-04-14 01:35:10 +0200184
185
Christoph Hack80909862008-04-14 01:35:10 +0200186def urlize(text, trim_url_limit=None, nofollow=False):
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200187 """Converts any URLs in text into clickable links. Works on http://,
Christoph Hack80909862008-04-14 01:35:10 +0200188 https:// and www. links. Links can have trailing punctuation (periods,
189 commas, close-parens) and leading punctuation (opening parens) and
190 it'll still do the right thing.
191
192 If trim_url_limit is not None, the URLs in link text will be limited
193 to trim_url_limit characters.
194
195 If nofollow is True, the URLs in link text will get a rel="nofollow"
196 attribute.
197 """
198 trim_url = lambda x, limit=trim_url_limit: limit is not None \
199 and (x[:limit] + (len(x) >=limit and '...'
200 or '')) or x
Armin Ronachere9098672013-05-19 14:16:13 +0100201 words = _word_split_re.split(text_type(escape(text)))
Christoph Hack80909862008-04-14 01:35:10 +0200202 nofollow_attr = nofollow and ' rel="nofollow"' or ''
203 for i, word in enumerate(words):
204 match = _punctuation_re.match(word)
205 if match:
206 lead, middle, trail = match.groups()
207 if middle.startswith('www.') or (
208 '@' not in middle and
209 not middle.startswith('http://') and
mozillazg66448932013-03-18 14:27:54 +0800210 not middle.startswith('https://') and
Christoph Hack80909862008-04-14 01:35:10 +0200211 len(middle) > 0 and
Armin Ronacher9a0078d2008-08-13 18:24:17 +0200212 middle[0] in _letters + _digits and (
Christoph Hack80909862008-04-14 01:35:10 +0200213 middle.endswith('.org') or
214 middle.endswith('.net') or
215 middle.endswith('.com')
216 )):
217 middle = '<a href="http://%s"%s>%s</a>' % (middle,
218 nofollow_attr, trim_url(middle))
219 if middle.startswith('http://') or \
220 middle.startswith('https://'):
221 middle = '<a href="%s"%s>%s</a>' % (middle,
222 nofollow_attr, trim_url(middle))
223 if '@' in middle and not middle.startswith('www.') and \
224 not ':' in middle and _simple_email_re.match(middle):
225 middle = '<a href="mailto:%s">%s</a>' % (middle, middle)
226 if lead + middle + trail != word:
227 words[i] = lead + middle + trail
228 return u''.join(words)
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200229
230
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200231def generate_lorem_ipsum(n=5, html=True, min=20, max=100):
232 """Generate some lorem impsum for the template."""
233 from jinja2.constants import LOREM_IPSUM_WORDS
Georg Brandl95632c42009-11-22 18:35:18 +0100234 from random import choice, randrange
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200235 words = LOREM_IPSUM_WORDS.split()
236 result = []
237
Thomas Waldmanne0003552013-05-17 23:52:14 +0200238 for _ in range(n):
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200239 next_capitalized = True
240 last_comma = last_fullstop = 0
241 word = None
242 last = None
243 p = []
244
245 # each paragraph contains out of 20 to 100 words.
Thomas Waldmanne0003552013-05-17 23:52:14 +0200246 for idx, _ in enumerate(range(randrange(min, max))):
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200247 while True:
248 word = choice(words)
249 if word != last:
250 last = word
251 break
252 if next_capitalized:
253 word = word.capitalize()
254 next_capitalized = False
255 # add commas
256 if idx - randrange(3, 8) > last_comma:
257 last_comma = idx
258 last_fullstop += 2
259 word += ','
260 # add end of sentences
261 if idx - randrange(10, 20) > last_fullstop:
262 last_comma = last_fullstop = idx
263 word += '.'
264 next_capitalized = True
265 p.append(word)
266
267 # ensure that the paragraph ends with a dot.
268 p = u' '.join(p)
269 if p.endswith(','):
270 p = p[:-1] + '.'
271 elif not p.endswith('.'):
272 p += '.'
273 result.append(p)
274
275 if not html:
276 return u'\n\n'.join(result)
277 return Markup(u'\n'.join(u'<p>%s</p>' % escape(x) for x in result))
278
279
Armin Ronacher51454012012-01-07 17:47:56 +0100280def unicode_urlencode(obj, charset='utf-8'):
Armin Ronacher1d4c6382012-01-07 17:46:40 +0100281 """URL escapes a single bytestring or unicode string with the
282 given charset if applicable to URL safe quoting under all rules
283 that need to be considered under all supported Python versions.
284
285 If non strings are provided they are converted to their unicode
286 representation first.
287 """
Armin Ronachere9098672013-05-19 14:16:13 +0100288 if not isinstance(obj, string_types):
289 obj = text_type(obj)
290 if isinstance(obj, text_type):
Armin Ronacher1d4c6382012-01-07 17:46:40 +0100291 obj = obj.encode(charset)
Armin Ronachere9098672013-05-19 14:16:13 +0100292 return text_type(url_quote(obj))
Armin Ronacher1d4c6382012-01-07 17:46:40 +0100293
294
Armin Ronacher814f6c22008-04-17 15:52:23 +0200295class LRUCache(object):
296 """A simple LRU Cache implementation."""
Armin Ronacher58f351d2008-05-28 21:30:14 +0200297
298 # this is fast for small capacities (something below 1000) but doesn't
299 # scale. But as long as it's only used as storage for templates this
300 # won't do any harm.
Armin Ronacher814f6c22008-04-17 15:52:23 +0200301
302 def __init__(self, capacity):
303 self.capacity = capacity
304 self._mapping = {}
305 self._queue = deque()
Armin Ronacher7962ce72008-05-20 17:52:52 +0200306 self._postinit()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200307
Armin Ronacher7962ce72008-05-20 17:52:52 +0200308 def _postinit(self):
Armin Ronacher814f6c22008-04-17 15:52:23 +0200309 # alias all queue methods for faster lookup
310 self._popleft = self._queue.popleft
311 self._pop = self._queue.pop
Thomas Waldmann07a7d552013-05-18 00:47:41 +0200312 self._remove = self._queue.remove
Armin Ronacherf15b8142013-05-20 17:06:41 +0100313 self._wlock = Lock()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200314 self._append = self._queue.append
315
Armin Ronacher7962ce72008-05-20 17:52:52 +0200316 def __getstate__(self):
317 return {
318 'capacity': self.capacity,
319 '_mapping': self._mapping,
320 '_queue': self._queue
321 }
322
323 def __setstate__(self, d):
324 self.__dict__.update(d)
325 self._postinit()
326
327 def __getnewargs__(self):
328 return (self.capacity,)
329
Armin Ronacher814f6c22008-04-17 15:52:23 +0200330 def copy(self):
Florent Xicluna0ec4f762012-02-05 13:09:15 +0100331 """Return a shallow copy of the instance."""
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200332 rv = self.__class__(self.capacity)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200333 rv._mapping.update(self._mapping)
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200334 rv._queue = deque(self._queue)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200335 return rv
336
337 def get(self, key, default=None):
338 """Return an item from the cache dict or `default`"""
Armin Ronacher000b4912008-05-01 18:40:15 +0200339 try:
Armin Ronacher814f6c22008-04-17 15:52:23 +0200340 return self[key]
Armin Ronacher000b4912008-05-01 18:40:15 +0200341 except KeyError:
342 return default
Armin Ronacher814f6c22008-04-17 15:52:23 +0200343
344 def setdefault(self, key, default=None):
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200345 """Set `default` if the key is not in the cache otherwise
Armin Ronacher814f6c22008-04-17 15:52:23 +0200346 leave unchanged. Return the value of this key.
347 """
Armin Ronacherd4e54382013-04-13 00:38:27 +0100348 self._wlock.acquire()
Armin Ronacher000b4912008-05-01 18:40:15 +0200349 try:
Armin Ronacherd4e54382013-04-13 00:38:27 +0100350 try:
351 return self[key]
352 except KeyError:
353 self[key] = default
354 return default
355 finally:
356 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200357
358 def clear(self):
359 """Clear the cache."""
Armin Ronacher000b4912008-05-01 18:40:15 +0200360 self._wlock.acquire()
361 try:
362 self._mapping.clear()
363 self._queue.clear()
364 finally:
365 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200366
367 def __contains__(self, key):
368 """Check if a key exists in this cache."""
369 return key in self._mapping
370
371 def __len__(self):
372 """Return the current size of the cache."""
373 return len(self._mapping)
374
375 def __repr__(self):
376 return '<%s %r>' % (
377 self.__class__.__name__,
378 self._mapping
379 )
380
381 def __getitem__(self, key):
382 """Get an item from the cache. Moves the item up so that it has the
383 highest priority then.
384
Florent Xicluna0ec4f762012-02-05 13:09:15 +0100385 Raise a `KeyError` if it does not exist.
Armin Ronacher814f6c22008-04-17 15:52:23 +0200386 """
Armin Ronacherd4e54382013-04-13 00:38:27 +0100387 self._wlock.acquire()
388 try:
389 rv = self._mapping[key]
390 if self._queue[-1] != key:
391 try:
392 self._remove(key)
393 except ValueError:
394 # if something removed the key from the container
395 # when we read, ignore the ValueError that we would
396 # get otherwise.
397 pass
398 self._append(key)
399 return rv
400 finally:
401 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200402
403 def __setitem__(self, key, value):
404 """Sets the value for an item. Moves the item up so that it
405 has the highest priority then.
406 """
Armin Ronacher000b4912008-05-01 18:40:15 +0200407 self._wlock.acquire()
408 try:
409 if key in self._mapping:
Armin Ronacherd4e54382013-04-13 00:38:27 +0100410 self._remove(key)
Armin Ronacher000b4912008-05-01 18:40:15 +0200411 elif len(self._mapping) == self.capacity:
412 del self._mapping[self._popleft()]
413 self._append(key)
414 self._mapping[key] = value
415 finally:
416 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200417
418 def __delitem__(self, key):
419 """Remove an item from the cache dict.
Florent Xicluna0ec4f762012-02-05 13:09:15 +0100420 Raise a `KeyError` if it does not exist.
Armin Ronacher814f6c22008-04-17 15:52:23 +0200421 """
Armin Ronacher000b4912008-05-01 18:40:15 +0200422 self._wlock.acquire()
423 try:
424 del self._mapping[key]
Armin Ronachere7c72bc2009-09-14 12:20:33 -0700425 try:
426 self._remove(key)
427 except ValueError:
428 # __getitem__ is not locked, it might happen
429 pass
Armin Ronacher000b4912008-05-01 18:40:15 +0200430 finally:
431 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200432
Armin Ronachere25f24d2008-05-19 11:20:41 +0200433 def items(self):
434 """Return a list of items."""
435 result = [(key, self._mapping[key]) for key in list(self._queue)]
436 result.reverse()
437 return result
438
439 def iteritems(self):
440 """Iterate over all items."""
441 return iter(self.items())
442
443 def values(self):
444 """Return a list of all values."""
445 return [x[1] for x in self.items()]
446
447 def itervalue(self):
448 """Iterate over all values."""
449 return iter(self.values())
450
451 def keys(self):
452 """Return a list of all keys ordered by most recent usage."""
453 return list(self)
454
455 def iterkeys(self):
456 """Iterate over all keys in the cache dict, ordered by
Armin Ronacher814f6c22008-04-17 15:52:23 +0200457 the most recent usage.
458 """
Armin Ronachere2244882008-05-19 09:25:57 +0200459 return reversed(tuple(self._queue))
Armin Ronacher814f6c22008-04-17 15:52:23 +0200460
Armin Ronachere25f24d2008-05-19 11:20:41 +0200461 __iter__ = iterkeys
462
Armin Ronacher814f6c22008-04-17 15:52:23 +0200463 def __reversed__(self):
464 """Iterate over the values in the cache dict, oldest items
465 coming first.
466 """
Armin Ronachere2244882008-05-19 09:25:57 +0200467 return iter(tuple(self._queue))
Armin Ronacher814f6c22008-04-17 15:52:23 +0200468
469 __copy__ = copy
470
Armin Ronacherbd33f112008-04-18 09:17:32 +0200471
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200472# register the LRU cache as mutable mapping if possible
473try:
474 from collections import MutableMapping
475 MutableMapping.register(LRUCache)
476except ImportError:
477 pass
478
479
Armin Ronacher28c74882013-05-20 01:51:26 +0100480@implements_iterator
481class Cycler(object):
Armin Ronacherccae0552008-10-05 23:08:58 +0200482 """A cycle helper for templates."""
483
484 def __init__(self, *items):
485 if not items:
486 raise RuntimeError('at least one item has to be provided')
487 self.items = items
488 self.reset()
489
490 def reset(self):
491 """Resets the cycle."""
492 self.pos = 0
493
494 @property
495 def current(self):
496 """Returns the current item."""
497 return self.items[self.pos]
498
Thomas Waldmann7d295622013-05-18 00:06:22 +0200499 def __next__(self):
Armin Ronacherccae0552008-10-05 23:08:58 +0200500 """Goes one item ahead and returns it."""
501 rv = self.current
502 self.pos = (self.pos + 1) % len(self.items)
503 return rv
504
505
Armin Ronacherd34eb122008-10-13 23:47:51 +0200506class Joiner(object):
507 """A joining helper for templates."""
508
509 def __init__(self, sep=u', '):
510 self.sep = sep
511 self.used = False
512
513 def __call__(self):
514 if not self.used:
515 self.used = True
516 return u''
517 return self.sep
518
519
Armin Ronacher294f2eb2013-05-19 13:49:12 +0100520# Imported here because that's where it was in the past
521from markupsafe import Markup, escape, soft_unicode