socialseed-e2e 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,246 @@
1
+ """Template engine for code scaffolding.
2
+
3
+ This module provides a template engine for generating code files
4
+ and project scaffolding using Python's string.Template.
5
+ """
6
+
7
+ import re
8
+ from pathlib import Path
9
+ from string import Template
10
+ from typing import Dict, Optional, Union
11
+
12
+
13
+ class TemplateEngine:
14
+ """Template engine for code generation and scaffolding.
15
+
16
+ This class provides methods to load and render templates with
17
+ variable substitution. Templates are stored as files and can
18
+ be rendered with custom variables.
19
+
20
+ Attributes:
21
+ template_dir: Directory containing template files
22
+
23
+ Example:
24
+ >>> engine = TemplateEngine()
25
+ >>> result = engine.render('service_page.py', {
26
+ ... 'service_name': 'users-api',
27
+ ... 'class_name': 'UsersApi'
28
+ ... })
29
+ """
30
+
31
+ def __init__(self, template_dir: Optional[Union[str, Path]] = None):
32
+ """Initialize the template engine.
33
+
34
+ Args:
35
+ template_dir: Directory containing template files.
36
+ If None, uses the default templates directory.
37
+ """
38
+ if template_dir is None:
39
+ # Use the templates directory in the package
40
+ self.template_dir = Path(__file__).parent.parent / "templates"
41
+ else:
42
+ self.template_dir = Path(template_dir)
43
+
44
+ def load_template(self, template_name: str) -> Template:
45
+ """Load a template from file.
46
+
47
+ Args:
48
+ template_name: Name of the template file (with or without .template extension)
49
+
50
+ Returns:
51
+ Template: A string.Template instance
52
+
53
+ Raises:
54
+ FileNotFoundError: If template file doesn't exist
55
+ """
56
+ # Ensure .template extension
57
+ if not template_name.endswith(".template"):
58
+ template_name = f"{template_name}.template"
59
+
60
+ template_path = self.template_dir / template_name
61
+
62
+ if not template_path.exists():
63
+ raise FileNotFoundError(
64
+ f"Template not found: {template_path}\n"
65
+ f"Available templates: {self.list_templates()}"
66
+ )
67
+
68
+ template_content = template_path.read_text(encoding="utf-8")
69
+ return Template(template_content)
70
+
71
+ def render(self, template_name: str, variables: Optional[Dict[str, str]] = None) -> str:
72
+ """Render a template with variable substitution.
73
+
74
+ Args:
75
+ template_name: Name of the template file
76
+ variables: Dictionary of variables for substitution
77
+
78
+ Returns:
79
+ str: Rendered template content
80
+
81
+ Raises:
82
+ FileNotFoundError: If template file doesn't exist
83
+ """
84
+ if variables is None:
85
+ variables = {}
86
+
87
+ template = self.load_template(template_name)
88
+ return template.safe_substitute(variables)
89
+
90
+ def render_to_file(
91
+ self,
92
+ template_name: str,
93
+ variables: Optional[Dict[str, str]],
94
+ output_path: Union[str, Path],
95
+ overwrite: bool = False,
96
+ ) -> Path:
97
+ """Render a template and write to file.
98
+
99
+ Args:
100
+ template_name: Name of the template file
101
+ variables: Dictionary of variables for substitution
102
+ output_path: Path where to write the rendered content
103
+ overwrite: If True, overwrite existing file
104
+
105
+ Returns:
106
+ Path: Path to the written file
107
+
108
+ Raises:
109
+ FileNotFoundError: If template file doesn't exist
110
+ FileExistsError: If output file exists and overwrite=False
111
+ """
112
+ output_path = Path(output_path)
113
+
114
+ if output_path.exists() and not overwrite:
115
+ raise FileExistsError(
116
+ f"File already exists: {output_path}. " f"Use overwrite=True to overwrite."
117
+ )
118
+
119
+ # Ensure parent directory exists
120
+ output_path.parent.mkdir(parents=True, exist_ok=True)
121
+
122
+ content = self.render(template_name, variables)
123
+ output_path.write_text(content, encoding="utf-8")
124
+
125
+ return output_path
126
+
127
+ def list_templates(self) -> list:
128
+ """List all available templates.
129
+
130
+ Returns:
131
+ list: List of template file names
132
+ """
133
+ if not self.template_dir.exists():
134
+ return []
135
+
136
+ return [
137
+ f.name for f in self.template_dir.iterdir() if f.is_file() and f.suffix == ".template"
138
+ ]
139
+
140
+ def get_template_variables(self, template_name: str) -> list:
141
+ """Extract variable names from a template.
142
+
143
+ Args:
144
+ template_name: Name of the template file
145
+
146
+ Returns:
147
+ list: List of variable names used in the template
148
+ """
149
+ template = self.load_template(template_name)
150
+
151
+ # Pattern to match ${var} or $var
152
+ pattern = r"\$\{([^}]+)\}|\$([a-zA-Z_][a-zA-Z0-9_]*)"
153
+ matches = re.findall(pattern, template.template)
154
+
155
+ # Extract variable names from matches
156
+ variables = []
157
+ for match in matches:
158
+ # match is a tuple (braced, unbraced)
159
+ var = match[0] if match[0] else match[1]
160
+ if var and var not in variables:
161
+ variables.append(var)
162
+
163
+ return variables
164
+
165
+ def validate_variables(self, template_name: str, variables: Dict[str, str]) -> tuple:
166
+ """Validate that all required variables are provided.
167
+
168
+ Args:
169
+ template_name: Name of the template file
170
+ variables: Dictionary of variables to validate
171
+
172
+ Returns:
173
+ tuple: (missing_vars, extra_vars)
174
+ - missing_vars: List of variables in template but not in variables dict
175
+ - extra_vars: List of variables in variables dict but not in template
176
+ """
177
+ template_vars = set(self.get_template_variables(template_name))
178
+ provided_vars = set(variables.keys())
179
+
180
+ missing = list(template_vars - provided_vars)
181
+ extra = list(provided_vars - template_vars)
182
+
183
+ return missing, extra
184
+
185
+
186
+ def to_class_name(name: str) -> str:
187
+ """Convert a service name to a class name.
188
+
189
+ Converts names like 'users-api' or 'users_api' to 'UsersApi'.
190
+
191
+ Args:
192
+ name: Service name (e.g., 'users-api', 'users_api')
193
+
194
+ Returns:
195
+ str: Class name (e.g., 'UsersApi')
196
+
197
+ Example:
198
+ >>> to_class_name('users-api')
199
+ 'UsersApi'
200
+ >>> to_class_name('my_service')
201
+ 'MyService'
202
+ """
203
+ # Replace hyphens and underscores with spaces, title case, then remove spaces
204
+ return name.replace("-", " ").replace("_", " ").title().replace(" ", "")
205
+
206
+
207
+ def to_snake_case(name: str) -> str:
208
+ """Convert a service name to snake_case.
209
+
210
+ Converts names like 'users-api' to 'users_api'.
211
+
212
+ Args:
213
+ name: Service name (e.g., 'users-api')
214
+
215
+ Returns:
216
+ str: Snake case name (e.g., 'users_api')
217
+
218
+ Example:
219
+ >>> to_snake_case('users-api')
220
+ 'users_api'
221
+ >>> to_snake_case('MyService')
222
+ 'my_service'
223
+ """
224
+ # Replace hyphens with underscores and convert to lowercase
225
+ return name.replace("-", "_").lower()
226
+
227
+
228
+ def to_camel_case(name: str) -> str:
229
+ """Convert a service name to camelCase.
230
+
231
+ Converts names like 'users-api' to 'usersApi'.
232
+
233
+ Args:
234
+ name: Service name (e.g., 'users-api')
235
+
236
+ Returns:
237
+ str: Camel case name (e.g., 'usersApi')
238
+
239
+ Example:
240
+ >>> to_camel_case('users-api')
241
+ 'usersApi'
242
+ >>> to_camel_case('my_service')
243
+ 'myService'
244
+ """
245
+ class_name = to_class_name(name)
246
+ return class_name[0].lower() + class_name[1:]