Mercurial > repos > bcclaywell > argo_navis
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, ('(', '<', '<'))), | |
23 '|'.join(map(re.escape, ('.', ',', ')', '>', '\n', '>'))) | |
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 |