Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
environment.py1676 linesDownload Raw Back to jinja2
1"""Classes for managing templates and their runtime and compile time2options.3"""4 5import os6import typing7import typing as t8import weakref9from collections import ChainMap10from functools import lru_cache11from functools import partial12from functools import reduce13from types import CodeType14 15from markupsafe import Markup16 17from . import nodes18from .compiler import CodeGenerator19from .compiler import generate20from .defaults import BLOCK_END_STRING21from .defaults import BLOCK_START_STRING22from .defaults import COMMENT_END_STRING23from .defaults import COMMENT_START_STRING24from .defaults import DEFAULT_FILTERS  # type: ignore[attr-defined]25from .defaults import DEFAULT_NAMESPACE26from .defaults import DEFAULT_POLICIES27from .defaults import DEFAULT_TESTS  # type: ignore[attr-defined]28from .defaults import KEEP_TRAILING_NEWLINE29from .defaults import LINE_COMMENT_PREFIX30from .defaults import LINE_STATEMENT_PREFIX31from .defaults import LSTRIP_BLOCKS32from .defaults import NEWLINE_SEQUENCE33from .defaults import TRIM_BLOCKS34from .defaults import VARIABLE_END_STRING35from .defaults import VARIABLE_START_STRING36from .exceptions import TemplateNotFound37from .exceptions import TemplateRuntimeError38from .exceptions import TemplatesNotFound39from .exceptions import TemplateSyntaxError40from .exceptions import UndefinedError41from .lexer import get_lexer42from .lexer import Lexer43from .lexer import TokenStream44from .nodes import EvalContext45from .parser import Parser46from .runtime import Context47from .runtime import new_context48from .runtime import Undefined49from .utils import _PassArg50from .utils import concat51from .utils import consume52from .utils import import_string53from .utils import internalcode54from .utils import LRUCache55from .utils import missing56 57if t.TYPE_CHECKING:58    import typing_extensions as te59 60    from .bccache import BytecodeCache61    from .ext import Extension62    from .loaders import BaseLoader63 64_env_bound = t.TypeVar("_env_bound", bound="Environment")65 66 67# for direct template usage we have up to ten living environments68@lru_cache(maxsize=10)69def get_spontaneous_environment(cls: t.Type[_env_bound], *args: t.Any) -> _env_bound:70    """Return a new spontaneous environment. A spontaneous environment71    is used for templates created directly rather than through an72    existing environment.73 74    :param cls: Environment class to create.75    :param args: Positional arguments passed to environment.76    """77    env = cls(*args)78    env.shared = True79    return env80 81 82def create_cache(83    size: int,84) -> t.Optional[t.MutableMapping[t.Tuple["weakref.ref[t.Any]", str], "Template"]]:85    """Return the cache class for the given size."""86    if size == 0:87        return None88 89    if size < 0:90        return {}91 92    return LRUCache(size)  # type: ignore93 94 95def copy_cache(96    cache: t.Optional[t.MutableMapping[t.Any, t.Any]],97) -> t.Optional[t.MutableMapping[t.Tuple["weakref.ref[t.Any]", str], "Template"]]:98    """Create an empty copy of the given cache."""99    if cache is None:100        return None101 102    if type(cache) is dict:  # noqa E721103        return {}104 105    return LRUCache(cache.capacity)  # type: ignore106 107 108def load_extensions(109    environment: "Environment",110    extensions: t.Sequence[t.Union[str, t.Type["Extension"]]],111) -> t.Dict[str, "Extension"]:112    """Load the extensions from the list and bind it to the environment.113    Returns a dict of instantiated extensions.114    """115    result = {}116 117    for extension in extensions:118        if isinstance(extension, str):119            extension = t.cast(t.Type["Extension"], import_string(extension))120 121        result[extension.identifier] = extension(environment)122 123    return result124 125 126def _environment_config_check(environment: "Environment") -> "Environment":127    """Perform a sanity check on the environment."""128    assert issubclass(129        environment.undefined, Undefined130    ), "'undefined' must be a subclass of 'jinja2.Undefined'."131    assert (132        environment.block_start_string133        != environment.variable_start_string134        != environment.comment_start_string135    ), "block, variable and comment start strings must be different."136    assert environment.newline_sequence in {137        "\r",138        "\r\n",139        "\n",140    }, "'newline_sequence' must be one of '\\n', '\\r\\n', or '\\r'."141    return environment142 143 144class Environment:145    r"""The core component of Jinja is the `Environment`.  It contains146    important shared variables like configuration, filters, tests,147    globals and others.  Instances of this class may be modified if148    they are not shared and if no template was loaded so far.149    Modifications on environments after the first template was loaded150    will lead to surprising effects and undefined behavior.151 152    Here are the possible initialization parameters:153 154        `block_start_string`155            The string marking the beginning of a block.  Defaults to ``'{%'``.156 157        `block_end_string`158            The string marking the end of a block.  Defaults to ``'%}'``.159 160        `variable_start_string`161            The string marking the beginning of a print statement.162            Defaults to ``'{{'``.163 164        `variable_end_string`165            The string marking the end of a print statement.  Defaults to166            ``'}}'``.167 168        `comment_start_string`169            The string marking the beginning of a comment.  Defaults to ``'{#'``.170 171        `comment_end_string`172            The string marking the end of a comment.  Defaults to ``'#}'``.173 174        `line_statement_prefix`175            If given and a string, this will be used as prefix for line based176            statements.  See also :ref:`line-statements`.177 178        `line_comment_prefix`179            If given and a string, this will be used as prefix for line based180            comments.  See also :ref:`line-statements`.181 182            .. versionadded:: 2.2183 184        `trim_blocks`185            If this is set to ``True`` the first newline after a block is186            removed (block, not variable tag!).  Defaults to `False`.187 188        `lstrip_blocks`189            If this is set to ``True`` leading spaces and tabs are stripped190            from the start of a line to a block.  Defaults to `False`.191 192        `newline_sequence`193            The sequence that starts a newline.  Must be one of ``'\r'``,194            ``'\n'`` or ``'\r\n'``.  The default is ``'\n'`` which is a195            useful default for Linux and OS X systems as well as web196            applications.197 198        `keep_trailing_newline`199            Preserve the trailing newline when rendering templates.200            The default is ``False``, which causes a single newline,201            if present, to be stripped from the end of the template.202 203            .. versionadded:: 2.7204 205        `extensions`206            List of Jinja extensions to use.  This can either be import paths207            as strings or extension classes.  For more information have a208            look at :ref:`the extensions documentation <jinja-extensions>`.209 210        `optimized`211            should the optimizer be enabled?  Default is ``True``.212 213        `undefined`214            :class:`Undefined` or a subclass of it that is used to represent215            undefined values in the template.216 217        `finalize`218            A callable that can be used to process the result of a variable219            expression before it is output.  For example one can convert220            ``None`` implicitly into an empty string here.221 222        `autoescape`223            If set to ``True`` the XML/HTML autoescaping feature is enabled by224            default.  For more details about autoescaping see225            :class:`~markupsafe.Markup`.  As of Jinja 2.4 this can also226            be a callable that is passed the template name and has to227            return ``True`` or ``False`` depending on autoescape should be228            enabled by default.229 230            .. versionchanged:: 2.4231               `autoescape` can now be a function232 233        `loader`234            The template loader for this environment.235 236        `cache_size`237            The size of the cache.  Per default this is ``400`` which means238            that if more than 400 templates are loaded the loader will clean239            out the least recently used template.  If the cache size is set to240            ``0`` templates are recompiled all the time, if the cache size is241            ``-1`` the cache will not be cleaned.242 243            .. versionchanged:: 2.8244               The cache size was increased to 400 from a low 50.245 246        `auto_reload`247            Some loaders load templates from locations where the template248            sources may change (ie: file system or database).  If249            ``auto_reload`` is set to ``True`` (default) every time a template is250            requested the loader checks if the source changed and if yes, it251            will reload the template.  For higher performance it's possible to252            disable that.253 254        `bytecode_cache`255            If set to a bytecode cache object, this object will provide a256            cache for the internal Jinja bytecode so that templates don't257            have to be parsed if they were not changed.258 259            See :ref:`bytecode-cache` for more information.260 261        `enable_async`262            If set to true this enables async template execution which263            allows using async functions and generators.264    """265 266    #: if this environment is sandboxed.  Modifying this variable won't make267    #: the environment sandboxed though.  For a real sandboxed environment268    #: have a look at jinja2.sandbox.  This flag alone controls the code269    #: generation by the compiler.270    sandboxed = False271 272    #: True if the environment is just an overlay273    overlayed = False274 275    #: the environment this environment is linked to if it is an overlay276    linked_to: t.Optional["Environment"] = None277 278    #: shared environments have this set to `True`.  A shared environment279    #: must not be modified280    shared = False281 282    #: the class that is used for code generation.  See283    #: :class:`~jinja2.compiler.CodeGenerator` for more information.284    code_generator_class: t.Type["CodeGenerator"] = CodeGenerator285 286    concat = "".join287 288    #: the context class that is used for templates.  See289    #: :class:`~jinja2.runtime.Context` for more information.290    context_class: t.Type[Context] = Context291 292    template_class: t.Type["Template"]293 294    def __init__(295        self,296        block_start_string: str = BLOCK_START_STRING,297        block_end_string: str = BLOCK_END_STRING,298        variable_start_string: str = VARIABLE_START_STRING,299        variable_end_string: str = VARIABLE_END_STRING,300        comment_start_string: str = COMMENT_START_STRING,301        comment_end_string: str = COMMENT_END_STRING,302        line_statement_prefix: t.Optional[str] = LINE_STATEMENT_PREFIX,303        line_comment_prefix: t.Optional[str] = LINE_COMMENT_PREFIX,304        trim_blocks: bool = TRIM_BLOCKS,305        lstrip_blocks: bool = LSTRIP_BLOCKS,306        newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = NEWLINE_SEQUENCE,307        keep_trailing_newline: bool = KEEP_TRAILING_NEWLINE,308        extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = (),309        optimized: bool = True,310        undefined: t.Type[Undefined] = Undefined,311        finalize: t.Optional[t.Callable[..., t.Any]] = None,312        autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = False,313        loader: t.Optional["BaseLoader"] = None,314        cache_size: int = 400,315        auto_reload: bool = True,316        bytecode_cache: t.Optional["BytecodeCache"] = None,317        enable_async: bool = False,318    ):319        # !!Important notice!!320        #   The constructor accepts quite a few arguments that should be321        #   passed by keyword rather than position.  However it's important to322        #   not change the order of arguments because it's used at least323        #   internally in those cases:324        #       -   spontaneous environments (i18n extension and Template)325        #       -   unittests326        #   If parameter changes are required only add parameters at the end327        #   and don't change the arguments (or the defaults!) of the arguments328        #   existing already.329 330        # lexer / parser information331        self.block_start_string = block_start_string332        self.block_end_string = block_end_string333        self.variable_start_string = variable_start_string334        self.variable_end_string = variable_end_string335        self.comment_start_string = comment_start_string336        self.comment_end_string = comment_end_string337        self.line_statement_prefix = line_statement_prefix338        self.line_comment_prefix = line_comment_prefix339        self.trim_blocks = trim_blocks340        self.lstrip_blocks = lstrip_blocks341        self.newline_sequence = newline_sequence342        self.keep_trailing_newline = keep_trailing_newline343 344        # runtime information345        self.undefined: t.Type[Undefined] = undefined346        self.optimized = optimized347        self.finalize = finalize348        self.autoescape = autoescape349 350        # defaults351        self.filters = DEFAULT_FILTERS.copy()352        self.tests = DEFAULT_TESTS.copy()353        self.globals = DEFAULT_NAMESPACE.copy()354 355        # set the loader provided356        self.loader = loader357        self.cache = create_cache(cache_size)358        self.bytecode_cache = bytecode_cache359        self.auto_reload = auto_reload360 361        # configurable policies362        self.policies = DEFAULT_POLICIES.copy()363 364        # load extensions365        self.extensions = load_extensions(self, extensions)366 367        self.is_async = enable_async368        _environment_config_check(self)369 370    def add_extension(self, extension: t.Union[str, t.Type["Extension"]]) -> None:371        """Adds an extension after the environment was created.372 373        .. versionadded:: 2.5374        """375        self.extensions.update(load_extensions(self, [extension]))376 377    def extend(self, **attributes: t.Any) -> None:378        """Add the items to the instance of the environment if they do not exist379        yet.  This is used by :ref:`extensions <writing-extensions>` to register380        callbacks and configuration values without breaking inheritance.381        """382        for key, value in attributes.items():383            if not hasattr(self, key):384                setattr(self, key, value)385 386    def overlay(387        self,388        block_start_string: str = missing,389        block_end_string: str = missing,390        variable_start_string: str = missing,391        variable_end_string: str = missing,392        comment_start_string: str = missing,393        comment_end_string: str = missing,394        line_statement_prefix: t.Optional[str] = missing,395        line_comment_prefix: t.Optional[str] = missing,396        trim_blocks: bool = missing,397        lstrip_blocks: bool = missing,398        newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = missing,399        keep_trailing_newline: bool = missing,400        extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = missing,401        optimized: bool = missing,402        undefined: t.Type[Undefined] = missing,403        finalize: t.Optional[t.Callable[..., t.Any]] = missing,404        autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = missing,405        loader: t.Optional["BaseLoader"] = missing,406        cache_size: int = missing,407        auto_reload: bool = missing,408        bytecode_cache: t.Optional["BytecodeCache"] = missing,409        enable_async: bool = False,410    ) -> "Environment":411        """Create a new overlay environment that shares all the data with the412        current environment except for cache and the overridden attributes.413        Extensions cannot be removed for an overlayed environment.  An overlayed414        environment automatically gets all the extensions of the environment it415        is linked to plus optional extra extensions.416 417        Creating overlays should happen after the initial environment was set418        up completely.  Not all attributes are truly linked, some are just419        copied over so modifications on the original environment may not shine420        through.421 422        .. versionchanged:: 3.1.2423            Added the ``newline_sequence``,, ``keep_trailing_newline``,424            and ``enable_async`` parameters to match ``__init__``.425        """426        args = dict(locals())427        del args["self"], args["cache_size"], args["extensions"], args["enable_async"]428 429        rv = object.__new__(self.__class__)430        rv.__dict__.update(self.__dict__)431        rv.overlayed = True432        rv.linked_to = self433 434        for key, value in args.items():435            if value is not missing:436                setattr(rv, key, value)437 438        if cache_size is not missing:439            rv.cache = create_cache(cache_size)440        else:441            rv.cache = copy_cache(self.cache)442 443        rv.extensions = {}444        for key, value in self.extensions.items():445            rv.extensions[key] = value.bind(rv)446        if extensions is not missing:447            rv.extensions.update(load_extensions(rv, extensions))448 449        if enable_async is not missing:450            rv.is_async = enable_async451 452        return _environment_config_check(rv)453 454    @property455    def lexer(self) -> Lexer:456        """The lexer for this environment."""457        return get_lexer(self)458 459    def iter_extensions(self) -> t.Iterator["Extension"]:460        """Iterates over the extensions by priority."""461        return iter(sorted(self.extensions.values(), key=lambda x: x.priority))462 463    def getitem(464        self, obj: t.Any, argument: t.Union[str, t.Any]465    ) -> t.Union[t.Any, Undefined]:466        """Get an item or attribute of an object but prefer the item."""467        try:468            return obj[argument]469        except (AttributeError, TypeError, LookupError):470            if isinstance(argument, str):471                try:472                    attr = str(argument)473                except Exception:474                    pass475                else:476                    try:477                        return getattr(obj, attr)478                    except AttributeError:479                        pass480            return self.undefined(obj=obj, name=argument)481 482    def getattr(self, obj: t.Any, attribute: str) -> t.Any:483        """Get an item or attribute of an object but prefer the attribute.484        Unlike :meth:`getitem` the attribute *must* be a string.485        """486        try:487            return getattr(obj, attribute)488        except AttributeError:489            pass490        try:491            return obj[attribute]492        except (TypeError, LookupError, AttributeError):493            return self.undefined(obj=obj, name=attribute)494 495    def _filter_test_common(496        self,497        name: t.Union[str, Undefined],498        value: t.Any,499        args: t.Optional[t.Sequence[t.Any]],500        kwargs: t.Optional[t.Mapping[str, t.Any]],501        context: t.Optional[Context],502        eval_ctx: t.Optional[EvalContext],503        is_filter: bool,504    ) -> t.Any:505        if is_filter:506            env_map = self.filters507            type_name = "filter"508        else:509            env_map = self.tests510            type_name = "test"511 512        func = env_map.get(name)  # type: ignore513 514        if func is None:515            msg = f"No {type_name} named {name!r}."516 517            if isinstance(name, Undefined):518                try:519                    name._fail_with_undefined_error()520                except Exception as e:521                    msg = f"{msg} ({e}; did you forget to quote the callable name?)"522 523            raise TemplateRuntimeError(msg)524 525        args = [value, *(args if args is not None else ())]526        kwargs = kwargs if kwargs is not None else {}527        pass_arg = _PassArg.from_obj(func)528 529        if pass_arg is _PassArg.context:530            if context is None:531                raise TemplateRuntimeError(532                    f"Attempted to invoke a context {type_name} without context."533                )534 535            args.insert(0, context)536        elif pass_arg is _PassArg.eval_context:537            if eval_ctx is None:538                if context is not None:539                    eval_ctx = context.eval_ctx540                else:541                    eval_ctx = EvalContext(self)542 543            args.insert(0, eval_ctx)544        elif pass_arg is _PassArg.environment:545            args.insert(0, self)546 547        return func(*args, **kwargs)548 549    def call_filter(550        self,551        name: str,552        value: t.Any,553        args: t.Optional[t.Sequence[t.Any]] = None,554        kwargs: t.Optional[t.Mapping[str, t.Any]] = None,555        context: t.Optional[Context] = None,556        eval_ctx: t.Optional[EvalContext] = None,557    ) -> t.Any:558        """Invoke a filter on a value the same way the compiler does.559 560        This might return a coroutine if the filter is running from an561        environment in async mode and the filter supports async562        execution. It's your responsibility to await this if needed.563 564        .. versionadded:: 2.7565        """566        return self._filter_test_common(567            name, value, args, kwargs, context, eval_ctx, True568        )569 570    def call_test(571        self,572        name: str,573        value: t.Any,574        args: t.Optional[t.Sequence[t.Any]] = None,575        kwargs: t.Optional[t.Mapping[str, t.Any]] = None,576        context: t.Optional[Context] = None,577        eval_ctx: t.Optional[EvalContext] = None,578    ) -> t.Any:579        """Invoke a test on a value the same way the compiler does.580 581        This might return a coroutine if the test is running from an582        environment in async mode and the test supports async execution.583        It's your responsibility to await this if needed.584 585        .. versionchanged:: 3.0586            Tests support ``@pass_context``, etc. decorators. Added587            the ``context`` and ``eval_ctx`` parameters.588 589        .. versionadded:: 2.7590        """591        return self._filter_test_common(592            name, value, args, kwargs, context, eval_ctx, False593        )594 595    @internalcode596    def parse(597        self,598        source: str,599        name: t.Optional[str] = None,600        filename: t.Optional[str] = None,601    ) -> nodes.Template:602        """Parse the sourcecode and return the abstract syntax tree.  This603        tree of nodes is used by the compiler to convert the template into604        executable source- or bytecode.  This is useful for debugging or to605        extract information from templates.606 607        If you are :ref:`developing Jinja extensions <writing-extensions>`608        this gives you a good overview of the node tree generated.609        """610        try:611            return self._parse(source, name, filename)612        except TemplateSyntaxError:613            self.handle_exception(source=source)614 615    def _parse(616        self, source: str, name: t.Optional[str], filename: t.Optional[str]617    ) -> nodes.Template:618        """Internal parsing function used by `parse` and `compile`."""619        return Parser(self, source, name, filename).parse()620 621    def lex(622        self,623        source: str,624        name: t.Optional[str] = None,625        filename: t.Optional[str] = None,626    ) -> t.Iterator[t.Tuple[int, str, str]]:627        """Lex the given sourcecode and return a generator that yields628        tokens as tuples in the form ``(lineno, token_type, value)``.629        This can be useful for :ref:`extension development <writing-extensions>`630        and debugging templates.631 632        This does not perform preprocessing.  If you want the preprocessing633        of the extensions to be applied you have to filter source through634        the :meth:`preprocess` method.635        """636        source = str(source)637        try:638            return self.lexer.tokeniter(source, name, filename)639        except TemplateSyntaxError:640            self.handle_exception(source=source)641 642    def preprocess(643        self,644        source: str,645        name: t.Optional[str] = None,646        filename: t.Optional[str] = None,647    ) -> str:648        """Preprocesses the source with all extensions.  This is automatically649        called for all parsing and compiling methods but *not* for :meth:`lex`650        because there you usually only want the actual source tokenized.651        """652        return reduce(653            lambda s, e: e.preprocess(s, name, filename),654            self.iter_extensions(),655            str(source),656        )657 658    def _tokenize(659        self,660        source: str,661        name: t.Optional[str],662        filename: t.Optional[str] = None,663        state: t.Optional[str] = None,664    ) -> TokenStream:665        """Called by the parser to do the preprocessing and filtering666        for all the extensions.  Returns a :class:`~jinja2.lexer.TokenStream`.667        """668        source = self.preprocess(source, name, filename)669        stream = self.lexer.tokenize(source, name, filename, state)670 671        for ext in self.iter_extensions():672            stream = ext.filter_stream(stream)  # type: ignore673 674            if not isinstance(stream, TokenStream):675                stream = TokenStream(stream, name, filename)676 677        return stream678 679    def _generate(680        self,681        source: nodes.Template,682        name: t.Optional[str],683        filename: t.Optional[str],684        defer_init: bool = False,685    ) -> str:686        """Internal hook that can be overridden to hook a different generate687        method in.688 689        .. versionadded:: 2.5690        """691        return generate(  # type: ignore692            source,693            self,694            name,695            filename,696            defer_init=defer_init,697            optimized=self.optimized,698        )699 700    def _compile(self, source: str, filename: str) -> CodeType:701        """Internal hook that can be overridden to hook a different compile702        method in.703 704        .. versionadded:: 2.5705        """706        return compile(source, filename, "exec")707 708    @typing.overload709    def compile(  # type: ignore710        self,711        source: t.Union[str, nodes.Template],712        name: t.Optional[str] = None,713        filename: t.Optional[str] = None,714        raw: "te.Literal[False]" = False,715        defer_init: bool = False,716    ) -> CodeType: ...717 718    @typing.overload719    def compile(720        self,721        source: t.Union[str, nodes.Template],722        name: t.Optional[str] = None,723        filename: t.Optional[str] = None,724        raw: "te.Literal[True]" = ...,725        defer_init: bool = False,726    ) -> str: ...727 728    @internalcode729    def compile(730        self,731        source: t.Union[str, nodes.Template],732        name: t.Optional[str] = None,733        filename: t.Optional[str] = None,734        raw: bool = False,735        defer_init: bool = False,736    ) -> t.Union[str, CodeType]:737        """Compile a node or template source code.  The `name` parameter is738        the load name of the template after it was joined using739        :meth:`join_path` if necessary, not the filename on the file system.740        the `filename` parameter is the estimated filename of the template on741        the file system.  If the template came from a database or memory this742        can be omitted.743 744        The return value of this method is a python code object.  If the `raw`745        parameter is `True` the return value will be a string with python746        code equivalent to the bytecode returned otherwise.  This method is747        mainly used internally.748 749        `defer_init` is use internally to aid the module code generator.  This750        causes the generated code to be able to import without the global751        environment variable to be set.752 753        .. versionadded:: 2.4754           `defer_init` parameter added.755        """756        source_hint = None757        try:758            if isinstance(source, str):759                source_hint = source760                source = self._parse(source, name, filename)761            source = self._generate(source, name, filename, defer_init=defer_init)762            if raw:763                return source764            if filename is None:765                filename = "<template>"766            return self._compile(source, filename)767        except TemplateSyntaxError:768            self.handle_exception(source=source_hint)769 770    def compile_expression(771        self, source: str, undefined_to_none: bool = True772    ) -> "TemplateExpression":773        """A handy helper method that returns a callable that accepts keyword774        arguments that appear as variables in the expression.  If called it775        returns the result of the expression.776 777        This is useful if applications want to use the same rules as Jinja778        in template "configuration files" or similar situations.779 780        Example usage:781 782        >>> env = Environment()783        >>> expr = env.compile_expression('foo == 42')784        >>> expr(foo=23)785        False786        >>> expr(foo=42)787        True788 789        Per default the return value is converted to `None` if the790        expression returns an undefined value.  This can be changed791        by setting `undefined_to_none` to `False`.792 793        >>> env.compile_expression('var')() is None794        True795        >>> env.compile_expression('var', undefined_to_none=False)()796        Undefined797 798        .. versionadded:: 2.1799        """800        parser = Parser(self, source, state="variable")801        try:802            expr = parser.parse_expression()803            if not parser.stream.eos:804                raise TemplateSyntaxError(805                    "chunk after expression", parser.stream.current.lineno, None, None806                )807            expr.set_environment(self)808        except TemplateSyntaxError:809            self.handle_exception(source=source)810 811        body = [nodes.Assign(nodes.Name("result", "store"), expr, lineno=1)]812        template = self.from_string(nodes.Template(body, lineno=1))813        return TemplateExpression(template, undefined_to_none)814 815    def compile_templates(816        self,817        target: t.Union[str, "os.PathLike[str]"],818        extensions: t.Optional[t.Collection[str]] = None,819        filter_func: t.Optional[t.Callable[[str], bool]] = None,820        zip: t.Optional[str] = "deflated",821        log_function: t.Optional[t.Callable[[str], None]] = None,822        ignore_errors: bool = True,823    ) -> None:824        """Finds all the templates the loader can find, compiles them825        and stores them in `target`.  If `zip` is `None`, instead of in a826        zipfile, the templates will be stored in a directory.827        By default a deflate zip algorithm is used. To switch to828        the stored algorithm, `zip` can be set to ``'stored'``.829 830        `extensions` and `filter_func` are passed to :meth:`list_templates`.831        Each template returned will be compiled to the target folder or832        zipfile.833 834        By default template compilation errors are ignored.  In case a835        log function is provided, errors are logged.  If you want template836        syntax errors to abort the compilation you can set `ignore_errors`837        to `False` and you will get an exception on syntax errors.838 839        .. versionadded:: 2.4840        """841        from .loaders import ModuleLoader842 843        if log_function is None:844 845            def log_function(x: str) -> None:846                pass847 848        assert log_function is not None849        assert self.loader is not None, "No loader configured."850 851        def write_file(filename: str, data: str) -> None:852            if zip:853                info = ZipInfo(filename)854                info.external_attr = 0o755 << 16855                zip_file.writestr(info, data)856            else:857                with open(os.path.join(target, filename), "wb") as f:858                    f.write(data.encode("utf8"))859 860        if zip is not None:861            from zipfile import ZIP_DEFLATED862            from zipfile import ZIP_STORED863            from zipfile import ZipFile864            from zipfile import ZipInfo865 866            zip_file = ZipFile(867                target, "w", dict(deflated=ZIP_DEFLATED, stored=ZIP_STORED)[zip]868            )869            log_function(f"Compiling into Zip archive {target!r}")870        else:871            if not os.path.isdir(target):872                os.makedirs(target)873            log_function(f"Compiling into folder {target!r}")874 875        try:876            for name in self.list_templates(extensions, filter_func):877                source, filename, _ = self.loader.get_source(self, name)878                try:879                    code = self.compile(source, name, filename, True, True)880                except TemplateSyntaxError as e:881                    if not ignore_errors:882                        raise883                    log_function(f'Could not compile "{name}": {e}')884                    continue885 886                filename = ModuleLoader.get_module_filename(name)887 888                write_file(filename, code)889                log_function(f'Compiled "{name}" as {filename}')890        finally:891            if zip:892                zip_file.close()893 894        log_function("Finished compiling templates")895 896    def list_templates(897        self,898        extensions: t.Optional[t.Collection[str]] = None,899        filter_func: t.Optional[t.Callable[[str], bool]] = None,900    ) -> t.List[str]:901        """Returns a list of templates for this environment.  This requires902        that the loader supports the loader's903        :meth:`~BaseLoader.list_templates` method.904 905        If there are other files in the template folder besides the906        actual templates, the returned list can be filtered.  There are two907        ways: either `extensions` is set to a list of file extensions for908        templates, or a `filter_func` can be provided which is a callable that909        is passed a template name and should return `True` if it should end up910        in the result list.911 912        If the loader does not support that, a :exc:`TypeError` is raised.913 914        .. versionadded:: 2.4915        """916        assert self.loader is not None, "No loader configured."917        names = self.loader.list_templates()918 919        if extensions is not None:920            if filter_func is not None:921                raise TypeError(922                    "either extensions or filter_func can be passed, but not both"923                )924 925            def filter_func(x: str) -> bool:926                return "." in x and x.rsplit(".", 1)[1] in extensions927 928        if filter_func is not None:929            names = [name for name in names if filter_func(name)]930 931        return names932 933    def handle_exception(self, source: t.Optional[str] = None) -> "te.NoReturn":934        """Exception handling helper.  This is used internally to either raise935        rewritten exceptions or return a rendered traceback for the template.936        """937        from .debug import rewrite_traceback_stack938 939        raise rewrite_traceback_stack(source=source)940 941    def join_path(self, template: str, parent: str) -> str:942        """Join a template with the parent.  By default all the lookups are943        relative to the loader root so this method returns the `template`944        parameter unchanged, but if the paths should be relative to the945        parent template, this function can be used to calculate the real946        template name.947 948        Subclasses may override this method and implement template path949        joining here.950        """951        return template952 953    @internalcode954    def _load_template(955        self, name: str, globals: t.Optional[t.MutableMapping[str, t.Any]]956    ) -> "Template":957        if self.loader is None:958            raise TypeError("no loader for this environment specified")959        cache_key = (weakref.ref(self.loader), name)960        if self.cache is not None:961            template = self.cache.get(cache_key)962            if template is not None and (963                not self.auto_reload or template.is_up_to_date964            ):965                # template.globals is a ChainMap, modifying it will only966                # affect the template, not the environment globals.967                if globals:968                    template.globals.update(globals)969 970                return template971 972        template = self.loader.load(self, name, self.make_globals(globals))973 974        if self.cache is not None:975            self.cache[cache_key] = template976        return template977 978    @internalcode979    def get_template(980        self,981        name: t.Union[str, "Template"],982        parent: t.Optional[str] = None,983        globals: t.Optional[t.MutableMapping[str, t.Any]] = None,984    ) -> "Template":985        """Load a template by name with :attr:`loader` and return a986        :class:`Template`. If the template does not exist a987        :exc:`TemplateNotFound` exception is raised.988 989        :param name: Name of the template to load. When loading990            templates from the filesystem, "/" is used as the path991            separator, even on Windows.992        :param parent: The name of the parent template importing this993            template. :meth:`join_path` can be used to implement name994            transformations with this.995        :param globals: Extend the environment :attr:`globals` with996            these extra variables available for all renders of this997            template. If the template has already been loaded and998            cached, its globals are updated with any new items.999 1000        .. versionchanged:: 3.01001            If a template is loaded from cache, ``globals`` will update1002            the template's globals instead of ignoring the new values.1003 1004        .. versionchanged:: 2.41005            If ``name`` is a :class:`Template` object it is returned1006            unchanged.1007        """1008        if isinstance(name, Template):1009            return name1010        if parent is not None:1011            name = self.join_path(name, parent)1012 1013        return self._load_template(name, globals)1014 1015    @internalcode1016    def select_template(1017        self,1018        names: t.Iterable[t.Union[str, "Template"]],1019        parent: t.Optional[str] = None,1020        globals: t.Optional[t.MutableMapping[str, t.Any]] = None,1021    ) -> "Template":1022        """Like :meth:`get_template`, but tries loading multiple names.1023        If none of the names can be loaded a :exc:`TemplatesNotFound`1024        exception is raised.1025 1026        :param names: List of template names to try loading in order.1027        :param parent: The name of the parent template importing this1028            template. :meth:`join_path` can be used to implement name1029            transformations with this.1030        :param globals: Extend the environment :attr:`globals` with1031            these extra variables available for all renders of this1032            template. If the template has already been loaded and1033            cached, its globals are updated with any new items.1034 1035        .. versionchanged:: 3.01036            If a template is loaded from cache, ``globals`` will update1037            the template's globals instead of ignoring the new values.1038 1039        .. versionchanged:: 2.111040            If ``names`` is :class:`Undefined`, an :exc:`UndefinedError`1041            is raised instead. If no templates were found and ``names``1042            contains :class:`Undefined`, the message is more helpful.1043 1044        .. versionchanged:: 2.41045            If ``names`` contains a :class:`Template` object it is1046            returned unchanged.1047 1048        .. versionadded:: 2.31049        """1050        if isinstance(names, Undefined):1051            names._fail_with_undefined_error()1052 1053        if not names:1054            raise TemplatesNotFound(1055                message="Tried to select from an empty list of templates."1056            )1057 1058        for name in names:1059            if isinstance(name, Template):1060                return name1061            if parent is not None:1062                name = self.join_path(name, parent)1063            try:1064                return self._load_template(name, globals)1065            except (TemplateNotFound, UndefinedError):1066                pass1067        raise TemplatesNotFound(names)  # type: ignore1068 1069    @internalcode1070    def get_or_select_template(1071        self,1072        template_name_or_list: t.Union[1073            str, "Template", t.List[t.Union[str, "Template"]]1074        ],1075        parent: t.Optional[str] = None,1076        globals: t.Optional[t.MutableMapping[str, t.Any]] = None,1077    ) -> "Template":1078        """Use :meth:`select_template` if an iterable of template names1079        is given, or :meth:`get_template` if one name is given.1080 1081        .. versionadded:: 2.31082        """1083        if isinstance(template_name_or_list, (str, Undefined)):1084            return self.get_template(template_name_or_list, parent, globals)1085        elif isinstance(template_name_or_list, Template):1086            return template_name_or_list1087        return self.select_template(template_name_or_list, parent, globals)1088 1089    def from_string(1090        self,1091        source: t.Union[str, nodes.Template],1092        globals: t.Optional[t.MutableMapping[str, t.Any]] = None,1093        template_class: t.Optional[t.Type["Template"]] = None,1094    ) -> "Template":1095        """Load a template from a source string without using1096        :attr:`loader`.1097 1098        :param source: Jinja source to compile into a template.1099        :param globals: Extend the environment :attr:`globals` with1100            these extra variables available for all renders of this1101            template. If the template has already been loaded and1102            cached, its globals are updated with any new items.1103        :param template_class: Return an instance of this1104            :class:`Template` class.1105        """1106        gs = self.make_globals(globals)1107        cls = template_class or self.template_class1108        return cls.from_code(self, self.compile(source), gs, None)1109 1110    def make_globals(1111        self, d: t.Optional[t.MutableMapping[str, t.Any]]1112    ) -> t.MutableMapping[str, t.Any]:1113        """Make the globals map for a template. Any given template1114        globals overlay the environment :attr:`globals`.1115 1116        Returns a :class:`collections.ChainMap`. This allows any changes1117        to a template's globals to only affect that template, while1118        changes to the environment's globals are still reflected.1119        However, avoid modifying any globals after a template is loaded.1120 1121        :param d: Dict of template-specific globals.1122 1123        .. versionchanged:: 3.01124            Use :class:`collections.ChainMap` to always prevent mutating1125            environment globals.1126        """1127        if d is None:1128            d = {}1129 1130        return ChainMap(d, self.globals)1131 1132 1133class Template:1134    """A compiled template that can be rendered.1135 1136    Use the methods on :class:`Environment` to create or load templates.1137    The environment is used to configure how templates are compiled and1138    behave.1139 1140    It is also possible to create a template object directly. This is1141    not usually recommended. The constructor takes most of the same1142    arguments as :class:`Environment`. All templates created with the1143    same environment arguments share the same ephemeral ``Environment``1144    instance behind the scenes.1145 1146    A template object should be considered immutable. Modifications on1147    the object are not supported.1148    """1149 1150    #: Type of environment to create when creating a template directly1151    #: rather than through an existing environment.1152    environment_class: t.Type[Environment] = Environment1153 1154    environment: Environment1155    globals: t.MutableMapping[str, t.Any]1156    name: t.Optional[str]1157    filename: t.Optional[str]1158    blocks: t.Dict[str, t.Callable[[Context], t.Iterator[str]]]1159    root_render_func: t.Callable[[Context], t.Iterator[str]]1160    _module: t.Optional["TemplateModule"]1161    _debug_info: str1162    _uptodate: t.Optional[t.Callable[[], bool]]1163 1164    def __new__(1165        cls,1166        source: t.Union[str, nodes.Template],1167        block_start_string: str = BLOCK_START_STRING,1168        block_end_string: str = BLOCK_END_STRING,1169        variable_start_string: str = VARIABLE_START_STRING,1170        variable_end_string: str = VARIABLE_END_STRING,1171        comment_start_string: str = COMMENT_START_STRING,1172        comment_end_string: str = COMMENT_END_STRING,1173        line_statement_prefix: t.Optional[str] = LINE_STATEMENT_PREFIX,1174        line_comment_prefix: t.Optional[str] = LINE_COMMENT_PREFIX,1175        trim_blocks: bool = TRIM_BLOCKS,1176        lstrip_blocks: bool = LSTRIP_BLOCKS,1177        newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = NEWLINE_SEQUENCE,1178        keep_trailing_newline: bool = KEEP_TRAILING_NEWLINE,1179        extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = (),1180        optimized: bool = True,1181        undefined: t.Type[Undefined] = Undefined,1182        finalize: t.Optional[t.Callable[..., t.Any]] = None,1183        autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = False,1184        enable_async: bool = False,1185    ) -> t.Any:  # it returns a `Template`, but this breaks the sphinx build...1186        env = get_spontaneous_environment(1187            cls.environment_class,  # type: ignore1188            block_start_string,1189            block_end_string,1190            variable_start_string,1191            variable_end_string,1192            comment_start_string,1193            comment_end_string,1194            line_statement_prefix,1195            line_comment_prefix,1196            trim_blocks,1197            lstrip_blocks,1198            newline_sequence,1199            keep_trailing_newline,1200            frozenset(extensions),

Showing the first 1,200 of 1676 lines. Download the file for the rest.

codekingpro/portable-devtools · Team Ai