| 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 errno | 12 import errno |
| 13 from collections import deque | 13 from collections import deque |
| 14 from threading import Lock |
| 14 from jinja2._compat import text_type, string_types, implements_iterator, \ | 15 from jinja2._compat import text_type, string_types, implements_iterator, \ |
| 15 allocate_lock, url_quote | 16 url_quote |
| 16 | 17 |
| 17 | 18 |
| 18 _word_split_re = re.compile(r'(\s+)') | 19 _word_split_re = re.compile(r'(\s+)') |
| 19 _punctuation_re = re.compile( | 20 _punctuation_re = re.compile( |
| 20 '^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$' % ( | 21 '^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$' % ( |
| 21 '|'.join(map(re.escape, ('(', '<', '<'))), | 22 '|'.join(map(re.escape, ('(', '<', '<'))), |
| 22 '|'.join(map(re.escape, ('.', ',', ')', '>', '\n', '>'))) | 23 '|'.join(map(re.escape, ('.', ',', ')', '>', '\n', '>'))) |
| 23 ) | 24 ) |
| 24 ) | 25 ) |
| 25 _simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$') | 26 _simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$') |
| (...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 142 raise | 143 raise |
| 143 | 144 |
| 144 | 145 |
| 145 def open_if_exists(filename, mode='rb'): | 146 def open_if_exists(filename, mode='rb'): |
| 146 """Returns a file descriptor for the filename if that file exists, | 147 """Returns a file descriptor for the filename if that file exists, |
| 147 otherwise `None`. | 148 otherwise `None`. |
| 148 """ | 149 """ |
| 149 try: | 150 try: |
| 150 return open(filename, mode) | 151 return open(filename, mode) |
| 151 except IOError as e: | 152 except IOError as e: |
| 152 if e.errno not in (errno.ENOENT, errno.EISDIR): | 153 if e.errno not in (errno.ENOENT, errno.EISDIR, errno.EINVAL): |
| 153 raise | 154 raise |
| 154 | 155 |
| 155 | 156 |
| 156 def object_type_repr(obj): | 157 def object_type_repr(obj): |
| 157 """Returns the name of the object's type. For some recognized | 158 """Returns the name of the object's type. For some recognized |
| 158 singletons the name of the object is returned instead. (For | 159 singletons the name of the object is returned instead. (For |
| 159 example for `None` and `Ellipsis`). | 160 example for `None` and `Ellipsis`). |
| 160 """ | 161 """ |
| 161 if obj is None: | 162 if obj is None: |
| 162 return 'None' | 163 return 'None' |
| (...skipping 12 matching lines...) Expand all Loading... |
| 175 builtin `pprint`. | 176 builtin `pprint`. |
| 176 """ | 177 """ |
| 177 try: | 178 try: |
| 178 from pretty import pretty | 179 from pretty import pretty |
| 179 return pretty(obj, verbose=verbose) | 180 return pretty(obj, verbose=verbose) |
| 180 except ImportError: | 181 except ImportError: |
| 181 from pprint import pformat | 182 from pprint import pformat |
| 182 return pformat(obj) | 183 return pformat(obj) |
| 183 | 184 |
| 184 | 185 |
| 185 def urlize(text, trim_url_limit=None, nofollow=False): | 186 def urlize(text, trim_url_limit=None, nofollow=False, target=None): |
| 186 """Converts any URLs in text into clickable links. Works on http://, | 187 """Converts any URLs in text into clickable links. Works on http://, |
| 187 https:// and www. links. Links can have trailing punctuation (periods, | 188 https:// and www. links. Links can have trailing punctuation (periods, |
| 188 commas, close-parens) and leading punctuation (opening parens) and | 189 commas, close-parens) and leading punctuation (opening parens) and |
| 189 it'll still do the right thing. | 190 it'll still do the right thing. |
| 190 | 191 |
| 191 If trim_url_limit is not None, the URLs in link text will be limited | 192 If trim_url_limit is not None, the URLs in link text will be limited |
| 192 to trim_url_limit characters. | 193 to trim_url_limit characters. |
| 193 | 194 |
| 194 If nofollow is True, the URLs in link text will get a rel="nofollow" | 195 If nofollow is True, the URLs in link text will get a rel="nofollow" |
| 195 attribute. | 196 attribute. |
| 197 |
| 198 If target is not None, a target attribute will be added to the link. |
| 196 """ | 199 """ |
| 197 trim_url = lambda x, limit=trim_url_limit: limit is not None \ | 200 trim_url = lambda x, limit=trim_url_limit: limit is not None \ |
| 198 and (x[:limit] + (len(x) >=limit and '...' | 201 and (x[:limit] + (len(x) >=limit and '...' |
| 199 or '')) or x | 202 or '')) or x |
| 200 words = _word_split_re.split(text_type(escape(text))) | 203 words = _word_split_re.split(text_type(escape(text))) |
| 201 nofollow_attr = nofollow and ' rel="nofollow"' or '' | 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 = '' |
| 202 for i, word in enumerate(words): | 209 for i, word in enumerate(words): |
| 203 match = _punctuation_re.match(word) | 210 match = _punctuation_re.match(word) |
| 204 if match: | 211 if match: |
| 205 lead, middle, trail = match.groups() | 212 lead, middle, trail = match.groups() |
| 206 if middle.startswith('www.') or ( | 213 if middle.startswith('www.') or ( |
| 207 '@' not in middle and | 214 '@' not in middle and |
| 208 not middle.startswith('http://') and | 215 not middle.startswith('http://') and |
| 209 not middle.startswith('https://') and | 216 not middle.startswith('https://') and |
| 210 len(middle) > 0 and | 217 len(middle) > 0 and |
| 211 middle[0] in _letters + _digits and ( | 218 middle[0] in _letters + _digits and ( |
| 212 middle.endswith('.org') or | 219 middle.endswith('.org') or |
| 213 middle.endswith('.net') or | 220 middle.endswith('.net') or |
| 214 middle.endswith('.com') | 221 middle.endswith('.com') |
| 215 )): | 222 )): |
| 216 middle = '<a href="http://%s"%s>%s</a>' % (middle, | 223 middle = '<a href="http://%s"%s%s>%s</a>' % (middle, |
| 217 nofollow_attr, trim_url(middle)) | 224 nofollow_attr, target_attr, trim_url(middle)) |
| 218 if middle.startswith('http://') or \ | 225 if middle.startswith('http://') or \ |
| 219 middle.startswith('https://'): | 226 middle.startswith('https://'): |
| 220 middle = '<a href="%s"%s>%s</a>' % (middle, | 227 middle = '<a href="%s"%s%s>%s</a>' % (middle, |
| 221 nofollow_attr, trim_url(middle)) | 228 nofollow_attr, target_attr, trim_url(middle)) |
| 222 if '@' in middle and not middle.startswith('www.') and \ | 229 if '@' in middle and not middle.startswith('www.') and \ |
| 223 not ':' in middle and _simple_email_re.match(middle): | 230 not ':' in middle and _simple_email_re.match(middle): |
| 224 middle = '<a href="mailto:%s">%s</a>' % (middle, middle) | 231 middle = '<a href="mailto:%s">%s</a>' % (middle, middle) |
| 225 if lead + middle + trail != word: | 232 if lead + middle + trail != word: |
| 226 words[i] = lead + middle + trail | 233 words[i] = lead + middle + trail |
| 227 return u''.join(words) | 234 return u''.join(words) |
| 228 | 235 |
| 229 | 236 |
| 230 def generate_lorem_ipsum(n=5, html=True, min=20, max=100): | 237 def generate_lorem_ipsum(n=5, html=True, min=20, max=100): |
| 231 """Generate some lorem impsum for the template.""" | 238 """Generate some lorem ipsum for the template.""" |
| 232 from jinja2.constants import LOREM_IPSUM_WORDS | 239 from jinja2.constants import LOREM_IPSUM_WORDS |
| 233 from random import choice, randrange | 240 from random import choice, randrange |
| 234 words = LOREM_IPSUM_WORDS.split() | 241 words = LOREM_IPSUM_WORDS.split() |
| 235 result = [] | 242 result = [] |
| 236 | 243 |
| 237 for _ in range(n): | 244 for _ in range(n): |
| 238 next_capitalized = True | 245 next_capitalized = True |
| 239 last_comma = last_fullstop = 0 | 246 last_comma = last_fullstop = 0 |
| 240 word = None | 247 word = None |
| 241 last = None | 248 last = None |
| (...skipping 27 matching lines...) Expand all Loading... |
| 269 p = p[:-1] + '.' | 276 p = p[:-1] + '.' |
| 270 elif not p.endswith('.'): | 277 elif not p.endswith('.'): |
| 271 p += '.' | 278 p += '.' |
| 272 result.append(p) | 279 result.append(p) |
| 273 | 280 |
| 274 if not html: | 281 if not html: |
| 275 return u'\n\n'.join(result) | 282 return u'\n\n'.join(result) |
| 276 return Markup(u'\n'.join(u'<p>%s</p>' % escape(x) for x in result)) | 283 return Markup(u'\n'.join(u'<p>%s</p>' % escape(x) for x in result)) |
| 277 | 284 |
| 278 | 285 |
| 279 def unicode_urlencode(obj, charset='utf-8'): | 286 def unicode_urlencode(obj, charset='utf-8', for_qs=False): |
| 280 """URL escapes a single bytestring or unicode string with the | 287 """URL escapes a single bytestring or unicode string with the |
| 281 given charset if applicable to URL safe quoting under all rules | 288 given charset if applicable to URL safe quoting under all rules |
| 282 that need to be considered under all supported Python versions. | 289 that need to be considered under all supported Python versions. |
| 283 | 290 |
| 284 If non strings are provided they are converted to their unicode | 291 If non strings are provided they are converted to their unicode |
| 285 representation first. | 292 representation first. |
| 286 """ | 293 """ |
| 287 if not isinstance(obj, string_types): | 294 if not isinstance(obj, string_types): |
| 288 obj = text_type(obj) | 295 obj = text_type(obj) |
| 289 if isinstance(obj, text_type): | 296 if isinstance(obj, text_type): |
| 290 obj = obj.encode(charset) | 297 obj = obj.encode(charset) |
| 291 return text_type(url_quote(obj)) | 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 |
| 292 | 303 |
| 293 | 304 |
| 294 class LRUCache(object): | 305 class LRUCache(object): |
| 295 """A simple LRU Cache implementation.""" | 306 """A simple LRU Cache implementation.""" |
| 296 | 307 |
| 297 # this is fast for small capacities (something below 1000) but doesn't | 308 # 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 | 309 # scale. But as long as it's only used as storage for templates this |
| 299 # won't do any harm. | 310 # won't do any harm. |
| 300 | 311 |
| 301 def __init__(self, capacity): | 312 def __init__(self, capacity): |
| 302 self.capacity = capacity | 313 self.capacity = capacity |
| 303 self._mapping = {} | 314 self._mapping = {} |
| 304 self._queue = deque() | 315 self._queue = deque() |
| 305 self._postinit() | 316 self._postinit() |
| 306 | 317 |
| 307 def _postinit(self): | 318 def _postinit(self): |
| 308 # alias all queue methods for faster lookup | 319 # alias all queue methods for faster lookup |
| 309 self._popleft = self._queue.popleft | 320 self._popleft = self._queue.popleft |
| 310 self._pop = self._queue.pop | 321 self._pop = self._queue.pop |
| 311 self._remove = self._queue.remove | 322 self._remove = self._queue.remove |
| 312 self._wlock = allocate_lock() | 323 self._wlock = Lock() |
| 313 self._append = self._queue.append | 324 self._append = self._queue.append |
| 314 | 325 |
| 315 def __getstate__(self): | 326 def __getstate__(self): |
| 316 return { | 327 return { |
| 317 'capacity': self.capacity, | 328 'capacity': self.capacity, |
| 318 '_mapping': self._mapping, | 329 '_mapping': self._mapping, |
| 319 '_queue': self._queue | 330 '_queue': self._queue |
| 320 } | 331 } |
| 321 | 332 |
| 322 def __setstate__(self, d): | 333 def __setstate__(self, d): |
| (...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 511 | 522 |
| 512 def __call__(self): | 523 def __call__(self): |
| 513 if not self.used: | 524 if not self.used: |
| 514 self.used = True | 525 self.used = True |
| 515 return u'' | 526 return u'' |
| 516 return self.sep | 527 return self.sep |
| 517 | 528 |
| 518 | 529 |
| 519 # Imported here because that's where it was in the past | 530 # Imported here because that's where it was in the past |
| 520 from markupsafe import Markup, escape, soft_unicode | 531 from markupsafe import Markup, escape, soft_unicode |
| OLD | NEW |