Revert of Roll src/third_party/WebKit e0eac24:489c548 (svn 193311:193320) (patchset...
[chromium-blink-merge.git] / third_party / jinja2 / utils.py
blobddc47da0a0462d480d4513f75f667f1472deb822
1 # -*- coding: utf-8 -*-
2 """
3 jinja2.utils
4 ~~~~~~~~~~~~
6 Utility functions.
8 :copyright: (c) 2010 by the Jinja Team.
9 :license: BSD, see LICENSE for more details.
10 """
11 import re
12 import errno
13 from collections import deque
14 from jinja2._compat import text_type, string_types, implements_iterator, \
15 allocate_lock, url_quote
18 _word_split_re = re.compile(r'(\s+)')
19 _punctuation_re = re.compile(
20 '^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$' % (
21 '|'.join(map(re.escape, ('(', '<', '&lt;'))),
22 '|'.join(map(re.escape, ('.', ',', ')', '>', '\n', '&gt;')))
25 _simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$')
26 _striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
27 _entity_re = re.compile(r'&([^;]+);')
28 _letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
29 _digits = '0123456789'
31 # special singleton representing missing values for the runtime
32 missing = type('MissingType', (), {'__repr__': lambda x: 'missing'})()
34 # internal code
35 internal_code = set()
37 concat = u''.join
40 def contextfunction(f):
41 """This decorator can be used to mark a function or method context callable.
42 A context callable is passed the active :class:`Context` as first argument when
43 called from the template. This is useful if a function wants to get access
44 to the context or functions provided on the context object. For example
45 a function that returns a sorted list of template variables the current
46 template exports could look like this::
48 @contextfunction
49 def get_exported_names(context):
50 return sorted(context.exported_vars)
51 """
52 f.contextfunction = True
53 return f
56 def evalcontextfunction(f):
57 """This decorator can be used to mark a function or method as an eval
58 context callable. This is similar to the :func:`contextfunction`
59 but instead of passing the context, an evaluation context object is
60 passed. For more information about the eval context, see
61 :ref:`eval-context`.
63 .. versionadded:: 2.4
64 """
65 f.evalcontextfunction = True
66 return f
69 def environmentfunction(f):
70 """This decorator can be used to mark a function or method as environment
71 callable. This decorator works exactly like the :func:`contextfunction`
72 decorator just that the first argument is the active :class:`Environment`
73 and not context.
74 """
75 f.environmentfunction = True
76 return f
79 def internalcode(f):
80 """Marks the function as internally used"""
81 internal_code.add(f.__code__)
82 return f
85 def is_undefined(obj):
86 """Check if the object passed is undefined. This does nothing more than
87 performing an instance check against :class:`Undefined` but looks nicer.
88 This can be used for custom filters or tests that want to react to
89 undefined variables. For example a custom default filter can look like
90 this::
92 def default(var, default=''):
93 if is_undefined(var):
94 return default
95 return var
96 """
97 from jinja2.runtime import Undefined
98 return isinstance(obj, Undefined)
101 def consume(iterable):
102 """Consumes an iterable without doing anything with it."""
103 for event in iterable:
104 pass
107 def clear_caches():
108 """Jinja2 keeps internal caches for environments and lexers. These are
109 used so that Jinja2 doesn't have to recreate environments and lexers all
110 the time. Normally you don't have to care about that but if you are
111 messuring memory consumption you may want to clean the caches.
113 from jinja2.environment import _spontaneous_environments
114 from jinja2.lexer import _lexer_cache
115 _spontaneous_environments.clear()
116 _lexer_cache.clear()
119 def import_string(import_name, silent=False):
120 """Imports an object based on a string. This is useful if you want to
121 use import paths as endpoints or something similar. An import path can
122 be specified either in dotted notation (``xml.sax.saxutils.escape``)
123 or with a colon as object delimiter (``xml.sax.saxutils:escape``).
125 If the `silent` is True the return value will be `None` if the import
126 fails.
128 :return: imported object
130 try:
131 if ':' in import_name:
132 module, obj = import_name.split(':', 1)
133 elif '.' in import_name:
134 items = import_name.split('.')
135 module = '.'.join(items[:-1])
136 obj = items[-1]
137 else:
138 return __import__(import_name)
139 return getattr(__import__(module, None, None, [obj]), obj)
140 except (ImportError, AttributeError):
141 if not silent:
142 raise
145 def open_if_exists(filename, mode='rb'):
146 """Returns a file descriptor for the filename if that file exists,
147 otherwise `None`.
149 try:
150 return open(filename, mode)
151 except IOError as e:
152 if e.errno not in (errno.ENOENT, errno.EISDIR):
153 raise
156 def object_type_repr(obj):
157 """Returns the name of the object's type. For some recognized
158 singletons the name of the object is returned instead. (For
159 example for `None` and `Ellipsis`).
161 if obj is None:
162 return 'None'
163 elif obj is Ellipsis:
164 return 'Ellipsis'
165 # __builtin__ in 2.x, builtins in 3.x
166 if obj.__class__.__module__ in ('__builtin__', 'builtins'):
167 name = obj.__class__.__name__
168 else:
169 name = obj.__class__.__module__ + '.' + obj.__class__.__name__
170 return '%s object' % name
173 def pformat(obj, verbose=False):
174 """Prettyprint an object. Either use the `pretty` library or the
175 builtin `pprint`.
177 try:
178 from pretty import pretty
179 return pretty(obj, verbose=verbose)
180 except ImportError:
181 from pprint import pformat
182 return pformat(obj)
185 def urlize(text, trim_url_limit=None, nofollow=False):
186 """Converts any URLs in text into clickable links. Works on http://,
187 https:// and www. links. Links can have trailing punctuation (periods,
188 commas, close-parens) and leading punctuation (opening parens) and
189 it'll still do the right thing.
191 If trim_url_limit is not None, the URLs in link text will be limited
192 to trim_url_limit characters.
194 If nofollow is True, the URLs in link text will get a rel="nofollow"
195 attribute.
197 trim_url = lambda x, limit=trim_url_limit: limit is not None \
198 and (x[:limit] + (len(x) >=limit and '...'
199 or '')) or x
200 words = _word_split_re.split(text_type(escape(text)))
201 nofollow_attr = nofollow and ' rel="nofollow"' or ''
202 for i, word in enumerate(words):
203 match = _punctuation_re.match(word)
204 if match:
205 lead, middle, trail = match.groups()
206 if middle.startswith('www.') or (
207 '@' not in middle and
208 not middle.startswith('http://') and
209 not middle.startswith('https://') and
210 len(middle) > 0 and
211 middle[0] in _letters + _digits and (
212 middle.endswith('.org') or
213 middle.endswith('.net') or
214 middle.endswith('.com')
216 middle = '<a href="http://%s"%s>%s</a>' % (middle,
217 nofollow_attr, trim_url(middle))
218 if middle.startswith('http://') or \
219 middle.startswith('https://'):
220 middle = '<a href="%s"%s>%s</a>' % (middle,
221 nofollow_attr, trim_url(middle))
222 if '@' in middle and not middle.startswith('www.') and \
223 not ':' in middle and _simple_email_re.match(middle):
224 middle = '<a href="mailto:%s">%s</a>' % (middle, middle)
225 if lead + middle + trail != word:
226 words[i] = lead + middle + trail
227 return u''.join(words)
230 def generate_lorem_ipsum(n=5, html=True, min=20, max=100):
231 """Generate some lorem impsum for the template."""
232 from jinja2.constants import LOREM_IPSUM_WORDS
233 from random import choice, randrange
234 words = LOREM_IPSUM_WORDS.split()
235 result = []
237 for _ in range(n):
238 next_capitalized = True
239 last_comma = last_fullstop = 0
240 word = None
241 last = None
242 p = []
244 # each paragraph contains out of 20 to 100 words.
245 for idx, _ in enumerate(range(randrange(min, max))):
246 while True:
247 word = choice(words)
248 if word != last:
249 last = word
250 break
251 if next_capitalized:
252 word = word.capitalize()
253 next_capitalized = False
254 # add commas
255 if idx - randrange(3, 8) > last_comma:
256 last_comma = idx
257 last_fullstop += 2
258 word += ','
259 # add end of sentences
260 if idx - randrange(10, 20) > last_fullstop:
261 last_comma = last_fullstop = idx
262 word += '.'
263 next_capitalized = True
264 p.append(word)
266 # ensure that the paragraph ends with a dot.
267 p = u' '.join(p)
268 if p.endswith(','):
269 p = p[:-1] + '.'
270 elif not p.endswith('.'):
271 p += '.'
272 result.append(p)
274 if not html:
275 return u'\n\n'.join(result)
276 return Markup(u'\n'.join(u'<p>%s</p>' % escape(x) for x in result))
279 def unicode_urlencode(obj, charset='utf-8'):
280 """URL escapes a single bytestring or unicode string with the
281 given charset if applicable to URL safe quoting under all rules
282 that need to be considered under all supported Python versions.
284 If non strings are provided they are converted to their unicode
285 representation first.
287 if not isinstance(obj, string_types):
288 obj = text_type(obj)
289 if isinstance(obj, text_type):
290 obj = obj.encode(charset)
291 return text_type(url_quote(obj))
294 class LRUCache(object):
295 """A simple LRU Cache implementation."""
297 # this is fast for small capacities (something below 1000) but doesn't
298 # scale. But as long as it's only used as storage for templates this
299 # won't do any harm.
301 def __init__(self, capacity):
302 self.capacity = capacity
303 self._mapping = {}
304 self._queue = deque()
305 self._postinit()
307 def _postinit(self):
308 # alias all queue methods for faster lookup
309 self._popleft = self._queue.popleft
310 self._pop = self._queue.pop
311 self._remove = self._queue.remove
312 self._wlock = allocate_lock()
313 self._append = self._queue.append
315 def __getstate__(self):
316 return {
317 'capacity': self.capacity,
318 '_mapping': self._mapping,
319 '_queue': self._queue
322 def __setstate__(self, d):
323 self.__dict__.update(d)
324 self._postinit()
326 def __getnewargs__(self):
327 return (self.capacity,)
329 def copy(self):
330 """Return a shallow copy of the instance."""
331 rv = self.__class__(self.capacity)
332 rv._mapping.update(self._mapping)
333 rv._queue = deque(self._queue)
334 return rv
336 def get(self, key, default=None):
337 """Return an item from the cache dict or `default`"""
338 try:
339 return self[key]
340 except KeyError:
341 return default
343 def setdefault(self, key, default=None):
344 """Set `default` if the key is not in the cache otherwise
345 leave unchanged. Return the value of this key.
347 self._wlock.acquire()
348 try:
349 try:
350 return self[key]
351 except KeyError:
352 self[key] = default
353 return default
354 finally:
355 self._wlock.release()
357 def clear(self):
358 """Clear the cache."""
359 self._wlock.acquire()
360 try:
361 self._mapping.clear()
362 self._queue.clear()
363 finally:
364 self._wlock.release()
366 def __contains__(self, key):
367 """Check if a key exists in this cache."""
368 return key in self._mapping
370 def __len__(self):
371 """Return the current size of the cache."""
372 return len(self._mapping)
374 def __repr__(self):
375 return '<%s %r>' % (
376 self.__class__.__name__,
377 self._mapping
380 def __getitem__(self, key):
381 """Get an item from the cache. Moves the item up so that it has the
382 highest priority then.
384 Raise a `KeyError` if it does not exist.
386 self._wlock.acquire()
387 try:
388 rv = self._mapping[key]
389 if self._queue[-1] != key:
390 try:
391 self._remove(key)
392 except ValueError:
393 # if something removed the key from the container
394 # when we read, ignore the ValueError that we would
395 # get otherwise.
396 pass
397 self._append(key)
398 return rv
399 finally:
400 self._wlock.release()
402 def __setitem__(self, key, value):
403 """Sets the value for an item. Moves the item up so that it
404 has the highest priority then.
406 self._wlock.acquire()
407 try:
408 if key in self._mapping:
409 self._remove(key)
410 elif len(self._mapping) == self.capacity:
411 del self._mapping[self._popleft()]
412 self._append(key)
413 self._mapping[key] = value
414 finally:
415 self._wlock.release()
417 def __delitem__(self, key):
418 """Remove an item from the cache dict.
419 Raise a `KeyError` if it does not exist.
421 self._wlock.acquire()
422 try:
423 del self._mapping[key]
424 try:
425 self._remove(key)
426 except ValueError:
427 # __getitem__ is not locked, it might happen
428 pass
429 finally:
430 self._wlock.release()
432 def items(self):
433 """Return a list of items."""
434 result = [(key, self._mapping[key]) for key in list(self._queue)]
435 result.reverse()
436 return result
438 def iteritems(self):
439 """Iterate over all items."""
440 return iter(self.items())
442 def values(self):
443 """Return a list of all values."""
444 return [x[1] for x in self.items()]
446 def itervalue(self):
447 """Iterate over all values."""
448 return iter(self.values())
450 def keys(self):
451 """Return a list of all keys ordered by most recent usage."""
452 return list(self)
454 def iterkeys(self):
455 """Iterate over all keys in the cache dict, ordered by
456 the most recent usage.
458 return reversed(tuple(self._queue))
460 __iter__ = iterkeys
462 def __reversed__(self):
463 """Iterate over the values in the cache dict, oldest items
464 coming first.
466 return iter(tuple(self._queue))
468 __copy__ = copy
471 # register the LRU cache as mutable mapping if possible
472 try:
473 from collections import MutableMapping
474 MutableMapping.register(LRUCache)
475 except ImportError:
476 pass
479 @implements_iterator
480 class Cycler(object):
481 """A cycle helper for templates."""
483 def __init__(self, *items):
484 if not items:
485 raise RuntimeError('at least one item has to be provided')
486 self.items = items
487 self.reset()
489 def reset(self):
490 """Resets the cycle."""
491 self.pos = 0
493 @property
494 def current(self):
495 """Returns the current item."""
496 return self.items[self.pos]
498 def __next__(self):
499 """Goes one item ahead and returns it."""
500 rv = self.current
501 self.pos = (self.pos + 1) % len(self.items)
502 return rv
505 class Joiner(object):
506 """A joining helper for templates."""
508 def __init__(self, sep=u', '):
509 self.sep = sep
510 self.used = False
512 def __call__(self):
513 if not self.used:
514 self.used = True
515 return u''
516 return self.sep
519 # Imported here because that's where it was in the past
520 from markupsafe import Markup, escape, soft_unicode