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
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-26 21:26 -0800
1"""Async dictionary template loader implementation."""
3import typing as t
5from jinja2.utils import internalcode
7from .base import AsyncBaseLoader, SourceType
9if t.TYPE_CHECKING:
10 from ..environment import AsyncEnvironment
13class AsyncDictLoader(AsyncBaseLoader):
14 """Async dictionary template loader with memory optimization.
16 This loader stores templates in memory as a dictionary, useful for
17 testing and applications that generate templates dynamically.
18 """
20 __slots__ = ("mapping",)
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.
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
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.
48 Args:
49 environment: The async environment instance
50 name: Template name to load
52 Returns:
53 Tuple of (source, filename, uptodate_func)
55 Raises:
56 TemplateNotFound: If template is not in the mapping
57 """
58 self._ensure_initialized()
60 # Try to get from cache first
61 cache_manager = self._get_cache_manager(environment)
62 cache_key = f"dict:{id(self)}:{name}"
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
69 if name not in self.mapping:
70 self._handle_template_not_found(name)
72 source = self.mapping[name]
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
84 source_data: SourceType = (source, None, uptodate)
86 # Cache the result
87 if cache_manager:
88 cache_manager.set("template", cache_key, source_data)
90 return source_data
92 @internalcode
93 async def list_templates_async(self) -> list[str]:
94 """List all templates in the mapping asynchronously.
96 Returns:
97 Sorted list of template names
98 """
99 self._ensure_initialized()
100 return sorted(self.mapping.keys())
102 def add_template(self, name: str, source: str) -> None:
103 """Add a new template to the mapping.
105 Args:
106 name: Template name
107 source: Template source code
108 """
109 self.mapping[name] = source
111 def remove_template(self, name: str) -> None:
112 """Remove a template from the mapping.
114 Args:
115 name: Template name to remove
117 Raises:
118 KeyError: If template is not in the mapping
119 """
120 del self.mapping[name]
122 def update_mapping(self, mapping: dict[str, str]) -> None:
123 """Update the template mapping.
125 Args:
126 mapping: New mapping to merge with existing templates
127 """
128 self.mapping.update(mapping)
130 def clear_templates(self) -> None:
131 """Clear all templates from the mapping."""
132 self.mapping.clear()
134 def has_template(self, name: str) -> bool:
135 """Check if a template exists in the mapping.
137 Args:
138 name: Template name to check
140 Returns:
141 True if template exists, False otherwise
142 """
143 return name in self.mapping