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]