blob: 8a7d4317c3f654188ba6a02f25c5a1c73ed7ecc9 [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 Ronacherce779a52013-05-20 02:11:16 +010014from jinja2._compat import text_type, string_types, implements_iterator, \
15 allocate_lock, url_quote, encode_filename, PY2
Armin Ronachere9098672013-05-19 14:16:13 +010016
Armin Ronacher8edbe492008-04-10 20:43:43 +020017
Armin Ronacherbe4ae242008-04-18 09:49:08 +020018_word_split_re = re.compile(r'(\s+)')
19_punctuation_re = re.compile(
20 '^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$' % (
Thomas Waldmann7d295622013-05-18 00:06:22 +020021 '|'.join(map(re.escape, ('(', '<', '&lt;'))),
22 '|'.join(map(re.escape, ('.', ',', ')', '>', '\n', '&gt;')))
Armin Ronacherbe4ae242008-04-18 09:49:08 +020023 )
24)
25_simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$')
Armin Ronacher76c280b2008-05-04 12:31:48 +020026_striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
27_entity_re = re.compile(r'&([^;]+);')
Armin Ronacher9a0078d2008-08-13 18:24:17 +020028_letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
29_digits = '0123456789'
Armin Ronacherbe4ae242008-04-18 09:49:08 +020030
Armin Ronacher7259c762008-04-30 13:03:59 +020031# special singleton representing missing values for the runtime
32missing = type('MissingType', (), {'__repr__': lambda x: 'missing'})()
33
Armin Ronacherd416a972009-02-24 22:58:00 +010034# internal code
35internal_code = set()
36
Thomas Waldmann07a7d552013-05-18 00:47:41 +020037concat = u''.join
Armin Ronacher7ceced52008-05-03 10:15:31 +020038
39
Armin Ronacher4f7d2d52008-04-22 10:40:26 +020040def contextfunction(f):
Armin Ronacher9bb7e472008-05-28 11:26:59 +020041 """This decorator can be used to mark a function or method context callable.
42 A context callable is passed the active :class:`Context` as first argument when
43 called from the template. This is useful if a function wants to get access
44 to the context or functions provided on the context object. For example
45 a function that returns a sorted list of template variables the current
46 template exports could look like this::
47
Armin Ronacher58f351d2008-05-28 21:30:14 +020048 @contextfunction
Armin Ronacher9bb7e472008-05-28 11:26:59 +020049 def get_exported_names(context):
50 return sorted(context.exported_vars)
Armin Ronacher4f7d2d52008-04-22 10:40:26 +020051 """
52 f.contextfunction = True
53 return f
54
55
Armin Ronacher8346bd72010-03-14 19:43:47 +010056def evalcontextfunction(f):
Florent Xicluna0ec4f762012-02-05 13:09:15 +010057 """This decorator can be used to mark a function or method as an eval
Armin Ronacher8346bd72010-03-14 19:43:47 +010058 context callable. This is similar to the :func:`contextfunction`
59 but instead of passing the context, an evaluation context object is
Armin Ronacherfe150f32010-03-15 02:42:41 +010060 passed. For more information about the eval context, see
61 :ref:`eval-context`.
Armin Ronacher8346bd72010-03-14 19:43:47 +010062
63 .. versionadded:: 2.4
64 """
65 f.evalcontextfunction = True
66 return f
67
68
Armin Ronacher203bfcb2008-04-24 21:54:44 +020069def environmentfunction(f):
Armin Ronacher9bb7e472008-05-28 11:26:59 +020070 """This decorator can be used to mark a function or method as environment
71 callable. This decorator works exactly like the :func:`contextfunction`
72 decorator just that the first argument is the active :class:`Environment`
73 and not context.
Armin Ronacher203bfcb2008-04-24 21:54:44 +020074 """
75 f.environmentfunction = True
76 return f
77
78
Armin Ronacherd416a972009-02-24 22:58:00 +010079def internalcode(f):
80 """Marks the function as internally used"""
Thomas Waldmanne0003552013-05-17 23:52:14 +020081 internal_code.add(f.__code__)
Armin Ronacherd416a972009-02-24 22:58:00 +010082 return f
83
84
Armin Ronacher9bb7e472008-05-28 11:26:59 +020085def is_undefined(obj):
86 """Check if the object passed is undefined. This does nothing more than
87 performing an instance check against :class:`Undefined` but looks nicer.
88 This can be used for custom filters or tests that want to react to
89 undefined variables. For example a custom default filter can look like
90 this::
91
92 def default(var, default=''):
93 if is_undefined(var):
94 return default
95 return var
96 """
97 from jinja2.runtime import Undefined
98 return isinstance(obj, Undefined)
99
100
Armin Ronacherba6e25a2008-11-02 15:58:14 +0100101def consume(iterable):
102 """Consumes an iterable without doing anything with it."""
103 for event in iterable:
104 pass
105
106
Armin Ronacher187bde12008-05-01 18:19:16 +0200107def clear_caches():
108 """Jinja2 keeps internal caches for environments and lexers. These are
109 used so that Jinja2 doesn't have to recreate environments and lexers all
110 the time. Normally you don't have to care about that but if you are
111 messuring memory consumption you may want to clean the caches.
112 """
113 from jinja2.environment import _spontaneous_environments
114 from jinja2.lexer import _lexer_cache
115 _spontaneous_environments.clear()
116 _lexer_cache.clear()
117
118
Armin Ronacherf59bac22008-04-20 13:11:43 +0200119def import_string(import_name, silent=False):
Florent Xicluna0ec4f762012-02-05 13:09:15 +0100120 """Imports an object based on a string. This is useful if you want to
Armin Ronacherf59bac22008-04-20 13:11:43 +0200121 use import paths as endpoints or something similar. An import path can
122 be specified either in dotted notation (``xml.sax.saxutils.escape``)
123 or with a colon as object delimiter (``xml.sax.saxutils:escape``).
124
125 If the `silent` is True the return value will be `None` if the import
126 fails.
127
128 :return: imported object
Armin Ronacher9a027f42008-04-17 11:13:40 +0200129 """
Armin Ronacherf59bac22008-04-20 13:11:43 +0200130 try:
131 if ':' in import_name:
132 module, obj = import_name.split(':', 1)
133 elif '.' in import_name:
134 items = import_name.split('.')
135 module = '.'.join(items[:-1])
136 obj = items[-1]
137 else:
138 return __import__(import_name)
139 return getattr(__import__(module, None, None, [obj]), obj)
140 except (ImportError, AttributeError):
141 if not silent:
142 raise
Armin Ronacher9a027f42008-04-17 11:13:40 +0200143
144
Armin Ronacher0faa8612010-02-09 15:04:51 +0100145def open_if_exists(filename, mode='rb'):
Armin Ronacherccae0552008-10-05 23:08:58 +0200146 """Returns a file descriptor for the filename if that file exists,
147 otherwise `None`.
148 """
149 try:
Armin Ronacher790b8a82010-02-10 00:05:46 +0100150 return open(filename, mode)
Thomas Waldmanne0003552013-05-17 23:52:14 +0200151 except IOError as e:
Armin Ronacherccae0552008-10-05 23:08:58 +0200152 if e.errno not in (errno.ENOENT, errno.EISDIR):
153 raise
154
155
Armin Ronacher98dbf5f2010-04-12 15:49:59 +0200156def object_type_repr(obj):
157 """Returns the name of the object's type. For some recognized
158 singletons the name of the object is returned instead. (For
159 example for `None` and `Ellipsis`).
160 """
161 if obj is None:
162 return 'None'
163 elif obj is Ellipsis:
164 return 'Ellipsis'
Armin Ronacher802f4722010-04-20 19:48:46 +0200165 # __builtin__ in 2.x, builtins in 3.x
166 if obj.__class__.__module__ in ('__builtin__', 'builtins'):
Armin Ronacher800ac7f2010-04-20 13:45:11 +0200167 name = obj.__class__.__name__
Armin Ronacher98dbf5f2010-04-12 15:49:59 +0200168 else:
Armin Ronacher800ac7f2010-04-20 13:45:11 +0200169 name = obj.__class__.__module__ + '.' + obj.__class__.__name__
Armin Ronacher98dbf5f2010-04-12 15:49:59 +0200170 return '%s object' % name
171
172
Christoph Hacke9e43bb2008-04-13 23:35:48 +0200173def pformat(obj, verbose=False):
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200174 """Prettyprint an object. Either use the `pretty` library or the
Christoph Hacke9e43bb2008-04-13 23:35:48 +0200175 builtin `pprint`.
176 """
177 try:
178 from pretty import pretty
179 return pretty(obj, verbose=verbose)
180 except ImportError:
181 from pprint import pformat
182 return pformat(obj)
Christoph Hack80909862008-04-14 01:35:10 +0200183
184
Christoph Hack80909862008-04-14 01:35:10 +0200185def urlize(text, trim_url_limit=None, nofollow=False):
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200186 """Converts any URLs in text into clickable links. Works on http://,
Christoph Hack80909862008-04-14 01:35:10 +0200187 https:// and www. links. Links can have trailing punctuation (periods,
188 commas, close-parens) and leading punctuation (opening parens) and
189 it'll still do the right thing.
190
191 If trim_url_limit is not None, the URLs in link text will be limited
192 to trim_url_limit characters.
193
194 If nofollow is True, the URLs in link text will get a rel="nofollow"
195 attribute.
196 """
197 trim_url = lambda x, limit=trim_url_limit: limit is not None \
198 and (x[:limit] + (len(x) >=limit and '...'
199 or '')) or x
Armin Ronachere9098672013-05-19 14:16:13 +0100200 words = _word_split_re.split(text_type(escape(text)))
Christoph Hack80909862008-04-14 01:35:10 +0200201 nofollow_attr = nofollow and ' rel="nofollow"' or ''
202 for i, word in enumerate(words):
203 match = _punctuation_re.match(word)
204 if match:
205 lead, middle, trail = match.groups()
206 if middle.startswith('www.') or (
207 '@' not in middle and
208 not middle.startswith('http://') and
mozillazg66448932013-03-18 14:27:54 +0800209 not middle.startswith('https://') and
Christoph Hack80909862008-04-14 01:35:10 +0200210 len(middle) > 0 and
Armin Ronacher9a0078d2008-08-13 18:24:17 +0200211 middle[0] in _letters + _digits and (
Christoph Hack80909862008-04-14 01:35:10 +0200212 middle.endswith('.org') or
213 middle.endswith('.net') or
214 middle.endswith('.com')
215 )):
216 middle = '<a href="http://%s"%s>%s</a>' % (middle,
217 nofollow_attr, trim_url(middle))
218 if middle.startswith('http://') or \
219 middle.startswith('https://'):
220 middle = '<a href="%s"%s>%s</a>' % (middle,
221 nofollow_attr, trim_url(middle))
222 if '@' in middle and not middle.startswith('www.') and \
223 not ':' in middle and _simple_email_re.match(middle):
224 middle = '<a href="mailto:%s">%s</a>' % (middle, middle)
225 if lead + middle + trail != word:
226 words[i] = lead + middle + trail
227 return u''.join(words)
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200228
229
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200230def generate_lorem_ipsum(n=5, html=True, min=20, max=100):
231 """Generate some lorem impsum for the template."""
232 from jinja2.constants import LOREM_IPSUM_WORDS
Georg Brandl95632c42009-11-22 18:35:18 +0100233 from random import choice, randrange
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200234 words = LOREM_IPSUM_WORDS.split()
235 result = []
236
Thomas Waldmanne0003552013-05-17 23:52:14 +0200237 for _ in range(n):
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200238 next_capitalized = True
239 last_comma = last_fullstop = 0
240 word = None
241 last = None
242 p = []
243
244 # each paragraph contains out of 20 to 100 words.
Thomas Waldmanne0003552013-05-17 23:52:14 +0200245 for idx, _ in enumerate(range(randrange(min, max))):
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200246 while True:
247 word = choice(words)
248 if word != last:
249 last = word
250 break
251 if next_capitalized:
252 word = word.capitalize()
253 next_capitalized = False
254 # add commas
255 if idx - randrange(3, 8) > last_comma:
256 last_comma = idx
257 last_fullstop += 2
258 word += ','
259 # add end of sentences
260 if idx - randrange(10, 20) > last_fullstop:
261 last_comma = last_fullstop = idx
262 word += '.'
263 next_capitalized = True
264 p.append(word)
265
266 # ensure that the paragraph ends with a dot.
267 p = u' '.join(p)
268 if p.endswith(','):
269 p = p[:-1] + '.'
270 elif not p.endswith('.'):
271 p += '.'
272 result.append(p)
273
274 if not html:
275 return u'\n\n'.join(result)
276 return Markup(u'\n'.join(u'<p>%s</p>' % escape(x) for x in result))
277
278
Armin Ronacher51454012012-01-07 17:47:56 +0100279def unicode_urlencode(obj, charset='utf-8'):
Armin Ronacher1d4c6382012-01-07 17:46:40 +0100280 """URL escapes a single bytestring or unicode string with the
281 given charset if applicable to URL safe quoting under all rules
282 that need to be considered under all supported Python versions.
283
284 If non strings are provided they are converted to their unicode
285 representation first.
286 """
Armin Ronachere9098672013-05-19 14:16:13 +0100287 if not isinstance(obj, string_types):
288 obj = text_type(obj)
289 if isinstance(obj, text_type):
Armin Ronacher1d4c6382012-01-07 17:46:40 +0100290 obj = obj.encode(charset)
Armin Ronachere9098672013-05-19 14:16:13 +0100291 return text_type(url_quote(obj))
Armin Ronacher1d4c6382012-01-07 17:46:40 +0100292
293
Armin Ronacher814f6c22008-04-17 15:52:23 +0200294class LRUCache(object):
295 """A simple LRU Cache implementation."""
Armin Ronacher58f351d2008-05-28 21:30:14 +0200296
297 # this is fast for small capacities (something below 1000) but doesn't
298 # scale. But as long as it's only used as storage for templates this
299 # won't do any harm.
Armin Ronacher814f6c22008-04-17 15:52:23 +0200300
301 def __init__(self, capacity):
302 self.capacity = capacity
303 self._mapping = {}
304 self._queue = deque()
Armin Ronacher7962ce72008-05-20 17:52:52 +0200305 self._postinit()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200306
Armin Ronacher7962ce72008-05-20 17:52:52 +0200307 def _postinit(self):
Armin Ronacher814f6c22008-04-17 15:52:23 +0200308 # alias all queue methods for faster lookup
309 self._popleft = self._queue.popleft
310 self._pop = self._queue.pop
Thomas Waldmann07a7d552013-05-18 00:47:41 +0200311 self._remove = self._queue.remove
Armin Ronacher000b4912008-05-01 18:40:15 +0200312 self._wlock = allocate_lock()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200313 self._append = self._queue.append
314
Armin Ronacher7962ce72008-05-20 17:52:52 +0200315 def __getstate__(self):
316 return {
317 'capacity': self.capacity,
318 '_mapping': self._mapping,
319 '_queue': self._queue
320 }
321
322 def __setstate__(self, d):
323 self.__dict__.update(d)
324 self._postinit()
325
326 def __getnewargs__(self):
327 return (self.capacity,)
328
Armin Ronacher814f6c22008-04-17 15:52:23 +0200329 def copy(self):
Florent Xicluna0ec4f762012-02-05 13:09:15 +0100330 """Return a shallow copy of the instance."""
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200331 rv = self.__class__(self.capacity)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200332 rv._mapping.update(self._mapping)
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200333 rv._queue = deque(self._queue)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200334 return rv
335
336 def get(self, key, default=None):
337 """Return an item from the cache dict or `default`"""
Armin Ronacher000b4912008-05-01 18:40:15 +0200338 try:
Armin Ronacher814f6c22008-04-17 15:52:23 +0200339 return self[key]
Armin Ronacher000b4912008-05-01 18:40:15 +0200340 except KeyError:
341 return default
Armin Ronacher814f6c22008-04-17 15:52:23 +0200342
343 def setdefault(self, key, default=None):
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200344 """Set `default` if the key is not in the cache otherwise
Armin Ronacher814f6c22008-04-17 15:52:23 +0200345 leave unchanged. Return the value of this key.
346 """
Armin Ronacherd4e54382013-04-13 00:38:27 +0100347 self._wlock.acquire()
Armin Ronacher000b4912008-05-01 18:40:15 +0200348 try:
Armin Ronacherd4e54382013-04-13 00:38:27 +0100349 try:
350 return self[key]
351 except KeyError:
352 self[key] = default
353 return default
354 finally:
355 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200356
357 def clear(self):
358 """Clear the cache."""
Armin Ronacher000b4912008-05-01 18:40:15 +0200359 self._wlock.acquire()
360 try:
361 self._mapping.clear()
362 self._queue.clear()
363 finally:
364 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200365
366 def __contains__(self, key):
367 """Check if a key exists in this cache."""
368 return key in self._mapping
369
370 def __len__(self):
371 """Return the current size of the cache."""
372 return len(self._mapping)
373
374 def __repr__(self):
375 return '<%s %r>' % (
376 self.__class__.__name__,
377 self._mapping
378 )
379
380 def __getitem__(self, key):
381 """Get an item from the cache. Moves the item up so that it has the
382 highest priority then.
383
Florent Xicluna0ec4f762012-02-05 13:09:15 +0100384 Raise a `KeyError` if it does not exist.
Armin Ronacher814f6c22008-04-17 15:52:23 +0200385 """
Armin Ronacherd4e54382013-04-13 00:38:27 +0100386 self._wlock.acquire()
387 try:
388 rv = self._mapping[key]
389 if self._queue[-1] != key:
390 try:
391 self._remove(key)
392 except ValueError:
393 # if something removed the key from the container
394 # when we read, ignore the ValueError that we would
395 # get otherwise.
396 pass
397 self._append(key)
398 return rv
399 finally:
400 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200401
402 def __setitem__(self, key, value):
403 """Sets the value for an item. Moves the item up so that it
404 has the highest priority then.
405 """
Armin Ronacher000b4912008-05-01 18:40:15 +0200406 self._wlock.acquire()
407 try:
408 if key in self._mapping:
Armin Ronacherd4e54382013-04-13 00:38:27 +0100409 self._remove(key)
Armin Ronacher000b4912008-05-01 18:40:15 +0200410 elif len(self._mapping) == self.capacity:
411 del self._mapping[self._popleft()]
412 self._append(key)
413 self._mapping[key] = value
414 finally:
415 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200416
417 def __delitem__(self, key):
418 """Remove an item from the cache dict.
Florent Xicluna0ec4f762012-02-05 13:09:15 +0100419 Raise a `KeyError` if it does not exist.
Armin Ronacher814f6c22008-04-17 15:52:23 +0200420 """
Armin Ronacher000b4912008-05-01 18:40:15 +0200421 self._wlock.acquire()
422 try:
423 del self._mapping[key]
Armin Ronachere7c72bc2009-09-14 12:20:33 -0700424 try:
425 self._remove(key)
426 except ValueError:
427 # __getitem__ is not locked, it might happen
428 pass
Armin Ronacher000b4912008-05-01 18:40:15 +0200429 finally:
430 self._wlock.release()
Armin Ronacher814f6c22008-04-17 15:52:23 +0200431
Armin Ronachere25f24d2008-05-19 11:20:41 +0200432 def items(self):
433 """Return a list of items."""
434 result = [(key, self._mapping[key]) for key in list(self._queue)]
435 result.reverse()
436 return result
437
438 def iteritems(self):
439 """Iterate over all items."""
440 return iter(self.items())
441
442 def values(self):
443 """Return a list of all values."""
444 return [x[1] for x in self.items()]
445
446 def itervalue(self):
447 """Iterate over all values."""
448 return iter(self.values())
449
450 def keys(self):
451 """Return a list of all keys ordered by most recent usage."""
452 return list(self)
453
454 def iterkeys(self):
455 """Iterate over all keys in the cache dict, ordered by
Armin Ronacher814f6c22008-04-17 15:52:23 +0200456 the most recent usage.
457 """
Armin Ronachere2244882008-05-19 09:25:57 +0200458 return reversed(tuple(self._queue))
Armin Ronacher814f6c22008-04-17 15:52:23 +0200459
Armin Ronachere25f24d2008-05-19 11:20:41 +0200460 __iter__ = iterkeys
461
Armin Ronacher814f6c22008-04-17 15:52:23 +0200462 def __reversed__(self):
463 """Iterate over the values in the cache dict, oldest items
464 coming first.
465 """
Armin Ronachere2244882008-05-19 09:25:57 +0200466 return iter(tuple(self._queue))
Armin Ronacher814f6c22008-04-17 15:52:23 +0200467
468 __copy__ = copy
469
Armin Ronacherbd33f112008-04-18 09:17:32 +0200470
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200471# register the LRU cache as mutable mapping if possible
472try:
473 from collections import MutableMapping
474 MutableMapping.register(LRUCache)
475except ImportError:
476 pass
477
478
Armin Ronacher28c74882013-05-20 01:51:26 +0100479@implements_iterator
480class Cycler(object):
Armin Ronacherccae0552008-10-05 23:08:58 +0200481 """A cycle helper for templates."""
482
483 def __init__(self, *items):
484 if not items:
485 raise RuntimeError('at least one item has to be provided')
486 self.items = items
487 self.reset()
488
489 def reset(self):
490 """Resets the cycle."""
491 self.pos = 0
492
493 @property
494 def current(self):
495 """Returns the current item."""
496 return self.items[self.pos]
497
Thomas Waldmann7d295622013-05-18 00:06:22 +0200498 def __next__(self):
Armin Ronacherccae0552008-10-05 23:08:58 +0200499 """Goes one item ahead and returns it."""
500 rv = self.current
501 self.pos = (self.pos + 1) % len(self.items)
502 return rv
503
504
Armin Ronacherd34eb122008-10-13 23:47:51 +0200505class Joiner(object):
506 """A joining helper for templates."""
507
508 def __init__(self, sep=u', '):
509 self.sep = sep
510 self.used = False
511
512 def __call__(self):
513 if not self.used:
514 self.used = True
515 return u''
516 return self.sep
517
518
Armin Ronacher294f2eb2013-05-19 13:49:12 +0100519# Imported here because that's where it was in the past
520from markupsafe import Markup, escape, soft_unicode