Coverage for src / ezcompiler / assets / templates / template_loader.py: 17.65%
80 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-27 06:49 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-27 06:49 +0000
1# ///////////////////////////////////////////////////////////////
2# TEMPLATE_LOADER - Template loading and resource access
3# Project: ezcompiler
4# ///////////////////////////////////////////////////////////////
6"""
7Template loader - Template loading and resource access for EzCompiler.
9This module provides functionality for loading template files from the
10assets directory. It serves as the resource access layer for templates,
11delegating processing to TemplateProcessor in utils.
12"""
14from __future__ import annotations
16# ///////////////////////////////////////////////////////////////
17# IMPORTS
18# ///////////////////////////////////////////////////////////////
19# Standard library imports
20from pathlib import Path
21from typing import Any
23# Local imports
24from ...utils.template_utils import TemplateProcessor
26# ///////////////////////////////////////////////////////////////
27# CLASSES
28# ///////////////////////////////////////////////////////////////
31class TemplateLoader:
32 """
33 Loader for template resource files.
35 Provides methods to load templates from .template files and process
36 them with variable substitution for configuration, version info,
37 and setup scripts.
39 Attributes:
40 _templates_dir: Base directory containing template subdirectories
41 _processor: TemplateProcessor instance for variable substitution
43 Example:
44 >>> loader = TemplateLoader()
45 >>> template = loader.load_template("config", "yaml")
46 >>> content = loader.process_config_template("yaml", config_dict)
47 """
49 # ////////////////////////////////////////////////
50 # INITIALIZATION
51 # ////////////////////////////////////////////////
53 def __init__(self) -> None:
54 """
55 Initialize the template loader.
57 Note:
58 Automatically detects templates_dir from module location.
59 """
60 self._templates_dir = Path(__file__).parent
61 self._processor = TemplateProcessor()
63 # ////////////////////////////////////////////////
64 # PATH AND LOADING METHODS
65 # ////////////////////////////////////////////////
67 def _get_template_path(self, template_type: str, format_type: str) -> Path:
68 """
69 Get the path to a specific template file.
71 Args:
72 template_type: Type of template (config, version, setup)
73 format_type: Format of the template (yaml, json, txt, py)
75 Returns:
76 Path: Path to the template file
78 Note:
79 Version templates have special naming: version_info.{format}.template
80 Other templates follow: {type}.{format}.template
81 """
82 if template_type == "version":
83 # Special case for version templates
84 return (
85 self._templates_dir
86 / template_type
87 / f"version_info.{format_type}.template"
88 )
89 else:
90 return (
91 self._templates_dir
92 / template_type
93 / f"{template_type}.{format_type}.template"
94 )
96 def load_template(self, template_type: str, format_type: str) -> str:
97 """
98 Load a template file.
100 Args:
101 template_type: Type of template (config, version, setup)
102 format_type: Format of the template (yaml, json, txt, py)
104 Returns:
105 str: Template content as string
107 Raises:
108 FileNotFoundError: If template file doesn't exist
109 """
110 template_path = self._get_template_path(template_type, format_type)
112 if not template_path.exists():
113 raise FileNotFoundError(f"Template not found: {template_path}")
115 with open(template_path, encoding="utf-8") as f:
116 return f.read()
118 def list_available_templates(self) -> dict[str, list[str]]:
119 """
120 List all available templates.
122 Returns:
123 dict[str, list[str]]: Dictionary mapping template types to available formats
125 Example:
126 >>> loader.list_available_templates()
127 {'config': ['yaml', 'json'], 'version': ['txt'], 'setup': ['py']}
128 """
129 templates: dict[str, list[str]] = {}
131 for template_dir in self._templates_dir.iterdir():
132 if template_dir.is_dir():
133 template_type = template_dir.name
134 formats = []
136 for template_file in template_dir.glob("*.template"):
137 # Extract format from filename (e.g., "config.yaml.template" -> "yaml")
138 format_type = template_file.stem.split(".")[-1]
139 formats.append(format_type)
141 templates[template_type] = formats
143 return templates
145 # ////////////////////////////////////////////////
146 # TEMPLATE PROCESSING METHODS
147 # ////////////////////////////////////////////////
149 def process_config_template(self, format_type: str, config: dict[str, Any]) -> str:
150 """
151 Process a configuration template.
153 Args:
154 format_type: Format of the template (yaml, json)
155 config: Configuration values dictionary
157 Returns:
158 str: Processed template string
160 Example:
161 >>> config = {"version": "1.0.0", "project_name": "MyApp"}
162 >>> content = loader.process_config_template("yaml", config)
163 """
164 template = self.load_template("config", format_type)
165 return self._processor.process_config_template(template, config)
167 def process_version_template(
168 self,
169 format_type: str,
170 version: str,
171 company_name: str,
172 project_description: str,
173 project_name: str,
174 ) -> str:
175 """
176 Process a version template.
178 Args:
179 format_type: Format of the template (txt)
180 version: Project version (e.g., "1.0.0")
181 company_name: Company name
182 project_description: Project description
183 project_name: Project name
185 Returns:
186 str: Processed template string
187 """
188 template = self.load_template("version", format_type)
189 return self._processor.process_version_template(
190 template, version, company_name, project_description, project_name
191 )
193 def process_setup_template(self, format_type: str, config: dict[str, Any]) -> str:
194 """
195 Process a setup template.
197 Args:
198 format_type: Format of the template (py)
199 config: Configuration values dictionary
201 Returns:
202 str: Processed template string
203 """
204 template = self.load_template("setup", format_type)
205 return self._processor.process_setup_template(template, config)
207 # ////////////////////////////////////////////////
208 # FILE GENERATION METHODS
209 # ////////////////////////////////////////////////
211 def create_file_from_template(
212 self, template_type: str, format_type: str, output_path: Path, **kwargs: Any
213 ) -> None:
214 """
215 Create a file from a template.
217 Args:
218 template_type: Type of template (config, version, setup)
219 format_type: Format of the template
220 output_path: Path where to save the file
221 **kwargs: Additional arguments for template processing
223 Raises:
224 ValueError: If template_type is unknown
226 Note:
227 Automatically creates parent directories if they don't exist.
228 """
229 if template_type == "config":
230 content = self.process_config_template(
231 format_type, kwargs.get("config", {})
232 )
233 elif template_type == "version":
234 content = self.process_version_template(
235 format_type,
236 kwargs.get("version", "1.0.0"),
237 kwargs.get("company_name", "Company"),
238 kwargs.get("project_description", "Description"),
239 kwargs.get("project_name", "Project"),
240 )
241 elif template_type == "setup":
242 content = self.process_setup_template(format_type, kwargs.get("config", {}))
243 else:
244 raise ValueError(f"Unknown template type: {template_type}")
246 # Ensure output directory exists
247 output_path.parent.mkdir(parents=True, exist_ok=True)
249 # Write the processed template
250 with open(output_path, "w", encoding="utf-8") as f:
251 f.write(content)
253 def generate_template_with_mockup(
254 self, template_type: str, format_type: str, output_path: Path
255 ) -> None:
256 """
257 Generate a template file with mockup values instead of placeholders.
259 Creates a valid file that can be used as a starting point and
260 edited manually with real values.
262 Args:
263 template_type: Type of template (config, version, setup)
264 format_type: Format of the template
265 output_path: Path where to save the file
267 Raises:
268 ValueError: If template_type is unknown
269 Exception: If generation fails
270 """
271 try:
272 # Load the template
273 template = self.load_template(template_type, format_type)
275 # Process with mockup values
276 if template_type == "config":
277 content = self._processor.process_template_with_mockup(template)
278 elif template_type == "version":
279 mockup_config = self._processor.create_mockup_config()
280 content = self._processor.process_version_template(
281 template,
282 mockup_config["version"],
283 mockup_config["company_name"],
284 mockup_config["project_description"],
285 mockup_config["project_name"],
286 )
287 elif template_type == "setup":
288 mockup_config = self._processor.create_mockup_config()
289 content = self._processor.process_setup_template(
290 template, mockup_config
291 )
292 else:
293 raise ValueError(f"Unknown template type: {template_type}")
295 # Ensure output directory exists
296 output_path.parent.mkdir(parents=True, exist_ok=True)
298 # Write the processed template with mockup values
299 with open(output_path, "w", encoding="utf-8") as f:
300 f.write(content)
302 except Exception as e:
303 raise Exception(f"Failed to generate template with mockup: {e}") from e
305 def generate_raw_template(
306 self, template_type: str, format_type: str, output_path: Path
307 ) -> None:
308 """
309 Generate a raw template file with placeholders.
311 Creates a template file with placeholders that can be processed
312 later with real values.
314 Args:
315 template_type: Type of template (config, version, setup)
316 format_type: Format of the template
317 output_path: Path where to save the file
319 Raises:
320 Exception: If generation fails
321 """
322 try:
323 # Load the template
324 template = self.load_template(template_type, format_type)
326 # Ensure output directory exists
327 output_path.parent.mkdir(parents=True, exist_ok=True)
329 # Write the raw template with placeholders
330 with open(output_path, "w", encoding="utf-8") as f:
331 f.write(template)
333 except Exception as e:
334 raise Exception(f"Failed to generate raw template: {e}") from e
336 # ////////////////////////////////////////////////
337 # VALIDATION METHODS
338 # ////////////////////////////////////////////////
340 def validate_template(self, template_type: str, format_type: str) -> bool:
341 """
342 Validate a template file.
344 Args:
345 template_type: Type of template
346 format_type: Format of the template
348 Returns:
349 bool: True if template is valid, False otherwise
351 Note:
352 Validates template syntax by loading and checking it.
353 """
354 try:
355 template = self.load_template(template_type, format_type)
356 return self._processor.validate_template(template)
357 except Exception:
358 return False