pytemplate-uv 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.
- pytemplate/__init__.py +1 -0
- pytemplate/main.py +142 -0
- pytemplate_uv-0.1.0.dist-info/LICENSE +21 -0
- pytemplate_uv-0.1.0.dist-info/METADATA +142 -0
- pytemplate_uv-0.1.0.dist-info/RECORD +53 -0
- pytemplate_uv-0.1.0.dist-info/WHEEL +5 -0
- pytemplate_uv-0.1.0.dist-info/entry_points.txt +3 -0
- pytemplate_uv-0.1.0.dist-info/top_level.txt +1 -0
- templates/fastapi-template/cookiecutter.json +23 -0
- templates/fastapi-template/{{cookiecutter.project_name}}/Dockerfile +38 -0
- templates/fastapi-template/{{cookiecutter.project_name}}/Makefile +145 -0
- templates/fastapi-template/{{cookiecutter.project_name}}/README.md +99 -0
- templates/fastapi-template/{{cookiecutter.project_name}}/pyproject.toml +115 -0
- templates/fastapi-template/{{cookiecutter.project_name}}/tests/__init__.py +0 -0
- templates/fastapi-template/{{cookiecutter.project_name}}/tests/conftest.py +85 -0
- templates/fastapi-template/{{cookiecutter.project_name}}/tests/test_cli.py +33 -0
- templates/fastapi-template/{{cookiecutter.project_name}}/tests/test_example.py +104 -0
- templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.envfile}} +40 -0
- templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/__init__.py +0 -0
- templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/__pycache__/__init__.cpython-311.pyc +0 -0
- templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/__pycache__/db.cpython-311.pyc +0 -0
- templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/__pycache__/main.cpython-311.pyc +0 -0
- templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/__pycache__/models.cpython-311.pyc +0 -0
- templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/cli.py +61 -0
- templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/core/__pycache__/config.cpython-311.pyc +0 -0
- templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/core/__pycache__/exceptions.cpython-311.pyc +0 -0
- templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/core/__pycache__/middleware.cpython-311.pyc +0 -0
- templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/core/config.py +48 -0
- templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/core/exceptions.py +73 -0
- templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/core/middleware.py +100 -0
- templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/db.py +12 -0
- templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/main.py +62 -0
- templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/models.py +7 -0
- templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/routers/__init__.py +0 -0
- templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/routers/__pycache__/__init__.cpython-311.pyc +0 -0
- templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/routers/__pycache__/example.cpython-311.pyc +0 -0
- templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/routers/example.py +102 -0
- templates/pyproject-template/cookiecutter.json +24 -0
- templates/pyproject-template/{{cookiecutter.project_name}}/Dockerfile +35 -0
- templates/pyproject-template/{{cookiecutter.project_name}}/Makefile +145 -0
- templates/pyproject-template/{{cookiecutter.project_name}}/README.md +80 -0
- templates/pyproject-template/{{cookiecutter.project_name}}/pyproject.toml +106 -0
- templates/pyproject-template/{{cookiecutter.project_name}}/tests/__init__.py +0 -0
- templates/pyproject-template/{{cookiecutter.project_name}}/tests/test_cli.py +33 -0
- templates/pyproject-template/{{cookiecutter.project_name}}/tests/test_main.py +2 -0
- templates/pyproject-template/{{cookiecutter.project_name}}/{{cookiecutter.envfile}} +41 -0
- templates/pyproject-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/__init__.py +0 -0
- templates/pyproject-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/__pycache__/__init__.cpython-311.pyc +0 -0
- templates/pyproject-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/__pycache__/example_module.cpython-311.pyc +0 -0
- templates/pyproject-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/__pycache__/main.cpython-311.pyc +0 -0
- templates/pyproject-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/cli.py +61 -0
- templates/pyproject-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/example_module.py +1 -0
- templates/pyproject-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/main.py +5 -0
pytemplate/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Pytemplate package initialization
|
pytemplate/main.py
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""Command-line interface for creating Python projects from templates."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import subprocess
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import typer
|
|
10
|
+
from rich.console import Console
|
|
11
|
+
from rich.panel import Panel
|
|
12
|
+
from rich.text import Text
|
|
13
|
+
|
|
14
|
+
app = typer.Typer(
|
|
15
|
+
name="pytemplate-uv",
|
|
16
|
+
help="Create Python projects from templates using uv package manager",
|
|
17
|
+
add_completion=True,
|
|
18
|
+
rich_help_panel=True,
|
|
19
|
+
)
|
|
20
|
+
console = Console()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _validate_template(template: str) -> Path:
|
|
24
|
+
"""Validate and return the path to the specified template.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
template (str): Name of the project template.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
Path: Path to the template directory.
|
|
31
|
+
|
|
32
|
+
Raises:
|
|
33
|
+
typer.BadParameter: If template is not found.
|
|
34
|
+
|
|
35
|
+
"""
|
|
36
|
+
templates_dir = Path(__file__).parent.parent / "templates"
|
|
37
|
+
template_path = templates_dir / f"{template}-template"
|
|
38
|
+
|
|
39
|
+
console.print(f"[yellow]Checking template path: {template_path}[/]")
|
|
40
|
+
|
|
41
|
+
if not template_path.exists():
|
|
42
|
+
available_templates = [
|
|
43
|
+
d.name.replace("-template", "") for d in templates_dir.glob("*-template") if d.is_dir()
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
error_text = Text("Template not found!", style="bold red")
|
|
47
|
+
suggestion_text = Text(
|
|
48
|
+
f"\nAvailable templates: {', '.join(available_templates)}", style="yellow"
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
console.print(Panel(Text.assemble(error_text, suggestion_text), border_style="red"))
|
|
52
|
+
raise typer.BadParameter(f"Template '{template}' not found")
|
|
53
|
+
|
|
54
|
+
console.print(f"[green]Using template path: {template_path}[/]")
|
|
55
|
+
return template_path
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _get_context() -> dict[str, str]:
|
|
59
|
+
"""Retrieve context variables for project template.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
A dictionary of context variables.
|
|
63
|
+
|
|
64
|
+
"""
|
|
65
|
+
return {
|
|
66
|
+
"author": os.environ.get("USER", "your name"),
|
|
67
|
+
"email": os.environ.get("USER_EMAIL", "your@email.com"),
|
|
68
|
+
"github_username": os.environ.get("GITHUB_USERNAME", "your_username"),
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _build_cookiecutter_command(
|
|
73
|
+
template_path: Path, context: dict, no_input: bool = False, force: bool = False
|
|
74
|
+
) -> list[str]:
|
|
75
|
+
"""Build the cookiecutter command with appropriate options.
|
|
76
|
+
Args:
|
|
77
|
+
template_path: Path to the project template
|
|
78
|
+
context: Context variables for the template
|
|
79
|
+
no_input: Whether to skip interactive prompts
|
|
80
|
+
force: Whether to overwrite existing project directory
|
|
81
|
+
Returns:
|
|
82
|
+
A list of command arguments.
|
|
83
|
+
|
|
84
|
+
""" # noqa: D205, D411
|
|
85
|
+
cookiecutter_cmd = [
|
|
86
|
+
"cookiecutter",
|
|
87
|
+
str(template_path),
|
|
88
|
+
*[f"{k}={v}" for k, v in context.items()],
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
if no_input:
|
|
92
|
+
cookiecutter_cmd.append("--no-input")
|
|
93
|
+
|
|
94
|
+
if force:
|
|
95
|
+
cookiecutter_cmd.append("--overwrite-if-exists")
|
|
96
|
+
|
|
97
|
+
return cookiecutter_cmd
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@app.command()
|
|
101
|
+
def create_project(
|
|
102
|
+
project_name: str = typer.Argument(None, help="Name of the project"),
|
|
103
|
+
template: str = typer.Option(
|
|
104
|
+
"pyproject", "--template", "-t", help="Template to use for project creation"
|
|
105
|
+
),
|
|
106
|
+
no_input: bool = typer.Option(
|
|
107
|
+
False, "--no-input", "-y", help="Skip prompts and use default values"
|
|
108
|
+
),
|
|
109
|
+
force: bool = typer.Option(False, "--force", "-f", help="Overwrite existing project directory"),
|
|
110
|
+
):
|
|
111
|
+
"""Create a new project from a specified template."""
|
|
112
|
+
template_path = _validate_template(template)
|
|
113
|
+
context = _get_context()
|
|
114
|
+
if project_name:
|
|
115
|
+
context["project_name"] = project_name
|
|
116
|
+
|
|
117
|
+
cookiecutter_cmd = _build_cookiecutter_command(template_path, context, no_input, force)
|
|
118
|
+
|
|
119
|
+
try:
|
|
120
|
+
result = subprocess.run(cookiecutter_cmd, text=True, check=True)
|
|
121
|
+
console.print("[green]Project created successfully![/]")
|
|
122
|
+
|
|
123
|
+
if result.stdout:
|
|
124
|
+
console.print(f"[green]{result.stdout.strip()}[/]")
|
|
125
|
+
|
|
126
|
+
except subprocess.CalledProcessError as e:
|
|
127
|
+
console.print("[red]Project creation failed:[/]")
|
|
128
|
+
console.print(f"Command: {' '.join(e.cmd)}")
|
|
129
|
+
|
|
130
|
+
if e.stdout:
|
|
131
|
+
console.print("[yellow]Standard Output:[/]")
|
|
132
|
+
console.print(e.stdout)
|
|
133
|
+
|
|
134
|
+
if e.stderr:
|
|
135
|
+
console.print("[red]Error Output:[/]")
|
|
136
|
+
console.print(e.stderr)
|
|
137
|
+
|
|
138
|
+
raise typer.Exit(code=1) # noqa: B904
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
if __name__ == "__main__":
|
|
142
|
+
app()
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Leo Liu
|
|
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,142 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: pytemplate-uv
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A template for creating Python projects using uv package manager
|
|
5
|
+
Author-email: Leo Liu <yuxuzi@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/yuxuzi/pytemplate-uv
|
|
8
|
+
Project-URL: Repository, https://github.com/yuxuzi/pytemplate-uv
|
|
9
|
+
Project-URL: Issues, https://github.com/yuxuzi/pytemplate-uv/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/yuxuzi/pytemplate-uv/blob/main/CHANGELOG.md
|
|
11
|
+
Keywords: python,template,project-generator,uv,cookiecutter,cli
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
+
Classifier: Topic :: Utilities
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: typer>=0.9.0
|
|
24
|
+
Requires-Dist: rich>=13.7.0
|
|
25
|
+
Requires-Dist: cookiecutter>=2.5.0
|
|
26
|
+
Requires-Dist: pytest>=7.4.4
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: ruff>=0.1.9; extra == "dev"
|
|
29
|
+
Requires-Dist: black>=23.12.1; extra == "dev"
|
|
30
|
+
Requires-Dist: pytest>=7.4.4; extra == "dev"
|
|
31
|
+
Requires-Dist: pre-commit>=3.6.0; extra == "dev"
|
|
32
|
+
Requires-Dist: mypy>=1.7.1; extra == "dev"
|
|
33
|
+
Requires-Dist: coverage>=7.3.2; extra == "dev"
|
|
34
|
+
Provides-Extra: docs
|
|
35
|
+
Requires-Dist: mkdocs>=1.5.3; extra == "docs"
|
|
36
|
+
Requires-Dist: mkdocstrings[python]>=0.24.0; extra == "docs"
|
|
37
|
+
|
|
38
|
+
# PyTemplate UV
|
|
39
|
+
|
|
40
|
+
## Overview
|
|
41
|
+
|
|
42
|
+
PyTemplate UV is a powerful CLI tool for creating Python project templates using the `uv` package manager. It provides a seamless way to generate standardized project structures with modern development practices.
|
|
43
|
+
|
|
44
|
+
## Features
|
|
45
|
+
|
|
46
|
+
- ๐ Quick project initialization
|
|
47
|
+
- ๐ง Support for multiple project templates (FastAPI, Standard Python)
|
|
48
|
+
- ๐ฆ Integrated with `uv` package management
|
|
49
|
+
- ๐งช Comprehensive testing infrastructure
|
|
50
|
+
- ๐ Linting and type checking support
|
|
51
|
+
|
|
52
|
+
## Prerequisites
|
|
53
|
+
|
|
54
|
+
- Python 3.11+
|
|
55
|
+
- `uv` package manager
|
|
56
|
+
- `cookiecutter`
|
|
57
|
+
|
|
58
|
+
## Installation
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pip install pytemplate-uv
|
|
62
|
+
# or
|
|
63
|
+
uv pip install pytemplate-uv
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Usage
|
|
67
|
+
|
|
68
|
+
### Create a Project
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Create a standard Python project (default)
|
|
72
|
+
pytemplate-uv create-project
|
|
73
|
+
|
|
74
|
+
# Create a FastAPI project
|
|
75
|
+
pytemplate-uv create-project --template fastapi
|
|
76
|
+
|
|
77
|
+
# Create a project with a custom name
|
|
78
|
+
pytemplate-uv create-project --name my-awesome-project
|
|
79
|
+
|
|
80
|
+
# Create a project with custom template and name
|
|
81
|
+
pytemplate-uv create-project --template fastapi --name my-api-project
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Project Creation Options
|
|
85
|
+
|
|
86
|
+
- `--template`: Choose the project template (default: pyproject)
|
|
87
|
+
- Options: `pyproject`, `fastapi`
|
|
88
|
+
- `--name`: Specify a custom project name
|
|
89
|
+
- Additional template-specific options can be passed as needed
|
|
90
|
+
|
|
91
|
+
### Next Steps After Project Creation
|
|
92
|
+
|
|
93
|
+
1. `cd` into your project directory
|
|
94
|
+
2. Run `uv venv` to create a virtual environment
|
|
95
|
+
3. Run `uv pip install -e .` to install dependencies
|
|
96
|
+
4. Run `make setup` to set up the development environment
|
|
97
|
+
|
|
98
|
+
## Development
|
|
99
|
+
|
|
100
|
+
### Setup
|
|
101
|
+
|
|
102
|
+
1. Clone the repository
|
|
103
|
+
2. Create a virtual environment:
|
|
104
|
+
```bash
|
|
105
|
+
uv venv
|
|
106
|
+
source .venv/bin/activate
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
3. Install development dependencies:
|
|
110
|
+
```bash
|
|
111
|
+
uv pip install -e .[dev]
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Running Tests
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
pytest
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Linting
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
ruff check .
|
|
124
|
+
black .
|
|
125
|
+
mypy .
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Contributing
|
|
129
|
+
|
|
130
|
+
Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details.
|
|
131
|
+
|
|
132
|
+
## License
|
|
133
|
+
|
|
134
|
+
MIT License - See [LICENSE](LICENSE) for details.
|
|
135
|
+
|
|
136
|
+
## Contact
|
|
137
|
+
|
|
138
|
+
Leo Liu - [GitHub](https://github.com/yuxuzi)
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
*Simplifying Python project creation with modern best practices* ๐โจ
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
pytemplate/__init__.py,sha256=VY5oWgjrWxifkHlWGCMvKNgCXT-MHQKoHTCerVYpd_I,36
|
|
2
|
+
pytemplate/main.py,sha256=ZWq98WOMtwcXkA_pVqWS0Tmc7aeij3woeQuOOjIqmNU,4258
|
|
3
|
+
templates/fastapi-template/cookiecutter.json,sha256=PPdGDw3mt0qIdkke_Sh0u_yWbTWzobwmXZnVV5WrKcA,660
|
|
4
|
+
templates/fastapi-template/{{cookiecutter.project_name}}/Dockerfile,sha256=dA0wD9ct9jyrYy-hRoUZUYI-TYApaRVT4vsCOwz9vJ4,1057
|
|
5
|
+
templates/fastapi-template/{{cookiecutter.project_name}}/Makefile,sha256=PFeKW96hdb9Wyq9Y09m_H0RXgSe976IfoD8qXqrlndQ,3689
|
|
6
|
+
templates/fastapi-template/{{cookiecutter.project_name}}/README.md,sha256=vmSIwHIK--ZyyREBmqM-WXXeqWEVHFUopUExtUNXRtc,1649
|
|
7
|
+
templates/fastapi-template/{{cookiecutter.project_name}}/pyproject.toml,sha256=vBacMor5LFcX9bzehgZhMcbElet89ldGAUA5IWImoQI,2964
|
|
8
|
+
templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.envfile}},sha256=F7VgE-qK4V5UkncPsSXI6fs8nTGMpX7TPumXyuHni14,782
|
|
9
|
+
templates/fastapi-template/{{cookiecutter.project_name}}/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
templates/fastapi-template/{{cookiecutter.project_name}}/tests/conftest.py,sha256=K8ePAhQuDhc-3IqmDy-zX8OzJOv722VfOjJeiwqieK8,2382
|
|
11
|
+
templates/fastapi-template/{{cookiecutter.project_name}}/tests/test_cli.py,sha256=WrAM11xHJYToK9vGDMzWLDe5Rul1XU3lxrd13_AHIn8,1047
|
|
12
|
+
templates/fastapi-template/{{cookiecutter.project_name}}/tests/test_example.py,sha256=EUF-8PnvP-abBjH-cDR8v02ZGFctIlGb18AVLcZu7n4,3573
|
|
13
|
+
templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
+
templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/cli.py,sha256=bkn_TQxTYy0QLHHWMirhPChqOitffSA1KwNtk82VjT4,1595
|
|
15
|
+
templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/db.py,sha256=R9OTAOt8WZWLP8ZtmbobrLbtGV_8PzEQaBJZbRStMQI,458
|
|
16
|
+
templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/main.py,sha256=m7Iwt8I__9BOTFRKIOiE-OXuxiPw-9NS6zZRbVCEmjQ,1481
|
|
17
|
+
templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/models.py,sha256=WLLtpctDIO6P39d4nUobRdmn774177KONrQjgzxdngU,273
|
|
18
|
+
templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/__pycache__/__init__.cpython-311.pyc,sha256=rpvf30Q0nPPW6jJW27HgT77rn5Uytuj7Pxg41InoMUs,397
|
|
19
|
+
templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/__pycache__/db.cpython-311.pyc,sha256=MZNvPK3jTLBQtgX6zIl-_rv1Fy1QsrcrdPFQAIY4mlM,1366
|
|
20
|
+
templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/__pycache__/main.cpython-311.pyc,sha256=WTK9Oj_JfSC_FYY5foGSKBJCV0O6UMFgARtFdVjQLRU,2826
|
|
21
|
+
templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/__pycache__/models.cpython-311.pyc,sha256=j46wmZckuJXvXJA08bU1Elu8QnqoJXpTBOUIIBrJELc,847
|
|
22
|
+
templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/core/config.py,sha256=LfXNZVw2oju5HwUxO0vYrwIcb6n5Z9ljy6AxAEjmBxc,1236
|
|
23
|
+
templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/core/exceptions.py,sha256=QxlrAOTTvEQAcrvGHVzfWAalGBLKEvRPRAeRVfShjSU,2125
|
|
24
|
+
templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/core/middleware.py,sha256=ZsYXcPvIcOYBD2sIueXUIsXBu7zCeCKnF3iufwH_ltg,3122
|
|
25
|
+
templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/core/__pycache__/config.cpython-311.pyc,sha256=kCGo1K1u1TusGDardoXyIQ4HnZzzy9S1D0oocnTDyEE,2294
|
|
26
|
+
templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/core/__pycache__/exceptions.cpython-311.pyc,sha256=cB6SKzzzwWHjpfnv24pRgZeF9TdKoF9qAi_uPNfL5_A,5211
|
|
27
|
+
templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/core/__pycache__/middleware.cpython-311.pyc,sha256=dTVnxQBZ1SyhVEoXld2qbx6CNZIzlVi3CfaueoRsSLc,4434
|
|
28
|
+
templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/routers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
29
|
+
templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/routers/example.py,sha256=oS1kC18aJK9xhFvqGGkHpDY7XGUK0yEsSWn2l6LsY98,2831
|
|
30
|
+
templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/routers/__pycache__/__init__.cpython-311.pyc,sha256=oF7_NSYEmm7TINmuYJ8zAdubRdMrpLkmbFbw1u_Sr6s,252
|
|
31
|
+
templates/fastapi-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/routers/__pycache__/example.cpython-311.pyc,sha256=W4wS240XIEuXNiRXXRAtlXqI5yQx7baaTLQ2hY3dDm0,5244
|
|
32
|
+
templates/pyproject-template/cookiecutter.json,sha256=ZCqlDi3Z4nG8wN8Cf3vNwyTE7_aBdmZDVQdmMla6Xx8,688
|
|
33
|
+
templates/pyproject-template/{{cookiecutter.project_name}}/Dockerfile,sha256=YBLJVbKfJQlhImd_eT9IFAvk899PpVqDirdD4aiDF5w,1046
|
|
34
|
+
templates/pyproject-template/{{cookiecutter.project_name}}/Makefile,sha256=CQnKkpd8inbVDZRcIg8LPKPzSpV6kX1pJg4t3WCAzCc,3683
|
|
35
|
+
templates/pyproject-template/{{cookiecutter.project_name}}/README.md,sha256=6CXB-ke6-TjH47dQ-KXlZHFCLA7_6A9XPs8iPShmiEM,1248
|
|
36
|
+
templates/pyproject-template/{{cookiecutter.project_name}}/pyproject.toml,sha256=wADwFzOGeYil-Z7_JDNpc7c3LyPQ3mFbYzeNCXJFnAs,2396
|
|
37
|
+
templates/pyproject-template/{{cookiecutter.project_name}}/{{cookiecutter.envfile}},sha256=H0tMaYSKUwgObikNNGd1mXivYpIjasxNG27HvHF7WJU,968
|
|
38
|
+
templates/pyproject-template/{{cookiecutter.project_name}}/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
39
|
+
templates/pyproject-template/{{cookiecutter.project_name}}/tests/test_cli.py,sha256=WrAM11xHJYToK9vGDMzWLDe5Rul1XU3lxrd13_AHIn8,1047
|
|
40
|
+
templates/pyproject-template/{{cookiecutter.project_name}}/tests/test_main.py,sha256=KG9-9kwp105ODKM-UV0TvOoA49h7E9nxSfNIt78X3tw,36
|
|
41
|
+
templates/pyproject-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
42
|
+
templates/pyproject-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/cli.py,sha256=oVkN8vEs-k1LTzYB7jyNT01IiZ_v9xO_xSlENXfpFII,1594
|
|
43
|
+
templates/pyproject-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/example_module.py,sha256=UfwjmUgqo_Pev8mZi6HuAOk4PT0Z1tWReZB7WapENl8,17
|
|
44
|
+
templates/pyproject-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/main.py,sha256=bkPX2DwXt65x2NLnIFPNoOWMTn0M5OXmr8XMr9xTl8o,78
|
|
45
|
+
templates/pyproject-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/__pycache__/__init__.cpython-311.pyc,sha256=64PchSH6nA26CgiIvfN1S-Ko7sSjEXft2c9q9rH43uU,399
|
|
46
|
+
templates/pyproject-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/__pycache__/example_module.cpython-311.pyc,sha256=m6eflFezuGlnjcwcZR_InP0IhHNQSLBGAMJ3a-nc8m4,252
|
|
47
|
+
templates/pyproject-template/{{cookiecutter.project_name}}/{{cookiecutter.package_name}}/__pycache__/main.cpython-311.pyc,sha256=Xre-KLGyTkkicox5fIEgahZ-ouxEW_lkvsn-F8cXXRI,498
|
|
48
|
+
pytemplate_uv-0.1.0.dist-info/LICENSE,sha256=qGN1csUyIHsyQcOo3taVjJbE0Z_mNoZUULIGY0Mc-lY,1064
|
|
49
|
+
pytemplate_uv-0.1.0.dist-info/METADATA,sha256=8mwONgHSxrnSReZbo9osggah3DpAxYExCoJRNtF3ars,3702
|
|
50
|
+
pytemplate_uv-0.1.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
51
|
+
pytemplate_uv-0.1.0.dist-info/entry_points.txt,sha256=JpY20DCqntUnmu5HOA8sXqoHpB2oKZ10pi1n8DrrLek,88
|
|
52
|
+
pytemplate_uv-0.1.0.dist-info/top_level.txt,sha256=m4b8YiLi0BJ7kR2AXXY3_Me075SCU1C7qEzucz92qgY,11
|
|
53
|
+
pytemplate_uv-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pytemplate
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"project_name": "my-fastapi-project",
|
|
3
|
+
"package_name": "{{cookiecutter.project_name|lower|replace('-', '_')}}",
|
|
4
|
+
"author": "{{ cookiecutter.get('author', 'Your Name') }}",
|
|
5
|
+
"email": "{{ cookiecutter.get('email', 'your@email.com') }}",
|
|
6
|
+
"github_username": "{{ cookiecutter.get('github_username', 'your_username') }}",
|
|
7
|
+
"description": "A FastAPI project template",
|
|
8
|
+
"python_version": "3.11",
|
|
9
|
+
"docs": "y",
|
|
10
|
+
"coverage": "y",
|
|
11
|
+
"dockerfile": "y",
|
|
12
|
+
"mypy": "y",
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"envfile": ".env",
|
|
15
|
+
"_copy_without_render": [
|
|
16
|
+
"docs",
|
|
17
|
+
"coverage",
|
|
18
|
+
"dockerfile",
|
|
19
|
+
"mypy",
|
|
20
|
+
"envfile",
|
|
21
|
+
"license"
|
|
22
|
+
]
|
|
23
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
FROM python:{{cookiecutter.python_version}}-slim as builder
|
|
2
|
+
|
|
3
|
+
WORKDIR /{{cookiecutter.project_name}}
|
|
4
|
+
|
|
5
|
+
# Install uv
|
|
6
|
+
RUN pip install --no-cache-dir uv
|
|
7
|
+
|
|
8
|
+
# Copy only the files needed for dependency installation
|
|
9
|
+
COPY pyproject.toml .
|
|
10
|
+
COPY {{cookiecutter.project_name}} ./{{cookiecutter.project_name}}
|
|
11
|
+
|
|
12
|
+
# Use uv to install dependencies
|
|
13
|
+
RUN uv pip install --system .
|
|
14
|
+
|
|
15
|
+
FROM python:{{cookiecutter.python_version}}-slim
|
|
16
|
+
|
|
17
|
+
WORKDIR /{{cookiecutter.project_name}}
|
|
18
|
+
|
|
19
|
+
# Create non-root user
|
|
20
|
+
RUN adduser --disabled-password --gecos '' appuser && \
|
|
21
|
+
pip install --no-cache-dir uv
|
|
22
|
+
|
|
23
|
+
# Copy the installed dependencies and project files
|
|
24
|
+
COPY --from=builder /usr/local/lib/python*/site-packages /usr/local/lib/python*/site-packages/
|
|
25
|
+
COPY {{cookiecutter.project_name}} ./{{cookiecutter.project_name}}
|
|
26
|
+
COPY pyproject.toml .
|
|
27
|
+
|
|
28
|
+
# Switch to non-root user
|
|
29
|
+
USER appuser
|
|
30
|
+
|
|
31
|
+
EXPOSE 8000
|
|
32
|
+
|
|
33
|
+
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \
|
|
34
|
+
CMD curl -f http://localhost:8000/health || exit 1
|
|
35
|
+
|
|
36
|
+
# Use make start to run the application
|
|
37
|
+
ENTRYPOINT ["make"]
|
|
38
|
+
CMD ["start"]
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Detect the operating system
|
|
2
|
+
ifeq ($(OS),Windows_NT)
|
|
3
|
+
VENV_ACTIVATE = .venv\Scripts\activate
|
|
4
|
+
PYTHON = .venv\Scripts\python
|
|
5
|
+
RM = rmdir /s /q
|
|
6
|
+
MKDIR = mkdir
|
|
7
|
+
else
|
|
8
|
+
VENV_ACTIVATE = . .venv/bin/activate
|
|
9
|
+
PYTHON = .venv/bin/python
|
|
10
|
+
RM = rm -rf
|
|
11
|
+
MKDIR = mkdir -p
|
|
12
|
+
endif
|
|
13
|
+
|
|
14
|
+
# Global prerequisite to ensure virtual environment is activated
|
|
15
|
+
.PHONY: venv-activate
|
|
16
|
+
venv-activate:
|
|
17
|
+
@if [ ! -d ".venv" ]; then \
|
|
18
|
+
echo "Virtual environment not found. Creating..."; \
|
|
19
|
+
uv venv; \
|
|
20
|
+
fi
|
|
21
|
+
@echo "Activating virtual environment..."
|
|
22
|
+
@$(VENV_ACTIVATE)
|
|
23
|
+
|
|
24
|
+
# Wrapper function to ensure virtual environment is activated
|
|
25
|
+
define ensure_venv
|
|
26
|
+
@if [ -z "$(VIRTUAL_ENV)" ]; then \
|
|
27
|
+
echo "Activating virtual environment..."; \
|
|
28
|
+
. .venv/bin/activate; \
|
|
29
|
+
fi
|
|
30
|
+
endef
|
|
31
|
+
|
|
32
|
+
# Apply venv-activate as a global prerequisite
|
|
33
|
+
.PHONY: all
|
|
34
|
+
all: venv-activate
|
|
35
|
+
|
|
36
|
+
# Modify all targets to depend on venv-activate
|
|
37
|
+
.PHONY: help setup dev start run lint lint-fix format format-check test test-cov clean docs build publish
|
|
38
|
+
|
|
39
|
+
help: venv-activate
|
|
40
|
+
@echo " {{cookiecutter.project_name}} - Project Management Commands"
|
|
41
|
+
@echo ""
|
|
42
|
+
@echo "Development:"
|
|
43
|
+
@echo " make setup - Create virtual environment and install dependencies"
|
|
44
|
+
@echo " make dev - Set up development environment (pre-commit, etc.)"
|
|
45
|
+
@echo " make start - Start the application"
|
|
46
|
+
@echo " make run - Alias for 'start'"
|
|
47
|
+
@echo ""
|
|
48
|
+
@echo "Code Quality:"
|
|
49
|
+
@echo " make lint - Run code linting"
|
|
50
|
+
@echo " make lint-fix - Run linter with auto-fix"
|
|
51
|
+
@echo " make format - Format code using Black"
|
|
52
|
+
@echo " make format-check - Check code formatting"
|
|
53
|
+
@echo ""
|
|
54
|
+
@echo "Testing:"
|
|
55
|
+
@echo " make test - Run tests"
|
|
56
|
+
@echo " make test-cov - Run tests with coverage report"
|
|
57
|
+
@echo ""
|
|
58
|
+
@echo "Documentation:"
|
|
59
|
+
@echo " make docs - Generate documentation"
|
|
60
|
+
@echo ""
|
|
61
|
+
@echo "Packaging:"
|
|
62
|
+
@echo " make build - Build distribution packages"
|
|
63
|
+
@echo " make publish - Publish package to PyPI"
|
|
64
|
+
@echo ""
|
|
65
|
+
@echo "Cleanup:"
|
|
66
|
+
@echo " make clean - Remove virtual environment and cache files"
|
|
67
|
+
|
|
68
|
+
# Setup project dependencies
|
|
69
|
+
setup: venv-activate
|
|
70
|
+
@echo "Installing dependencies..."
|
|
71
|
+
$(ensure_venv)
|
|
72
|
+
uv pip install -e ".[dev, docs, test]" && pre-commit install
|
|
73
|
+
|
|
74
|
+
# Start the application
|
|
75
|
+
start: venv-activate
|
|
76
|
+
@echo "Starting application..."
|
|
77
|
+
$(ensure_venv)
|
|
78
|
+
fastapi dev {{cookiecutter.package_name}}/main.py
|
|
79
|
+
|
|
80
|
+
# Alias for start
|
|
81
|
+
run: start
|
|
82
|
+
|
|
83
|
+
# Linting
|
|
84
|
+
lint: venv-activate
|
|
85
|
+
@echo "Running linter..."
|
|
86
|
+
$(ensure_venv)
|
|
87
|
+
ruff check .
|
|
88
|
+
|
|
89
|
+
# Linting with auto-fix
|
|
90
|
+
lint-fix: venv-activate
|
|
91
|
+
@echo "Running linter with auto-fix..."
|
|
92
|
+
$(ensure_venv)
|
|
93
|
+
ruff check . --fix
|
|
94
|
+
|
|
95
|
+
# Code formatting
|
|
96
|
+
format: venv-activate
|
|
97
|
+
@echo "Formatting code with Black..."
|
|
98
|
+
$(ensure_venv)
|
|
99
|
+
black .
|
|
100
|
+
|
|
101
|
+
# Check code formatting
|
|
102
|
+
format-check: venv-activate
|
|
103
|
+
@echo "Checking code formatting..."
|
|
104
|
+
$(ensure_venv)
|
|
105
|
+
black . --check
|
|
106
|
+
|
|
107
|
+
# Run tests
|
|
108
|
+
test: venv-activate
|
|
109
|
+
@echo "Running tests..."
|
|
110
|
+
$(ensure_venv)
|
|
111
|
+
pytest tests/
|
|
112
|
+
|
|
113
|
+
# Run tests with coverage
|
|
114
|
+
test-cov: venv-activate
|
|
115
|
+
@echo "Running tests with coverage..."
|
|
116
|
+
$(ensure_venv)
|
|
117
|
+
pytest --cov={{cookiecutter.package_name}} --cov-report=term-missing
|
|
118
|
+
|
|
119
|
+
# Clean up virtual environment and cache files
|
|
120
|
+
clean:
|
|
121
|
+
@echo "Cleaning up..."
|
|
122
|
+
$(RM) .venv
|
|
123
|
+
$(RM) .pytest_cache
|
|
124
|
+
$(RM) .coverage
|
|
125
|
+
find . -type d -name "__pycache__" -exec $(RM) {} +
|
|
126
|
+
|
|
127
|
+
# Generate documentation
|
|
128
|
+
docs: venv-activate
|
|
129
|
+
@echo "Generating documentation..."
|
|
130
|
+
$(ensure_venv)
|
|
131
|
+
pdoc -o docs {{cookiecutter.package_name}}
|
|
132
|
+
|
|
133
|
+
# Build distribution packages
|
|
134
|
+
build: venv-activate
|
|
135
|
+
@echo "Building distribution packages..."
|
|
136
|
+
$(ensure_venv)
|
|
137
|
+
uv pip install build
|
|
138
|
+
python -m build
|
|
139
|
+
|
|
140
|
+
# Publish to PyPI
|
|
141
|
+
publish: build
|
|
142
|
+
@echo "Publishing to PyPI..."
|
|
143
|
+
$(ensure_venv)
|
|
144
|
+
uv pip install twine
|
|
145
|
+
twine upload dist/*
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# {{cookiecutter.project_name}}
|
|
2
|
+
|
|
3
|
+
{{cookiecutter.description}}
|
|
4
|
+
|
|
5
|
+
## ๐ Features
|
|
6
|
+
|
|
7
|
+
- Modern FastAPI web application
|
|
8
|
+
- Async SQLModel database integration
|
|
9
|
+
- Comprehensive development tooling
|
|
10
|
+
- Robust testing and type checking
|
|
11
|
+
- Docker support
|
|
12
|
+
|
|
13
|
+
## ๐ฆ Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Using pip
|
|
17
|
+
pip install {{cookiecutter.package_name}}
|
|
18
|
+
|
|
19
|
+
# Using uv
|
|
20
|
+
uv pip install {{cookiecutter.package_name}}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## ๐ง Development Setup
|
|
24
|
+
|
|
25
|
+
1. Clone the repository
|
|
26
|
+
2. Create a virtual environment
|
|
27
|
+
```bash
|
|
28
|
+
uv venv
|
|
29
|
+
. .venv/bin/activate
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
3. Install dependencies
|
|
33
|
+
```bash
|
|
34
|
+
make setup
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## ๐ป Usage
|
|
38
|
+
|
|
39
|
+
### CLI Commands
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Show help
|
|
43
|
+
{{cookiecutter.package_name}} --help
|
|
44
|
+
|
|
45
|
+
# Hello command
|
|
46
|
+
{{cookiecutter.package_name}} hello --name World
|
|
47
|
+
|
|
48
|
+
# Project info
|
|
49
|
+
{{cookiecutter.package_name}} info
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### API Endpoints
|
|
53
|
+
|
|
54
|
+
- Swagger UI: `http://localhost:8000/docs`
|
|
55
|
+
- OpenAPI JSON: `http://localhost:8000/openapi.json`
|
|
56
|
+
|
|
57
|
+
## ๐งช Testing
|
|
58
|
+
|
|
59
|
+
Run tests with comprehensive coverage:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
make test
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## ๐ Development Workflow
|
|
66
|
+
|
|
67
|
+
- `make start`: Start the FastAPI server
|
|
68
|
+
- `make lint`: Run code linters
|
|
69
|
+
- `make format`: Auto-format code
|
|
70
|
+
- `make test`: Run tests
|
|
71
|
+
- `make docs`: Generate documentation
|
|
72
|
+
|
|
73
|
+
## ๐ณ Docker Support
|
|
74
|
+
|
|
75
|
+
Build and run the Docker container:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# Build the image
|
|
79
|
+
docker build -t {{cookiecutter.package_name}} .
|
|
80
|
+
|
|
81
|
+
# Run the container
|
|
82
|
+
docker run -p 8000:8000 {{cookiecutter.package_name}}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## ๐ค Contributing
|
|
86
|
+
|
|
87
|
+
1. Fork the repository
|
|
88
|
+
2. Create your feature branch
|
|
89
|
+
3. Commit your changes
|
|
90
|
+
4. Push to the branch
|
|
91
|
+
5. Create a Pull Request
|
|
92
|
+
|
|
93
|
+
## ๐ License
|
|
94
|
+
|
|
95
|
+
{{cookiecutter.license}} License
|
|
96
|
+
|
|
97
|
+
## ๐ฅ Authors
|
|
98
|
+
|
|
99
|
+
- {{cookiecutter.author}} <{{cookiecutter.email}}>
|