1 # -*- coding: utf-8 -*-
8 :copyright: 2008 by Armin Ronacher.
9 :license: BSD, see LICENSE for more details.
15 from thread import allocate_lock
17 from dummy_thread import allocate_lock
18 from htmlentitydefs import name2codepoint
19 from collections import deque
20 from copy import deepcopy
21 from itertools import imap
24 _word_split_re = re.compile(r'(\s+)')
25 _punctuation_re = re.compile(
26 '^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$' % (
27 '|'.join(imap(re.escape, ('(', '<', '<'))),
28 '|'.join(imap(re.escape, ('.', ',', ')', '>', '\n', '>')))
31 _simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$')
32 _striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
33 _entity_re = re.compile(r'&([^;]+);')
34 _entities = name2codepoint.copy()
35 _entities['apos'] = 39
37 # special singleton representing missing values for the runtime
38 missing = type('MissingType', (), {'__repr__': lambda x: 'missing'})()
41 # concatenate a list of strings and convert them to unicode.
42 # unfortunately there is a bug in python 2.4 and lower that causes
43 # unicode.join trash the traceback.
47 raise TypeError(_test_gen_bug)
49 _concat(_test_gen_bug())
50 except TypeError, _error:
51 if not _error.args or _error.args[0] is not _test_gen_bug:
54 return _concat(list(gen))
56 # this hack is needed so that the current frame
57 # does not show up in the traceback.
58 exc_type, exc_value, tb = sys.exc_info()
59 raise exc_type, exc_value, tb.tb_next
62 del _test_gen_bug, _error
65 def contextfunction(f):
66 """This decorator can be used to mark a callable as context callable. A
67 context callable is passed the active context as first argument if it
68 was directly stored in the context.
70 f.contextfunction = True
74 def environmentfunction(f):
75 """This decorator can be used to mark a callable as environment callable.
76 A environment callable is passed the current environment as first argument
77 if it was directly stored in the context.
79 f.environmentfunction = True
84 """Jinja2 keeps internal caches for environments and lexers. These are
85 used so that Jinja2 doesn't have to recreate environments and lexers all
86 the time. Normally you don't have to care about that but if you are
87 messuring memory consumption you may want to clean the caches.
89 from jinja2.environment import _spontaneous_environments
90 from jinja2.lexer import _lexer_cache
91 _spontaneous_environments.clear()
95 def import_string(import_name, silent=False):
96 """Imports an object based on a string. This use useful if you want to
97 use import paths as endpoints or something similar. An import path can
98 be specified either in dotted notation (``xml.sax.saxutils.escape``)
99 or with a colon as object delimiter (``xml.sax.saxutils:escape``).
101 If the `silent` is True the return value will be `None` if the import
104 :return: imported object
107 if ':' in import_name:
108 module, obj = import_name.split(':', 1)
109 elif '.' in import_name:
110 items = import_name.split('.')
111 module = '.'.join(items[:-1])
114 return __import__(import_name)
115 return getattr(__import__(module, None, None, [obj]), obj)
116 except (ImportError, AttributeError):
121 def pformat(obj, verbose=False):
122 """Prettyprint an object. Either use the `pretty` library or the
126 from pretty import pretty
127 return pretty(obj, verbose=verbose)
129 from pprint import pformat
133 def urlize(text, trim_url_limit=None, nofollow=False):
134 """Converts any URLs in text into clickable links. Works on http://,
135 https:// and www. links. Links can have trailing punctuation (periods,
136 commas, close-parens) and leading punctuation (opening parens) and
137 it'll still do the right thing.
139 If trim_url_limit is not None, the URLs in link text will be limited
140 to trim_url_limit characters.
142 If nofollow is True, the URLs in link text will get a rel="nofollow"
145 trim_url = lambda x, limit=trim_url_limit: limit is not None \
146 and (x[:limit] + (len(x) >=limit and '...'
148 words = _word_split_re.split(text)
149 nofollow_attr = nofollow and ' rel="nofollow"' or ''
150 for i, word in enumerate(words):
151 match = _punctuation_re.match(word)
153 lead, middle, trail = match.groups()
154 if middle.startswith('www.') or (
155 '@' not in middle and
156 not middle.startswith('http://') and
158 middle[0] in string.letters + string.digits and (
159 middle.endswith('.org') or
160 middle.endswith('.net') or
161 middle.endswith('.com')
163 middle = '<a href="http://%s"%s>%s</a>' % (middle,
164 nofollow_attr, trim_url(middle))
165 if middle.startswith('http://') or \
166 middle.startswith('https://'):
167 middle = '<a href="%s"%s>%s</a>' % (middle,
168 nofollow_attr, trim_url(middle))
169 if '@' in middle and not middle.startswith('www.') and \
170 not ':' in middle and _simple_email_re.match(middle):
171 middle = '<a href="mailto:%s">%s</a>' % (middle, middle)
172 if lead + middle + trail != word:
173 words[i] = lead + middle + trail
174 return u''.join(words)
177 def generate_lorem_ipsum(n=5, html=True, min=20, max=100):
178 """Generate some lorem impsum for the template."""
179 from jinja2.constants import LOREM_IPSUM_WORDS
180 from random import choice, random, randrange
181 words = LOREM_IPSUM_WORDS.split()
185 next_capitalized = True
186 last_comma = last_fullstop = 0
191 # each paragraph contains out of 20 to 100 words.
192 for idx, _ in enumerate(xrange(randrange(min, max))):
199 word = word.capitalize()
200 next_capitalized = False
202 if idx - randrange(3, 8) > last_comma:
206 # add end of sentences
207 if idx - randrange(10, 20) > last_fullstop:
208 last_comma = last_fullstop = idx
210 next_capitalized = True
213 # ensure that the paragraph ends with a dot.
217 elif not p.endswith('.'):
222 return u'\n\n'.join(result)
223 return Markup(u'\n'.join(u'<p>%s</p>' % escape(x) for x in result))
226 class Markup(unicode):
227 """Marks a string as being safe for inclusion in HTML/XML output without
228 needing to be escaped. This implements the `__html__` interface a couple
229 of frameworks and web applications use.
231 The `escape` function returns markup objects so that double escaping can't
232 happen. If you want to use autoescaping in Jinja just set the finalizer
233 of the environment to `escape`.
240 def __add__(self, other):
241 if hasattr(other, '__html__') or isinstance(other, basestring):
242 return self.__class__(unicode(self) + unicode(escape(other)))
243 return NotImplemented
245 def __radd__(self, other):
246 if hasattr(other, '__html__') or isinstance(other, basestring):
247 return self.__class__(unicode(escape(other)) + unicode(self))
248 return NotImplemented
250 def __mul__(self, num):
251 if not isinstance(num, (int, long)):
252 return NotImplemented
253 return self.__class__(unicode.__mul__(self, num))
256 def __mod__(self, arg):
257 if isinstance(arg, tuple):
258 arg = tuple(imap(_MarkupEscapeHelper, arg))
260 arg = _MarkupEscapeHelper(arg)
261 return self.__class__(unicode.__mod__(self, arg))
265 self.__class__.__name__,
266 unicode.__repr__(self)
270 return self.__class__(unicode.join(self, imap(escape, seq)))
271 join.__doc__ = unicode.join.__doc__
273 def split(self, *args, **kwargs):
274 return map(self.__class__, unicode.split(self, *args, **kwargs))
275 split.__doc__ = unicode.split.__doc__
277 def rsplit(self, *args, **kwargs):
278 return map(self.__class__, unicode.rsplit(self, *args, **kwargs))
279 rsplit.__doc__ = unicode.rsplit.__doc__
281 def splitlines(self, *args, **kwargs):
282 return map(self.__class__, unicode.splitlines(self, *args, **kwargs))
283 splitlines.__doc__ = unicode.splitlines.__doc__
286 """Unescape markup."""
289 if name in _entities:
290 return unichr(_entities[name])
292 if name[:2] in ('#x', '#X'):
293 return unichr(int(name[2:], 16))
294 elif name.startswith('#'):
295 return unichr(int(name[1:]))
299 return _entity_re.sub(handle_match, unicode(self))
302 """Strip tags and resolve enities."""
303 stripped = u' '.join(_striptags_re.sub('', self).split())
304 return Markup(stripped).unescape()
308 """Escape the string. Works like :func:`escape`."""
310 if rv.__class__ is not cls:
314 def make_wrapper(name):
315 orig = getattr(unicode, name)
316 def func(self, *args, **kwargs):
318 for idx, arg in enumerate(args):
319 if hasattr(arg, '__html__') or isinstance(arg, basestring):
320 args[idx] = escape(arg)
321 for name, arg in kwargs.iteritems():
322 if hasattr(arg, '__html__') or isinstance(arg, basestring):
323 kwargs[name] = escape(arg)
324 return self.__class__(orig(self, *args, **kwargs))
325 func.__name__ = orig.__name__
326 func.__doc__ = orig.__doc__
328 for method in '__getitem__', '__getslice__', 'capitalize', \
329 'title', 'lower', 'upper', 'replace', 'ljust', \
330 'rjust', 'lstrip', 'rstrip', 'center', 'strip', \
331 'translate', 'expandtabs', 'swapcase', 'zfill':
332 locals()[method] = make_wrapper(method)
335 if hasattr(unicode, 'partition'):
337 partition=make_wrapper('partition'),
338 rpartition=make_wrapper('rpartition')
340 del method, make_wrapper
343 class _MarkupEscapeHelper(object):
344 """Helper for Markup.__mod__"""
346 def __init__(self, obj):
349 __getitem__ = lambda s, x: _MarkupEscapeHelper(s.obj[x])
350 __unicode__ = lambda s: unicode(escape(s.obj))
351 __str__ = lambda s: str(escape(s.obj))
352 __repr__ = lambda s: str(repr(escape(s.obj)))
353 __int__ = lambda s: int(s.obj)
354 __float__ = lambda s: float(s.obj)
357 class LRUCache(object):
358 """A simple LRU Cache implementation."""
359 # this is fast for small capacities (something around 200) but doesn't
360 # scale. But as long as it's only used for the database connections in
361 # a non request fallback it's fine.
363 def __init__(self, capacity):
364 self.capacity = capacity
366 self._queue = deque()
370 # alias all queue methods for faster lookup
371 self._popleft = self._queue.popleft
372 self._pop = self._queue.pop
373 if hasattr(self._queue, 'remove'):
374 self._remove = self._queue.remove
375 self._wlock = allocate_lock()
376 self._append = self._queue.append
378 def _remove(self, obj):
379 """Python 2.4 compatibility."""
380 for idx, item in enumerate(self._queue):
385 def __getstate__(self):
387 'capacity': self.capacity,
388 '_mapping': self._mapping,
389 '_queue': self._queue
392 def __setstate__(self, d):
393 self.__dict__.update(d)
396 def __getnewargs__(self):
397 return (self.capacity,)
400 """Return an shallow copy of the instance."""
401 rv = self.__class__(self.capacity)
402 rv._mapping.update(self._mapping)
403 rv._queue = deque(self._queue)
406 def get(self, key, default=None):
407 """Return an item from the cache dict or `default`"""
413 def setdefault(self, key, default=None):
414 """Set `default` if the key is not in the cache otherwise
415 leave unchanged. Return the value of this key.
424 """Clear the cache."""
425 self._wlock.acquire()
427 self._mapping.clear()
430 self._wlock.release()
432 def __contains__(self, key):
433 """Check if a key exists in this cache."""
434 return key in self._mapping
437 """Return the current size of the cache."""
438 return len(self._mapping)
442 self.__class__.__name__,
446 def __getitem__(self, key):
447 """Get an item from the cache. Moves the item up so that it has the
448 highest priority then.
450 Raise an `KeyError` if it does not exist.
452 rv = self._mapping[key]
453 if self._queue[-1] != key:
458 def __setitem__(self, key, value):
459 """Sets the value for an item. Moves the item up so that it
460 has the highest priority then.
462 self._wlock.acquire()
464 if key in self._mapping:
466 elif len(self._mapping) == self.capacity:
467 del self._mapping[self._popleft()]
469 self._mapping[key] = value
471 self._wlock.release()
473 def __delitem__(self, key):
474 """Remove an item from the cache dict.
475 Raise an `KeyError` if it does not exist.
477 self._wlock.acquire()
479 del self._mapping[key]
482 self._wlock.release()
485 """Return a list of items."""
486 result = [(key, self._mapping[key]) for key in list(self._queue)]
491 """Iterate over all items."""
492 return iter(self.items())
495 """Return a list of all values."""
496 return [x[1] for x in self.items()]
499 """Iterate over all values."""
500 return iter(self.values())
503 """Return a list of all keys ordered by most recent usage."""
507 """Iterate over all keys in the cache dict, ordered by
508 the most recent usage.
510 return reversed(tuple(self._queue))
514 def __reversed__(self):
515 """Iterate over the values in the cache dict, oldest items
518 return iter(tuple(self._queue))
523 # we have to import it down here as the speedups module imports the
524 # markup type which is define above.
526 from jinja2._speedups import escape, soft_unicode
529 """Convert the characters &, <, >, ' and " in string s to HTML-safe
530 sequences. Use this if you need to display text that might contain
531 such characters in HTML. Marks return value as markup string.
533 if hasattr(s, '__html__'):
535 return Markup(unicode(s)
536 .replace('&', '&')
537 .replace('>', '>')
538 .replace('<', '<')
539 .replace("'", ''')
540 .replace('"', '"')
544 """Make a string unicode if it isn't already. That way a markup
545 string is not converted back to unicode.
547 if not isinstance(s, unicode):
554 from functools import partial
556 class partial(object):
557 def __init__(self, _func, *args, **kwargs):
560 self._kwargs = kwargs
561 def __call__(self, *args, **kwargs):
562 kwargs.update(self._kwargs)
563 return self._func(*(self._args + args), **kwargs)