Plain is headed towards 1.0! Subscribe for development updates →

 1import importlib.util
 2from importlib import import_module
 3
 4from plain.packages import packages_registry
 5from plain.runtime import settings
 6from plain.utils.functional import LazyObject
 7from plain.utils.module_loading import import_string
 8
 9from .environments import DefaultEnvironment, get_template_dirs
10
11
12class JinjaEnvironment(LazyObject):
13    def __init__(self, *args, **kwargs):
14        self.__dict__["_imported_modules"] = set()
15        super().__init__(*args, **kwargs)
16
17    def _setup(self):
18        environment_setting = settings.TEMPLATES_JINJA_ENVIRONMENT
19
20        if isinstance(environment_setting, str):
21            env = import_string(environment_setting)()
22        else:
23            env = environment_setting()
24
25        # We have to set _wrapped before we trigger the autoloading of "register" commands
26        self._wrapped = env
27
28        for package_config in packages_registry.get_package_configs():
29            # Autoload template helpers if the package provides a ``templates`` module
30            import_name = f"{package_config.name}.templates"
31            if import_name in self._imported_modules:
32                continue
33            if importlib.util.find_spec(import_name) is None:
34                continue
35            import_module(import_name)
36            self._imported_modules.add(import_name)
37
38        # Autoload template helpers from the local ``app`` package if present
39        import_name = "app.templates"
40        if import_name not in self._imported_modules:
41            if importlib.util.find_spec(import_name) is not None:
42                import_module(import_name)
43                self._imported_modules.add(import_name)
44
45
46environment = JinjaEnvironment()
47
48
49def register_template_extension(extension_class):
50    environment.add_extension(extension_class)
51    return extension_class
52
53
54def register_template_global(value, name=None):
55    """
56    Adds a global to the Jinja environment.
57
58    Can be used as a decorator on a function:
59
60            @register_template_global
61            def my_global():
62                return "Hello, world!"
63
64    Or as a function:
65
66            register_template_global("Hello, world!", name="my_global")
67    """
68    if callable(value):
69        environment.globals[name or value.__name__] = value
70    elif name:
71        environment.globals[name] = value
72    else:
73        raise ValueError("name must be provided if value is not callable")
74
75    return value
76
77
78def register_template_filter(func, name=None):
79    """Adds a filter to the Jinja environment."""
80    environment.filters[name or func.__name__] = func
81    return func
82
83
84__all__ = [
85    "environment",
86    "DefaultEnvironment",
87    "get_template_dirs",
88    "register_template_extension",
89    "register_template_filter",
90    "register_template_global",
91]