Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/flask/templating.py: 26%
110 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-09 06:08 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-09 06:08 +0000
1from __future__ import annotations
3import typing as t
5from jinja2 import BaseLoader
6from jinja2 import Environment as BaseEnvironment
7from jinja2 import Template
8from jinja2 import TemplateNotFound
10from .globals import _cv_app
11from .globals import _cv_request
12from .globals import current_app
13from .globals import request
14from .helpers import stream_with_context
15from .signals import before_render_template
16from .signals import template_rendered
18if t.TYPE_CHECKING: # pragma: no cover
19 from .app import Flask
20 from .scaffold import Scaffold
23def _default_template_ctx_processor() -> dict[str, t.Any]:
24 """Default template context processor. Injects `request`,
25 `session` and `g`.
26 """
27 appctx = _cv_app.get(None)
28 reqctx = _cv_request.get(None)
29 rv: dict[str, t.Any] = {}
30 if appctx is not None:
31 rv["g"] = appctx.g
32 if reqctx is not None:
33 rv["request"] = reqctx.request
34 rv["session"] = reqctx.session
35 return rv
38class Environment(BaseEnvironment):
39 """Works like a regular Jinja2 environment but has some additional
40 knowledge of how Flask's blueprint works so that it can prepend the
41 name of the blueprint to referenced templates if necessary.
42 """
44 def __init__(self, app: Flask, **options: t.Any) -> None:
45 if "loader" not in options:
46 options["loader"] = app.create_global_jinja_loader()
47 BaseEnvironment.__init__(self, **options)
48 self.app = app
51class DispatchingJinjaLoader(BaseLoader):
52 """A loader that looks for templates in the application and all
53 the blueprint folders.
54 """
56 def __init__(self, app: Flask) -> None:
57 self.app = app
59 def get_source( # type: ignore
60 self, environment: Environment, template: str
61 ) -> tuple[str, str | None, t.Callable | None]:
62 if self.app.config["EXPLAIN_TEMPLATE_LOADING"]:
63 return self._get_source_explained(environment, template)
64 return self._get_source_fast(environment, template)
66 def _get_source_explained(
67 self, environment: Environment, template: str
68 ) -> tuple[str, str | None, t.Callable | None]:
69 attempts = []
70 rv: tuple[str, str | None, t.Callable[[], bool] | None] | None
71 trv: None | (tuple[str, str | None, t.Callable[[], bool] | None]) = None
73 for srcobj, loader in self._iter_loaders(template):
74 try:
75 rv = loader.get_source(environment, template)
76 if trv is None:
77 trv = rv
78 except TemplateNotFound:
79 rv = None
80 attempts.append((loader, srcobj, rv))
82 from .debughelpers import explain_template_loading_attempts
84 explain_template_loading_attempts(self.app, template, attempts)
86 if trv is not None:
87 return trv
88 raise TemplateNotFound(template)
90 def _get_source_fast(
91 self, environment: Environment, template: str
92 ) -> tuple[str, str | None, t.Callable | None]:
93 for _srcobj, loader in self._iter_loaders(template):
94 try:
95 return loader.get_source(environment, template)
96 except TemplateNotFound:
97 continue
98 raise TemplateNotFound(template)
100 def _iter_loaders(
101 self, template: str
102 ) -> t.Generator[tuple[Scaffold, BaseLoader], None, None]:
103 loader = self.app.jinja_loader
104 if loader is not None:
105 yield self.app, loader
107 for blueprint in self.app.iter_blueprints():
108 loader = blueprint.jinja_loader
109 if loader is not None:
110 yield blueprint, loader
112 def list_templates(self) -> list[str]:
113 result = set()
114 loader = self.app.jinja_loader
115 if loader is not None:
116 result.update(loader.list_templates())
118 for blueprint in self.app.iter_blueprints():
119 loader = blueprint.jinja_loader
120 if loader is not None:
121 for template in loader.list_templates():
122 result.add(template)
124 return list(result)
127def _render(app: Flask, template: Template, context: dict[str, t.Any]) -> str:
128 app.update_template_context(context)
129 before_render_template.send(
130 app, _async_wrapper=app.ensure_sync, template=template, context=context
131 )
132 rv = template.render(context)
133 template_rendered.send(
134 app, _async_wrapper=app.ensure_sync, template=template, context=context
135 )
136 return rv
139def render_template(
140 template_name_or_list: str | Template | list[str | Template],
141 **context: t.Any,
142) -> str:
143 """Render a template by name with the given context.
145 :param template_name_or_list: The name of the template to render. If
146 a list is given, the first name to exist will be rendered.
147 :param context: The variables to make available in the template.
148 """
149 app = current_app._get_current_object() # type: ignore[attr-defined]
150 template = app.jinja_env.get_or_select_template(template_name_or_list)
151 return _render(app, template, context)
154def render_template_string(source: str, **context: t.Any) -> str:
155 """Render a template from the given source string with the given
156 context.
158 :param source: The source code of the template to render.
159 :param context: The variables to make available in the template.
160 """
161 app = current_app._get_current_object() # type: ignore[attr-defined]
162 template = app.jinja_env.from_string(source)
163 return _render(app, template, context)
166def _stream(
167 app: Flask, template: Template, context: dict[str, t.Any]
168) -> t.Iterator[str]:
169 app.update_template_context(context)
170 before_render_template.send(
171 app, _async_wrapper=app.ensure_sync, template=template, context=context
172 )
174 def generate() -> t.Iterator[str]:
175 yield from template.generate(context)
176 template_rendered.send(
177 app, _async_wrapper=app.ensure_sync, template=template, context=context
178 )
180 rv = generate()
182 # If a request context is active, keep it while generating.
183 if request:
184 rv = stream_with_context(rv)
186 return rv
189def stream_template(
190 template_name_or_list: str | Template | list[str | Template],
191 **context: t.Any,
192) -> t.Iterator[str]:
193 """Render a template by name with the given context as a stream.
194 This returns an iterator of strings, which can be used as a
195 streaming response from a view.
197 :param template_name_or_list: The name of the template to render. If
198 a list is given, the first name to exist will be rendered.
199 :param context: The variables to make available in the template.
201 .. versionadded:: 2.2
202 """
203 app = current_app._get_current_object() # type: ignore[attr-defined]
204 template = app.jinja_env.get_or_select_template(template_name_or_list)
205 return _stream(app, template, context)
208def stream_template_string(source: str, **context: t.Any) -> t.Iterator[str]:
209 """Render a template from the given source string with the given
210 context as a stream. This returns an iterator of strings, which can
211 be used as a streaming response from a view.
213 :param source: The source code of the template to render.
214 :param context: The variables to make available in the template.
216 .. versionadded:: 2.2
217 """
218 app = current_app._get_current_object() # type: ignore[attr-defined]
219 template = app.jinja_env.from_string(source)
220 return _stream(app, template, context)