Spaces:
Runtime error
Runtime error
| from __future__ import annotations | |
| import typing as t | |
| from jinja2 import BaseLoader | |
| from jinja2 import Environment as BaseEnvironment | |
| from jinja2 import Template | |
| from jinja2 import TemplateNotFound | |
| from .globals import _cv_app | |
| from .globals import _cv_request | |
| from .globals import current_app | |
| from .globals import request | |
| from .helpers import stream_with_context | |
| from .signals import before_render_template | |
| from .signals import template_rendered | |
| if t.TYPE_CHECKING: # pragma: no cover | |
| from .app import Flask | |
| from .scaffold import Scaffold | |
| def _default_template_ctx_processor() -> dict[str, t.Any]: | |
| """Default template context processor. Injects `request`, | |
| `session` and `g`. | |
| """ | |
| appctx = _cv_app.get(None) | |
| reqctx = _cv_request.get(None) | |
| rv: dict[str, t.Any] = {} | |
| if appctx is not None: | |
| rv["g"] = appctx.g | |
| if reqctx is not None: | |
| rv["request"] = reqctx.request | |
| rv["session"] = reqctx.session | |
| return rv | |
| class Environment(BaseEnvironment): | |
| """Works like a regular Jinja2 environment but has some additional | |
| knowledge of how Flask's blueprint works so that it can prepend the | |
| name of the blueprint to referenced templates if necessary. | |
| """ | |
| def __init__(self, app: Flask, **options: t.Any) -> None: | |
| if "loader" not in options: | |
| options["loader"] = app.create_global_jinja_loader() | |
| BaseEnvironment.__init__(self, **options) | |
| self.app = app | |
| class DispatchingJinjaLoader(BaseLoader): | |
| """A loader that looks for templates in the application and all | |
| the blueprint folders. | |
| """ | |
| def __init__(self, app: Flask) -> None: | |
| self.app = app | |
| def get_source( # type: ignore | |
| self, environment: Environment, template: str | |
| ) -> tuple[str, str | None, t.Callable | None]: | |
| if self.app.config["EXPLAIN_TEMPLATE_LOADING"]: | |
| return self._get_source_explained(environment, template) | |
| return self._get_source_fast(environment, template) | |
| def _get_source_explained( | |
| self, environment: Environment, template: str | |
| ) -> tuple[str, str | None, t.Callable | None]: | |
| attempts = [] | |
| rv: tuple[str, str | None, t.Callable[[], bool] | None] | None | |
| trv: None | (tuple[str, str | None, t.Callable[[], bool] | None]) = None | |
| for srcobj, loader in self._iter_loaders(template): | |
| try: | |
| rv = loader.get_source(environment, template) | |
| if trv is None: | |
| trv = rv | |
| except TemplateNotFound: | |
| rv = None | |
| attempts.append((loader, srcobj, rv)) | |
| from .debughelpers import explain_template_loading_attempts | |
| explain_template_loading_attempts(self.app, template, attempts) | |
| if trv is not None: | |
| return trv | |
| raise TemplateNotFound(template) | |
| def _get_source_fast( | |
| self, environment: Environment, template: str | |
| ) -> tuple[str, str | None, t.Callable | None]: | |
| for _srcobj, loader in self._iter_loaders(template): | |
| try: | |
| return loader.get_source(environment, template) | |
| except TemplateNotFound: | |
| continue | |
| raise TemplateNotFound(template) | |
| def _iter_loaders( | |
| self, template: str | |
| ) -> t.Generator[tuple[Scaffold, BaseLoader], None, None]: | |
| loader = self.app.jinja_loader | |
| if loader is not None: | |
| yield self.app, loader | |
| for blueprint in self.app.iter_blueprints(): | |
| loader = blueprint.jinja_loader | |
| if loader is not None: | |
| yield blueprint, loader | |
| def list_templates(self) -> list[str]: | |
| result = set() | |
| loader = self.app.jinja_loader | |
| if loader is not None: | |
| result.update(loader.list_templates()) | |
| for blueprint in self.app.iter_blueprints(): | |
| loader = blueprint.jinja_loader | |
| if loader is not None: | |
| for template in loader.list_templates(): | |
| result.add(template) | |
| return list(result) | |
| def _render(app: Flask, template: Template, context: dict[str, t.Any]) -> str: | |
| app.update_template_context(context) | |
| before_render_template.send( | |
| app, _async_wrapper=app.ensure_sync, template=template, context=context | |
| ) | |
| rv = template.render(context) | |
| template_rendered.send( | |
| app, _async_wrapper=app.ensure_sync, template=template, context=context | |
| ) | |
| return rv | |
| def render_template( | |
| template_name_or_list: str | Template | list[str | Template], | |
| **context: t.Any, | |
| ) -> str: | |
| """Render a template by name with the given context. | |
| :param template_name_or_list: The name of the template to render. If | |
| a list is given, the first name to exist will be rendered. | |
| :param context: The variables to make available in the template. | |
| """ | |
| app = current_app._get_current_object() # type: ignore[attr-defined] | |
| template = app.jinja_env.get_or_select_template(template_name_or_list) | |
| return _render(app, template, context) | |
| def render_template_string(source: str, **context: t.Any) -> str: | |
| """Render a template from the given source string with the given | |
| context. | |
| :param source: The source code of the template to render. | |
| :param context: The variables to make available in the template. | |
| """ | |
| app = current_app._get_current_object() # type: ignore[attr-defined] | |
| template = app.jinja_env.from_string(source) | |
| return _render(app, template, context) | |
| def _stream( | |
| app: Flask, template: Template, context: dict[str, t.Any] | |
| ) -> t.Iterator[str]: | |
| app.update_template_context(context) | |
| before_render_template.send( | |
| app, _async_wrapper=app.ensure_sync, template=template, context=context | |
| ) | |
| def generate() -> t.Iterator[str]: | |
| yield from template.generate(context) | |
| template_rendered.send( | |
| app, _async_wrapper=app.ensure_sync, template=template, context=context | |
| ) | |
| rv = generate() | |
| # If a request context is active, keep it while generating. | |
| if request: | |
| rv = stream_with_context(rv) | |
| return rv | |
| def stream_template( | |
| template_name_or_list: str | Template | list[str | Template], | |
| **context: t.Any, | |
| ) -> t.Iterator[str]: | |
| """Render a template by name with the given context as a stream. | |
| This returns an iterator of strings, which can be used as a | |
| streaming response from a view. | |
| :param template_name_or_list: The name of the template to render. If | |
| a list is given, the first name to exist will be rendered. | |
| :param context: The variables to make available in the template. | |
| .. versionadded:: 2.2 | |
| """ | |
| app = current_app._get_current_object() # type: ignore[attr-defined] | |
| template = app.jinja_env.get_or_select_template(template_name_or_list) | |
| return _stream(app, template, context) | |
| def stream_template_string(source: str, **context: t.Any) -> t.Iterator[str]: | |
| """Render a template from the given source string with the given | |
| context as a stream. This returns an iterator of strings, which can | |
| be used as a streaming response from a view. | |
| :param source: The source code of the template to render. | |
| :param context: The variables to make available in the template. | |
| .. versionadded:: 2.2 | |
| """ | |
| app = current_app._get_current_object() # type: ignore[attr-defined] | |
| template = app.jinja_env.from_string(source) | |
| return _stream(app, template, context) | |