Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/flask/templating.py: 26%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
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 .sansio.app import App
21 from .sansio.scaffold import Scaffold
24def _default_template_ctx_processor() -> dict[str, t.Any]:
25 """Default template context processor. Injects `request`,
26 `session` and `g`.
27 """
28 appctx = _cv_app.get(None)
29 reqctx = _cv_request.get(None)
30 rv: dict[str, t.Any] = {}
31 if appctx is not None:
32 rv["g"] = appctx.g
33 if reqctx is not None:
34 rv["request"] = reqctx.request
35 rv["session"] = reqctx.session
36 return rv
39class Environment(BaseEnvironment):
40 """Works like a regular Jinja2 environment but has some additional
41 knowledge of how Flask's blueprint works so that it can prepend the
42 name of the blueprint to referenced templates if necessary.
43 """
45 def __init__(self, app: App, **options: t.Any) -> None:
46 if "loader" not in options:
47 options["loader"] = app.create_global_jinja_loader()
48 BaseEnvironment.__init__(self, **options)
49 self.app = app
52class DispatchingJinjaLoader(BaseLoader):
53 """A loader that looks for templates in the application and all
54 the blueprint folders.
55 """
57 def __init__(self, app: App) -> None:
58 self.app = app
60 def get_source(
61 self, environment: BaseEnvironment, template: str
62 ) -> tuple[str, str | None, t.Callable[[], bool] | None]:
63 if self.app.config["EXPLAIN_TEMPLATE_LOADING"]:
64 return self._get_source_explained(environment, template)
65 return self._get_source_fast(environment, template)
67 def _get_source_explained(
68 self, environment: BaseEnvironment, template: str
69 ) -> tuple[str, str | None, t.Callable[[], bool] | None]:
70 attempts = []
71 rv: tuple[str, str | None, t.Callable[[], bool] | None] | None
72 trv: None | (tuple[str, str | None, t.Callable[[], bool] | None]) = None
74 for srcobj, loader in self._iter_loaders(template):
75 try:
76 rv = loader.get_source(environment, template)
77 if trv is None:
78 trv = rv
79 except TemplateNotFound:
80 rv = None
81 attempts.append((loader, srcobj, rv))
83 from .debughelpers import explain_template_loading_attempts
85 explain_template_loading_attempts(self.app, template, attempts)
87 if trv is not None:
88 return trv
89 raise TemplateNotFound(template)
91 def _get_source_fast(
92 self, environment: BaseEnvironment, template: str
93 ) -> tuple[str, str | None, t.Callable[[], bool] | None]:
94 for _srcobj, loader in self._iter_loaders(template):
95 try:
96 return loader.get_source(environment, template)
97 except TemplateNotFound:
98 continue
99 raise TemplateNotFound(template)
101 def _iter_loaders(self, template: str) -> t.Iterator[tuple[Scaffold, BaseLoader]]:
102 loader = self.app.jinja_loader
103 if loader is not None:
104 yield self.app, loader
106 for blueprint in self.app.iter_blueprints():
107 loader = blueprint.jinja_loader
108 if loader is not None:
109 yield blueprint, loader
111 def list_templates(self) -> list[str]:
112 result = set()
113 loader = self.app.jinja_loader
114 if loader is not None:
115 result.update(loader.list_templates())
117 for blueprint in self.app.iter_blueprints():
118 loader = blueprint.jinja_loader
119 if loader is not None:
120 for template in loader.list_templates():
121 result.add(template)
123 return list(result)
126def _render(app: Flask, template: Template, context: dict[str, t.Any]) -> str:
127 app.update_template_context(context)
128 before_render_template.send(
129 app, _async_wrapper=app.ensure_sync, template=template, context=context
130 )
131 rv = template.render(context)
132 template_rendered.send(
133 app, _async_wrapper=app.ensure_sync, template=template, context=context
134 )
135 return rv
138def render_template(
139 template_name_or_list: str | Template | list[str | Template],
140 **context: t.Any,
141) -> str:
142 """Render a template by name with the given context.
144 :param template_name_or_list: The name of the template to render. If
145 a list is given, the first name to exist will be rendered.
146 :param context: The variables to make available in the template.
147 """
148 app = current_app._get_current_object() # type: ignore[attr-defined]
149 template = app.jinja_env.get_or_select_template(template_name_or_list)
150 return _render(app, template, context)
153def render_template_string(source: str, **context: t.Any) -> str:
154 """Render a template from the given source string with the given
155 context.
157 :param source: The source code of the template to render.
158 :param context: The variables to make available in the template.
159 """
160 app = current_app._get_current_object() # type: ignore[attr-defined]
161 template = app.jinja_env.from_string(source)
162 return _render(app, template, context)
165def _stream(
166 app: Flask, template: Template, context: dict[str, t.Any]
167) -> t.Iterator[str]:
168 app.update_template_context(context)
169 before_render_template.send(
170 app, _async_wrapper=app.ensure_sync, template=template, context=context
171 )
173 def generate() -> t.Iterator[str]:
174 yield from template.generate(context)
175 template_rendered.send(
176 app, _async_wrapper=app.ensure_sync, template=template, context=context
177 )
179 rv = generate()
181 # If a request context is active, keep it while generating.
182 if request:
183 rv = stream_with_context(rv)
185 return rv
188def stream_template(
189 template_name_or_list: str | Template | list[str | Template],
190 **context: t.Any,
191) -> t.Iterator[str]:
192 """Render a template by name with the given context as a stream.
193 This returns an iterator of strings, which can be used as a
194 streaming response from a view.
196 :param template_name_or_list: The name of the template to render. If
197 a list is given, the first name to exist will be rendered.
198 :param context: The variables to make available in the template.
200 .. versionadded:: 2.2
201 """
202 app = current_app._get_current_object() # type: ignore[attr-defined]
203 template = app.jinja_env.get_or_select_template(template_name_or_list)
204 return _stream(app, template, context)
207def stream_template_string(source: str, **context: t.Any) -> t.Iterator[str]:
208 """Render a template from the given source string with the given
209 context as a stream. This returns an iterator of strings, which can
210 be used as a streaming response from a view.
212 :param source: The source code of the template to render.
213 :param context: The variables to make available in the template.
215 .. versionadded:: 2.2
216 """
217 app = current_app._get_current_object() # type: ignore[attr-defined]
218 template = app.jinja_env.from_string(source)
219 return _stream(app, template, context)