comparison env/lib/python3.9/site-packages/jinja2/utils.py @ 0:4f3585e2f14b draft default tip

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