Coverage for /Users/davegaeddert/Developer/dropseed/plain/plain/plain/templates/jinja/environments.py: 96%

26 statements  

« prev     ^ index     » next       coverage.py v7.6.9, created at 2024-12-23 11:16 -0600

1import functools 

2from pathlib import Path 

3 

4from jinja2 import Environment, StrictUndefined 

5from jinja2.loaders import FileSystemLoader 

6 

7from plain.packages import packages 

8from plain.runtime import settings 

9 

10from .filters import default_filters 

11from .globals import default_globals 

12 

13 

14def finalize_callable_error(obj): 

15 """Prevent direct rendering of a callable (likely just forgotten ()) by raising a TypeError""" 

16 if callable(obj): 

17 raise TypeError(f"{obj} is callable, did you forget parentheses?") 

18 

19 # TODO find a way to prevent <object representation> from being rendered 

20 # if obj.__class__.__str__ is object.__str__: 

21 # raise TypeError(f"{obj} does not have a __str__ method") 

22 

23 return obj 

24 

25 

26def get_template_dirs(): 

27 jinja_templates = Path(__file__).parent / "templates" 

28 app_templates = settings.path.parent / "templates" 

29 return (jinja_templates, app_templates) + _get_app_template_dirs() 

30 

31 

32@functools.lru_cache 

33def _get_app_template_dirs(): 

34 """ 

35 Return an iterable of paths of directories to load app templates from. 

36 

37 dirname is the name of the subdirectory containing templates inside 

38 installed applications. 

39 """ 

40 dirname = "templates" 

41 template_dirs = [ 

42 Path(package_config.path) / dirname 

43 for package_config in packages.get_package_configs() 

44 if package_config.path and (Path(package_config.path) / dirname).is_dir() 

45 ] 

46 # Immutable return value because it will be cached and shared by callers. 

47 return tuple(template_dirs) 

48 

49 

50class DefaultEnvironment(Environment): 

51 def __init__(self): 

52 super().__init__( 

53 loader=FileSystemLoader(get_template_dirs()), 

54 autoescape=True, 

55 auto_reload=settings.DEBUG, 

56 undefined=StrictUndefined, 

57 finalize=finalize_callable_error, 

58 extensions=["jinja2.ext.loopcontrols", "jinja2.ext.debug"], 

59 ) 

60 

61 # Load the top-level defaults 

62 self.globals.update(default_globals) 

63 self.filters.update(default_filters)