123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111 |
- import functools
- from collections import Counter
- 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 = {}
- 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 = [
- 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)
|