blob: 195a9420c27f79dc74afad9a354dad7b355095bc [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 Ronacher814f6c22008-04-17 15:52:23 +020013from collections import deque
14from copy import deepcopy
Armin Ronacher18c6ca02008-04-17 10:03:29 +020015from functools import update_wrapper
16from itertools import imap
Armin Ronacher8edbe492008-04-10 20:43:43 +020017
18
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)*)$' % (
22 '|'.join(imap(re.escape, ('(', '<', '&lt;'))),
23 '|'.join(imap(re.escape, ('.', ',', ')', '>', '\n', '&gt;')))
24 )
25)
26_simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$')
27
28
Armin Ronacherf59bac22008-04-20 13:11:43 +020029def import_string(import_name, silent=False):
30 """Imports an object based on a string. This use useful if you want to
31 use import paths as endpoints or something similar. An import path can
32 be specified either in dotted notation (``xml.sax.saxutils.escape``)
33 or with a colon as object delimiter (``xml.sax.saxutils:escape``).
34
35 If the `silent` is True the return value will be `None` if the import
36 fails.
37
38 :return: imported object
Armin Ronacher9a027f42008-04-17 11:13:40 +020039 """
Armin Ronacherf59bac22008-04-20 13:11:43 +020040 try:
41 if ':' in import_name:
42 module, obj = import_name.split(':', 1)
43 elif '.' in import_name:
44 items = import_name.split('.')
45 module = '.'.join(items[:-1])
46 obj = items[-1]
47 else:
48 return __import__(import_name)
49 return getattr(__import__(module, None, None, [obj]), obj)
50 except (ImportError, AttributeError):
51 if not silent:
52 raise
Armin Ronacher9a027f42008-04-17 11:13:40 +020053
54
Christoph Hacke9e43bb2008-04-13 23:35:48 +020055def pformat(obj, verbose=False):
Armin Ronacherbe4ae242008-04-18 09:49:08 +020056 """Prettyprint an object. Either use the `pretty` library or the
Christoph Hacke9e43bb2008-04-13 23:35:48 +020057 builtin `pprint`.
58 """
59 try:
60 from pretty import pretty
61 return pretty(obj, verbose=verbose)
62 except ImportError:
63 from pprint import pformat
64 return pformat(obj)
Christoph Hack80909862008-04-14 01:35:10 +020065
66
Christoph Hack80909862008-04-14 01:35:10 +020067def urlize(text, trim_url_limit=None, nofollow=False):
Armin Ronacherbe4ae242008-04-18 09:49:08 +020068 """Converts any URLs in text into clickable links. Works on http://,
Christoph Hack80909862008-04-14 01:35:10 +020069 https:// and www. links. Links can have trailing punctuation (periods,
70 commas, close-parens) and leading punctuation (opening parens) and
71 it'll still do the right thing.
72
73 If trim_url_limit is not None, the URLs in link text will be limited
74 to trim_url_limit characters.
75
76 If nofollow is True, the URLs in link text will get a rel="nofollow"
77 attribute.
78 """
79 trim_url = lambda x, limit=trim_url_limit: limit is not None \
80 and (x[:limit] + (len(x) >=limit and '...'
81 or '')) or x
82 words = _word_split_re.split(text)
83 nofollow_attr = nofollow and ' rel="nofollow"' or ''
84 for i, word in enumerate(words):
85 match = _punctuation_re.match(word)
86 if match:
87 lead, middle, trail = match.groups()
88 if middle.startswith('www.') or (
89 '@' not in middle and
90 not middle.startswith('http://') and
91 len(middle) > 0 and
92 middle[0] in string.letters + string.digits and (
93 middle.endswith('.org') or
94 middle.endswith('.net') or
95 middle.endswith('.com')
96 )):
97 middle = '<a href="http://%s"%s>%s</a>' % (middle,
98 nofollow_attr, trim_url(middle))
99 if middle.startswith('http://') or \
100 middle.startswith('https://'):
101 middle = '<a href="%s"%s>%s</a>' % (middle,
102 nofollow_attr, trim_url(middle))
103 if '@' in middle and not middle.startswith('www.') and \
104 not ':' in middle and _simple_email_re.match(middle):
105 middle = '<a href="mailto:%s">%s</a>' % (middle, middle)
106 if lead + middle + trail != word:
107 words[i] = lead + middle + trail
108 return u''.join(words)
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200109
110
111class Markup(unicode):
112 """Marks a string as being safe for inclusion in HTML/XML output without
113 needing to be escaped. This implements the `__html__` interface a couple
114 of frameworks and web applications use.
115
116 The `escape` function returns markup objects so that double escaping can't
117 happen. If you want to use autoescaping in Jinja just set the finalizer
118 of the environment to `escape`.
119 """
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200120 __slots__ = ()
121
122 def __html__(self):
123 return self
124
125 def __add__(self, other):
126 if hasattr(other, '__html__') or isinstance(other, basestring):
127 return self.__class__(unicode(self) + unicode(escape(other)))
128 return NotImplemented
129
130 def __radd__(self, other):
131 if hasattr(other, '__html__') or isinstance(other, basestring):
132 return self.__class__(unicode(escape(other)) + unicode(self))
133 return NotImplemented
134
135 def __mul__(self, num):
136 if not isinstance(num, (int, long)):
137 return NotImplemented
138 return self.__class__(unicode.__mul__(self, num))
139 __rmul__ = __mul__
140
141 def __mod__(self, arg):
142 if isinstance(arg, tuple):
143 arg = tuple(imap(_MarkupEscapeHelper, arg))
144 else:
145 arg = _MarkupEscapeHelper(arg)
146 return self.__class__(unicode.__mod__(self, arg))
147
148 def __repr__(self):
149 return '%s(%s)' % (
150 self.__class__.__name__,
151 unicode.__repr__(self)
152 )
153
154 def join(self, seq):
155 return self.__class__(unicode.join(self, imap(escape, seq)))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200156 join.__doc__ = unicode.join.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200157
158 def split(self, *args, **kwargs):
159 return map(self.__class__, unicode.split(self, *args, **kwargs))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200160 split.__doc__ = unicode.split.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200161
162 def rsplit(self, *args, **kwargs):
163 return map(self.__class__, unicode.rsplit(self, *args, **kwargs))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200164 rsplit.__doc__ = unicode.rsplit.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200165
166 def splitlines(self, *args, **kwargs):
167 return map(self.__class__, unicode.splitlines(self, *args, **kwargs))
Armin Ronacherf59bac22008-04-20 13:11:43 +0200168 splitlines.__doc__ = unicode.splitlines.__doc__
Armin Ronacher18c6ca02008-04-17 10:03:29 +0200169
170 def make_wrapper(name):
171 orig = getattr(unicode, name)
172 def func(self, *args, **kwargs):
173 args = list(args)
174 for idx, arg in enumerate(args):
175 if hasattr(arg, '__html__') or isinstance(arg, basestring):
176 args[idx] = escape(arg)
177 for name, arg in kwargs.iteritems():
178 if hasattr(arg, '__html__') or isinstance(arg, basestring):
179 kwargs[name] = escape(arg)
180 return self.__class__(orig(self, *args, **kwargs))
181 return update_wrapper(func, orig, ('__name__', '__doc__'))
182 for method in '__getitem__', '__getslice__', 'capitalize', \
183 'title', 'lower', 'upper', 'replace', 'ljust', \
184 'rjust', 'lstrip', 'rstrip', 'partition', 'center', \
185 'strip', 'translate', 'expandtabs', 'rpartition', \
186 'swapcase', 'zfill':
187 locals()[method] = make_wrapper(method)
188 del method, make_wrapper
189
190
191class _MarkupEscapeHelper(object):
192 """Helper for Markup.__mod__"""
193
194 def __init__(self, obj):
195 self.obj = obj
196
197 __getitem__ = lambda s, x: _MarkupEscapeHelper(s.obj[x])
198 __unicode__ = lambda s: unicode(escape(s.obj))
199 __str__ = lambda s: str(escape(s.obj))
200 __repr__ = lambda s: str(repr(escape(s.obj)))
201 __int__ = lambda s: int(s.obj)
202 __float__ = lambda s: float(s.obj)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200203
204
205class LRUCache(object):
206 """A simple LRU Cache implementation."""
207 # this is fast for small capacities (something around 200) but doesn't
208 # scale. But as long as it's only used for the database connections in
209 # a non request fallback it's fine.
210
211 def __init__(self, capacity):
212 self.capacity = capacity
213 self._mapping = {}
214 self._queue = deque()
215
216 # alias all queue methods for faster lookup
217 self._popleft = self._queue.popleft
218 self._pop = self._queue.pop
219 if hasattr(self._queue, 'remove'):
220 self._remove = self._queue.remove
221 self._append = self._queue.append
222
223 def _remove(self, obj):
224 """Python 2.4 compatibility."""
225 for idx, item in enumerate(self._queue):
226 if item == obj:
227 del self._queue[idx]
228 break
229
230 def copy(self):
231 """Return an shallow copy of the instance."""
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200232 rv = self.__class__(self.capacity)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200233 rv._mapping.update(self._mapping)
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200234 rv._queue = deque(self._queue)
Armin Ronacher814f6c22008-04-17 15:52:23 +0200235 return rv
236
237 def get(self, key, default=None):
238 """Return an item from the cache dict or `default`"""
239 if key in self:
240 return self[key]
241 return default
242
243 def setdefault(self, key, default=None):
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200244 """Set `default` if the key is not in the cache otherwise
Armin Ronacher814f6c22008-04-17 15:52:23 +0200245 leave unchanged. Return the value of this key.
246 """
247 if key in self:
248 return self[key]
249 self[key] = default
250 return default
251
252 def clear(self):
253 """Clear the cache."""
254 self._mapping.clear()
255 self._queue.clear()
256
257 def __contains__(self, key):
258 """Check if a key exists in this cache."""
259 return key in self._mapping
260
261 def __len__(self):
262 """Return the current size of the cache."""
263 return len(self._mapping)
264
265 def __repr__(self):
266 return '<%s %r>' % (
267 self.__class__.__name__,
268 self._mapping
269 )
270
271 def __getitem__(self, key):
272 """Get an item from the cache. Moves the item up so that it has the
273 highest priority then.
274
275 Raise an `KeyError` if it does not exist.
276 """
277 rv = self._mapping[key]
278 if self._queue[-1] != key:
279 self._remove(key)
280 self._append(key)
281 return rv
282
283 def __setitem__(self, key, value):
284 """Sets the value for an item. Moves the item up so that it
285 has the highest priority then.
286 """
287 if key in self._mapping:
288 self._remove(key)
289 elif len(self._mapping) == self.capacity:
290 del self._mapping[self._popleft()]
291 self._append(key)
292 self._mapping[key] = value
293
294 def __delitem__(self, key):
295 """Remove an item from the cache dict.
296 Raise an `KeyError` if it does not exist.
297 """
298 del self._mapping[key]
299 self._remove(key)
300
301 def __iter__(self):
302 """Iterate over all values in the cache dict, ordered by
303 the most recent usage.
304 """
305 return reversed(self._queue)
306
307 def __reversed__(self):
308 """Iterate over the values in the cache dict, oldest items
309 coming first.
310 """
311 return iter(self._queue)
312
313 __copy__ = copy
314
Armin Ronacherbd33f112008-04-18 09:17:32 +0200315
316# we have to import it down here as the speedups module imports the
317# markup type which is define above.
318try:
Armin Ronacherf59bac22008-04-20 13:11:43 +0200319 from jinja2._speedups import escape, soft_unicode
Armin Ronacherbd33f112008-04-18 09:17:32 +0200320except ImportError:
321 def escape(obj):
322 """Convert the characters &, <, >, and " in string s to HTML-safe
323 sequences. Use this if you need to display text that might contain
324 such characters in HTML.
325 """
326 if hasattr(obj, '__html__'):
327 return obj.__html__()
328 return Markup(unicode(obj)
329 .replace('&', '&amp;')
330 .replace('>', '&gt;')
331 .replace('<', '&lt;')
332 .replace('"', '&quot;')
333 )
Armin Ronacherf59bac22008-04-20 13:11:43 +0200334
335 def soft_unicode(s):
336 """Make a string unicode if it isn't already. That way a markup
337 string is not converted back to unicode.
338 """
339 if not isinstance(s, unicode):
340 s = unicode(s)
341 return s