123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107 |
- import functools
- from collections import Counter, OrderedDict
- from pathlib import Path
-
- from django.apps import apps
- from django.conf import settings
- from django.core.exceptions import ImproperlyConfigured
- from django.utils.functional import cached_property
- from django.utils.module_loading import import_string
-
-
- class InvalidTemplateEngineError(ImproperlyConfigured):
- pass
-
-
- class EngineHandler:
- def __init__(self, templates=None):
- """
- templates is an optional list of template engine definitions
- (structured like settings.TEMPLATES).
- """
- self._templates = templates
- self._engines = {}
-
- @cached_property
- def templates(self):
- if self._templates is None:
- self._templates = settings.TEMPLATES
-
- templates = OrderedDict()
- backend_names = []
- for tpl in self._templates:
- try:
- # This will raise an exception if 'BACKEND' doesn't exist or
- # isn't a string containing at least one dot.
- default_name = tpl['BACKEND'].rsplit('.', 2)[-2]
- except Exception:
- invalid_backend = tpl.get('BACKEND', '<not defined>')
- raise ImproperlyConfigured(
- "Invalid BACKEND for a template engine: {}. Check "
- "your TEMPLATES setting.".format(invalid_backend))
-
- tpl = {
- 'NAME': default_name,
- 'DIRS': [],
- 'APP_DIRS': False,
- 'OPTIONS': {},
- **tpl,
- }
-
- templates[tpl['NAME']] = tpl
- backend_names.append(tpl['NAME'])
-
- counts = Counter(backend_names)
- duplicates = [alias for alias, count in counts.most_common() if count > 1]
- if duplicates:
- raise ImproperlyConfigured(
- "Template engine aliases aren't unique, duplicates: {}. "
- "Set a unique NAME for each engine in settings.TEMPLATES."
- .format(", ".join(duplicates)))
-
- return templates
-
- def __getitem__(self, alias):
- try:
- return self._engines[alias]
- except KeyError:
- try:
- params = self.templates[alias]
- except KeyError:
- raise InvalidTemplateEngineError(
- "Could not find config for '{}' "
- "in settings.TEMPLATES".format(alias))
-
- # If importing or initializing the backend raises an exception,
- # self._engines[alias] isn't set and this code may get executed
- # again, so we must preserve the original params. See #24265.
- params = params.copy()
- backend = params.pop('BACKEND')
- engine_cls = import_string(backend)
- engine = engine_cls(params)
-
- self._engines[alias] = engine
- return engine
-
- def __iter__(self):
- return iter(self.templates)
-
- def all(self):
- return [self[alias] for alias in self]
-
-
- @functools.lru_cache()
- def get_app_template_dirs(dirname):
- """
- Return an iterable of paths of directories to load app templates from.
-
- dirname is the name of the subdirectory containing templates inside
- installed applications.
- """
- template_dirs = [
- str(Path(app_config.path) / dirname)
- for app_config in apps.get_app_configs()
- if app_config.path and (Path(app_config.path) / dirname).is_dir()
- ]
- # Immutable return value because it will be cached and shared by callers.
- return tuple(template_dirs)
|