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