1from __future__ import annotations
 2
 3from collections.abc import Callable
 4from typing import Any
 5
 6from plain.packages import packages_registry
 7from plain.runtime import settings
 8from plain.utils.functional import LazyObject
 9from plain.utils.module_loading import import_string
10
11from .environments import DefaultEnvironment, get_template_dirs
12
13
14class JinjaEnvironment(LazyObject):
15    def _setup(self) -> None:
16        environment_setting = settings.TEMPLATES_JINJA_ENVIRONMENT
17
18        if isinstance(environment_setting, str):
19            env = import_string(environment_setting)()
20        else:
21            env = environment_setting()
22
23        # We have to set _wrapped before we trigger the autoloading of "register" commands
24        self._wrapped = env
25
26        # Autoload template helpers using the registry method
27        packages_registry.autodiscover_modules("templates", include_app=True)
28
29
30environment = JinjaEnvironment()
31
32
33def register_template_extension(extension_class: type) -> type:
34    environment.add_extension(extension_class)
35    return extension_class
36
37
38def register_template_global(value: Any, name: str | None = None) -> Any:
39    """
40    Adds a global to the Jinja environment.
41
42    Can be used as a decorator on a function:
43
44            @register_template_global
45            def my_global():
46                return "Hello, world!"
47
48    Or as a function:
49
50            register_template_global("Hello, world!", name="my_global")
51    """
52    if callable(value):
53        environment.globals[name or value.__name__] = value
54    elif name:
55        environment.globals[name] = value
56    else:
57        raise ValueError("name must be provided if value is not callable")
58
59    return value
60
61
62def register_template_filter(
63    func: Callable[..., Any], name: str | None = None
64) -> Callable[..., Any]:
65    """Adds a filter to the Jinja environment."""
66    filter_name = name if name is not None else func.__name__  # type: ignore[attr-defined]
67    environment.filters[filter_name] = func
68    return func
69
70
71__all__ = [
72    "environment",
73    "DefaultEnvironment",
74    "get_template_dirs",
75    "register_template_extension",
76    "register_template_filter",
77    "register_template_global",
78]