Coverage for jinja2_async_environment/loaders/dict.py: 83%

47 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-09-03 14:09 -0700

1"""Async dictionary template loader implementation.""" 

2 

3import typing as t 

4 

5from jinja2.utils import internalcode 

6 

7from .base import AsyncBaseLoader, SourceType 

8 

9if t.TYPE_CHECKING: 

10 from ..environment import AsyncEnvironment 

11 

12 

13class AsyncDictLoader(AsyncBaseLoader): 

14 """Async dictionary template loader with memory optimization. 

15 

16 This loader stores templates in memory as a dictionary, useful for 

17 testing and applications that generate templates dynamically. 

18 """ 

19 

20 __slots__ = ("mapping",) 

21 

22 def __init__( 

23 self, 

24 mapping: dict[str, str], 

25 searchpath: t.Any = None, # For backward compatibility 

26 ) -> None: 

27 """Initialize the dictionary loader. 

28 

29 Args: 

30 mapping: Dictionary mapping template names to template source 

31 searchpath: Path or sequence of paths for compatibility 

32 """ 

33 # Call parent with provided searchpath 

34 if searchpath is not None: 

35 super().__init__(searchpath) 

36 else: 

37 # Call parent with empty searchpath for backward compatibility 

38 super().__init__([]) 

39 self.mapping = dict(mapping) # Create a copy for safety 

40 

41 @internalcode 

42 async def get_source_async( 

43 self, environment: "AsyncEnvironment", name: str 

44 ) -> SourceType: 

45 """Get template source from dictionary asynchronously with caching. 

46 

47 Args: 

48 environment: The async environment instance 

49 name: Template name to load 

50 

51 Returns: 

52 Tuple of (source, filename, uptodate_func) 

53 

54 Raises: 

55 TemplateNotFound: If template is not in the mapping 

56 """ 

57 self._ensure_initialized() 

58 

59 # Try to get from cache first 

60 cache_manager = self._get_cache_manager(environment) 

61 cache_key = f"dict:{name}" 

62 

63 if cache_manager: 

64 cached_source = cache_manager.get("template", cache_key) 

65 if cached_source is not None: 

66 return cached_source 

67 

68 if name not in self.mapping: 

69 self._handle_template_not_found(name) 

70 

71 source = self.mapping[name] 

72 

73 # For dictionary loader, we use None as filename and create an uptodate function 

74 # that checks if the template still exists and has the same content 

75 def uptodate() -> bool: 

76 # Check if template still exists 

77 if name not in self.mapping: 

78 return False 

79 # Check if content is the same 

80 current_content = self.mapping[name] 

81 return current_content == source 

82 

83 source_data = (source, None, uptodate) 

84 

85 # Cache the result 

86 if cache_manager: 

87 cache_manager.set("template", cache_key, source_data) 

88 

89 return source_data 

90 

91 @internalcode 

92 async def list_templates_async(self) -> list[str]: 

93 """List all templates in the mapping asynchronously. 

94 

95 Returns: 

96 Sorted list of template names 

97 """ 

98 self._ensure_initialized() 

99 return sorted(self.mapping.keys()) 

100 

101 def add_template(self, name: str, source: str) -> None: 

102 """Add a new template to the mapping. 

103 

104 Args: 

105 name: Template name 

106 source: Template source code 

107 """ 

108 self.mapping[name] = source 

109 

110 def remove_template(self, name: str) -> None: 

111 """Remove a template from the mapping. 

112 

113 Args: 

114 name: Template name to remove 

115 

116 Raises: 

117 KeyError: If template is not in the mapping 

118 """ 

119 del self.mapping[name] 

120 

121 def update_mapping(self, mapping: dict[str, str]) -> None: 

122 """Update the template mapping. 

123 

124 Args: 

125 mapping: New mapping to merge with existing templates 

126 """ 

127 self.mapping.update(mapping) 

128 

129 def clear_templates(self) -> None: 

130 """Clear all templates from the mapping.""" 

131 self.mapping.clear() 

132 

133 def has_template(self, name: str) -> bool: 

134 """Check if a template exists in the mapping. 

135 

136 Args: 

137 name: Template name to check 

138 

139 Returns: 

140 True if template exists, False otherwise 

141 """ 

142 return name in self.mapping