]> jfr.im git - dlqueue.git/blame - venv/lib/python3.11/site-packages/flask/templating.py
init: venv aand flask
[dlqueue.git] / venv / lib / python3.11 / site-packages / flask / templating.py
CommitLineData
e0df8241
JR
1from __future__ import annotations
2
3import typing as t
4
5from jinja2 import BaseLoader
6from jinja2 import Environment as BaseEnvironment
7from jinja2 import Template
8from jinja2 import TemplateNotFound
9
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
17
18if t.TYPE_CHECKING: # pragma: no cover
19 from .app import Flask
20 from .sansio.app import App
21 from .sansio.scaffold import Scaffold
22
23
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
37
38
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 """
44
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
50
51
52class DispatchingJinjaLoader(BaseLoader):
53 """A loader that looks for templates in the application and all
54 the blueprint folders.
55 """
56
57 def __init__(self, app: App) -> None:
58 self.app = app
59
60 def get_source( # type: ignore
61 self, environment: Environment, template: str
62 ) -> tuple[str, str | None, t.Callable | 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)
66
67 def _get_source_explained(
68 self, environment: Environment, template: str
69 ) -> tuple[str, str | None, t.Callable | 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
73
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))
82
83 from .debughelpers import explain_template_loading_attempts
84
85 explain_template_loading_attempts(self.app, template, attempts)
86
87 if trv is not None:
88 return trv
89 raise TemplateNotFound(template)
90
91 def _get_source_fast(
92 self, environment: Environment, template: str
93 ) -> tuple[str, str | None, t.Callable | 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)
100
101 def _iter_loaders(
102 self, template: str
103 ) -> t.Generator[tuple[Scaffold, BaseLoader], None, None]:
104 loader = self.app.jinja_loader
105 if loader is not None:
106 yield self.app, loader
107
108 for blueprint in self.app.iter_blueprints():
109 loader = blueprint.jinja_loader
110 if loader is not None:
111 yield blueprint, loader
112
113 def list_templates(self) -> list[str]:
114 result = set()
115 loader = self.app.jinja_loader
116 if loader is not None:
117 result.update(loader.list_templates())
118
119 for blueprint in self.app.iter_blueprints():
120 loader = blueprint.jinja_loader
121 if loader is not None:
122 for template in loader.list_templates():
123 result.add(template)
124
125 return list(result)
126
127
128def _render(app: Flask, template: Template, context: dict[str, t.Any]) -> str:
129 app.update_template_context(context)
130 before_render_template.send(
131 app, _async_wrapper=app.ensure_sync, template=template, context=context
132 )
133 rv = template.render(context)
134 template_rendered.send(
135 app, _async_wrapper=app.ensure_sync, template=template, context=context
136 )
137 return rv
138
139
140def render_template(
141 template_name_or_list: str | Template | list[str | Template],
142 **context: t.Any,
143) -> str:
144 """Render a template by name with the given context.
145
146 :param template_name_or_list: The name of the template to render. If
147 a list is given, the first name to exist will be rendered.
148 :param context: The variables to make available in the template.
149 """
150 app = current_app._get_current_object() # type: ignore[attr-defined]
151 template = app.jinja_env.get_or_select_template(template_name_or_list)
152 return _render(app, template, context)
153
154
155def render_template_string(source: str, **context: t.Any) -> str:
156 """Render a template from the given source string with the given
157 context.
158
159 :param source: The source code of the template to render.
160 :param context: The variables to make available in the template.
161 """
162 app = current_app._get_current_object() # type: ignore[attr-defined]
163 template = app.jinja_env.from_string(source)
164 return _render(app, template, context)
165
166
167def _stream(
168 app: Flask, template: Template, context: dict[str, t.Any]
169) -> t.Iterator[str]:
170 app.update_template_context(context)
171 before_render_template.send(
172 app, _async_wrapper=app.ensure_sync, template=template, context=context
173 )
174
175 def generate() -> t.Iterator[str]:
176 yield from template.generate(context)
177 template_rendered.send(
178 app, _async_wrapper=app.ensure_sync, template=template, context=context
179 )
180
181 rv = generate()
182
183 # If a request context is active, keep it while generating.
184 if request:
185 rv = stream_with_context(rv)
186
187 return rv
188
189
190def stream_template(
191 template_name_or_list: str | Template | list[str | Template],
192 **context: t.Any,
193) -> t.Iterator[str]:
194 """Render a template by name with the given context as a stream.
195 This returns an iterator of strings, which can be used as a
196 streaming response from a view.
197
198 :param template_name_or_list: The name of the template to render. If
199 a list is given, the first name to exist will be rendered.
200 :param context: The variables to make available in the template.
201
202 .. versionadded:: 2.2
203 """
204 app = current_app._get_current_object() # type: ignore[attr-defined]
205 template = app.jinja_env.get_or_select_template(template_name_or_list)
206 return _stream(app, template, context)
207
208
209def stream_template_string(source: str, **context: t.Any) -> t.Iterator[str]:
210 """Render a template from the given source string with the given
211 context as a stream. This returns an iterator of strings, which can
212 be used as a streaming response from a view.
213
214 :param source: The source code of the template to render.
215 :param context: The variables to make available in the template.
216
217 .. versionadded:: 2.2
218 """
219 app = current_app._get_current_object() # type: ignore[attr-defined]
220 template = app.jinja_env.from_string(source)
221 return _stream(app, template, context)