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

47 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2025-11-26 21:26 -0800

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 a default path since searchpath cannot be empty 

34 # For DictLoader, we use a virtual path since templates are in memory 

35 if searchpath is not None: 

36 super().__init__(searchpath) 

37 else: 

38 # Use a virtual path for dictionary loader since no actual filesystem path is needed 

39 super().__init__(["/virtual_dict_path"]) 

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

41 

42 @internalcode 

43 async def get_source_async( 

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

45 ) -> SourceType: 

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

47 

48 Args: 

49 environment: The async environment instance 

50 name: Template name to load 

51 

52 Returns: 

53 Tuple of (source, filename, uptodate_func) 

54 

55 Raises: 

56 TemplateNotFound: If template is not in the mapping 

57 """ 

58 self._ensure_initialized() 

59 

60 # Try to get from cache first 

61 cache_manager = self._get_cache_manager(environment) 

62 cache_key = f"dict:{id(self)}:{name}" 

63 

64 if cache_manager: 

65 cached_source: SourceType | None = cache_manager.get("template", cache_key) 

66 if cached_source is not None: 

67 return cached_source 

68 

69 if name not in self.mapping: 

70 self._handle_template_not_found(name) 

71 

72 source = self.mapping[name] 

73 

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

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

76 def uptodate() -> bool: 

77 # Check if template still exists 

78 if name not in self.mapping: 

79 return False 

80 # Check if content is the same 

81 current_content = self.mapping[name] 

82 return current_content == source 

83 

84 source_data: SourceType = (source, None, uptodate) 

85 

86 # Cache the result 

87 if cache_manager: 

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

89 

90 return source_data 

91 

92 @internalcode 

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

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

95 

96 Returns: 

97 Sorted list of template names 

98 """ 

99 self._ensure_initialized() 

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

101 

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

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

104 

105 Args: 

106 name: Template name 

107 source: Template source code 

108 """ 

109 self.mapping[name] = source 

110 

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

112 """Remove a template from the mapping. 

113 

114 Args: 

115 name: Template name to remove 

116 

117 Raises: 

118 KeyError: If template is not in the mapping 

119 """ 

120 del self.mapping[name] 

121 

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

123 """Update the template mapping. 

124 

125 Args: 

126 mapping: New mapping to merge with existing templates 

127 """ 

128 self.mapping.update(mapping) 

129 

130 def clear_templates(self) -> None: 

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

132 self.mapping.clear() 

133 

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

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

136 

137 Args: 

138 name: Template name to check 

139 

140 Returns: 

141 True if template exists, False otherwise 

142 """ 

143 return name in self.mapping