gentem 0.1.3__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.
- gentem/__init__.py +3 -0
- gentem/__main__.py +6 -0
- gentem/cli.py +163 -0
- gentem/commands/__init__.py +1 -0
- gentem/commands/fastapi.py +746 -0
- gentem/commands/new.py +741 -0
- gentem/template_engine.py +191 -0
- gentem/utils/__init__.py +13 -0
- gentem/utils/validators.py +158 -0
- gentem-0.1.3.dist-info/METADATA +183 -0
- gentem-0.1.3.dist-info/RECORD +15 -0
- gentem-0.1.3.dist-info/WHEEL +5 -0
- gentem-0.1.3.dist-info/entry_points.txt +2 -0
- gentem-0.1.3.dist-info/licenses/LICENSE +21 -0
- gentem-0.1.3.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"""Template engine for Gentem using Jinja2."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any, Dict, Optional
|
|
6
|
+
|
|
7
|
+
from jinja2 import (
|
|
8
|
+
BaseLoader,
|
|
9
|
+
Environment,
|
|
10
|
+
FileSystemLoader,
|
|
11
|
+
TemplateSyntaxError,
|
|
12
|
+
UndefinedError,
|
|
13
|
+
)
|
|
14
|
+
from rich import print
|
|
15
|
+
from rich.panel import Panel
|
|
16
|
+
from rich.tree import Tree
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class TemplateEngine:
|
|
20
|
+
"""Jinja2 template engine for generating project files."""
|
|
21
|
+
|
|
22
|
+
def __init__(self, template_dir: Optional[str] = None) -> None:
|
|
23
|
+
"""Initialize the template engine.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
template_dir: Base directory for templates. If not provided,
|
|
27
|
+
uses the gentem templates directory.
|
|
28
|
+
"""
|
|
29
|
+
if template_dir:
|
|
30
|
+
self.template_dir = Path(template_dir)
|
|
31
|
+
else:
|
|
32
|
+
# Find the package directory
|
|
33
|
+
package_dir = Path(__file__).parent.parent
|
|
34
|
+
self.template_dir = package_dir / "templates"
|
|
35
|
+
|
|
36
|
+
self.env = Environment(
|
|
37
|
+
loader=FileSystemLoader(str(self.template_dir)),
|
|
38
|
+
autoescape=True,
|
|
39
|
+
trim_blocks=True,
|
|
40
|
+
lstrip_blocks=True,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
def get_template(self, template_path: str) -> "jinja2.Template":
|
|
44
|
+
"""Get a template by path.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
template_path: Path to the template relative to template_dir.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
The Jinja2 template.
|
|
51
|
+
|
|
52
|
+
Raises:
|
|
53
|
+
FileNotFoundError: If template not found.
|
|
54
|
+
"""
|
|
55
|
+
try:
|
|
56
|
+
return self.env.get_template(template_path)
|
|
57
|
+
except TemplateSyntaxError as e:
|
|
58
|
+
raise TemplateSyntaxError(
|
|
59
|
+
f"Syntax error in template {template_path}: {e.message}",
|
|
60
|
+
lineno=e.lineno,
|
|
61
|
+
) from e
|
|
62
|
+
except UndefinedError as e:
|
|
63
|
+
raise UndefinedError(
|
|
64
|
+
f"Undefined variable in template {template_path}: {e.message}"
|
|
65
|
+
) from e
|
|
66
|
+
|
|
67
|
+
def render_template(
|
|
68
|
+
self,
|
|
69
|
+
template_path: str,
|
|
70
|
+
context: Dict[str, Any],
|
|
71
|
+
) -> str:
|
|
72
|
+
"""Render a template with the given context.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
template_path: Path to the template relative to template_dir.
|
|
76
|
+
context: Variables to pass to the template.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
The rendered template content.
|
|
80
|
+
"""
|
|
81
|
+
template = self.get_template(template_path)
|
|
82
|
+
return template.render(**context)
|
|
83
|
+
|
|
84
|
+
def render_file(
|
|
85
|
+
self,
|
|
86
|
+
template_path: str,
|
|
87
|
+
context: Dict[str, Any],
|
|
88
|
+
output_path: Path,
|
|
89
|
+
) -> None:
|
|
90
|
+
"""Render a template to a file.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
template_path: Path to the template relative to template_dir.
|
|
94
|
+
context: Variables to pass to the template.
|
|
95
|
+
output_path: Path to write the rendered file.
|
|
96
|
+
"""
|
|
97
|
+
content = self.render_template(template_path, context)
|
|
98
|
+
|
|
99
|
+
# Ensure parent directory exists
|
|
100
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
101
|
+
|
|
102
|
+
# Write the file
|
|
103
|
+
output_path.write_text(content, encoding="utf-8")
|
|
104
|
+
|
|
105
|
+
def list_templates(self, subdir: Optional[str] = None) -> list[str]:
|
|
106
|
+
"""List all templates in a subdirectory.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
subdir: Subdirectory within template_dir to list.
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
List of template paths.
|
|
113
|
+
"""
|
|
114
|
+
search_dir = self.template_dir
|
|
115
|
+
if subdir:
|
|
116
|
+
search_dir = search_dir / subdir
|
|
117
|
+
|
|
118
|
+
if not search_dir.exists():
|
|
119
|
+
return []
|
|
120
|
+
|
|
121
|
+
templates = []
|
|
122
|
+
for root, _, files in os.walk(search_dir):
|
|
123
|
+
for file in files:
|
|
124
|
+
if file.endswith((".j2", ".jinja2")):
|
|
125
|
+
rel_path = Path(root).relative_to(self.template_dir)
|
|
126
|
+
templates.append(str(rel_path / file))
|
|
127
|
+
|
|
128
|
+
return sorted(templates)
|
|
129
|
+
|
|
130
|
+
def preview_tree(
|
|
131
|
+
self,
|
|
132
|
+
template_paths: list[str],
|
|
133
|
+
context: Dict[str, Any],
|
|
134
|
+
) -> Tree:
|
|
135
|
+
"""Preview the file tree that would be generated.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
template_paths: List of template paths to render.
|
|
139
|
+
context: Variables to pass to the templates.
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
A Rich Tree showing the file structure.
|
|
143
|
+
"""
|
|
144
|
+
tree = Tree("Project Structure", guide_style="bold.cyan")
|
|
145
|
+
|
|
146
|
+
for template_path in template_paths:
|
|
147
|
+
# Get the output path (remove .j2 extension)
|
|
148
|
+
output_path = template_path
|
|
149
|
+
if output_path.endswith((".j2", ".jinja2")):
|
|
150
|
+
output_path = output_path[:-3] # Remove .j2
|
|
151
|
+
|
|
152
|
+
# Render the path with context
|
|
153
|
+
try:
|
|
154
|
+
rendered_path = self.render_template(
|
|
155
|
+
f"_paths/{output_path}.path.j2", context
|
|
156
|
+
)
|
|
157
|
+
except Exception:
|
|
158
|
+
# Fallback to template path
|
|
159
|
+
rendered_path = output_path
|
|
160
|
+
|
|
161
|
+
# Add to tree
|
|
162
|
+
parts = Path(rendered_path).parts
|
|
163
|
+
current = tree
|
|
164
|
+
for i, part in enumerate(parts):
|
|
165
|
+
is_last = i == len(parts) - 1
|
|
166
|
+
if is_last:
|
|
167
|
+
current.add(f"[cyan]{part}[/]")
|
|
168
|
+
else:
|
|
169
|
+
# Find or create branch
|
|
170
|
+
found = False
|
|
171
|
+
for child in current.children:
|
|
172
|
+
if child.label and str(child.label).strip("[]") == part:
|
|
173
|
+
current = child
|
|
174
|
+
found = True
|
|
175
|
+
break
|
|
176
|
+
if not found:
|
|
177
|
+
current = current.add(f"[bold]{part}/[/]")
|
|
178
|
+
|
|
179
|
+
return tree
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
# Global template engine instance
|
|
183
|
+
_engine: Optional[TemplateEngine] = None
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def get_template_engine() -> TemplateEngine:
|
|
187
|
+
"""Get the global template engine instance."""
|
|
188
|
+
global _engine
|
|
189
|
+
if _engine is None:
|
|
190
|
+
_engine = TemplateEngine()
|
|
191
|
+
return _engine
|
gentem/utils/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Utilities package for Gentem."""
|
|
2
|
+
|
|
3
|
+
from gentem.utils.validators import (
|
|
4
|
+
validate_project_name,
|
|
5
|
+
validate_python_identifier,
|
|
6
|
+
validate_license_type,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"validate_project_name",
|
|
11
|
+
"validate_python_identifier",
|
|
12
|
+
"validate_license_type",
|
|
13
|
+
]
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""Input validators for Gentem."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ValidationError(Exception):
|
|
9
|
+
"""Raised when validation fails."""
|
|
10
|
+
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def validate_python_identifier(name: str) -> bool:
|
|
15
|
+
"""Check if the name is a valid Python identifier."""
|
|
16
|
+
pattern = r"^[a-zA-Z_][a-zA-Z0-9_]*$"
|
|
17
|
+
return bool(re.match(pattern, name))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def validate_project_name(name: str) -> str:
|
|
21
|
+
"""Validate and return the project name.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
name: The project name to validate.
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
The validated project name.
|
|
28
|
+
|
|
29
|
+
Raises:
|
|
30
|
+
ValidationError: If the name is invalid.
|
|
31
|
+
"""
|
|
32
|
+
if not name:
|
|
33
|
+
raise ValidationError("Project name cannot be empty.")
|
|
34
|
+
|
|
35
|
+
if not validate_python_identifier(name):
|
|
36
|
+
raise ValidationError(
|
|
37
|
+
f"'{name}' is not a valid Python project name. "
|
|
38
|
+
"Use only letters, numbers, and underscores, starting with a letter or underscore."
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
# Check for Python reserved words
|
|
42
|
+
reserved_words = {
|
|
43
|
+
"and", "as", "assert", "async", "await", "break", "class", "continue",
|
|
44
|
+
"def", "del", "elif", "else", "except", "finally", "for", "from",
|
|
45
|
+
"global", "if", "import", "in", "is", "lambda", "nonlocal", "not",
|
|
46
|
+
"or", "pass", "raise", "return", "try", "while", "with", "yield", "True",
|
|
47
|
+
"False", "None",
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if name.lower() in reserved_words:
|
|
51
|
+
raise ValidationError(
|
|
52
|
+
f"'{name}' is a Python reserved word and cannot be used as a project name."
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
return name
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def validate_license_type(license_type: str) -> str:
|
|
59
|
+
"""Validate and normalize the license type.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
license_type: The license type to validate.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
The normalized license type.
|
|
66
|
+
|
|
67
|
+
Raises:
|
|
68
|
+
ValidationError: If the license type is invalid.
|
|
69
|
+
"""
|
|
70
|
+
valid_licenses = {"mit", "apache", "gpl", "bsd", "none", ""}
|
|
71
|
+
|
|
72
|
+
normalized = license_type.lower().strip()
|
|
73
|
+
if normalized not in valid_licenses:
|
|
74
|
+
raise ValidationError(
|
|
75
|
+
f"Invalid license type: '{license_type}'. "
|
|
76
|
+
f"Valid options are: {', '.join(sorted(valid_licenses))}"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
return normalized
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def validate_project_type(project_type: str) -> str:
|
|
83
|
+
"""Validate and normalize the project type.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
project_type: The project type to validate.
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
The normalized project type.
|
|
90
|
+
|
|
91
|
+
Raises:
|
|
92
|
+
ValidationError: If the project type is invalid.
|
|
93
|
+
"""
|
|
94
|
+
valid_types = {"library", "cli", "script"}
|
|
95
|
+
|
|
96
|
+
normalized = project_type.lower().strip()
|
|
97
|
+
if normalized not in valid_types:
|
|
98
|
+
raise ValidationError(
|
|
99
|
+
f"Invalid project type: '{project_type}'. "
|
|
100
|
+
f"Valid options are: {', '.join(sorted(valid_types))}"
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
return normalized
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def validate_db_type(db_type: str) -> Optional[str]:
|
|
107
|
+
"""Validate the database type.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
db_type: The database type to validate.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
The normalized database type or None.
|
|
114
|
+
|
|
115
|
+
Raises:
|
|
116
|
+
ValidationError: If the database type is invalid.
|
|
117
|
+
"""
|
|
118
|
+
if not db_type:
|
|
119
|
+
return None
|
|
120
|
+
|
|
121
|
+
valid_db_types = {"asyncpg", "sqlite", "postgres", "postgresql"}
|
|
122
|
+
|
|
123
|
+
normalized = db_type.lower().strip()
|
|
124
|
+
if normalized not in valid_db_types:
|
|
125
|
+
raise ValidationError(
|
|
126
|
+
f"Invalid database type: '{db_type}'. "
|
|
127
|
+
f"Valid options are: {', '.join(sorted(valid_db_types))} or empty for none."
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Normalize postgres/postgresql to asyncpg for now
|
|
131
|
+
if normalized in {"postgres", "postgresql"}:
|
|
132
|
+
normalized = "asyncpg"
|
|
133
|
+
|
|
134
|
+
return normalized
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def validate_output_path(path: str, dry_run: bool = False) -> Path:
|
|
138
|
+
"""Validate the output path.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
path: The path to validate.
|
|
142
|
+
dry_run: Whether this is a dry run.
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
The validated Path object.
|
|
146
|
+
|
|
147
|
+
Raises:
|
|
148
|
+
ValidationError: If the path is invalid.
|
|
149
|
+
"""
|
|
150
|
+
output_path = Path(path)
|
|
151
|
+
|
|
152
|
+
if output_path.exists() and not dry_run:
|
|
153
|
+
raise ValidationError(
|
|
154
|
+
f"Directory '{output_path}' already exists. "
|
|
155
|
+
"Please choose a different project name or remove the existing directory."
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
return output_path
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gentem
|
|
3
|
+
Version: 0.1.3
|
|
4
|
+
Summary: A Python CLI template boilerplate generator
|
|
5
|
+
Author-email: Abu Bakr <mabs2406@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: cli,boilerplate,template,generator,scaffolding
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Environment :: Console
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Requires-Python: >=3.9
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: typer>=0.9.0
|
|
21
|
+
Requires-Dist: jinja2>=3.1.0
|
|
22
|
+
Requires-Dist: rich>=13.0.0
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: pytest>=7.4.0; extra == "dev"
|
|
25
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
|
|
26
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
27
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
28
|
+
Requires-Dist: mypy>=1.5.0; extra == "dev"
|
|
29
|
+
Requires-Dist: types-pyyaml>=6.0.0; extra == "dev"
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
# Gentem
|
|
33
|
+
|
|
34
|
+

|
|
35
|
+

|
|
36
|
+

|
|
37
|
+

|
|
38
|
+
|
|
39
|
+

|
|
40
|
+

|
|
41
|
+

|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
A Python CLI template boilerplate generator for quickly scaffolding Python projects.
|
|
45
|
+
|
|
46
|
+
## Features
|
|
47
|
+
|
|
48
|
+
- **Project Scaffolding**: Generate Python projects with a single command
|
|
49
|
+
- **Multiple Project Types**: Support for library, CLI tool, and script projects
|
|
50
|
+
- **FastAPI Templates**: Pre-configured FastAPI project templates with optional database support
|
|
51
|
+
- **Opinionated Structure**: Best practices baked into every template
|
|
52
|
+
- **Interactive Preview**: `--dry-run` option to preview before creating files
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pip install gentem
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Or install from source:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
git clone https://github.com/knightlesssword/gentem.git
|
|
64
|
+
cd gentem
|
|
65
|
+
pip install -e .
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Usage
|
|
69
|
+
|
|
70
|
+
### Creating a New Project
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# Create a library project
|
|
74
|
+
gentem new mylib --type library
|
|
75
|
+
|
|
76
|
+
# Create a CLI tool project
|
|
77
|
+
gentem new mycli --type cli
|
|
78
|
+
|
|
79
|
+
# Create a simple script project
|
|
80
|
+
gentem new myscript --type script
|
|
81
|
+
|
|
82
|
+
# With author and description
|
|
83
|
+
gentem new mylib --type library --author "John Doe" --description "My library"
|
|
84
|
+
|
|
85
|
+
# With license
|
|
86
|
+
gentem new mylib --type library --license mit
|
|
87
|
+
gentem new mylib --type library --license apache
|
|
88
|
+
gentem new mylib --type library --license gpl
|
|
89
|
+
|
|
90
|
+
# Preview without creating files
|
|
91
|
+
gentem new mylib --type library --dry-run
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Creating a FastAPI Project
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# Create a basic FastAPI project
|
|
98
|
+
gentem fastapi myapi
|
|
99
|
+
|
|
100
|
+
# Create with async mode and lifespan
|
|
101
|
+
gentem fastapi myapi --async
|
|
102
|
+
|
|
103
|
+
# Create with database support (asyncpg)
|
|
104
|
+
gentem fastapi myapi --db asyncpg
|
|
105
|
+
|
|
106
|
+
# Combine options
|
|
107
|
+
gentem fastapi myapi --async --db asyncpg --author "John Doe"
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Project Structure
|
|
111
|
+
|
|
112
|
+
### Library Template
|
|
113
|
+
```
|
|
114
|
+
mylib/
|
|
115
|
+
├── src/
|
|
116
|
+
│ └── mylib/
|
|
117
|
+
│ ├── __init__.py
|
|
118
|
+
│ └── core.py
|
|
119
|
+
├── tests/
|
|
120
|
+
│ ├── __init__.py
|
|
121
|
+
│ └── test_core.py
|
|
122
|
+
├── pyproject.toml
|
|
123
|
+
├── README.md
|
|
124
|
+
├── LICENSE
|
|
125
|
+
└── .gitignore
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### FastAPI Template
|
|
129
|
+
```
|
|
130
|
+
myapi/
|
|
131
|
+
├── src/
|
|
132
|
+
│ └── myapi/
|
|
133
|
+
│ ├── __init__.py
|
|
134
|
+
│ ├── main.py # FastAPI application
|
|
135
|
+
│ ├── core/
|
|
136
|
+
│ │ ├── __init__.py
|
|
137
|
+
│ │ ├── config.py # Settings
|
|
138
|
+
│ │ └── exceptions.py # Custom exceptions
|
|
139
|
+
│ ├── deps/
|
|
140
|
+
│ │ └── __init__.py # Dependencies
|
|
141
|
+
│ ├── utils/
|
|
142
|
+
│ │ └── __init__.py # Utility functions
|
|
143
|
+
│ ├── v1/
|
|
144
|
+
│ │ ├── __init__.py
|
|
145
|
+
│ │ └── apis/
|
|
146
|
+
│ │ ├── __init__.py
|
|
147
|
+
│ │ └── routes.py # API routes
|
|
148
|
+
│ ├── services/
|
|
149
|
+
│ │ └── __init__.py # Business logic
|
|
150
|
+
│ ├── schemas/
|
|
151
|
+
│ │ └── __init__.py # Pydantic schemas
|
|
152
|
+
│ └── models/
|
|
153
|
+
│ └── __init__.py # SQLAlchemy models
|
|
154
|
+
├── .env
|
|
155
|
+
├── requirements.txt
|
|
156
|
+
├── .gitignore
|
|
157
|
+
└── README.md
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Development
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
# Install development dependencies
|
|
164
|
+
pip install -e ".[dev]"
|
|
165
|
+
|
|
166
|
+
# Run tests
|
|
167
|
+
pytest
|
|
168
|
+
|
|
169
|
+
# Format code
|
|
170
|
+
black .
|
|
171
|
+
ruff check --fix .
|
|
172
|
+
|
|
173
|
+
# Type checking
|
|
174
|
+
mypy .
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Contributing
|
|
178
|
+
|
|
179
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
180
|
+
|
|
181
|
+
## License
|
|
182
|
+
|
|
183
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
gentem/__init__.py,sha256=gW0VRbNnpFuyVzLPzO7A0vp0O0bs5oIV-cQx7RBX30E,86
|
|
2
|
+
gentem/__main__.py,sha256=1j3yOSbbv3Yfrr9HMuQD2zes6SpiiqwIksPSFcntlps,114
|
|
3
|
+
gentem/cli.py,sha256=tTgEUTS78nZBsZ0DyJb9vMLWu1Ow8L7KVetRRe8BCNU,4007
|
|
4
|
+
gentem/template_engine.py,sha256=WWu5m1h63lD0pxVHUSftEnFFWNf1OYqHQ01pGvb9qq0,6019
|
|
5
|
+
gentem/commands/__init__.py,sha256=_dNABeV14JFMeOB1htS-bzqj87EA99X1VSXw-37QIhM,36
|
|
6
|
+
gentem/commands/fastapi.py,sha256=BUH4Y-KkOA56XJNO01zyFlfbMClNlNT9KOWE0vMiolI,20267
|
|
7
|
+
gentem/commands/new.py,sha256=OYTTSBz9X6MObYKpIKPFhyzQJ3PyU1owcfxwL1pH6XQ,22912
|
|
8
|
+
gentem/utils/__init__.py,sha256=TJuXgn7gV-zF8HZuQv3uU-BlwlU1mJpiBDYoeliCFWo,283
|
|
9
|
+
gentem/utils/validators.py,sha256=lFDd1Qn-F2Npyq0illN4vI1SehKxf2qm2bIeWd3mwHM,4389
|
|
10
|
+
gentem-0.1.3.dist-info/licenses/LICENSE,sha256=ng7Oliq_MR4vJnb76Dg2QccUXSozR4-bIRzNTOvtGUc,1086
|
|
11
|
+
gentem-0.1.3.dist-info/METADATA,sha256=EE6QeyVV8gQgEX-0jFBF3XW-sTz_04HCeMU_bqmSAEE,5095
|
|
12
|
+
gentem-0.1.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
13
|
+
gentem-0.1.3.dist-info/entry_points.txt,sha256=M2X3uoUyPS1rcKCwy2hpT3PT1KyAhoOXCfvGoxj0NLM,43
|
|
14
|
+
gentem-0.1.3.dist-info/top_level.txt,sha256=kGBfHc-pTzlx-A68GISTSLXtBd3__eBad_zXoBW4-wA,7
|
|
15
|
+
gentem-0.1.3.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Abu Bakr
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
gentem
|