comparison venv/lib/python2.7/site-packages/jinja2/utils.py @ 0:d67268158946 draft

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