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