Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.10/site-packages/django/template/backends/django.py: 31%

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

96 statements  

1from collections import defaultdict 

2from importlib import import_module 

3from pkgutil import walk_packages 

4 

5from django.apps import apps 

6from django.conf import settings 

7from django.core.checks import Error, Warning 

8from django.template import TemplateDoesNotExist 

9from django.template.context import make_context 

10from django.template.engine import Engine 

11from django.template.library import InvalidTemplateLibrary 

12 

13from .base import BaseEngine 

14 

15 

16class DjangoTemplates(BaseEngine): 

17 app_dirname = "templates" 

18 

19 def __init__(self, params): 

20 params = params.copy() 

21 options = params.pop("OPTIONS").copy() 

22 options.setdefault("autoescape", True) 

23 options.setdefault("debug", settings.DEBUG) 

24 options.setdefault("file_charset", "utf-8") 

25 libraries = options.get("libraries", {}) 

26 options["libraries"] = self.get_templatetag_libraries(libraries) 

27 super().__init__(params) 

28 self.engine = Engine(self.dirs, self.app_dirs, **options) 

29 

30 def check(self, **kwargs): 

31 return [ 

32 *self._check_string_if_invalid_is_string(), 

33 *self._check_for_template_tags_with_the_same_name(), 

34 ] 

35 

36 def _check_string_if_invalid_is_string(self): 

37 value = self.engine.string_if_invalid 

38 if not isinstance(value, str): 

39 return [ 

40 Error( 

41 "'string_if_invalid' in TEMPLATES OPTIONS must be a string but " 

42 "got: %r (%s)." % (value, type(value)), 

43 obj=self, 

44 id="templates.E002", 

45 ) 

46 ] 

47 return [] 

48 

49 def _check_for_template_tags_with_the_same_name(self): 

50 libraries = defaultdict(set) 

51 

52 for module_name, module_path in get_template_tag_modules(): 

53 libraries[module_name].add(module_path) 

54 

55 for module_name, module_path in self.engine.libraries.items(): 

56 libraries[module_name].add(module_path) 

57 

58 errors = [] 

59 

60 for library_name, items in libraries.items(): 

61 if len(items) > 1: 

62 items = ", ".join(repr(item) for item in sorted(items)) 

63 errors.append( 

64 Warning( 

65 f"{library_name!r} is used for multiple template tag modules: " 

66 f"{items}", 

67 obj=self, 

68 id="templates.W003", 

69 ) 

70 ) 

71 

72 return errors 

73 

74 def from_string(self, template_code): 

75 return Template(self.engine.from_string(template_code), self) 

76 

77 def get_template(self, template_name): 

78 try: 

79 return Template(self.engine.get_template(template_name), self) 

80 except TemplateDoesNotExist as exc: 

81 reraise(exc, self) 

82 

83 def get_templatetag_libraries(self, custom_libraries): 

84 """ 

85 Return a collation of template tag libraries from installed 

86 applications and the supplied custom_libraries argument. 

87 """ 

88 libraries = get_installed_libraries() 

89 libraries.update(custom_libraries) 

90 return libraries 

91 

92 

93class Template: 

94 def __init__(self, template, backend): 

95 self.template = template 

96 self.backend = backend 

97 

98 @property 

99 def origin(self): 

100 return self.template.origin 

101 

102 def render(self, context=None, request=None): 

103 context = make_context( 

104 context, request, autoescape=self.backend.engine.autoescape 

105 ) 

106 try: 

107 return self.template.render(context) 

108 except TemplateDoesNotExist as exc: 

109 reraise(exc, self.backend) 

110 

111 

112def copy_exception(exc, backend=None): 

113 """ 

114 Create a new TemplateDoesNotExist. Preserve its declared attributes and 

115 template debug data but discard __traceback__, __context__, and __cause__ 

116 to make this object suitable for keeping around (in a cache, for example). 

117 """ 

118 backend = backend or exc.backend 

119 new = exc.__class__(*exc.args, tried=exc.tried, backend=backend, chain=exc.chain) 

120 if hasattr(exc, "template_debug"): 

121 new.template_debug = exc.template_debug 

122 return new 

123 

124 

125def reraise(exc, backend): 

126 """ 

127 Reraise TemplateDoesNotExist while maintaining template debug information. 

128 """ 

129 new = copy_exception(exc, backend) 

130 raise new from exc 

131 

132 

133def get_template_tag_modules(): 

134 """ 

135 Yield (module_name, module_path) pairs for all installed template tag 

136 libraries. 

137 """ 

138 candidates = ["django.templatetags"] 

139 candidates.extend( 

140 f"{app_config.name}.templatetags" for app_config in apps.get_app_configs() 

141 ) 

142 

143 for candidate in candidates: 

144 try: 

145 pkg = import_module(candidate) 

146 except ImportError: 

147 # No templatetags package defined. This is safe to ignore. 

148 continue 

149 

150 if hasattr(pkg, "__path__"): 

151 for name in get_package_libraries(pkg): 

152 yield name.removeprefix(candidate).lstrip("."), name 

153 

154 

155def get_installed_libraries(): 

156 """ 

157 Return the built-in template tag libraries and those from installed 

158 applications. Libraries are stored in a dictionary where keys are the 

159 individual module names, not the full module paths. Example: 

160 django.templatetags.i18n is stored as i18n. 

161 """ 

162 return { 

163 module_name: full_name for module_name, full_name in get_template_tag_modules() 

164 } 

165 

166 

167def get_package_libraries(pkg): 

168 """ 

169 Recursively yield template tag libraries defined in submodules of a 

170 package. 

171 """ 

172 for entry in walk_packages(pkg.__path__, pkg.__name__ + "."): 

173 try: 

174 module = import_module(entry[1]) 

175 except ImportError as e: 

176 raise InvalidTemplateLibrary( 

177 "Invalid template library specified. ImportError raised when " 

178 "trying to load '%s': %s" % (entry[1], e) 

179 ) from e 

180 

181 if hasattr(module, "register"): 

182 yield entry[1]