Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/starlette/templating.py: 39%

49 statements  

« prev     ^ index     » next       coverage.py v7.2.2, created at 2023-03-26 06:12 +0000

1import typing 

2from os import PathLike 

3 

4from starlette.background import BackgroundTask 

5from starlette.requests import Request 

6from starlette.responses import Response 

7from starlette.types import Receive, Scope, Send 

8 

9try: 

10 import jinja2 

11 

12 # @contextfunction was renamed to @pass_context in Jinja 3.0, and was removed in 3.1 

13 # hence we try to get pass_context (most installs will be >=3.1) 

14 # and fall back to contextfunction, 

15 # adding a type ignore for mypy to let us access an attribute that may not exist 

16 if hasattr(jinja2, "pass_context"): 

17 pass_context = jinja2.pass_context 

18 else: # pragma: nocover 

19 pass_context = jinja2.contextfunction # type: ignore[attr-defined] 

20except ImportError: # pragma: nocover 

21 jinja2 = None # type: ignore[assignment] 

22 

23 

24class _TemplateResponse(Response): 

25 media_type = "text/html" 

26 

27 def __init__( 

28 self, 

29 template: typing.Any, 

30 context: dict, 

31 status_code: int = 200, 

32 headers: typing.Optional[typing.Mapping[str, str]] = None, 

33 media_type: typing.Optional[str] = None, 

34 background: typing.Optional[BackgroundTask] = None, 

35 ): 

36 self.template = template 

37 self.context = context 

38 content = template.render(context) 

39 super().__init__(content, status_code, headers, media_type, background) 

40 

41 async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: 

42 request = self.context.get("request", {}) 

43 extensions = request.get("extensions", {}) 

44 if "http.response.debug" in extensions: 

45 await send( 

46 { 

47 "type": "http.response.debug", 

48 "info": { 

49 "template": self.template, 

50 "context": self.context, 

51 }, 

52 } 

53 ) 

54 await super().__call__(scope, receive, send) 

55 

56 

57class Jinja2Templates: 

58 """ 

59 templates = Jinja2Templates("templates") 

60 

61 return templates.TemplateResponse("index.html", {"request": request}) 

62 """ 

63 

64 def __init__( 

65 self, 

66 directory: typing.Union[str, PathLike], 

67 context_processors: typing.Optional[ 

68 typing.List[typing.Callable[[Request], typing.Dict[str, typing.Any]]] 

69 ] = None, 

70 **env_options: typing.Any 

71 ) -> None: 

72 assert jinja2 is not None, "jinja2 must be installed to use Jinja2Templates" 

73 self.env = self._create_env(directory, **env_options) 

74 self.context_processors = context_processors or [] 

75 

76 def _create_env( 

77 self, directory: typing.Union[str, PathLike], **env_options: typing.Any 

78 ) -> "jinja2.Environment": 

79 @pass_context 

80 def url_for(context: dict, name: str, **path_params: typing.Any) -> str: 

81 request = context["request"] 

82 return request.url_for(name, **path_params) 

83 

84 loader = jinja2.FileSystemLoader(directory) 

85 env_options.setdefault("loader", loader) 

86 env_options.setdefault("autoescape", True) 

87 

88 env = jinja2.Environment(**env_options) 

89 env.globals["url_for"] = url_for 

90 return env 

91 

92 def get_template(self, name: str) -> "jinja2.Template": 

93 return self.env.get_template(name) 

94 

95 def TemplateResponse( 

96 self, 

97 name: str, 

98 context: dict, 

99 status_code: int = 200, 

100 headers: typing.Optional[typing.Mapping[str, str]] = None, 

101 media_type: typing.Optional[str] = None, 

102 background: typing.Optional[BackgroundTask] = None, 

103 ) -> _TemplateResponse: 

104 if "request" not in context: 

105 raise ValueError('context must include a "request" key') 

106 

107 request = typing.cast(Request, context["request"]) 

108 for context_processor in self.context_processors: 

109 context.update(context_processor(request)) 

110 

111 template = self.get_template(name) 

112 return _TemplateResponse( 

113 template, 

114 context, 

115 status_code=status_code, 

116 headers=headers, 

117 media_type=media_type, 

118 background=background, 

119 )