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

110 statements  

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( 

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) 

66 

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 

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: 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) 

100 

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 

105 

106 for blueprint in self.app.iter_blueprints(): 

107 loader = blueprint.jinja_loader 

108 if loader is not None: 

109 yield blueprint, loader 

110 

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()) 

116 

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) 

122 

123 return list(result) 

124 

125 

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 

136 

137 

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. 

143 

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) 

151 

152 

153def render_template_string(source: str, **context: t.Any) -> str: 

154 """Render a template from the given source string with the given 

155 context. 

156 

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) 

163 

164 

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 ) 

172 

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 ) 

178 

179 rv = generate() 

180 

181 # If a request context is active, keep it while generating. 

182 if request: 

183 rv = stream_with_context(rv) 

184 

185 return rv 

186 

187 

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. 

195 

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. 

199 

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) 

205 

206 

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. 

211 

212 :param source: The source code of the template to render. 

213 :param context: The variables to make available in the template. 

214 

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)