Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/flask/templating.py: 26%
109 statements
« prev ^ index » next coverage.py v7.0.1, created at 2022-12-25 06:11 +0000
« prev ^ index » next coverage.py v7.0.1, created at 2022-12-25 06:11 +0000
1import typing as t
3from jinja2 import BaseLoader
4from jinja2 import Environment as BaseEnvironment
5from jinja2 import Template
6from jinja2 import TemplateNotFound
8from .globals import _cv_app
9from .globals import _cv_request
10from .globals import current_app
11from .globals import request
12from .helpers import stream_with_context
13from .signals import before_render_template
14from .signals import template_rendered
16if t.TYPE_CHECKING: # pragma: no cover
17 from .app import Flask
18 from .scaffold import Scaffold
21def _default_template_ctx_processor() -> t.Dict[str, t.Any]:
22 """Default template context processor. Injects `request`,
23 `session` and `g`.
24 """
25 appctx = _cv_app.get(None)
26 reqctx = _cv_request.get(None)
27 rv: t.Dict[str, t.Any] = {}
28 if appctx is not None:
29 rv["g"] = appctx.g
30 if reqctx is not None:
31 rv["request"] = reqctx.request
32 rv["session"] = reqctx.session
33 return rv
36class Environment(BaseEnvironment):
37 """Works like a regular Jinja2 environment but has some additional
38 knowledge of how Flask's blueprint works so that it can prepend the
39 name of the blueprint to referenced templates if necessary.
40 """
42 def __init__(self, app: "Flask", **options: t.Any) -> None:
43 if "loader" not in options:
44 options["loader"] = app.create_global_jinja_loader()
45 BaseEnvironment.__init__(self, **options)
46 self.app = app
49class DispatchingJinjaLoader(BaseLoader):
50 """A loader that looks for templates in the application and all
51 the blueprint folders.
52 """
54 def __init__(self, app: "Flask") -> None:
55 self.app = app
57 def get_source( # type: ignore
58 self, environment: Environment, template: str
59 ) -> t.Tuple[str, t.Optional[str], t.Optional[t.Callable]]:
60 if self.app.config["EXPLAIN_TEMPLATE_LOADING"]:
61 return self._get_source_explained(environment, template)
62 return self._get_source_fast(environment, template)
64 def _get_source_explained(
65 self, environment: Environment, template: str
66 ) -> t.Tuple[str, t.Optional[str], t.Optional[t.Callable]]:
67 attempts = []
68 rv: t.Optional[t.Tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]]
69 trv: t.Optional[
70 t.Tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]
71 ] = 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 ) -> t.Tuple[str, t.Optional[str], t.Optional[t.Callable]]:
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[t.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) -> t.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: t.Dict[str, t.Any]) -> str:
128 app.update_template_context(context)
129 before_render_template.send(app, template=template, context=context)
130 rv = template.render(context)
131 template_rendered.send(app, template=template, context=context)
132 return rv
135def render_template(
136 template_name_or_list: t.Union[str, Template, t.List[t.Union[str, Template]]],
137 **context: t.Any
138) -> str:
139 """Render a template by name with the given context.
141 :param template_name_or_list: The name of the template to render. If
142 a list is given, the first name to exist will be rendered.
143 :param context: The variables to make available in the template.
144 """
145 app = current_app._get_current_object() # type: ignore[attr-defined]
146 template = app.jinja_env.get_or_select_template(template_name_or_list)
147 return _render(app, template, context)
150def render_template_string(source: str, **context: t.Any) -> str:
151 """Render a template from the given source string with the given
152 context.
154 :param source: The source code of the template to render.
155 :param context: The variables to make available in the template.
156 """
157 app = current_app._get_current_object() # type: ignore[attr-defined]
158 template = app.jinja_env.from_string(source)
159 return _render(app, template, context)
162def _stream(
163 app: "Flask", template: Template, context: t.Dict[str, t.Any]
164) -> t.Iterator[str]:
165 app.update_template_context(context)
166 before_render_template.send(app, template=template, context=context)
168 def generate() -> t.Iterator[str]:
169 yield from template.generate(context)
170 template_rendered.send(app, template=template, context=context)
172 rv = generate()
174 # If a request context is active, keep it while generating.
175 if request:
176 rv = stream_with_context(rv)
178 return rv
181def stream_template(
182 template_name_or_list: t.Union[str, Template, t.List[t.Union[str, Template]]],
183 **context: t.Any
184) -> t.Iterator[str]:
185 """Render a template by name with the given context as a stream.
186 This returns an iterator of strings, which can be used as a
187 streaming response from a view.
189 :param template_name_or_list: The name of the template to render. If
190 a list is given, the first name to exist will be rendered.
191 :param context: The variables to make available in the template.
193 .. versionadded:: 2.2
194 """
195 app = current_app._get_current_object() # type: ignore[attr-defined]
196 template = app.jinja_env.get_or_select_template(template_name_or_list)
197 return _stream(app, template, context)
200def stream_template_string(source: str, **context: t.Any) -> t.Iterator[str]:
201 """Render a template from the given source string with the given
202 context as a stream. This returns an iterator of strings, which can
203 be used as a streaming response from a view.
205 :param source: The source code of the template to render.
206 :param context: The variables to make available in the template.
208 .. versionadded:: 2.2
209 """
210 app = current_app._get_current_object() # type: ignore[attr-defined]
211 template = app.jinja_env.from_string(source)
212 return _stream(app, template, context)