from __future__ import annotations from dataclasses import dataclass, field from functools import cached_property from inspect import isclass from typing import TYPE_CHECKING, Callable, Generic, TypeVar, cast from litestar.exceptions import ImproperlyConfiguredException from litestar.template import TemplateEngineProtocol __all__ = ("TemplateConfig",) if TYPE_CHECKING: from litestar.types import PathType EngineType = TypeVar("EngineType", bound=TemplateEngineProtocol) @dataclass class TemplateConfig(Generic[EngineType]): """Configuration for Templating. To enable templating, pass an instance of this class to the :class:`Litestar ` constructor using the 'template_config' key. """ engine: type[EngineType] | EngineType | None = field(default=None) """A template engine adhering to the :class:`TemplateEngineProtocol `.""" directory: PathType | list[PathType] | None = field(default=None) """A directory or list of directories from which to serve templates.""" engine_callback: Callable[[EngineType], None] | None = field(default=None) """A callback function that allows modifying the instantiated templating protocol.""" instance: EngineType | None = field(default=None) """An instance of the templating protocol.""" def __post_init__(self) -> None: """Ensure that directory is set if engine is a class.""" if isclass(self.engine) and not self.directory: raise ImproperlyConfiguredException("directory is a required kwarg when passing a template engine class") """Ensure that directory is not set if instance is.""" if self.instance is not None and self.directory is not None: raise ImproperlyConfiguredException("directory cannot be set if instance is") def to_engine(self) -> EngineType: """Instantiate the template engine.""" template_engine = cast( "EngineType", self.engine(directory=self.directory, engine_instance=None) if isclass(self.engine) else self.engine, ) if callable(self.engine_callback): self.engine_callback(template_engine) return template_engine @cached_property def engine_instance(self) -> EngineType: """Return the template engine instance.""" return self.to_engine() if self.instance is None else self.instance