Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(183)

Side by Side Diff: third_party/jinja2/utils.py

Issue 23506004: Update Jinja2 (Python template library) to 2.7.1 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebased Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « third_party/jinja2/tests.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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, ('(', '<', '&lt;'))), 21 '|'.join(map(re.escape, ('(', '<', '&lt;'))),
26 '|'.join(imap(re.escape, ('.', ',', ')', '>', '\n', '&gt;'))) 22 '|'.join(map(re.escape, ('.', ',', ')', '>', '\n', '&gt;')))
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
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
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
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
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
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)
OLDNEW
« no previous file with comments | « third_party/jinja2/tests.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698