snapstack 1.0.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.
Files changed (102) hide show
  1. pysnap/__init__.py +1 -0
  2. pysnap/_shared/Dockerfile.j2 +34 -0
  3. pysnap/_shared/ci.yml.j2 +53 -0
  4. pysnap/_shared/docker-compose.yml.j2 +46 -0
  5. pysnap/_shared/dockerignore.j2 +13 -0
  6. pysnap/_shared/env_example.j2 +24 -0
  7. pysnap/_shared/gitignore.j2 +15 -0
  8. pysnap/commands/__init__.py +1 -0
  9. pysnap/commands/add.py +171 -0
  10. pysnap/commands/create.py +136 -0
  11. pysnap/commands/templates_cmd.py +134 -0
  12. pysnap/commands/update.py +133 -0
  13. pysnap/community.py +113 -0
  14. pysnap/config.py +76 -0
  15. pysnap/generator.py +262 -0
  16. pysnap/main.py +65 -0
  17. pysnap/manifest.py +101 -0
  18. pysnap/plugins.py +123 -0
  19. pysnap/preview.py +131 -0
  20. pysnap/prompts.py +217 -0
  21. pysnap/registry.py +123 -0
  22. pysnap/templates/django/.dockerignore.j2 +15 -0
  23. pysnap/templates/django/.github/workflows/ci.yml.j2 +34 -0
  24. pysnap/templates/django/.gitignore.j2 +14 -0
  25. pysnap/templates/django/Dockerfile.j2 +14 -0
  26. pysnap/templates/django/README.md.j2 +36 -0
  27. pysnap/templates/django/apps/__init__.py.j2 +0 -0
  28. pysnap/templates/django/apps/core/__init__.py.j2 +0 -0
  29. pysnap/templates/django/apps/core/apps.py.j2 +6 -0
  30. pysnap/templates/django/apps/core/urls.py.j2 +7 -0
  31. pysnap/templates/django/apps/core/views.py.j2 +6 -0
  32. pysnap/templates/django/apps/users/__init__.py.j2 +0 -0
  33. pysnap/templates/django/apps/users/apps.py.j2 +6 -0
  34. pysnap/templates/django/apps/users/models.py.j2 +14 -0
  35. pysnap/templates/django/apps/users/serializers.py.j2 +13 -0
  36. pysnap/templates/django/apps/users/urls.py.j2 +10 -0
  37. pysnap/templates/django/apps/users/views.py.j2 +22 -0
  38. pysnap/templates/django/config/__init__.py.j2 +0 -0
  39. pysnap/templates/django/config/asgi.py.j2 +9 -0
  40. pysnap/templates/django/config/settings.py.j2 +110 -0
  41. pysnap/templates/django/config/urls.py.j2 +12 -0
  42. pysnap/templates/django/config/wsgi.py.j2 +9 -0
  43. pysnap/templates/django/docker-compose.yml.j2 +29 -0
  44. pysnap/templates/django/manage.py.j2 +22 -0
  45. pysnap/templates/django/pyproject.toml.j2 +40 -0
  46. pysnap/templates/django/template.json +50 -0
  47. pysnap/templates/django/tests/__init__.py.j2 +1 -0
  48. pysnap/templates/django/tests/conftest.py.j2 +6 -0
  49. pysnap/templates/django/tests/test_health.py.j2 +9 -0
  50. pysnap/templates/fastapi/.dockerignore.j2 +8 -0
  51. pysnap/templates/fastapi/.github/workflows/ci.yml.j2 +46 -0
  52. pysnap/templates/fastapi/.gitignore.j2 +13 -0
  53. pysnap/templates/fastapi/Dockerfile.j2 +14 -0
  54. pysnap/templates/fastapi/README.md.j2 +57 -0
  55. pysnap/templates/fastapi/api/__init__.py.j2 +0 -0
  56. pysnap/templates/fastapi/api/routes/__init__.py.j2 +0 -0
  57. pysnap/templates/fastapi/api/routes/auth.py.j2 +18 -0
  58. pysnap/templates/fastapi/api/routes/health.py.j2 +8 -0
  59. pysnap/templates/fastapi/app/__init__.py.j2 +1 -0
  60. pysnap/templates/fastapi/core/__init__.py.j2 +0 -0
  61. pysnap/templates/fastapi/core/config.py.j2 +26 -0
  62. pysnap/templates/fastapi/core/security.py.j2 +22 -0
  63. pysnap/templates/fastapi/db/__init__.py.j2 +0 -0
  64. pysnap/templates/fastapi/db/base.py.j2 +5 -0
  65. pysnap/templates/fastapi/db/session.py.j2 +15 -0
  66. pysnap/templates/fastapi/docker-compose.yml.j2 +30 -0
  67. pysnap/templates/fastapi/main.py.j2 +27 -0
  68. pysnap/templates/fastapi/models/__init__.py.j2 +0 -0
  69. pysnap/templates/fastapi/models/user.py.j2 +13 -0
  70. pysnap/templates/fastapi/pyproject.toml.j2 +48 -0
  71. pysnap/templates/fastapi/schemas/__init__.py.j2 +0 -0
  72. pysnap/templates/fastapi/schemas/user.py.j2 +18 -0
  73. pysnap/templates/fastapi/template.json +53 -0
  74. pysnap/templates/fastapi/tests/__init__.py.j2 +0 -0
  75. pysnap/templates/fastapi/tests/conftest.py.j2 +9 -0
  76. pysnap/templates/fastapi/tests/test_health.py.j2 +9 -0
  77. pysnap/templates/flask/.dockerignore.j2 +14 -0
  78. pysnap/templates/flask/.github/workflows/ci.yml.j2 +34 -0
  79. pysnap/templates/flask/.gitignore.j2 +13 -0
  80. pysnap/templates/flask/Dockerfile.j2 +14 -0
  81. pysnap/templates/flask/README.md.j2 +34 -0
  82. pysnap/templates/flask/app/__init__.py.j2 +30 -0
  83. pysnap/templates/flask/app/config.py.j2 +23 -0
  84. pysnap/templates/flask/app/extensions.py.j2 +9 -0
  85. pysnap/templates/flask/app/models/__init__.py.j2 +1 -0
  86. pysnap/templates/flask/app/models/user.py.j2 +16 -0
  87. pysnap/templates/flask/app/routes/__init__.py.j2 +1 -0
  88. pysnap/templates/flask/app/routes/auth.py.j2 +31 -0
  89. pysnap/templates/flask/app/routes/health.py.j2 +11 -0
  90. pysnap/templates/flask/docker-compose.yml.j2 +29 -0
  91. pysnap/templates/flask/pyproject.toml.j2 +39 -0
  92. pysnap/templates/flask/template.json +44 -0
  93. pysnap/templates/flask/tests/__init__.py.j2 +1 -0
  94. pysnap/templates/flask/tests/conftest.py.j2 +16 -0
  95. pysnap/templates/flask/tests/test_health.py.j2 +8 -0
  96. pysnap/templates/flask/wsgi.py.j2 +8 -0
  97. pysnap/validator.py +89 -0
  98. snapstack-1.0.0.dist-info/METADATA +267 -0
  99. snapstack-1.0.0.dist-info/RECORD +102 -0
  100. snapstack-1.0.0.dist-info/WHEEL +4 -0
  101. snapstack-1.0.0.dist-info/entry_points.txt +2 -0
  102. snapstack-1.0.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,29 @@
1
+ services:
2
+ app:
3
+ build: .
4
+ ports:
5
+ - "5000:5000"
6
+ env_file:
7
+ - .env
8
+ {% if database == "postgresql" %}
9
+ depends_on:
10
+ db:
11
+ condition: service_healthy
12
+ db:
13
+ image: postgres:16-alpine
14
+ environment:
15
+ POSTGRES_USER: ${POSTGRES_USER:-user}
16
+ POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-password}
17
+ POSTGRES_DB: ${POSTGRES_DB:-{{ project_name_slug }}}
18
+ ports:
19
+ - "5432:5432"
20
+ volumes:
21
+ - pgdata:/var/lib/postgresql/data
22
+ healthcheck:
23
+ test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER:-user}"]
24
+ interval: 5s
25
+ timeout: 5s
26
+ retries: 5
27
+ volumes:
28
+ pgdata:
29
+ {% endif %}
@@ -0,0 +1,39 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "{{ project_name }}"
7
+ version = "0.1.0"
8
+ description = "Generated by pysnap"
9
+ requires-python = ">=3.11"
10
+ dependencies = [
11
+ "flask>=3.0.0",
12
+ {%- if database != "none" %}
13
+ "flask-sqlalchemy>=3.1.0",
14
+ {%- endif %}
15
+ {%- if database == "postgresql" %}
16
+ "psycopg[binary]>=3.1.0",
17
+ {%- elif database == "sqlite" %}
18
+ "aiosqlite>=0.20.0",
19
+ {%- endif %}
20
+ {%- if include_auth %}
21
+ "flask-jwt-extended>=4.6.0",
22
+ {%- endif %}
23
+ ]
24
+
25
+ [project.optional-dependencies]
26
+ dev = [
27
+ {%- if include_tests %}
28
+ "pytest>=8.0.0",
29
+ "pytest-flask>=1.3.0",
30
+ {%- endif %}
31
+ "ruff>=0.8.0",
32
+ ]
33
+
34
+ [tool.hatch.build.targets.wheel]
35
+ packages = ["app"]
36
+
37
+ [tool.ruff]
38
+ line-length = 100
39
+ target-version = "py311"
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "flask",
3
+ "display_name": "Flask",
4
+ "description": "Minimal but production-ready Flask project with Blueprints, health check, and SQLAlchemy 2",
5
+ "version": "1.0.0",
6
+ "files": {
7
+ "always": {
8
+ "pyproject.toml.j2": "pyproject.toml",
9
+ ".gitignore.j2": ".gitignore",
10
+ ".env.example.j2": ".env.example",
11
+ "README.md.j2": "README.md",
12
+ "app/__init__.py.j2": "app/__init__.py",
13
+ "app/config.py.j2": "app/config.py",
14
+ "app/extensions.py.j2": "app/extensions.py",
15
+ "app/routes/__init__.py.j2": "app/routes/__init__.py",
16
+ "app/routes/health.py.j2": "app/routes/health.py",
17
+ "wsgi.py.j2": "wsgi.py"
18
+ },
19
+ "when_database": {
20
+ "app/models/__init__.py.j2": "app/models/__init__.py"
21
+ },
22
+ "when_auth": {
23
+ "app/routes/auth.py.j2": "app/routes/auth.py",
24
+ "app/models/user.py.j2": "app/models/user.py"
25
+ },
26
+ "when_docker": {
27
+ "Dockerfile.j2": "Dockerfile",
28
+ "docker-compose.yml.j2": "docker-compose.yml",
29
+ ".dockerignore.j2": ".dockerignore"
30
+ },
31
+ "when_tests": {
32
+ "tests/__init__.py.j2": "tests/__init__.py",
33
+ "tests/conftest.py.j2": "tests/conftest.py",
34
+ "tests/test_health.py.j2": "tests/test_health.py"
35
+ },
36
+ "when_ci": {
37
+ ".github/workflows/ci.yml.j2": ".github/workflows/ci.yml"
38
+ }
39
+ },
40
+ "prompts": [],
41
+ "hooks": {
42
+ "post_generate": []
43
+ }
44
+ }
@@ -0,0 +1 @@
1
+ """Flask tests package."""
@@ -0,0 +1,16 @@
1
+ """Flask test fixtures."""
2
+
3
+ import pytest
4
+ from app import create_app
5
+
6
+
7
+ @pytest.fixture()
8
+ def app():
9
+ app = create_app()
10
+ app.config.update({"TESTING": True})
11
+ yield app
12
+
13
+
14
+ @pytest.fixture()
15
+ def client(app):
16
+ return app.test_client()
@@ -0,0 +1,8 @@
1
+ """Health check tests."""
2
+
3
+
4
+ def test_health(client):
5
+ response = client.get("/api/health")
6
+ assert response.status_code == 200
7
+ data = response.get_json()
8
+ assert data["status"] == "ok"
@@ -0,0 +1,8 @@
1
+ """WSGI entry point for {{ project_name }}."""
2
+
3
+ from app import create_app
4
+
5
+ app = create_app()
6
+
7
+ if __name__ == "__main__":
8
+ app.run()
pysnap/validator.py ADDED
@@ -0,0 +1,89 @@
1
+ """
2
+ Post-generation validation.
3
+
4
+ Checks that all generated .py files are syntactically valid and optionally
5
+ runs pytest in the generated project directory.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import py_compile
11
+ import subprocess
12
+ import sys
13
+ from pathlib import Path
14
+ from typing import Any
15
+
16
+ from rich.console import Console
17
+ from rich.panel import Panel
18
+
19
+
20
+ def validate_syntax(project_path: Path) -> list[tuple[str, str]]:
21
+ """Run py_compile on every .py file in *project_path*.
22
+
23
+ Returns
24
+ -------
25
+ list of (relative_path, error_message)
26
+ Empty list means all files are valid.
27
+ """
28
+ failures: list[tuple[str, str]] = []
29
+ for py_file in sorted(project_path.rglob("*.py")):
30
+ try:
31
+ py_compile.compile(str(py_file), doraise=True)
32
+ except py_compile.PyCompileError as exc:
33
+ rel = str(py_file.relative_to(project_path))
34
+ failures.append((rel, str(exc)))
35
+ return failures
36
+
37
+
38
+ def validate_tests(project_path: Path) -> tuple[bool, str]:
39
+ """Run pytest in *project_path* if a tests/ directory exists.
40
+
41
+ Returns
42
+ -------
43
+ (passed, output)
44
+ """
45
+ tests_dir = project_path / "tests"
46
+ if not tests_dir.exists():
47
+ return True, "No tests directory found -- skipping pytest."
48
+
49
+ result = subprocess.run(
50
+ [sys.executable, "-m", "pytest", str(tests_dir), "--tb=short", "-q"],
51
+ capture_output=True,
52
+ text=True,
53
+ cwd=str(project_path),
54
+ )
55
+ output = result.stdout + result.stderr
56
+ return result.returncode == 0, output
57
+
58
+
59
+ def report_validation(
60
+ failures: list[tuple[str, str]],
61
+ console: Console,
62
+ test_result: tuple[bool, str] | None = None,
63
+ ) -> None:
64
+ """Render validation results using Rich panels."""
65
+ if not failures and (test_result is None or test_result[0]):
66
+ console.print(
67
+ Panel(
68
+ "[bold green]Validation passed[/bold green] -- all generated .py files are valid.",
69
+ border_style="green",
70
+ )
71
+ )
72
+ return
73
+
74
+ if failures:
75
+ details = "\n".join(f" [red]{path}[/red]: {msg}" for path, msg in failures)
76
+ console.print(
77
+ Panel(
78
+ f"[bold red]Syntax validation failed[/bold red]\n\n{details}",
79
+ border_style="red",
80
+ )
81
+ )
82
+
83
+ if test_result is not None and not test_result[0]:
84
+ console.print(
85
+ Panel(
86
+ f"[bold red]Tests failed[/bold red]\n\n{test_result[1]}",
87
+ border_style="red",
88
+ )
89
+ )
@@ -0,0 +1,267 @@
1
+ Metadata-Version: 2.4
2
+ Name: snapstack
3
+ Version: 1.0.0
4
+ Summary: The create-next-app experience for Python — zero-config FastAPI/Django/Flask scaffolding
5
+ Project-URL: Homepage, https://github.com/thewizard2030/pysnap
6
+ Project-URL: Repository, https://github.com/thewizard2030/pysnap
7
+ Project-URL: Issues, https://github.com/thewizard2030/pysnap/issues
8
+ Author-email: Your Name <you@example.com>
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: boilerplate,cli,django,fastapi,flask,project-starter,scaffolding,snapstack
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Software Development :: Code Generators
20
+ Requires-Python: >=3.11
21
+ Requires-Dist: jinja2>=3.1.0
22
+ Requires-Dist: questionary>=2.0.0
23
+ Requires-Dist: rich>=13.0.0
24
+ Requires-Dist: typer>=0.12.0
25
+ Provides-Extra: dev
26
+ Requires-Dist: pytest-cov>=5.0.0; extra == 'dev'
27
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
28
+ Requires-Dist: ruff>=0.8.0; extra == 'dev'
29
+ Description-Content-Type: text/markdown
30
+
31
+ <div align="center">
32
+
33
+ # snapstack
34
+
35
+ **The `create-next-app` experience for Python backend projects.**
36
+
37
+ Scaffold a fully wired FastAPI, Django, or Flask project in seconds -- not hours.
38
+
39
+ [![PyPI version](https://img.shields.io/pypi/v/snapstack?color=blue&label=PyPI)](https://pypi.org/project/snapstack/)
40
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
41
+ [![Tests](https://img.shields.io/badge/tests-59%20passed-brightgreen)](https://github.com/thewizard2030/pysnap/actions)
42
+ [![License: MIT](https://img.shields.io/badge/license-MIT-yellow.svg)](LICENSE)
43
+
44
+ </div>
45
+
46
+ ---
47
+
48
+ Every Python developer knows the drill: new project, 45 minutes of boilerplate. Folder structure, config, Docker, CI, tests, auth -- all wired up by hand, every single time.
49
+
50
+ JavaScript solved this years ago with `create-next-app`. Python didn't have an equivalent. **Until now.**
51
+
52
+ ```bash
53
+ pip install snapstack
54
+ snapstack create my-api
55
+ ```
56
+
57
+ Answer a few prompts. Get a production-ready project. Start coding your actual features.
58
+
59
+ ---
60
+
61
+ ## What You Get
62
+
63
+ Every generated project includes:
64
+
65
+ - **Working code on first run** -- zero manual edits required
66
+ - **Health check endpoint** already wired and tested
67
+ - **pyproject.toml** with hatchling, typed config, and dev extras
68
+ - **pytest test suite** with fixtures and a passing test
69
+ - **Dockerfile + docker-compose** with multi-stage build and health checks
70
+ - **GitHub Actions CI** with lint + test pipeline
71
+ - **JWT authentication** (optional) with login, register, and refresh
72
+ - **SQLAlchemy 2 / Django ORM** with async support and migrations
73
+ - **.env.example** with documented config variables
74
+ - **.pysnap.json manifest** for future `snapstack update` upgrades
75
+
76
+ ## Frameworks
77
+
78
+ <table>
79
+ <tr>
80
+ <td width="33%">
81
+
82
+ ### FastAPI
83
+
84
+ Async API with Pydantic v2, SQLAlchemy 2, uvicorn
85
+
86
+ ```bash
87
+ snapstack create my-api \
88
+ --framework fastapi \
89
+ --db postgresql \
90
+ --auth --docker --ci
91
+ ```
92
+
93
+ </td>
94
+ <td width="33%">
95
+
96
+ ### Django
97
+
98
+ Classic Django with DRF, SimpleJWT, python-decouple
99
+
100
+ ```bash
101
+ snapstack create my-app \
102
+ --framework django \
103
+ --db postgresql \
104
+ --auth --docker --ci
105
+ ```
106
+
107
+ </td>
108
+ <td width="33%">
109
+
110
+ ### Flask
111
+
112
+ Minimal Flask with Blueprints, SQLAlchemy 2, factory pattern
113
+
114
+ ```bash
115
+ snapstack create my-service \
116
+ --framework flask \
117
+ --db sqlite \
118
+ --docker --ci
119
+ ```
120
+
121
+ </td>
122
+ </tr>
123
+ </table>
124
+
125
+ ## Generated Project Structure
126
+
127
+ ```
128
+ my-api/
129
+ ├── app/
130
+ │ ├── api/routes/ # Route handlers
131
+ │ ├── core/config.py # Typed settings (.env)
132
+ │ ├── core/security.py # JWT auth (optional)
133
+ │ ├── db/session.py # Async database session
134
+ │ ├── models/ # ORM models
135
+ │ ├── schemas/ # Pydantic schemas
136
+ │ └── main.py # App entrypoint
137
+ ├── tests/
138
+ │ ├── conftest.py
139
+ │ └── test_health.py
140
+ ├── .github/workflows/ci.yml
141
+ ├── Dockerfile
142
+ ├── docker-compose.yml
143
+ ├── pyproject.toml
144
+ ├── .env.example
145
+ ├── .pysnap.json # Manifest for `snapstack update`
146
+ └── README.md
147
+ ```
148
+
149
+ ## Install
150
+
151
+ ```bash
152
+ # Recommended
153
+ uv tool install snapstack
154
+
155
+ # Or with pip
156
+ pip install snapstack
157
+ ```
158
+
159
+ Requires Python 3.11+.
160
+
161
+ ## Quick Start
162
+
163
+ ### Interactive mode (guided prompts)
164
+
165
+ ```bash
166
+ snapstack create my-api
167
+ ```
168
+
169
+ ### Non-interactive mode (CI-friendly)
170
+
171
+ ```bash
172
+ snapstack create my-api \
173
+ --framework fastapi \
174
+ --db sqlite \
175
+ --no-auth \
176
+ --docker \
177
+ --ci \
178
+ --tests \
179
+ --pm uv
180
+ ```
181
+
182
+ ### Preview before generating
183
+
184
+ ```bash
185
+ snapstack create my-api
186
+ # Shows a file tree preview before writing anything
187
+ ```
188
+
189
+ ### Add features to existing projects
190
+
191
+ ```bash
192
+ cd my-existing-project
193
+ snapstack add docker # Add Dockerfile + docker-compose
194
+ snapstack add ci # Add GitHub Actions workflow
195
+ snapstack add auth # Add JWT authentication
196
+ snapstack add tests # Add pytest boilerplate
197
+ ```
198
+
199
+ ### Update infrastructure files
200
+
201
+ ```bash
202
+ snapstack update # Interactive diff review
203
+ snapstack update --dry-run # See changes without applying
204
+ snapstack update --accept-all
205
+ ```
206
+
207
+ ### Community templates
208
+
209
+ ```bash
210
+ # Browse available templates
211
+ snapstack templates list
212
+ snapstack templates search "graphql"
213
+ ```
214
+
215
+ ## Plugin System
216
+
217
+ Third-party packages can register custom framework templates via Python entry points:
218
+
219
+ ```toml
220
+ # In a plugin's pyproject.toml
221
+ [project.entry-points."pysnap.plugins"]
222
+ my-template = "my_package.plugin:register"
223
+ ```
224
+
225
+ Plugins appear automatically in prompts after `pip install`.
226
+
227
+ ## CLI Reference
228
+
229
+ | Command | Description |
230
+ | -------------------------------------- | -------------------------------------------------- |
231
+ | `snapstack create <name>` | Scaffold a new project |
232
+ | `snapstack add <component>` | Add docker, ci, auth, or tests to existing project |
233
+ | `snapstack update` | Update infrastructure files from latest templates |
234
+ | `snapstack templates list` | List all available templates |
235
+ | `snapstack templates search <keyword>` | Search community template registry |
236
+ | `snapstack --version` | Show version |
237
+
238
+ ## Flags for `snapstack create`
239
+
240
+ | Flag | Values | Default |
241
+ | -------------------------- | ------------------------------ | ------------------------- |
242
+ | `--framework`, `-f` | `fastapi`, `django`, `flask` | `fastapi` |
243
+ | `--db`, `-d` | `sqlite`, `postgresql`, `none` | `sqlite` |
244
+ | `--auth` / `--no-auth` | | `--no-auth` |
245
+ | `--docker` / `--no-docker` | | `--docker` |
246
+ | `--ci` / `--no-ci` | | `--ci` |
247
+ | `--tests` / `--no-tests` | | `--tests` |
248
+ | `--pm` | `uv`, `pip`, `poetry` | `uv` |
249
+ | `--output`, `-o` | path | `.` |
250
+ | `--no-preview` | | show preview |
251
+ | `--no-validate` | | validate after generation |
252
+
253
+ ## Contributing
254
+
255
+ Contributions welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
256
+
257
+ ```bash
258
+ # Development setup
259
+ git clone https://github.com/thewizard2030/pysnap.git
260
+ cd pysnap
261
+ pip install -e ".[dev]"
262
+ pytest
263
+ ```
264
+
265
+ ## License
266
+
267
+ [MIT](LICENSE)
@@ -0,0 +1,102 @@
1
+ pysnap/__init__.py,sha256=ZhzQKWZ8RFrTIkj7z87B144DI95e8LMfA5w8NDWQDtg,23
2
+ pysnap/community.py,sha256=7wGrE1-wL2TPlGfSylHK36kdQ5r7DliH0VjHHwsUyuQ,3283
3
+ pysnap/config.py,sha256=2h9_usESCCgSK0pjeSpGOKHptercDjcU5JWuNr1A_YU,2097
4
+ pysnap/generator.py,sha256=7jteIAHuTR6jqQXk9gg7KYIpuccpSpP2yGRFUq6lu50,8830
5
+ pysnap/main.py,sha256=MBvzc5OcGZOvEYkMOfvuDjCGfMEN8kqKideMhLLW6g8,1925
6
+ pysnap/manifest.py,sha256=UxjZ0sDBjkaa_8v8fX1ukw_rOkCOfUu12f4rTyEBmtk,3280
7
+ pysnap/plugins.py,sha256=1yy5lmMwsZjShy_0z5YMc8bGo401Sibkk5L8c4WXB9I,3980
8
+ pysnap/preview.py,sha256=3M5fqHGCh_q4q6FJ2XUDsNw4v3mg8dm-V4Vx5qMAO5g,3930
9
+ pysnap/prompts.py,sha256=66GLnqCGc--8VjCYvCGXZIm8dMNvM5NWC0GXvNslZGE,6868
10
+ pysnap/registry.py,sha256=1rq2g87H_tp9z3o85N0eOxYBzY4VCmx2vaiLHfBfkDM,3606
11
+ pysnap/validator.py,sha256=ojawtqs5_pYN2j3TV0AEC5wNQYtZu-AZpqO42V2DRoo,2629
12
+ pysnap/_shared/Dockerfile.j2,sha256=sKtCbWEbzMM1-BuLWnQBH43wnhUYNibD9oe4X24akaQ,932
13
+ pysnap/_shared/ci.yml.j2,sha256=PAGzolo0Mn0p60vhJLWgatu6wcN_iHOkMsAiIpIUfQQ,1040
14
+ pysnap/_shared/docker-compose.yml.j2,sha256=UNOKihi3gVTYS8O0nfdFeCB7oQ-VEohZK7-XGQu8UT0,1003
15
+ pysnap/_shared/dockerignore.j2,sha256=eEo-_jNPHjcjGIHbD6jdtm8zPuwduKy-RnVlNxmRLf0,132
16
+ pysnap/_shared/env_example.j2,sha256=LkPP81xa4xE_eXwPTFesAWB3SLBKiFCw7Is1pHnvxN0,714
17
+ pysnap/_shared/gitignore.j2,sha256=N_ryYqw5x0smCa_rimRHRSyG0bnye541OOxC60IJpAk,155
18
+ pysnap/commands/__init__.py,sha256=zZrm4lb-8r2hGgGKgxmBGc5Wk6dHlAZigwjeEWvzdxI,39
19
+ pysnap/commands/add.py,sha256=c5KWU-iou0VFnCfr4BLpb8g0nYzWrB343Zt3-J060M8,5695
20
+ pysnap/commands/create.py,sha256=G8X2R_wbWOa_9mmKOJTGi9EPc6PRAYCsyGzA_TTNcyU,4691
21
+ pysnap/commands/templates_cmd.py,sha256=IfaEOllmE-jBeSD94A4rARDXgD7KhUDZGifyis0ommw,4322
22
+ pysnap/commands/update.py,sha256=5i5O30pcFyKWTCqN9dyMX05qNgiWPbMbSrCws8FFVwM,4400
23
+ pysnap/templates/django/.dockerignore.j2,sha256=SN3WRpLlVXyivIYHWixXMDKAaxjxRflKplV4sl4cbAg,153
24
+ pysnap/templates/django/.gitignore.j2,sha256=L-Pj26QsHaTM9vpgEQfkDi2wi5VTPXYvDgMUx_YLuyI,145
25
+ pysnap/templates/django/Dockerfile.j2,sha256=BBQVohS_9csyuMOj31AkL-P3xP4FGoNQT5WudpYFYuA,427
26
+ pysnap/templates/django/README.md.j2,sha256=BQDRu6-LaVkX6Gp4pdbEfn5mxynjV7XSfd6y9JjPvZQ,583
27
+ pysnap/templates/django/docker-compose.yml.j2,sha256=dgdLiOv_z0ANdYPXr3wE9qxUuVp0htD_TgMWDoHRgB4,677
28
+ pysnap/templates/django/manage.py.j2,sha256=04KqIph4ereNnI8CsvOcXK2WQD82CP1bK1ZllRWKw-c,684
29
+ pysnap/templates/django/pyproject.toml.j2,sha256=kd7UfX2ZmilZ9mdWJtan7ODCFOYoZia4fTnzLQflO68,852
30
+ pysnap/templates/django/template.json,sha256=TzFwZ2Ud2QBk7O5dPcNrFEqgkLsl3uQixB6mB0wGxcQ,2087
31
+ pysnap/templates/django/.github/workflows/ci.yml.j2,sha256=6WQGAO1lvboBwCgjznOa_Qus_Wi6eNFryfw-EVXl7Cs,742
32
+ pysnap/templates/django/apps/__init__.py.j2,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
+ pysnap/templates/django/apps/core/__init__.py.j2,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
+ pysnap/templates/django/apps/core/apps.py.j2,sha256=kbGjkM2ifkNolj3XhS4i3LHTW-0tsRcrsYyxMvkOZ0w,151
35
+ pysnap/templates/django/apps/core/urls.py.j2,sha256=TpcAaVlb0ftP6grdc7qo59040cEH_URtd2EZXagd_fo,146
36
+ pysnap/templates/django/apps/core/views.py.j2,sha256=X8tKRoQJn0aFz08FWICD7Lp_QZgd8tM4H8lHYLRYAYE,180
37
+ pysnap/templates/django/apps/users/__init__.py.j2,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
+ pysnap/templates/django/apps/users/apps.py.j2,sha256=UuphC1T-F3xyHI-9rEE8wGc_obrJFY6QPBZTei1YJhs,153
39
+ pysnap/templates/django/apps/users/models.py.j2,sha256=W40WChvVIj_jX-9X9aYVNAGks5e_XYq0YWwSR_4llsE,314
40
+ pysnap/templates/django/apps/users/serializers.py.j2,sha256=fRBOgDFkyI2VKqN19YM8NlcLNJUWqfgXMjS1dMv5iRU,398
41
+ pysnap/templates/django/apps/users/urls.py.j2,sha256=v9F3uhPCXSQ-ljE9MdnItiUirmrJE4PkVJpWZFRXeEE,396
42
+ pysnap/templates/django/apps/users/views.py.j2,sha256=iJ3Dm9D9yLeGjmWyC3O12yMgNQs02JNEdYGrC7nHuYA,829
43
+ pysnap/templates/django/config/__init__.py.j2,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
+ pysnap/templates/django/config/asgi.py.j2,sha256=tBBu3hlkv7dzVtA7glDhRCE9M8nYFIVlGwz6ljYN-aE,227
45
+ pysnap/templates/django/config/settings.py.j2,sha256=FS80Ykaj_-vuqoSv99-g83xPG9T8tYc5L_dwZbySABU,3251
46
+ pysnap/templates/django/config/urls.py.j2,sha256=cDqWSukGEY4W5pwrACDksQP0Hra2Dm6Pf7z-3tTNPsg,302
47
+ pysnap/templates/django/config/wsgi.py.j2,sha256=7hy8snNltrHZlf1miF6UA_QDR-8yiG97LfBCe5_BOq0,227
48
+ pysnap/templates/django/tests/__init__.py.j2,sha256=YJKI6UvBOFtijXsytiWFSlZ-CHYeIjFxh22o_8Pkw3A,29
49
+ pysnap/templates/django/tests/conftest.py.j2,sha256=dgnvjgyNrcm0ej2YdUzM7RDMCqz74uKbvqrSR3bKcbs,87
50
+ pysnap/templates/django/tests/test_health.py.j2,sha256=0gOeH8x3hq0cRoIwUiA4qd_HW_1WX3tYTNoGQHhGsQQ,221
51
+ pysnap/templates/fastapi/.dockerignore.j2,sha256=rAHLK4ZCEWegzGX9PDMqzgPCYD8NnOduK2NCqY6pvKI,63
52
+ pysnap/templates/fastapi/.gitignore.j2,sha256=QfSwIg-J6r5hcSFBCwfJ9GPeHU9T8M70BpTLK9lS7VU,118
53
+ pysnap/templates/fastapi/Dockerfile.j2,sha256=xim8vC18P_ptivFI8lA856daDSzW9mDvalzyLlMXsTk,458
54
+ pysnap/templates/fastapi/README.md.j2,sha256=t5sSnqadpc1CZY50FViLSWx_hGgxH8Sw-zrOjK7wW_c,1015
55
+ pysnap/templates/fastapi/docker-compose.yml.j2,sha256=A6KW1V0rEeFM93Ja4NloJaT3bkAozBS1Q_Auk39V4jc,610
56
+ pysnap/templates/fastapi/main.py.j2,sha256=9Hh1Ja2U9t1pmNtXguPkmRy48FIbU0EkLvRESoYpHBQ,744
57
+ pysnap/templates/fastapi/pyproject.toml.j2,sha256=yRXcrRBS57DyVb9UusPP3OpaWEP8em_cjHJylMKsPFk,992
58
+ pysnap/templates/fastapi/template.json,sha256=WpZdg56cVzhHGo40tqRs-IoJi3gWbcc5PaSXnS7J6VQ,1819
59
+ pysnap/templates/fastapi/.github/workflows/ci.yml.j2,sha256=LiDX3xpcH7-RnLHXPQoAYnVH2T_0WZEf6uzECRTnF30,830
60
+ pysnap/templates/fastapi/api/__init__.py.j2,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
61
+ pysnap/templates/fastapi/api/routes/__init__.py.j2,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
62
+ pysnap/templates/fastapi/api/routes/auth.py.j2,sha256=Ti_fmU99sQgvCj0WKNUOl16Un3fDLjkAMAWrC05heI4,691
63
+ pysnap/templates/fastapi/api/routes/health.py.j2,sha256=28bLbPtg7XCZKATIzPpV2DZnZJxHBcdIc6NcQbLJxgY,175
64
+ pysnap/templates/fastapi/app/__init__.py.j2,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
65
+ pysnap/templates/fastapi/core/__init__.py.j2,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
66
+ pysnap/templates/fastapi/core/config.py.j2,sha256=FDdgGkiuWw4En2l-A7w79_fnUVzQZrym_H2yPyZURCo,869
67
+ pysnap/templates/fastapi/core/security.py.j2,sha256=pzsiIab_1JvzyCHwKg0MtQnwr-yJRFjIDEfmA-vqYuw,779
68
+ pysnap/templates/fastapi/db/__init__.py.j2,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
69
+ pysnap/templates/fastapi/db/base.py.j2,sha256=mH7f2d_jiyxJSSx9Gk53QBXRa3LiKBsBjkFgvmtH1WA,83
70
+ pysnap/templates/fastapi/db/session.py.j2,sha256=jirWyfC2ATtr5Upqehs7Km5sg9P3MWas_IA_xH9qJ8Q,532
71
+ pysnap/templates/fastapi/models/__init__.py.j2,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
72
+ pysnap/templates/fastapi/models/user.py.j2,sha256=GrAxbs6ahDhc-AiauLO0-Y8ieHLXfgwepk37U3qF2Zc,497
73
+ pysnap/templates/fastapi/schemas/__init__.py.j2,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
74
+ pysnap/templates/fastapi/schemas/user.py.j2,sha256=FeJTY2d37DDfA8HDgB0PbRipXlw6BjAM2lwfqV5CwOU,314
75
+ pysnap/templates/fastapi/tests/__init__.py.j2,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
76
+ pysnap/templates/fastapi/tests/conftest.py.j2,sha256=1cXf2CpKPxanMoAKNYVct_C8ZLdpL-l6dg6JuMMhXcc,231
77
+ pysnap/templates/fastapi/tests/test_health.py.j2,sha256=aUywAGaNJsvgZBamfX1X2sYg4pkOYmnogOBLhPxeqOk,251
78
+ pysnap/templates/flask/.dockerignore.j2,sha256=L7sgez5NnZWwWz5MDLhZCuD9pQGXN6wE2S02ahoU8lU,139
79
+ pysnap/templates/flask/.gitignore.j2,sha256=2DuD1LsnZlC5OuIzYl4f9DYdnv92fn25ibi0HVbnSd4,131
80
+ pysnap/templates/flask/Dockerfile.j2,sha256=B_Bhj9NLkIU5Zi4og3eQ90diE0J8lkWxjeFxwgc5mCo,425
81
+ pysnap/templates/flask/README.md.j2,sha256=Ake0fMrD99GzhXTnSKciGnIGHJs-lofKRbmUvspI_2Y,527
82
+ pysnap/templates/flask/docker-compose.yml.j2,sha256=hUOxgx_0FFabbaJaWhh2bCS2L8vnWMlRYoCoHElZLo0,677
83
+ pysnap/templates/flask/pyproject.toml.j2,sha256=frZ98JRIIKPV3EHBzeI3IQ3MLP_0VN7lrojTBP_D7X4,806
84
+ pysnap/templates/flask/template.json,sha256=KEkCSs2RxhAnItQD-06HbC5XedL1pqsXsUyxIR_90sU,1591
85
+ pysnap/templates/flask/wsgi.py.j2,sha256=r_MHiSxJbxn1kNZZBTo3NPwfjOKq2TDcBkwBuzPztoY,145
86
+ pysnap/templates/flask/.github/workflows/ci.yml.j2,sha256=6WQGAO1lvboBwCgjznOa_Qus_Wi6eNFryfw-EVXl7Cs,742
87
+ pysnap/templates/flask/app/__init__.py.j2,sha256=_rpXot_ND2FxmkaV1oUVej-1Eihjlfe2VINCxwQdoVw,693
88
+ pysnap/templates/flask/app/config.py.j2,sha256=EWLuOAX_FC3KKiITuRUfDG6ycDSU_LzAqI1C1rm6XjI,778
89
+ pysnap/templates/flask/app/extensions.py.j2,sha256=jx3Re1oXxFmyeSZmji1UJ1l0Bis35l8V0Hd_b4eysSQ,230
90
+ pysnap/templates/flask/app/models/__init__.py.j2,sha256=V1Vexk1zHoXd-1K272wxkFDBEjjkWp-JmZPn8k0F_tU,23
91
+ pysnap/templates/flask/app/models/user.py.j2,sha256=pRBSoTGhYMK_EHTurDwqwot8q0fidzsERPFPpmbm8mU,510
92
+ pysnap/templates/flask/app/routes/__init__.py.j2,sha256=jQzbC58bIMb-1Dpoelj5Zb6_AB4StIrAhruNCCqtjzM,23
93
+ pysnap/templates/flask/app/routes/auth.py.j2,sha256=BX8gKOd3XJ4d8pOTJ0a9ajq_pBMbCdIEvwPPbgVEEBk,1084
94
+ pysnap/templates/flask/app/routes/health.py.j2,sha256=Bg08AhOns5qOwu2najdBH-0fnaCYmM4vdPYfV0cP1ZY,286
95
+ pysnap/templates/flask/tests/__init__.py.j2,sha256=oyxQLhjBfuEYuXJ8qLn5uBeVL4f8hL4EEXMKm6RsYX8,28
96
+ pysnap/templates/flask/tests/conftest.py.j2,sha256=eHR2G-kunP64ODjL7Tb9TVTlgnKjZM8b8tFmQGy49D8,260
97
+ pysnap/templates/flask/tests/test_health.py.j2,sha256=EppZQChFe-ao_03SwtgFGoAtVyw-W9PXRSbeBa2L_Ak,206
98
+ snapstack-1.0.0.dist-info/METADATA,sha256=Ousf1jYAaK9RrQZ1jcLXf-_ECEVJFquxTaMFHdS3jtU,7983
99
+ snapstack-1.0.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
100
+ snapstack-1.0.0.dist-info/entry_points.txt,sha256=qLRFQX1qWtEjnkLhNLh8-NHV5GeVILndKx29yyxSYRg,46
101
+ snapstack-1.0.0.dist-info/licenses/LICENSE,sha256=JHv2qniMHo36leewRJ8ry7P01OJQpN3RpBxalvfEFtA,1097
102
+ snapstack-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ snapstack = pysnap.main:app
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 pysnap contributors
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.