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