Started to work on Python 3 support.
[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__', '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     # not in python 3
479     if hasattr(unicode, '__getslice__'):
480         __getslice__ = make_wrapper('__getslice__')
481
482     del method, make_wrapper
483
484
485 def _escape_argspec(obj, iterable):
486     """Helper for various string-wrapped functions."""
487     for key, value in iterable:
488         if hasattr(value, '__html__') or isinstance(value, basestring):
489             obj[key] = escape(value)
490     return obj
491
492
493 class _MarkupEscapeHelper(object):
494     """Helper for Markup.__mod__"""
495
496     def __init__(self, obj):
497         self.obj = obj
498
499     __getitem__ = lambda s, x: _MarkupEscapeHelper(s.obj[x])
500     __unicode__ = lambda s: unicode(escape(s.obj))
501     __str__ = lambda s: str(escape(s.obj))
502     __repr__ = lambda s: str(escape(repr(s.obj)))
503     __int__ = lambda s: int(s.obj)
504     __float__ = lambda s: float(s.obj)
505
506
507 class LRUCache(object):
508     """A simple LRU Cache implementation."""
509
510     # this is fast for small capacities (something below 1000) but doesn't
511     # scale.  But as long as it's only used as storage for templates this
512     # won't do any harm.
513
514     def __init__(self, capacity):
515         self.capacity = capacity
516         self._mapping = {}
517         self._queue = deque()
518         self._postinit()
519
520     def _postinit(self):
521         # alias all queue methods for faster lookup
522         self._popleft = self._queue.popleft
523         self._pop = self._queue.pop
524         if hasattr(self._queue, 'remove'):
525             self._remove = self._queue.remove
526         self._wlock = allocate_lock()
527         self._append = self._queue.append
528
529     def _remove(self, obj):
530         """Python 2.4 compatibility."""
531         for idx, item in enumerate(self._queue):
532             if item == obj:
533                 del self._queue[idx]
534                 break
535
536     def __getstate__(self):
537         return {
538             'capacity':     self.capacity,
539             '_mapping':     self._mapping,
540             '_queue':       self._queue
541         }
542
543     def __setstate__(self, d):
544         self.__dict__.update(d)
545         self._postinit()
546
547     def __getnewargs__(self):
548         return (self.capacity,)
549
550     def copy(self):
551         """Return an shallow copy of the instance."""
552         rv = self.__class__(self.capacity)
553         rv._mapping.update(self._mapping)
554         rv._queue = deque(self._queue)
555         return rv
556
557     def get(self, key, default=None):
558         """Return an item from the cache dict or `default`"""
559         try:
560             return self[key]
561         except KeyError:
562             return default
563
564     def setdefault(self, key, default=None):
565         """Set `default` if the key is not in the cache otherwise
566         leave unchanged. Return the value of this key.
567         """
568         try:
569             return self[key]
570         except KeyError:
571             self[key] = default
572             return default
573
574     def clear(self):
575         """Clear the cache."""
576         self._wlock.acquire()
577         try:
578             self._mapping.clear()
579             self._queue.clear()
580         finally:
581             self._wlock.release()
582
583     def __contains__(self, key):
584         """Check if a key exists in this cache."""
585         return key in self._mapping
586
587     def __len__(self):
588         """Return the current size of the cache."""
589         return len(self._mapping)
590
591     def __repr__(self):
592         return '<%s %r>' % (
593             self.__class__.__name__,
594             self._mapping
595         )
596
597     def __getitem__(self, key):
598         """Get an item from the cache. Moves the item up so that it has the
599         highest priority then.
600
601         Raise an `KeyError` if it does not exist.
602         """
603         rv = self._mapping[key]
604         if self._queue[-1] != key:
605             try:
606                 self._remove(key)
607             except:
608                 # if something removed the key from the container
609                 # when we read, ignore the ValueError that we would
610                 # get otherwise.
611                 pass
612             self._append(key)
613         return rv
614
615     def __setitem__(self, key, value):
616         """Sets the value for an item. Moves the item up so that it
617         has the highest priority then.
618         """
619         self._wlock.acquire()
620         try:
621             if key in self._mapping:
622                 self._remove(key)
623             elif len(self._mapping) == self.capacity:
624                 del self._mapping[self._popleft()]
625             self._append(key)
626             self._mapping[key] = value
627         finally:
628             self._wlock.release()
629
630     def __delitem__(self, key):
631         """Remove an item from the cache dict.
632         Raise an `KeyError` if it does not exist.
633         """
634         self._wlock.acquire()
635         try:
636             del self._mapping[key]
637             self._remove(key)
638         finally:
639             self._wlock.release()
640
641     def items(self):
642         """Return a list of items."""
643         result = [(key, self._mapping[key]) for key in list(self._queue)]
644         result.reverse()
645         return result
646
647     def iteritems(self):
648         """Iterate over all items."""
649         return iter(self.items())
650
651     def values(self):
652         """Return a list of all values."""
653         return [x[1] for x in self.items()]
654
655     def itervalue(self):
656         """Iterate over all values."""
657         return iter(self.values())
658
659     def keys(self):
660         """Return a list of all keys ordered by most recent usage."""
661         return list(self)
662
663     def iterkeys(self):
664         """Iterate over all keys in the cache dict, ordered by
665         the most recent usage.
666         """
667         return reversed(tuple(self._queue))
668
669     __iter__ = iterkeys
670
671     def __reversed__(self):
672         """Iterate over the values in the cache dict, oldest items
673         coming first.
674         """
675         return iter(tuple(self._queue))
676
677     __copy__ = copy
678
679
680 # register the LRU cache as mutable mapping if possible
681 try:
682     from collections import MutableMapping
683     MutableMapping.register(LRUCache)
684 except ImportError:
685     pass
686
687
688 class Cycler(object):
689     """A cycle helper for templates."""
690
691     def __init__(self, *items):
692         if not items:
693             raise RuntimeError('at least one item has to be provided')
694         self.items = items
695         self.reset()
696
697     def reset(self):
698         """Resets the cycle."""
699         self.pos = 0
700
701     @property
702     def current(self):
703         """Returns the current item."""
704         return self.items[self.pos]
705
706     def next(self):
707         """Goes one item ahead and returns it."""
708         rv = self.current
709         self.pos = (self.pos + 1) % len(self.items)
710         return rv
711
712
713 class Joiner(object):
714     """A joining helper for templates."""
715
716     def __init__(self, sep=u', '):
717         self.sep = sep
718         self.used = False
719
720     def __call__(self):
721         if not self.used:
722             self.used = True
723             return u''
724         return self.sep
725
726
727 # we have to import it down here as the speedups module imports the
728 # markup type which is define above.
729 try:
730     from jinja2._speedups import escape, soft_unicode
731 except ImportError:
732     def escape(s):
733         """Convert the characters &, <, >, ' and " in string s to HTML-safe
734         sequences.  Use this if you need to display text that might contain
735         such characters in HTML.  Marks return value as markup string.
736         """
737         if hasattr(s, '__html__'):
738             return s.__html__()
739         return Markup(unicode(s)
740             .replace('&', '&amp;')
741             .replace('>', '&gt;')
742             .replace('<', '&lt;')
743             .replace("'", '&#39;')
744             .replace('"', '&#34;')
745         )
746
747     def soft_unicode(s):
748         """Make a string unicode if it isn't already.  That way a markup
749         string is not converted back to unicode.
750         """
751         if not isinstance(s, unicode):
752             s = unicode(s)
753         return s
754
755
756 # partials
757 try:
758     from functools import partial
759 except ImportError:
760     class partial(object):
761         def __init__(self, _func, *args, **kwargs):
762             self._func = _func
763             self._args = args
764             self._kwargs = kwargs
765         def __call__(self, *args, **kwargs):
766             kwargs.update(self._kwargs)
767             return self._func(*(self._args + args), **kwargs)