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

1import typing as t 

2 

3from jinja2 import BaseLoader 

4from jinja2 import Environment as BaseEnvironment 

5from jinja2 import Template 

6from jinja2 import TemplateNotFound 

7 

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 

15 

16if t.TYPE_CHECKING: # pragma: no cover 

17 from .app import Flask 

18 from .scaffold import Scaffold 

19 

20 

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 

34 

35 

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 """ 

41 

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 

47 

48 

49class DispatchingJinjaLoader(BaseLoader): 

50 """A loader that looks for templates in the application and all 

51 the blueprint folders. 

52 """ 

53 

54 def __init__(self, app: "Flask") -> None: 

55 self.app = app 

56 

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) 

63 

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 

72 

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

81 

82 from .debughelpers import explain_template_loading_attempts 

83 

84 explain_template_loading_attempts(self.app, template, attempts) 

85 

86 if trv is not None: 

87 return trv 

88 raise TemplateNotFound(template) 

89 

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) 

99 

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 

106 

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

108 loader = blueprint.jinja_loader 

109 if loader is not None: 

110 yield blueprint, loader 

111 

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

117 

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) 

123 

124 return list(result) 

125 

126 

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 

133 

134 

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. 

140 

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) 

148 

149 

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

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

152 context. 

153 

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) 

160 

161 

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) 

167 

168 def generate() -> t.Iterator[str]: 

169 yield from template.generate(context) 

170 template_rendered.send(app, template=template, context=context) 

171 

172 rv = generate() 

173 

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

175 if request: 

176 rv = stream_with_context(rv) 

177 

178 return rv 

179 

180 

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. 

188 

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. 

192 

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) 

198 

199 

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. 

204 

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

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

207 

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)