# Django 2.2.9.dev20191202104323 文档

# django.template.utils 源代码

    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)