OLD | NEW |
1 # -*- coding: utf-8 -*- | 1 # -*- coding: utf-8 -*- |
2 """ | 2 """ |
3 jinja2.utils | 3 jinja2.utils |
4 ~~~~~~~~~~~~ | 4 ~~~~~~~~~~~~ |
5 | 5 |
6 Utility functions. | 6 Utility functions. |
7 | 7 |
8 :copyright: (c) 2010 by the Jinja Team. | 8 :copyright: (c) 2010 by the Jinja Team. |
9 :license: BSD, see LICENSE for more details. | 9 :license: BSD, see LICENSE for more details. |
10 """ | 10 """ |
11 import re | 11 import re |
12 import sys | |
13 import errno | 12 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 | 13 from collections import deque |
19 from itertools import imap | 14 from jinja2._compat import text_type, string_types, implements_iterator, \ |
| 15 allocate_lock, url_quote |
20 | 16 |
21 | 17 |
22 _word_split_re = re.compile(r'(\s+)') | 18 _word_split_re = re.compile(r'(\s+)') |
23 _punctuation_re = re.compile( | 19 _punctuation_re = re.compile( |
24 '^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$' % ( | 20 '^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$' % ( |
25 '|'.join(imap(re.escape, ('(', '<', '<'))), | 21 '|'.join(map(re.escape, ('(', '<', '<'))), |
26 '|'.join(imap(re.escape, ('.', ',', ')', '>', '\n', '>'))) | 22 '|'.join(map(re.escape, ('.', ',', ')', '>', '\n', '>'))) |
27 ) | 23 ) |
28 ) | 24 ) |
29 _simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$') | 25 _simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$') |
30 _striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)') | 26 _striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)') |
31 _entity_re = re.compile(r'&([^;]+);') | 27 _entity_re = re.compile(r'&([^;]+);') |
32 _letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' | 28 _letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' |
33 _digits = '0123456789' | 29 _digits = '0123456789' |
34 | 30 |
35 # special singleton representing missing values for the runtime | 31 # special singleton representing missing values for the runtime |
36 missing = type('MissingType', (), {'__repr__': lambda x: 'missing'})() | 32 missing = type('MissingType', (), {'__repr__': lambda x: 'missing'})() |
37 | 33 |
38 # internal code | 34 # internal code |
39 internal_code = set() | 35 internal_code = set() |
40 | 36 |
41 | 37 concat = u''.join |
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 Exception: | |
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 # for python 2.x we create outselves a next() function that does the | |
67 # basics without exception catching. | |
68 try: | |
69 next = next | |
70 except NameError: | |
71 def next(x): | |
72 return x.next() | |
73 | |
74 | |
75 # if this python version is unable to deal with unicode filenames | |
76 # when passed to encode we let this function encode it properly. | |
77 # This is used in a couple of places. As far as Jinja is concerned | |
78 # filenames are unicode *or* bytestrings in 2.x and unicode only in | |
79 # 3.x because compile cannot handle bytes | |
80 if sys.version_info < (3, 0): | |
81 def _encode_filename(filename): | |
82 if isinstance(filename, unicode): | |
83 return filename.encode('utf-8') | |
84 return filename | |
85 else: | |
86 def _encode_filename(filename): | |
87 assert filename is None or isinstance(filename, str), \ | |
88 'filenames must be strings' | |
89 return filename | |
90 | |
91 from keyword import iskeyword as is_python_keyword | |
92 | |
93 | |
94 # common types. These do exist in the special types module too which however | |
95 # does not exist in IronPython out of the box. Also that way we don't have | |
96 # to deal with implementation specific stuff here | |
97 class _C(object): | |
98 def method(self): pass | |
99 def _func(): | |
100 yield None | |
101 FunctionType = type(_func) | |
102 GeneratorType = type(_func()) | |
103 MethodType = type(_C.method) | |
104 CodeType = type(_C.method.func_code) | |
105 try: | |
106 raise TypeError() | |
107 except TypeError: | |
108 _tb = sys.exc_info()[2] | |
109 TracebackType = type(_tb) | |
110 FrameType = type(_tb.tb_frame) | |
111 del _C, _tb, _func | |
112 | 38 |
113 | 39 |
114 def contextfunction(f): | 40 def contextfunction(f): |
115 """This decorator can be used to mark a function or method context callable. | 41 """This decorator can be used to mark a function or method context callable. |
116 A context callable is passed the active :class:`Context` as first argument w
hen | 42 A context callable is passed the active :class:`Context` as first argument w
hen |
117 called from the template. This is useful if a function wants to get access | 43 called from the template. This is useful if a function wants to get access |
118 to the context or functions provided on the context object. For example | 44 to the context or functions provided on the context object. For example |
119 a function that returns a sorted list of template variables the current | 45 a function that returns a sorted list of template variables the current |
120 template exports could look like this:: | 46 template exports could look like this:: |
121 | 47 |
122 @contextfunction | 48 @contextfunction |
123 def get_exported_names(context): | 49 def get_exported_names(context): |
124 return sorted(context.exported_vars) | 50 return sorted(context.exported_vars) |
125 """ | 51 """ |
126 f.contextfunction = True | 52 f.contextfunction = True |
127 return f | 53 return f |
128 | 54 |
129 | 55 |
130 def evalcontextfunction(f): | 56 def evalcontextfunction(f): |
131 """This decoraotr can be used to mark a function or method as an eval | 57 """This decorator can be used to mark a function or method as an eval |
132 context callable. This is similar to the :func:`contextfunction` | 58 context callable. This is similar to the :func:`contextfunction` |
133 but instead of passing the context, an evaluation context object is | 59 but instead of passing the context, an evaluation context object is |
134 passed. For more information about the eval context, see | 60 passed. For more information about the eval context, see |
135 :ref:`eval-context`. | 61 :ref:`eval-context`. |
136 | 62 |
137 .. versionadded:: 2.4 | 63 .. versionadded:: 2.4 |
138 """ | 64 """ |
139 f.evalcontextfunction = True | 65 f.evalcontextfunction = True |
140 return f | 66 return f |
141 | 67 |
142 | 68 |
143 def environmentfunction(f): | 69 def environmentfunction(f): |
144 """This decorator can be used to mark a function or method as environment | 70 """This decorator can be used to mark a function or method as environment |
145 callable. This decorator works exactly like the :func:`contextfunction` | 71 callable. This decorator works exactly like the :func:`contextfunction` |
146 decorator just that the first argument is the active :class:`Environment` | 72 decorator just that the first argument is the active :class:`Environment` |
147 and not context. | 73 and not context. |
148 """ | 74 """ |
149 f.environmentfunction = True | 75 f.environmentfunction = True |
150 return f | 76 return f |
151 | 77 |
152 | 78 |
153 def internalcode(f): | 79 def internalcode(f): |
154 """Marks the function as internally used""" | 80 """Marks the function as internally used""" |
155 internal_code.add(f.func_code) | 81 internal_code.add(f.__code__) |
156 return f | 82 return f |
157 | 83 |
158 | 84 |
159 def is_undefined(obj): | 85 def is_undefined(obj): |
160 """Check if the object passed is undefined. This does nothing more than | 86 """Check if the object passed is undefined. This does nothing more than |
161 performing an instance check against :class:`Undefined` but looks nicer. | 87 performing an instance check against :class:`Undefined` but looks nicer. |
162 This can be used for custom filters or tests that want to react to | 88 This can be used for custom filters or tests that want to react to |
163 undefined variables. For example a custom default filter can look like | 89 undefined variables. For example a custom default filter can look like |
164 this:: | 90 this:: |
165 | 91 |
(...skipping 18 matching lines...) Expand all Loading... |
184 the time. Normally you don't have to care about that but if you are | 110 the time. Normally you don't have to care about that but if you are |
185 messuring memory consumption you may want to clean the caches. | 111 messuring memory consumption you may want to clean the caches. |
186 """ | 112 """ |
187 from jinja2.environment import _spontaneous_environments | 113 from jinja2.environment import _spontaneous_environments |
188 from jinja2.lexer import _lexer_cache | 114 from jinja2.lexer import _lexer_cache |
189 _spontaneous_environments.clear() | 115 _spontaneous_environments.clear() |
190 _lexer_cache.clear() | 116 _lexer_cache.clear() |
191 | 117 |
192 | 118 |
193 def import_string(import_name, silent=False): | 119 def import_string(import_name, silent=False): |
194 """Imports an object based on a string. This use useful if you want to | 120 """Imports an object based on a string. This is useful if you want to |
195 use import paths as endpoints or something similar. An import path can | 121 use import paths as endpoints or something similar. An import path can |
196 be specified either in dotted notation (``xml.sax.saxutils.escape``) | 122 be specified either in dotted notation (``xml.sax.saxutils.escape``) |
197 or with a colon as object delimiter (``xml.sax.saxutils:escape``). | 123 or with a colon as object delimiter (``xml.sax.saxutils:escape``). |
198 | 124 |
199 If the `silent` is True the return value will be `None` if the import | 125 If the `silent` is True the return value will be `None` if the import |
200 fails. | 126 fails. |
201 | 127 |
202 :return: imported object | 128 :return: imported object |
203 """ | 129 """ |
204 try: | 130 try: |
(...skipping 10 matching lines...) Expand all Loading... |
215 if not silent: | 141 if not silent: |
216 raise | 142 raise |
217 | 143 |
218 | 144 |
219 def open_if_exists(filename, mode='rb'): | 145 def open_if_exists(filename, mode='rb'): |
220 """Returns a file descriptor for the filename if that file exists, | 146 """Returns a file descriptor for the filename if that file exists, |
221 otherwise `None`. | 147 otherwise `None`. |
222 """ | 148 """ |
223 try: | 149 try: |
224 return open(filename, mode) | 150 return open(filename, mode) |
225 except IOError, e: | 151 except IOError as e: |
226 if e.errno not in (errno.ENOENT, errno.EISDIR): | 152 if e.errno not in (errno.ENOENT, errno.EISDIR): |
227 raise | 153 raise |
228 | 154 |
229 | 155 |
230 def object_type_repr(obj): | 156 def object_type_repr(obj): |
231 """Returns the name of the object's type. For some recognized | 157 """Returns the name of the object's type. For some recognized |
232 singletons the name of the object is returned instead. (For | 158 singletons the name of the object is returned instead. (For |
233 example for `None` and `Ellipsis`). | 159 example for `None` and `Ellipsis`). |
234 """ | 160 """ |
235 if obj is None: | 161 if obj is None: |
(...skipping 28 matching lines...) Expand all Loading... |
264 | 190 |
265 If trim_url_limit is not None, the URLs in link text will be limited | 191 If trim_url_limit is not None, the URLs in link text will be limited |
266 to trim_url_limit characters. | 192 to trim_url_limit characters. |
267 | 193 |
268 If nofollow is True, the URLs in link text will get a rel="nofollow" | 194 If nofollow is True, the URLs in link text will get a rel="nofollow" |
269 attribute. | 195 attribute. |
270 """ | 196 """ |
271 trim_url = lambda x, limit=trim_url_limit: limit is not None \ | 197 trim_url = lambda x, limit=trim_url_limit: limit is not None \ |
272 and (x[:limit] + (len(x) >=limit and '...' | 198 and (x[:limit] + (len(x) >=limit and '...' |
273 or '')) or x | 199 or '')) or x |
274 words = _word_split_re.split(unicode(escape(text))) | 200 words = _word_split_re.split(text_type(escape(text))) |
275 nofollow_attr = nofollow and ' rel="nofollow"' or '' | 201 nofollow_attr = nofollow and ' rel="nofollow"' or '' |
276 for i, word in enumerate(words): | 202 for i, word in enumerate(words): |
277 match = _punctuation_re.match(word) | 203 match = _punctuation_re.match(word) |
278 if match: | 204 if match: |
279 lead, middle, trail = match.groups() | 205 lead, middle, trail = match.groups() |
280 if middle.startswith('www.') or ( | 206 if middle.startswith('www.') or ( |
281 '@' not in middle and | 207 '@' not in middle and |
282 not middle.startswith('http://') and | 208 not middle.startswith('http://') and |
| 209 not middle.startswith('https://') and |
283 len(middle) > 0 and | 210 len(middle) > 0 and |
284 middle[0] in _letters + _digits and ( | 211 middle[0] in _letters + _digits and ( |
285 middle.endswith('.org') or | 212 middle.endswith('.org') or |
286 middle.endswith('.net') or | 213 middle.endswith('.net') or |
287 middle.endswith('.com') | 214 middle.endswith('.com') |
288 )): | 215 )): |
289 middle = '<a href="http://%s"%s>%s</a>' % (middle, | 216 middle = '<a href="http://%s"%s>%s</a>' % (middle, |
290 nofollow_attr, trim_url(middle)) | 217 nofollow_attr, trim_url(middle)) |
291 if middle.startswith('http://') or \ | 218 if middle.startswith('http://') or \ |
292 middle.startswith('https://'): | 219 middle.startswith('https://'): |
293 middle = '<a href="%s"%s>%s</a>' % (middle, | 220 middle = '<a href="%s"%s>%s</a>' % (middle, |
294 nofollow_attr, trim_url(middle)) | 221 nofollow_attr, trim_url(middle)) |
295 if '@' in middle and not middle.startswith('www.') and \ | 222 if '@' in middle and not middle.startswith('www.') and \ |
296 not ':' in middle and _simple_email_re.match(middle): | 223 not ':' in middle and _simple_email_re.match(middle): |
297 middle = '<a href="mailto:%s">%s</a>' % (middle, middle) | 224 middle = '<a href="mailto:%s">%s</a>' % (middle, middle) |
298 if lead + middle + trail != word: | 225 if lead + middle + trail != word: |
299 words[i] = lead + middle + trail | 226 words[i] = lead + middle + trail |
300 return u''.join(words) | 227 return u''.join(words) |
301 | 228 |
302 | 229 |
303 def generate_lorem_ipsum(n=5, html=True, min=20, max=100): | 230 def generate_lorem_ipsum(n=5, html=True, min=20, max=100): |
304 """Generate some lorem impsum for the template.""" | 231 """Generate some lorem impsum for the template.""" |
305 from jinja2.constants import LOREM_IPSUM_WORDS | 232 from jinja2.constants import LOREM_IPSUM_WORDS |
306 from random import choice, randrange | 233 from random import choice, randrange |
307 words = LOREM_IPSUM_WORDS.split() | 234 words = LOREM_IPSUM_WORDS.split() |
308 result = [] | 235 result = [] |
309 | 236 |
310 for _ in xrange(n): | 237 for _ in range(n): |
311 next_capitalized = True | 238 next_capitalized = True |
312 last_comma = last_fullstop = 0 | 239 last_comma = last_fullstop = 0 |
313 word = None | 240 word = None |
314 last = None | 241 last = None |
315 p = [] | 242 p = [] |
316 | 243 |
317 # each paragraph contains out of 20 to 100 words. | 244 # each paragraph contains out of 20 to 100 words. |
318 for idx, _ in enumerate(xrange(randrange(min, max))): | 245 for idx, _ in enumerate(range(randrange(min, max))): |
319 while True: | 246 while True: |
320 word = choice(words) | 247 word = choice(words) |
321 if word != last: | 248 if word != last: |
322 last = word | 249 last = word |
323 break | 250 break |
324 if next_capitalized: | 251 if next_capitalized: |
325 word = word.capitalize() | 252 word = word.capitalize() |
326 next_capitalized = False | 253 next_capitalized = False |
327 # add commas | 254 # add commas |
328 if idx - randrange(3, 8) > last_comma: | 255 if idx - randrange(3, 8) > last_comma: |
(...skipping 13 matching lines...) Expand all Loading... |
342 p = p[:-1] + '.' | 269 p = p[:-1] + '.' |
343 elif not p.endswith('.'): | 270 elif not p.endswith('.'): |
344 p += '.' | 271 p += '.' |
345 result.append(p) | 272 result.append(p) |
346 | 273 |
347 if not html: | 274 if not html: |
348 return u'\n\n'.join(result) | 275 return u'\n\n'.join(result) |
349 return Markup(u'\n'.join(u'<p>%s</p>' % escape(x) for x in result)) | 276 return Markup(u'\n'.join(u'<p>%s</p>' % escape(x) for x in result)) |
350 | 277 |
351 | 278 |
| 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. |
| 283 |
| 284 If non strings are provided they are converted to their unicode |
| 285 representation first. |
| 286 """ |
| 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)) |
| 292 |
| 293 |
352 class LRUCache(object): | 294 class LRUCache(object): |
353 """A simple LRU Cache implementation.""" | 295 """A simple LRU Cache implementation.""" |
354 | 296 |
355 # this is fast for small capacities (something below 1000) but doesn't | 297 # this is fast for small capacities (something below 1000) but doesn't |
356 # scale. But as long as it's only used as storage for templates this | 298 # scale. But as long as it's only used as storage for templates this |
357 # won't do any harm. | 299 # won't do any harm. |
358 | 300 |
359 def __init__(self, capacity): | 301 def __init__(self, capacity): |
360 self.capacity = capacity | 302 self.capacity = capacity |
361 self._mapping = {} | 303 self._mapping = {} |
362 self._queue = deque() | 304 self._queue = deque() |
363 self._postinit() | 305 self._postinit() |
364 | 306 |
365 def _postinit(self): | 307 def _postinit(self): |
366 # alias all queue methods for faster lookup | 308 # alias all queue methods for faster lookup |
367 self._popleft = self._queue.popleft | 309 self._popleft = self._queue.popleft |
368 self._pop = self._queue.pop | 310 self._pop = self._queue.pop |
369 if hasattr(self._queue, 'remove'): | 311 self._remove = self._queue.remove |
370 self._remove = self._queue.remove | |
371 self._wlock = allocate_lock() | 312 self._wlock = allocate_lock() |
372 self._append = self._queue.append | 313 self._append = self._queue.append |
373 | 314 |
374 def _remove(self, obj): | |
375 """Python 2.4 compatibility.""" | |
376 for idx, item in enumerate(self._queue): | |
377 if item == obj: | |
378 del self._queue[idx] | |
379 break | |
380 | |
381 def __getstate__(self): | 315 def __getstate__(self): |
382 return { | 316 return { |
383 'capacity': self.capacity, | 317 'capacity': self.capacity, |
384 '_mapping': self._mapping, | 318 '_mapping': self._mapping, |
385 '_queue': self._queue | 319 '_queue': self._queue |
386 } | 320 } |
387 | 321 |
388 def __setstate__(self, d): | 322 def __setstate__(self, d): |
389 self.__dict__.update(d) | 323 self.__dict__.update(d) |
390 self._postinit() | 324 self._postinit() |
391 | 325 |
392 def __getnewargs__(self): | 326 def __getnewargs__(self): |
393 return (self.capacity,) | 327 return (self.capacity,) |
394 | 328 |
395 def copy(self): | 329 def copy(self): |
396 """Return an shallow copy of the instance.""" | 330 """Return a shallow copy of the instance.""" |
397 rv = self.__class__(self.capacity) | 331 rv = self.__class__(self.capacity) |
398 rv._mapping.update(self._mapping) | 332 rv._mapping.update(self._mapping) |
399 rv._queue = deque(self._queue) | 333 rv._queue = deque(self._queue) |
400 return rv | 334 return rv |
401 | 335 |
402 def get(self, key, default=None): | 336 def get(self, key, default=None): |
403 """Return an item from the cache dict or `default`""" | 337 """Return an item from the cache dict or `default`""" |
404 try: | 338 try: |
405 return self[key] | 339 return self[key] |
406 except KeyError: | 340 except KeyError: |
407 return default | 341 return default |
408 | 342 |
409 def setdefault(self, key, default=None): | 343 def setdefault(self, key, default=None): |
410 """Set `default` if the key is not in the cache otherwise | 344 """Set `default` if the key is not in the cache otherwise |
411 leave unchanged. Return the value of this key. | 345 leave unchanged. Return the value of this key. |
412 """ | 346 """ |
| 347 self._wlock.acquire() |
413 try: | 348 try: |
414 return self[key] | 349 try: |
415 except KeyError: | 350 return self[key] |
416 self[key] = default | 351 except KeyError: |
417 return default | 352 self[key] = default |
| 353 return default |
| 354 finally: |
| 355 self._wlock.release() |
418 | 356 |
419 def clear(self): | 357 def clear(self): |
420 """Clear the cache.""" | 358 """Clear the cache.""" |
421 self._wlock.acquire() | 359 self._wlock.acquire() |
422 try: | 360 try: |
423 self._mapping.clear() | 361 self._mapping.clear() |
424 self._queue.clear() | 362 self._queue.clear() |
425 finally: | 363 finally: |
426 self._wlock.release() | 364 self._wlock.release() |
427 | 365 |
428 def __contains__(self, key): | 366 def __contains__(self, key): |
429 """Check if a key exists in this cache.""" | 367 """Check if a key exists in this cache.""" |
430 return key in self._mapping | 368 return key in self._mapping |
431 | 369 |
432 def __len__(self): | 370 def __len__(self): |
433 """Return the current size of the cache.""" | 371 """Return the current size of the cache.""" |
434 return len(self._mapping) | 372 return len(self._mapping) |
435 | 373 |
436 def __repr__(self): | 374 def __repr__(self): |
437 return '<%s %r>' % ( | 375 return '<%s %r>' % ( |
438 self.__class__.__name__, | 376 self.__class__.__name__, |
439 self._mapping | 377 self._mapping |
440 ) | 378 ) |
441 | 379 |
442 def __getitem__(self, key): | 380 def __getitem__(self, key): |
443 """Get an item from the cache. Moves the item up so that it has the | 381 """Get an item from the cache. Moves the item up so that it has the |
444 highest priority then. | 382 highest priority then. |
445 | 383 |
446 Raise an `KeyError` if it does not exist. | 384 Raise a `KeyError` if it does not exist. |
447 """ | 385 """ |
448 rv = self._mapping[key] | 386 self._wlock.acquire() |
449 if self._queue[-1] != key: | 387 try: |
450 try: | 388 rv = self._mapping[key] |
451 self._remove(key) | 389 if self._queue[-1] != key: |
452 except ValueError: | 390 try: |
453 # if something removed the key from the container | 391 self._remove(key) |
454 # when we read, ignore the ValueError that we would | 392 except ValueError: |
455 # get otherwise. | 393 # if something removed the key from the container |
456 pass | 394 # when we read, ignore the ValueError that we would |
457 self._append(key) | 395 # get otherwise. |
458 return rv | 396 pass |
| 397 self._append(key) |
| 398 return rv |
| 399 finally: |
| 400 self._wlock.release() |
459 | 401 |
460 def __setitem__(self, key, value): | 402 def __setitem__(self, key, value): |
461 """Sets the value for an item. Moves the item up so that it | 403 """Sets the value for an item. Moves the item up so that it |
462 has the highest priority then. | 404 has the highest priority then. |
463 """ | 405 """ |
464 self._wlock.acquire() | 406 self._wlock.acquire() |
465 try: | 407 try: |
466 if key in self._mapping: | 408 if key in self._mapping: |
467 try: | 409 self._remove(key) |
468 self._remove(key) | |
469 except ValueError: | |
470 # __getitem__ is not locked, it might happen | |
471 pass | |
472 elif len(self._mapping) == self.capacity: | 410 elif len(self._mapping) == self.capacity: |
473 del self._mapping[self._popleft()] | 411 del self._mapping[self._popleft()] |
474 self._append(key) | 412 self._append(key) |
475 self._mapping[key] = value | 413 self._mapping[key] = value |
476 finally: | 414 finally: |
477 self._wlock.release() | 415 self._wlock.release() |
478 | 416 |
479 def __delitem__(self, key): | 417 def __delitem__(self, key): |
480 """Remove an item from the cache dict. | 418 """Remove an item from the cache dict. |
481 Raise an `KeyError` if it does not exist. | 419 Raise a `KeyError` if it does not exist. |
482 """ | 420 """ |
483 self._wlock.acquire() | 421 self._wlock.acquire() |
484 try: | 422 try: |
485 del self._mapping[key] | 423 del self._mapping[key] |
486 try: | 424 try: |
487 self._remove(key) | 425 self._remove(key) |
488 except ValueError: | 426 except ValueError: |
489 # __getitem__ is not locked, it might happen | 427 # __getitem__ is not locked, it might happen |
490 pass | 428 pass |
491 finally: | 429 finally: |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
531 | 469 |
532 | 470 |
533 # register the LRU cache as mutable mapping if possible | 471 # register the LRU cache as mutable mapping if possible |
534 try: | 472 try: |
535 from collections import MutableMapping | 473 from collections import MutableMapping |
536 MutableMapping.register(LRUCache) | 474 MutableMapping.register(LRUCache) |
537 except ImportError: | 475 except ImportError: |
538 pass | 476 pass |
539 | 477 |
540 | 478 |
| 479 @implements_iterator |
541 class Cycler(object): | 480 class Cycler(object): |
542 """A cycle helper for templates.""" | 481 """A cycle helper for templates.""" |
543 | 482 |
544 def __init__(self, *items): | 483 def __init__(self, *items): |
545 if not items: | 484 if not items: |
546 raise RuntimeError('at least one item has to be provided') | 485 raise RuntimeError('at least one item has to be provided') |
547 self.items = items | 486 self.items = items |
548 self.reset() | 487 self.reset() |
549 | 488 |
550 def reset(self): | 489 def reset(self): |
551 """Resets the cycle.""" | 490 """Resets the cycle.""" |
552 self.pos = 0 | 491 self.pos = 0 |
553 | 492 |
554 @property | 493 @property |
555 def current(self): | 494 def current(self): |
556 """Returns the current item.""" | 495 """Returns the current item.""" |
557 return self.items[self.pos] | 496 return self.items[self.pos] |
558 | 497 |
559 def next(self): | 498 def __next__(self): |
560 """Goes one item ahead and returns it.""" | 499 """Goes one item ahead and returns it.""" |
561 rv = self.current | 500 rv = self.current |
562 self.pos = (self.pos + 1) % len(self.items) | 501 self.pos = (self.pos + 1) % len(self.items) |
563 return rv | 502 return rv |
564 | 503 |
565 | 504 |
566 class Joiner(object): | 505 class Joiner(object): |
567 """A joining helper for templates.""" | 506 """A joining helper for templates.""" |
568 | 507 |
569 def __init__(self, sep=u', '): | 508 def __init__(self, sep=u', '): |
570 self.sep = sep | 509 self.sep = sep |
571 self.used = False | 510 self.used = False |
572 | 511 |
573 def __call__(self): | 512 def __call__(self): |
574 if not self.used: | 513 if not self.used: |
575 self.used = True | 514 self.used = True |
576 return u'' | 515 return u'' |
577 return self.sep | 516 return self.sep |
578 | 517 |
579 | 518 |
580 # try markupsafe first, if that fails go with Jinja2's bundled version | 519 # Imported here because that's where it was in the past |
581 # of markupsafe. Markupsafe was previously Jinja2's implementation of | 520 from markupsafe import Markup, escape, soft_unicode |
582 # the Markup object but was moved into a separate package in a patchleve | |
583 # release | |
584 try: | |
585 from markupsafe import Markup, escape, soft_unicode | |
586 except ImportError: | |
587 from jinja2._markupsafe import Markup, escape, soft_unicode | |
588 | |
589 | |
590 # partials | |
591 try: | |
592 from functools import partial | |
593 except ImportError: | |
594 class partial(object): | |
595 def __init__(self, _func, *args, **kwargs): | |
596 self._func = _func | |
597 self._args = args | |
598 self._kwargs = kwargs | |
599 def __call__(self, *args, **kwargs): | |
600 kwargs.update(self._kwargs) | |
601 return self._func(*(self._args + args), **kwargs) | |
OLD | NEW |