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

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

55 statements  

1import functools 

2from collections import Counter 

3from pathlib import Path 

4 

5from django.apps import apps 

6from django.conf import settings 

7from django.core.exceptions import ImproperlyConfigured 

8from django.utils.functional import cached_property 

9from django.utils.module_loading import import_string 

10 

11 

12class InvalidTemplateEngineError(ImproperlyConfigured): 

13 pass 

14 

15 

16class EngineHandler: 

17 def __init__(self, templates=None): 

18 """ 

19 templates is an optional list of template engine definitions 

20 (structured like settings.TEMPLATES). 

21 """ 

22 self._templates = templates 

23 self._engines = {} 

24 

25 @cached_property 

26 def templates(self): 

27 if self._templates is None: 

28 self._templates = settings.TEMPLATES 

29 

30 templates = {} 

31 backend_names = [] 

32 for tpl in self._templates: 

33 try: 

34 # This will raise an exception if 'BACKEND' doesn't exist or 

35 # isn't a string containing at least one dot. 

36 default_name = tpl["BACKEND"].rsplit(".", 2)[-2] 

37 except Exception: 

38 invalid_backend = tpl.get("BACKEND", "<not defined>") 

39 raise ImproperlyConfigured( 

40 "Invalid BACKEND for a template engine: {}. Check " 

41 "your TEMPLATES setting.".format(invalid_backend) 

42 ) 

43 

44 tpl = { 

45 "NAME": default_name, 

46 "DIRS": [], 

47 "APP_DIRS": False, 

48 "OPTIONS": {}, 

49 **tpl, 

50 } 

51 

52 templates[tpl["NAME"]] = tpl 

53 backend_names.append(tpl["NAME"]) 

54 

55 counts = Counter(backend_names) 

56 duplicates = [alias for alias, count in counts.most_common() if count > 1] 

57 if duplicates: 

58 raise ImproperlyConfigured( 

59 "Template engine aliases aren't unique, duplicates: {}. " 

60 "Set a unique NAME for each engine in settings.TEMPLATES.".format( 

61 ", ".join(duplicates) 

62 ) 

63 ) 

64 

65 return templates 

66 

67 def __getitem__(self, alias): 

68 try: 

69 return self._engines[alias] 

70 except KeyError: 

71 try: 

72 params = self.templates[alias] 

73 except KeyError: 

74 raise InvalidTemplateEngineError( 

75 "Could not find config for '{}' " 

76 "in settings.TEMPLATES".format(alias) 

77 ) 

78 

79 # If importing or initializing the backend raises an exception, 

80 # self._engines[alias] isn't set and this code may get executed 

81 # again, so we must preserve the original params. See #24265. 

82 params = params.copy() 

83 backend = params.pop("BACKEND") 

84 engine_cls = import_string(backend) 

85 engine = engine_cls(params) 

86 

87 self._engines[alias] = engine 

88 return engine 

89 

90 def __iter__(self): 

91 return iter(self.templates) 

92 

93 def all(self): 

94 return [self[alias] for alias in self] 

95 

96 

97@functools.lru_cache 

98def get_app_template_dirs(dirname): 

99 """ 

100 Return an iterable of paths of directories to load app templates from. 

101 

102 dirname is the name of the subdirectory containing templates inside 

103 installed applications. 

104 """ 

105 # Immutable return value because it will be cached and shared by callers. 

106 return tuple( 

107 path 

108 for app_config in apps.get_app_configs() 

109 if app_config.path and (path := Path(app_config.path) / dirname).is_dir() 

110 )