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