comparison env/lib/python3.7/site-packages/jinja2/utils.py @ 0:26e78fe6e8c4 draft

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