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.
- socialseed_e2e/__init__.py +51 -0
- socialseed_e2e/__version__.py +21 -0
- socialseed_e2e/cli.py +611 -0
- socialseed_e2e/core/__init__.py +35 -0
- socialseed_e2e/core/base_page.py +839 -0
- socialseed_e2e/core/check_deps.py +43 -0
- socialseed_e2e/core/config.py +119 -0
- socialseed_e2e/core/config_loader.py +604 -0
- socialseed_e2e/core/headers.py +20 -0
- socialseed_e2e/core/interfaces.py +22 -0
- socialseed_e2e/core/loaders.py +51 -0
- socialseed_e2e/core/models.py +24 -0
- socialseed_e2e/core/test_orchestrator.py +84 -0
- socialseed_e2e/services/__init__.py +9 -0
- socialseed_e2e/templates/__init__.py +32 -0
- socialseed_e2e/templates/config.py.template +20 -0
- socialseed_e2e/templates/data_schema.py.template +116 -0
- socialseed_e2e/templates/e2e.conf.template +20 -0
- socialseed_e2e/templates/service_page.py.template +83 -0
- socialseed_e2e/templates/test_module.py.template +46 -0
- socialseed_e2e/utils/__init__.py +44 -0
- socialseed_e2e/utils/template_engine.py +246 -0
- socialseed_e2e/utils/validators.py +588 -0
- socialseed_e2e-0.1.0.dist-info/METADATA +333 -0
- socialseed_e2e-0.1.0.dist-info/RECORD +29 -0
- socialseed_e2e-0.1.0.dist-info/WHEEL +5 -0
- socialseed_e2e-0.1.0.dist-info/entry_points.txt +3 -0
- socialseed_e2e-0.1.0.dist-info/licenses/LICENSE +21 -0
- socialseed_e2e-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -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:]
|