Improved Jinja's debugging support by introducing "@internalcode" which marks code...
[jinja2.git] / jinja2 / utils.py
1 # -*- coding: utf-8 -*-
2 """
3     jinja2.utils
4     ~~~~~~~~~~~~
5
6     Utility functions.
7
8     :copyright: (c) 2009 by the Jinja Team.
9     :license: BSD, see LICENSE for more details.
10 """
11 import re
12 import sys
13 import errno
14 try:
15     from thread import allocate_lock
16 except ImportError:
17     from dummy_thread import allocate_lock
18 from collections import deque
19 from itertools import imap
20
21
22 _word_split_re = re.compile(r'(\s+)')
23 _punctuation_re = re.compile(
24     '^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$' % (
25         '|'.join(imap(re.escape, ('(', '<', '&lt;'))),
26         '|'.join(imap(re.escape, ('.', ',', ')', '>', '\n', '&gt;')))
27     )
28 )
29 _simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$')
30 _striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
31 _entity_re = re.compile(r'&([^;]+);')
32 _letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
33 _digits = '0123456789'
34
35 # special singleton representing missing values for the runtime
36 missing = type('MissingType', (), {'__repr__': lambda x: 'missing'})()
37
38 # internal code
39 internal_code = set()
40
41
42 # concatenate a list of strings and convert them to unicode.
43 # unfortunately there is a bug in python 2.4 and lower that causes
44 # unicode.join trash the traceback.
45 _concat = u''.join
46 try:
47     def _test_gen_bug():
48         raise TypeError(_test_gen_bug)
49         yield None
50     _concat(_test_gen_bug())
51 except TypeError, _error:
52     if not _error.args or _error.args[0] is not _test_gen_bug:
53         def concat(gen):
54             try:
55                 return _concat(list(gen))
56             except:
57                 # this hack is needed so that the current frame
58                 # does not show up in the traceback.
59                 exc_type, exc_value, tb = sys.exc_info()
60                 raise exc_type, exc_value, tb.tb_next
61     else:
62         concat = _concat
63     del _test_gen_bug, _error
64
65
66 # ironpython without stdlib doesn't have keyword
67 try:
68     from keyword import iskeyword as is_python_keyword
69 except ImportError:
70     _py_identifier_re = re.compile(r'^[a-zA-Z_][a-zA-Z0-9]*$')
71     def is_python_keyword(name):
72         if _py_identifier_re.search(name) is None:
73             return False
74         try:
75             exec name + " = 42"
76         except SyntaxError:
77             return False
78         return True
79
80
81 # common types.  These do exist in the special types module too which however
82 # does not exist in IronPython out of the box.
83 class _C(object):
84     def method(self): pass
85 def _func():
86     yield None
87 FunctionType = type(_func)
88 GeneratorType = type(_func())
89 MethodType = type(_C.method)
90 CodeType = type(_C.method.func_code)
91 try:
92     raise TypeError()
93 except TypeError:
94     _tb = sys.exc_info()[2]
95     TracebackType = type(_tb)
96     FrameType = type(_tb.tb_frame)
97 del _C, _tb, _func
98
99
100 def contextfunction(f):
101     """This decorator can be used to mark a function or method context callable.
102     A context callable is passed the active :class:`Context` as first argument when
103     called from the template.  This is useful if a function wants to get access
104     to the context or functions provided on the context object.  For example
105     a function that returns a sorted list of template variables the current
106     template exports could look like this::
107
108         @contextfunction
109         def get_exported_names(context):
110             return sorted(context.exported_vars)
111     """
112     f.contextfunction = True
113     return f
114
115
116 def environmentfunction(f):
117     """This decorator can be used to mark a function or method as environment
118     callable.  This decorator works exactly like the :func:`contextfunction`
119     decorator just that the first argument is the active :class:`Environment`
120     and not context.
121     """
122     f.environmentfunction = True
123     return f
124
125
126 def internalcode(f):
127     """Marks the function as internally used"""
128     internal_code.add(f.func_code)
129     return f
130
131
132 def is_undefined(obj):
133     """Check if the object passed is undefined.  This does nothing more than
134     performing an instance check against :class:`Undefined` but looks nicer.
135     This can be used for custom filters or tests that want to react to
136     undefined variables.  For example a custom default filter can look like
137     this::
138
139         def default(var, default=''):
140             if is_undefined(var):
141                 return default
142             return var
143     """
144     from jinja2.runtime import Undefined
145     return isinstance(obj, Undefined)
146
147
148 def consume(iterable):
149     """Consumes an iterable without doing anything with it."""
150     for event in iterable:
151         pass
152
153
154 def clear_caches():
155     """Jinja2 keeps internal caches for environments and lexers.  These are
156     used so that Jinja2 doesn't have to recreate environments and lexers all
157     the time.  Normally you don't have to care about that but if you are
158     messuring memory consumption you may want to clean the caches.
159     """
160     from jinja2.environment import _spontaneous_environments
161     from jinja2.lexer import _lexer_cache
162     _spontaneous_environments.clear()
163     _lexer_cache.clear()
164
165
166 def import_string(import_name, silent=False):
167     """Imports an object based on a string.  This use useful if you want to
168     use import paths as endpoints or something similar.  An import path can
169     be specified either in dotted notation (``xml.sax.saxutils.escape``)
170     or with a colon as object delimiter (``xml.sax.saxutils:escape``).
171
172     If the `silent` is True the return value will be `None` if the import
173     fails.
174
175     :return: imported object
176     """
177     try:
178         if ':' in import_name:
179             module, obj = import_name.split(':', 1)
180         elif '.' in import_name:
181             items = import_name.split('.')
182             module = '.'.join(items[:-1])
183             obj = items[-1]
184         else:
185             return __import__(import_name)
186         return getattr(__import__(module, None, None, [obj]), obj)
187     except (ImportError, AttributeError):
188         if not silent:
189             raise
190
191
192 def open_if_exists(filename, mode='r'):
193     """Returns a file descriptor for the filename if that file exists,
194     otherwise `None`.
195     """
196     try:
197         return file(filename, mode)
198     except IOError, e:
199         if e.errno not in (errno.ENOENT, errno.EISDIR):
200             raise
201
202
203 def pformat(obj, verbose=False):
204     """Prettyprint an object.  Either use the `pretty` library or the
205     builtin `pprint`.
206     """
207     try:
208         from pretty import pretty
209         return pretty(obj, verbose=verbose)
210     except ImportError:
211         from pprint import pformat
212         return pformat(obj)
213
214
215 def urlize(text, trim_url_limit=None, nofollow=False):
216     """Converts any URLs in text into clickable links. Works on http://,
217     https:// and www. links. Links can have trailing punctuation (periods,
218     commas, close-parens) and leading punctuation (opening parens) and
219     it'll still do the right thing.
220
221     If trim_url_limit is not None, the URLs in link text will be limited
222     to trim_url_limit characters.
223
224     If nofollow is True, the URLs in link text will get a rel="nofollow"
225     attribute.
226     """
227     trim_url = lambda x, limit=trim_url_limit: limit is not None \
228                          and (x[:limit] + (len(x) >=limit and '...'
229                          or '')) or x
230     words = _word_split_re.split(unicode(escape(text)))
231     nofollow_attr = nofollow and ' rel="nofollow"' or ''
232     for i, word in enumerate(words):
233         match = _punctuation_re.match(word)
234         if match:
235             lead, middle, trail = match.groups()
236             if middle.startswith('www.') or (
237                 '@' not in middle and
238                 not middle.startswith('http://') and
239                 len(middle) > 0 and
240                 middle[0] in _letters + _digits and (
241                     middle.endswith('.org') or
242                     middle.endswith('.net') or
243                     middle.endswith('.com')
244                 )):
245                 middle = '<a href="http://%s"%s>%s</a>' % (middle,
246                     nofollow_attr, trim_url(middle))
247             if middle.startswith('http://') or \
248                middle.startswith('https://'):
249                 middle = '<a href="%s"%s>%s</a>' % (middle,
250                     nofollow_attr, trim_url(middle))
251             if '@' in middle and not middle.startswith('www.') and \
252                not ':' in middle and _simple_email_re.match(middle):
253                 middle = '<a href="mailto:%s">%s</a>' % (middle, middle)
254             if lead + middle + trail != word:
255                 words[i] = lead + middle + trail
256     return u''.join(words)
257
258
259 def generate_lorem_ipsum(n=5, html=True, min=20, max=100):
260     """Generate some lorem impsum for the template."""
261     from jinja2.constants import LOREM_IPSUM_WORDS
262     from random import choice, random, randrange
263     words = LOREM_IPSUM_WORDS.split()
264     result = []
265
266     for _ in xrange(n):
267         next_capitalized = True
268         last_comma = last_fullstop = 0
269         word = None
270         last = None
271         p = []
272
273         # each paragraph contains out of 20 to 100 words.
274         for idx, _ in enumerate(xrange(randrange(min, max))):
275             while True:
276                 word = choice(words)
277                 if word != last:
278                     last = word
279                     break
280             if next_capitalized:
281                 word = word.capitalize()
282                 next_capitalized = False
283             # add commas
284             if idx - randrange(3, 8) > last_comma:
285                 last_comma = idx
286                 last_fullstop += 2
287                 word += ','
288             # add end of sentences
289             if idx - randrange(10, 20) > last_fullstop:
290                 last_comma = last_fullstop = idx
291                 word += '.'
292                 next_capitalized = True
293             p.append(word)
294
295         # ensure that the paragraph ends with a dot.
296         p = u' '.join(p)
297         if p.endswith(','):
298             p = p[:-1] + '.'
299         elif not p.endswith('.'):
300             p += '.'
301         result.append(p)
302
303     if not html:
304         return u'\n\n'.join(result)
305     return Markup(u'\n'.join(u'<p>%s</p>' % escape(x) for x in result))
306
307
308 class Markup(unicode):
309     r"""Marks a string as being safe for inclusion in HTML/XML output without
310     needing to be escaped.  This implements the `__html__` interface a couple
311     of frameworks and web applications use.  :class:`Markup` is a direct
312     subclass of `unicode` and provides all the methods of `unicode` just that
313     it escapes arguments passed and always returns `Markup`.
314
315     The `escape` function returns markup objects so that double escaping can't
316     happen.  If you want to use autoescaping in Jinja just enable the
317     autoescaping feature in the environment.
318
319     The constructor of the :class:`Markup` class can be used for three
320     different things:  When passed an unicode object it's assumed to be safe,
321     when passed an object with an HTML representation (has an `__html__`
322     method) that representation is used, otherwise the object passed is
323     converted into a unicode string and then assumed to be safe:
324
325     >>> Markup("Hello <em>World</em>!")
326     Markup(u'Hello <em>World</em>!')
327     >>> class Foo(object):
328     ...  def __html__(self):
329     ...   return '<a href="#">foo</a>'
330     ... 
331     >>> Markup(Foo())
332     Markup(u'<a href="#">foo</a>')
333
334     If you want object passed being always treated as unsafe you can use the
335     :meth:`escape` classmethod to create a :class:`Markup` object:
336
337     >>> Markup.escape("Hello <em>World</em>!")
338     Markup(u'Hello &lt;em&gt;World&lt;/em&gt;!')
339
340     Operations on a markup string are markup aware which means that all
341     arguments are passed through the :func:`escape` function:
342
343     >>> em = Markup("<em>%s</em>")
344     >>> em % "foo & bar"
345     Markup(u'<em>foo &amp; bar</em>')
346     >>> strong = Markup("<strong>%(text)s</strong>")
347     >>> strong % {'text': '<blink>hacker here</blink>'}
348     Markup(u'<strong>&lt;blink&gt;hacker here&lt;/blink&gt;</strong>')
349     >>> Markup("<em>Hello</em> ") + "<foo>"
350     Markup(u'<em>Hello</em> &lt;foo&gt;')
351     """
352     __slots__ = ()
353
354     def __new__(cls, base=u'', encoding=None, errors='strict'):
355         if hasattr(base, '__html__'):
356             base = base.__html__()
357         if encoding is None:
358             return unicode.__new__(cls, base)
359         return unicode.__new__(cls, base, encoding, errors)
360
361     def __html__(self):
362         return self
363
364     def __add__(self, other):
365         if hasattr(other, '__html__') or isinstance(other, basestring):
366             return self.__class__(unicode(self) + unicode(escape(other)))
367         return NotImplemented
368
369     def __radd__(self, other):
370         if hasattr(other, '__html__') or isinstance(other, basestring):
371             return self.__class__(unicode(escape(other)) + unicode(self))
372         return NotImplemented
373
374     def __mul__(self, num):
375         if isinstance(num, (int, long)):
376             return self.__class__(unicode.__mul__(self, num))
377         return NotImplemented
378     __rmul__ = __mul__
379
380     def __mod__(self, arg):
381         if isinstance(arg, tuple):
382             arg = tuple(imap(_MarkupEscapeHelper, arg))
383         else:
384             arg = _MarkupEscapeHelper(arg)
385         return self.__class__(unicode.__mod__(self, arg))
386
387     def __repr__(self):
388         return '%s(%s)' % (
389             self.__class__.__name__,
390             unicode.__repr__(self)
391         )
392
393     def join(self, seq):
394         return self.__class__(unicode.join(self, imap(escape, seq)))
395     join.__doc__ = unicode.join.__doc__
396
397     def split(self, *args, **kwargs):
398         return map(self.__class__, unicode.split(self, *args, **kwargs))
399     split.__doc__ = unicode.split.__doc__
400
401     def rsplit(self, *args, **kwargs):
402         return map(self.__class__, unicode.rsplit(self, *args, **kwargs))
403     rsplit.__doc__ = unicode.rsplit.__doc__
404
405     def splitlines(self, *args, **kwargs):
406         return map(self.__class__, unicode.splitlines(self, *args, **kwargs))
407     splitlines.__doc__ = unicode.splitlines.__doc__
408
409     def unescape(self):
410         r"""Unescape markup again into an unicode string.  This also resolves
411         known HTML4 and XHTML entities:
412
413         >>> Markup("Main &raquo; <em>About</em>").unescape()
414         u'Main \xbb <em>About</em>'
415         """
416         from jinja2.constants import HTML_ENTITIES
417         def handle_match(m):
418             name = m.group(1)
419             if name in HTML_ENTITIES:
420                 return unichr(HTML_ENTITIES[name])
421             try:
422                 if name[:2] in ('#x', '#X'):
423                     return unichr(int(name[2:], 16))
424                 elif name.startswith('#'):
425                     return unichr(int(name[1:]))
426             except ValueError:
427                 pass
428             return u''
429         return _entity_re.sub(handle_match, unicode(self))
430
431     def striptags(self):
432         r"""Unescape markup into an unicode string and strip all tags.  This
433         also resolves known HTML4 and XHTML entities.  Whitespace is
434         normalized to one:
435
436         >>> Markup("Main &raquo;  <em>About</em>").striptags()
437         u'Main \xbb About'
438         """
439         stripped = u' '.join(_striptags_re.sub('', self).split())
440         return Markup(stripped).unescape()
441
442     @classmethod
443     def escape(cls, s):
444         """Escape the string.  Works like :func:`escape` with the difference
445         that for subclasses of :class:`Markup` this function would return the
446         correct subclass.
447         """
448         rv = escape(s)
449         if rv.__class__ is not cls:
450             return cls(rv)
451         return rv
452
453     def make_wrapper(name):
454         orig = getattr(unicode, name)
455         def func(self, *args, **kwargs):
456             args = _escape_argspec(list(args), enumerate(args))
457             _escape_argspec(kwargs, kwargs.iteritems())
458             return self.__class__(orig(self, *args, **kwargs))
459         func.__name__ = orig.__name__
460         func.__doc__ = orig.__doc__
461         return func
462
463     for method in '__getitem__', '__getslice__', 'capitalize', \
464                   'title', 'lower', 'upper', 'replace', 'ljust', \
465                   'rjust', 'lstrip', 'rstrip', 'center', 'strip', \
466                   'translate', 'expandtabs', 'swapcase', 'zfill':
467         locals()[method] = make_wrapper(method)
468
469     # new in python 2.5
470     if hasattr(unicode, 'partition'):
471         partition = make_wrapper('partition'),
472         rpartition = make_wrapper('rpartition')
473
474     # new in python 2.6
475     if hasattr(unicode, 'format'):
476         format = make_wrapper('format')
477
478     del method, make_wrapper
479
480
481 def _escape_argspec(obj, iterable):
482     """Helper for various string-wrapped functions."""
483     for key, value in iterable:
484         if hasattr(value, '__html__') or isinstance(value, basestring):
485             obj[key] = escape(value)
486     return obj
487
488
489 class _MarkupEscapeHelper(object):
490     """Helper for Markup.__mod__"""
491
492     def __init__(self, obj):
493         self.obj = obj
494
495     __getitem__ = lambda s, x: _MarkupEscapeHelper(s.obj[x])
496     __unicode__ = lambda s: unicode(escape(s.obj))
497     __str__ = lambda s: str(escape(s.obj))
498     __repr__ = lambda s: str(escape(repr(s.obj)))
499     __int__ = lambda s: int(s.obj)
500     __float__ = lambda s: float(s.obj)
501
502
503 class LRUCache(object):
504     """A simple LRU Cache implementation."""
505
506     # this is fast for small capacities (something below 1000) but doesn't
507     # scale.  But as long as it's only used as storage for templates this
508     # won't do any harm.
509
510     def __init__(self, capacity):
511         self.capacity = capacity
512         self._mapping = {}
513         self._queue = deque()
514         self._postinit()
515
516     def _postinit(self):
517         # alias all queue methods for faster lookup
518         self._popleft = self._queue.popleft
519         self._pop = self._queue.pop
520         if hasattr(self._queue, 'remove'):
521             self._remove = self._queue.remove
522         self._wlock = allocate_lock()
523         self._append = self._queue.append
524
525     def _remove(self, obj):
526         """Python 2.4 compatibility."""
527         for idx, item in enumerate(self._queue):
528             if item == obj:
529                 del self._queue[idx]
530                 break
531
532     def __getstate__(self):
533         return {
534             'capacity':     self.capacity,
535             '_mapping':     self._mapping,
536             '_queue':       self._queue
537         }
538
539     def __setstate__(self, d):
540         self.__dict__.update(d)
541         self._postinit()
542
543     def __getnewargs__(self):
544         return (self.capacity,)
545
546     def copy(self):
547         """Return an shallow copy of the instance."""
548         rv = self.__class__(self.capacity)
549         rv._mapping.update(self._mapping)
550         rv._queue = deque(self._queue)
551         return rv
552
553     def get(self, key, default=None):
554         """Return an item from the cache dict or `default`"""
555         try:
556             return self[key]
557         except KeyError:
558             return default
559
560     def setdefault(self, key, default=None):
561         """Set `default` if the key is not in the cache otherwise
562         leave unchanged. Return the value of this key.
563         """
564         try:
565             return self[key]
566         except KeyError:
567             self[key] = default
568             return default
569
570     def clear(self):
571         """Clear the cache."""
572         self._wlock.acquire()
573         try:
574             self._mapping.clear()
575             self._queue.clear()
576         finally:
577             self._wlock.release()
578
579     def __contains__(self, key):
580         """Check if a key exists in this cache."""
581         return key in self._mapping
582
583     def __len__(self):
584         """Return the current size of the cache."""
585         return len(self._mapping)
586
587     def __repr__(self):
588         return '<%s %r>' % (
589             self.__class__.__name__,
590             self._mapping
591         )
592
593     def __getitem__(self, key):
594         """Get an item from the cache. Moves the item up so that it has the
595         highest priority then.
596
597         Raise an `KeyError` if it does not exist.
598         """
599         rv = self._mapping[key]
600         if self._queue[-1] != key:
601             try:
602                 self._remove(key)
603             except:
604                 # if something removed the key from the container
605                 # when we read, ignore the ValueError that we would
606                 # get otherwise.
607                 pass
608             self._append(key)
609         return rv
610
611     def __setitem__(self, key, value):
612         """Sets the value for an item. Moves the item up so that it
613         has the highest priority then.
614         """
615         self._wlock.acquire()
616         try:
617             if key in self._mapping:
618                 self._remove(key)
619             elif len(self._mapping) == self.capacity:
620                 del self._mapping[self._popleft()]
621             self._append(key)
622             self._mapping[key] = value
623         finally:
624             self._wlock.release()
625
626     def __delitem__(self, key):
627         """Remove an item from the cache dict.
628         Raise an `KeyError` if it does not exist.
629         """
630         self._wlock.acquire()
631         try:
632             del self._mapping[key]
633             self._remove(key)
634         finally:
635             self._wlock.release()
636
637     def items(self):
638         """Return a list of items."""
639         result = [(key, self._mapping[key]) for key in list(self._queue)]
640         result.reverse()
641         return result
642
643     def iteritems(self):
644         """Iterate over all items."""
645         return iter(self.items())
646
647     def values(self):
648         """Return a list of all values."""
649         return [x[1] for x in self.items()]
650
651     def itervalue(self):
652         """Iterate over all values."""
653         return iter(self.values())
654
655     def keys(self):
656         """Return a list of all keys ordered by most recent usage."""
657         return list(self)
658
659     def iterkeys(self):
660         """Iterate over all keys in the cache dict, ordered by
661         the most recent usage.
662         """
663         return reversed(tuple(self._queue))
664
665     __iter__ = iterkeys
666
667     def __reversed__(self):
668         """Iterate over the values in the cache dict, oldest items
669         coming first.
670         """
671         return iter(tuple(self._queue))
672
673     __copy__ = copy
674
675
676 # register the LRU cache as mutable mapping if possible
677 try:
678     from collections import MutableMapping
679     MutableMapping.register(LRUCache)
680 except ImportError:
681     pass
682
683
684 class Cycler(object):
685     """A cycle helper for templates."""
686
687     def __init__(self, *items):
688         if not items:
689             raise RuntimeError('at least one item has to be provided')
690         self.items = items
691         self.reset()
692
693     def reset(self):
694         """Resets the cycle."""
695         self.pos = 0
696
697     @property
698     def current(self):
699         """Returns the current item."""
700         return self.items[self.pos]
701
702     def next(self):
703         """Goes one item ahead and returns it."""
704         rv = self.current
705         self.pos = (self.pos + 1) % len(self.items)
706         return rv
707
708
709 class Joiner(object):
710     """A joining helper for templates."""
711
712     def __init__(self, sep=u', '):
713         self.sep = sep
714         self.used = False
715
716     def __call__(self):
717         if not self.used:
718             self.used = True
719             return u''
720         return self.sep
721
722
723 # we have to import it down here as the speedups module imports the
724 # markup type which is define above.
725 try:
726     from jinja2._speedups import escape, soft_unicode
727 except ImportError:
728     def escape(s):
729         """Convert the characters &, <, >, ' and " in string s to HTML-safe
730         sequences.  Use this if you need to display text that might contain
731         such characters in HTML.  Marks return value as markup string.
732         """
733         if hasattr(s, '__html__'):
734             return s.__html__()
735         return Markup(unicode(s)
736             .replace('&', '&amp;')
737             .replace('>', '&gt;')
738             .replace('<', '&lt;')
739             .replace("'", '&#39;')
740             .replace('"', '&#34;')
741         )
742
743     def soft_unicode(s):
744         """Make a string unicode if it isn't already.  That way a markup
745         string is not converted back to unicode.
746         """
747         if not isinstance(s, unicode):
748             s = unicode(s)
749         return s
750
751
752 # partials
753 try:
754     from functools import partial
755 except ImportError:
756     class partial(object):
757         def __init__(self, _func, *args, **kwargs):
758             self._func = _func
759             self._args = args
760             self._kwargs = kwargs
761         def __call__(self, *args, **kwargs):
762             kwargs.update(self._kwargs)
763             return self._func(*(self._args + args), **kwargs)