1 # -*- coding: utf-8 -*-
8 **license information**: some of the regular expressions and
9 the ``urlize`` function were taken from the django framework.
11 :copyright: 2007 by Armin Ronacher, Lawrence Journal-World.
12 :license: BSD, see LICENSE for more details.
18 from types import MethodType, FunctionType
19 from compiler.ast import CallFunc, Name, Const
20 from jinja.nodes import Trans
21 from jinja.datastructure import Context, TemplateData
22 from jinja.exceptions import SecurityException, TemplateNotFound
24 #: the python2.4 version of deque is missing the remove method
25 #: because a for loop with a lookup for the missing value written
26 #: in python is slower we just use deque if we have python2.5 or higher
27 if sys.version_info >= (2, 5):
28 from collections import deque
32 #: number of maximal range items
35 _word_split_re = re.compile(r'(\s+)')
37 _punctuation_re = re.compile(
38 '^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$' % (
39 '|'.join([re.escape(p) for p in ('(', '<', '<')]),
40 '|'.join([re.escape(p) for p in ('.', ',', ')', '>', '\n', '>')])
44 _simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$')
46 #: used by from_string as cache
47 _from_string_env = None
52 def urlize(text, trim_url_limit=None, nofollow=False):
54 Converts any URLs in text into clickable links. Works on http://,
55 https:// and www. links. Links can have trailing punctuation (periods,
56 commas, close-parens) and leading punctuation (opening parens) and
57 it'll still do the right thing.
59 If trim_url_limit is not None, the URLs in link text will be limited
60 to trim_url_limit characters.
62 If nofollow is True, the URLs in link text will get a rel="nofollow"
65 trim_url = lambda x, limit=trim_url_limit: limit is not None \
66 and (x[:limit] + (len(x) >=limit and '...'
68 words = _word_split_re.split(text)
69 nofollow_attr = nofollow and ' rel="nofollow"' or ''
70 for i, word in enumerate(words):
71 match = _punctuation_re.match(word)
73 lead, middle, trail = match.groups()
74 if middle.startswith('www.') or (
76 not middle.startswith('http://') and
78 middle[0] in string.letters + string.digits and (
79 middle.endswith('.org') or
80 middle.endswith('.net') or
81 middle.endswith('.com')
83 middle = '<a href="http://%s"%s>%s</a>' % (middle,
84 nofollow_attr, trim_url(middle))
85 if middle.startswith('http://') or \
86 middle.startswith('https://'):
87 middle = '<a href="%s"%s>%s</a>' % (middle,
88 nofollow_attr, trim_url(middle))
89 if '@' in middle and not middle.startswith('www.') and \
90 not ':' in middle and _simple_email_re.match(middle):
91 middle = '<a href="mailto:%s">%s</a>' % (middle, middle)
92 if lead + middle + trail != word:
93 words[i] = lead + middle + trail
94 return u''.join(words)
97 def from_string(source):
99 Create a template from the template source.
101 global _from_string_env
102 if _from_string_env is None:
103 from jinja.environment import Environment
104 _from_string_env = Environment()
105 return _from_string_env.from_string(source)
108 def get_attribute(obj, name):
110 Return the attribute from name. Raise either `AttributeError`
111 or `SecurityException` if something goes wrong.
113 if not isinstance(name, basestring):
114 raise AttributeError(name)
115 if name[:2] == name[-2:] == '__' or name[:2] == '::':
116 raise SecurityException('not allowed to access internal attributes')
117 if (obj.__class__ is FunctionType and name.startswith('func_') or
118 obj.__class__ is MethodType and name.startswith('im_')):
119 raise SecurityException('not allowed to access function attributes')
120 r = getattr(obj, 'jinja_allowed_attributes', None)
121 if r is not None and name not in r:
122 raise SecurityException('not allowed attribute accessed')
123 return getattr(obj, name)
126 def debug_context(env, context):
128 Use this function in templates to get a printed context.
130 from pprint import pformat
131 return pformat(context.to_dict())
132 debug_context.jinja_context_callable = True
135 def safe_range(start, stop=None, step=None):
137 "Safe" form of range that does not generate too large lists.
139 # this also works with None since None is always smaller than
141 if start > MAX_RANGE:
148 return range(0, start, step)
149 return range(start, stop, step)
152 def generate_lorem_ipsum(n=5, html=True, min=20, max=100):
154 Generate some lorem impsum for the template.
156 from jinja.constants import LOREM_IPSUM_WORDS
157 from random import choice, random, randrange
158 words = LOREM_IPSUM_WORDS.split()
162 next_capitalized = True
163 last_comma = last_fullstop = 0
168 # each paragraph contains out of 20 to 100 words.
169 for idx, _ in enumerate(xrange(randrange(min, max))):
176 word = word.capitalize()
177 next_capitalized = False
179 if idx - randrange(3, 8) > last_comma:
183 # add end of sentences
184 if idx - randrange(10, 20) > last_fullstop:
185 last_comma = last_fullstop = idx
187 next_capitalized = True
190 # ensure that the paragraph ends with a dot.
194 elif not p.endswith('.'):
199 return u'\n\n'.join(result)
200 return u'\n'.join([u'<p>%s</p>' % escape(x) for x in result])
203 def watch_changes(env, context, iterable, *attributes):
205 Wise replacement for ``{% ifchanged %}``.
207 # find the attributes to watch
211 for attribute in attributes:
212 if isinstance(attribute, (str, unicode, int, long, bool)):
213 tmp.append(attribute)
215 tests.append(tuple(attribute))
217 tests.append(tuple(attribute))
218 last = tuple([object() for x in tests])
219 # or no attributes if we watch the object itself
224 # iterate trough it and keep check the attributes or values
225 for item in iterable:
229 cur = tuple([env.get_attributes(item, x) for x in tests])
236 watch_changes.jinja_context_callable = True
239 # python2.4 and lower has a bug regarding joining of broken generators.
240 # because of the runtime debugging system we have to keep track of the
241 # number of frames to skip. that's what RUNTIME_EXCEPTION_OFFSET is for.
242 if sys.version_info < (2, 5):
243 capture_generator = lambda gen: u''.join(tuple(gen))
244 RUNTIME_EXCEPTION_OFFSET = 2
246 # this should be faster and used in python2.5 and higher
248 capture_generator = u''.join
249 RUNTIME_EXCEPTION_OFFSET = 1
254 Used by the python translator to capture output of substreams.
255 (macros, filter sections etc)
257 def wrapped(*args, **kwargs):
258 return TemplateData(capture_generator(f(*args, **kwargs)))
262 def fake_template_exception(exception, filename, lineno, source,
265 Raise an exception "in a template". Return a traceback
266 object. This is used for runtime debugging, not compile time.
268 # some traceback systems allow to skip frames
269 __traceback_hide__ = True
270 if isinstance(context_or_env, Context):
271 env = context_or_env.environment
272 namespace = context_or_env.to_dict()
277 # generate an jinja unique filename used so that linecache
278 # gets data that doesn't interferes with other modules
280 from random import randrange
281 vfilename = 'jinja://~%d' % randrange(0, 10000)
282 filename = '<string>'
284 vfilename = 'jinja://%s' % filename
286 offset = '\n' * (lineno - 1)
287 code = compile(offset + 'raise __exception_to_raise__',
288 vfilename or '<template>', 'exec')
290 loader = TracebackLoader(env, source, filename)
291 loader.update_linecache(vfilename)
293 '__name__': vfilename,
294 '__file__': vfilename,
295 '__loader__': loader,
296 '__exception_to_raise__': exception
299 exec code in globals, namespace
301 return sys.exc_info()
304 def translate_exception(template, exc_type, exc_value, traceback, context):
306 Translate an exception and return the new traceback.
308 error_line = traceback.tb_lineno
309 for code_line, tmpl_filename, tmpl_line in template._debug_info[::-1]:
310 if code_line <= error_line:
313 # no debug symbol found. give up
316 return fake_template_exception(exc_value, tmpl_filename, tmpl_line,
317 template._source, context)[2]
320 def raise_syntax_error(exception, env, source=None):
322 This method raises an exception that includes more debugging
323 informations so that debugging works better. Unlike
324 `translate_exception` this method raises the exception with
327 exc_info = fake_template_exception(exception, exception.filename,
328 exception.lineno, source, env)
329 raise exc_info[0], exc_info[1], exc_info[2]
332 def collect_translations(ast):
334 Collect all translatable strings for the given ast. The
335 return value is a list of tuples in the form ``(lineno, singular,
336 plural)``. If a translation doesn't require a plural form the
337 third item is `None`.
343 if node.__class__ is Trans:
344 result.append((node.lineno, node.singular, node.plural))
345 elif node.__class__ is CallFunc and \
346 node.node.__class__ is Name and \
347 node.node.name == '_':
348 if len(node.args) in (1, 3):
350 for arg in node.args:
351 if not arg.__class__ is Const:
353 args.append(arg.value)
359 singular, plural, _ = args
360 result.append((node.lineno, singular, plural))
361 todo.extend(node.getChildNodes())
362 result.sort(lambda a, b: cmp(a[0], b[0]))
366 class TracebackLoader(object):
368 Fake importer that just returns the source of a template.
371 def __init__(self, environment, source, filename):
372 self.loader = environment.loader
374 self.filename = filename
376 def update_linecache(self, virtual_filename):
378 Hacky way to let traceback systems know about the
379 Jinja template sourcecode. Very hackish indeed.
381 # check for linecache, not every implementation of python
382 # might have such an module.
384 from linecache import cache
387 data = self.get_source(None)
388 cache[virtual_filename] = (
391 data.splitlines(True),
395 def get_source(self, impname):
397 if self.source is not None:
399 elif self.loader is not None:
401 source = self.loader.get_source(self.filename)
402 except TemplateNotFound:
404 if isinstance(source, unicode):
405 source = source.encode('utf-8')
409 class CacheDict(object):
411 A dict like object that stores a limited number of items and forgets
412 about the least recently used items::
414 >>> cache = CacheDict(3)
421 If we now access 'A' again it has a higher priority than B::
426 If we add a new item 'D' now 'B' will disappear::
434 If you iterate over the object the most recently used item will be
437 >>> for item in cache:
443 If you want to iterate the other way round use ``reverse(cache)``.
445 Implementation note: This is not a nice way to solve that problem but
446 for smaller capacities it's faster than a linked list.
447 Perfect for template environments where you don't expect too many
451 def __init__(self, capacity):
452 self.capacity = capacity
455 # use a deque here if possible
456 if deque is not None:
457 self._queue = deque()
458 self._popleft = self._queue.popleft
459 # python2.3/2.4, just use a list
462 pop = self._queue.pop
463 self._popleft = lambda: pop(0)
465 # alias all queue methods for faster lookup
466 self._pop = self._queue.pop
467 self._remove = self._queue.remove
468 self._append = self._queue.append
472 Return an shallow copy of the instance.
474 rv = CacheDict(self.capacity)
475 rv._mapping.update(self._mapping)
476 rv._queue = self._queue[:]
479 def get(self, key, default=None):
481 Return an item from the cache dict or `default`
487 def setdefault(self, key, default=None):
489 Set `default` if the key is not in the cache otherwise
490 leave unchanged. Return the value of this key.
499 Clear the cache dict.
501 self._mapping.clear()
504 except AttributeError:
507 def __contains__(self, key):
509 Check if a key exists in this cache dict.
511 return key in self._mapping
515 Return the current size of the cache dict.
517 return len(self._mapping)
521 self.__class__.__name__,
525 def __getitem__(self, key):
527 Get an item from the cache dict. Moves the item up so that
528 it has the highest priority then.
530 Raise an `KeyError` if it does not exist.
532 rv = self._mapping[key]
533 if self._queue[-1] != key:
538 def __setitem__(self, key, value):
540 Sets the value for an item. Moves the item up so that it
541 has the highest priority then.
543 if key in self._mapping:
545 elif len(self._mapping) == self.capacity:
546 del self._mapping[self._popleft()]
548 self._mapping[key] = value
550 def __delitem__(self, key):
552 Remove an item from the cache dict.
553 Raise an `KeyError` if it does not exist.
555 del self._mapping[key]
560 Iterate over all values in the cache dict, ordered by
561 the most recent usage.
564 return reversed(self._queue)
566 return iter(self._queue[::-1])
568 def __reversed__(self):
570 Iterate over the values in the cache dict, oldest items
573 return iter(self._queue)
577 def __deepcopy__(self):
579 Return a deep copy of the cache dict.
581 from copy import deepcopy
582 rv = CacheDict(self.capacity)
583 rv._mapping = deepcopy(self._mapping)
584 rv._queue = deepcopy(self._queue)