flint-kit 0.14.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.
- flint/__init__.py +3 -0
- flint/__main__.py +4 -0
- flint/cli.py +289 -0
- flint/errors.py +17 -0
- flint/generator.py +446 -0
- flint/naming.py +97 -0
- flint/postgen.py +112 -0
- flint/prefs.py +110 -0
- flint/prompts.py +268 -0
- flint/skills/alembic/content/SKILL.md.jinja +78 -0
- flint/skills/alembic/content/guides/first-migration.md.jinja +58 -0
- flint/skills/alembic/content/guides/reading-a-migration.md.jinja +109 -0
- flint/skills/alembic/content/references/env-and-config.md.jinja +130 -0
- flint/skills/alembic/content/references/gotchas.md.jinja +100 -0
- flint/skills/alembic/skill.json +5 -0
- flint/skills/celery/content/SKILL.md.jinja +83 -0
- flint/skills/celery/content/guides/add-a-task.md.jinja +92 -0
- flint/skills/celery/content/references/gotchas.md.jinja +88 -0
- flint/skills/celery/content/references/tasks-and-results.md.jinja +93 -0
- flint/skills/celery/skill.json +5 -0
- flint/skills/fastapi/content/SKILL.md.jinja +54 -0
- flint/skills/fastapi/content/guides/add-an-endpoint.md.jinja +97 -0
- flint/skills/fastapi/content/guides/testing.md.jinja +49 -0
- flint/skills/fastapi/content/references/gotchas.md.jinja +61 -0
- flint/skills/fastapi/content/references/routing-and-dependencies.md.jinja +85 -0
- flint/skills/fastapi/skill.json +5 -0
- flint/skills/flask/content/SKILL.md.jinja +94 -0
- flint/skills/flask/content/guides/add-a-resource.md.jinja +139 -0
- flint/skills/flask/content/guides/testing.md.jinja +110 -0
- flint/skills/flask/content/references/app-factory-and-config.md.jinja +110 -0
- flint/skills/flask/content/references/gotchas.md.jinja +146 -0
- flint/skills/flask/content/references/routing-and-blueprints.md.jinja +155 -0
- flint/skills/flask/skill.json +5 -0
- flint/skills/flask-migrate/content/SKILL.md.jinja +70 -0
- flint/skills/flask-migrate/content/guides/first-migration.md.jinja +63 -0
- flint/skills/flask-migrate/content/references/cli-and-wiring.md.jinja +76 -0
- flint/skills/flask-migrate/content/references/gotchas.md.jinja +66 -0
- flint/skills/flask-migrate/skill.json +5 -0
- flint/skills/flask-sqlalchemy/content/SKILL.md.jinja +84 -0
- flint/skills/flask-sqlalchemy/content/guides/add-a-model.md.jinja +90 -0
- flint/skills/flask-sqlalchemy/content/references/db-object-and-wiring.md.jinja +105 -0
- flint/skills/flask-sqlalchemy/content/references/gotchas.md.jinja +107 -0
- flint/skills/flask-sqlalchemy/skill.json +5 -0
- flint/skills/pydantic-settings/content/SKILL.md.jinja +75 -0
- flint/skills/pydantic-settings/content/guides/add-a-setting.md.jinja +78 -0
- flint/skills/pydantic-settings/content/references/framework-integration.md.jinja +104 -0
- flint/skills/pydantic-settings/content/references/settings-and-env.md.jinja +112 -0
- flint/skills/pydantic-settings/skill.json +5 -0
- flint/skills/pytest/content/SKILL.md.jinja +102 -0
- flint/skills/pytest/content/guides/parametrize.md.jinja +77 -0
- flint/skills/pytest/content/guides/writing-a-good-test.md.jinja +85 -0
- flint/skills/pytest/content/references/database-isolation.md.jinja +115 -0
- flint/skills/pytest/content/references/gotchas.md.jinja +113 -0
- flint/skills/pytest/skill.json +5 -0
- flint/skills/redis/content/SKILL.md.jinja +76 -0
- flint/skills/redis/content/guides/add-caching-to-an-endpoint.md.jinja +169 -0
- flint/skills/redis/content/references/gotchas.md.jinja +54 -0
- flint/skills/redis/content/references/redis-operations.md.jinja +67 -0
- flint/skills/redis/skill.json +5 -0
- flint/skills/sqlalchemy/content/SKILL.md.jinja +101 -0
- flint/skills/sqlalchemy/content/guides/add-a-model.md.jinja +205 -0
- flint/skills/sqlalchemy/content/references/gotchas.md.jinja +140 -0
- flint/skills/sqlalchemy/content/references/sessions-and-queries.md.jinja +153 -0
- flint/skills/sqlalchemy/skill.json +5 -0
- flint/skills/sqlmodel/content/SKILL.md.jinja +84 -0
- flint/skills/sqlmodel/content/guides/add-a-model.md.jinja +131 -0
- flint/skills/sqlmodel/content/references/gotchas.md.jinja +80 -0
- flint/skills/sqlmodel/content/references/models-and-sessions.md.jinja +142 -0
- flint/skills/sqlmodel/skill.json +5 -0
- flint/skills/taskiq/content/SKILL.md.jinja +72 -0
- flint/skills/taskiq/content/guides/add-a-task.md.jinja +88 -0
- flint/skills/taskiq/content/references/broker-and-tasks.md.jinja +121 -0
- flint/skills/taskiq/content/references/gotchas.md.jinja +100 -0
- flint/skills/taskiq/skill.json +5 -0
- flint/templates/fastapi/hello-world/README.md +63 -0
- flint/templates/fastapi/hello-world/config/env.jinja +2 -0
- flint/templates/fastapi/hello-world/config/src/{{package_name}}/core/__init__.py.jinja +0 -0
- flint/templates/fastapi/hello-world/config/src/{{package_name}}/core/config.py.jinja +11 -0
- flint/templates/fastapi/hello-world/config/src/{{package_name}}/main.py.jinja +10 -0
- flint/templates/fastapi/hello-world/docker/Dockerfile.jinja +19 -0
- flint/templates/fastapi/hello-world/docker/dockerignore.jinja +8 -0
- flint/templates/fastapi/hello-world/files/AGENTS.md.jinja +58 -0
- flint/templates/fastapi/hello-world/files/README.md.jinja +46 -0
- flint/templates/fastapi/hello-world/files/gitignore.jinja +6 -0
- flint/templates/fastapi/hello-world/files/pyproject.toml.jinja +24 -0
- flint/templates/fastapi/hello-world/files/src/{{package_name}}/__init__.py.jinja +1 -0
- flint/templates/fastapi/hello-world/files/src/{{package_name}}/main.py.jinja +8 -0
- flint/templates/fastapi/hello-world/files/tests/test_main.py.jinja +11 -0
- flint/templates/fastapi/hello-world/template.json +23 -0
- flint/templates/fastapi/rest-api/README.md +142 -0
- flint/templates/fastapi/rest-api/db-sqlalchemy/src/{{package_name}}/core/db.py.jinja +19 -0
- flint/templates/fastapi/rest-api/db-sqlalchemy/src/{{package_name}}/models.py.jinja +13 -0
- flint/templates/fastapi/rest-api/db-sqlalchemy/src/{{package_name}}/routes/items.py.jinja +41 -0
- flint/templates/fastapi/rest-api/db-sqlalchemy/tests/conftest.py.jinja +44 -0
- flint/templates/fastapi/rest-api/db-sqlalchemy/tests/test_main.py.jinja +29 -0
- flint/templates/fastapi/rest-api/db-sqlmodel/src/{{package_name}}/core/db.py.jinja +20 -0
- flint/templates/fastapi/rest-api/db-sqlmodel/src/{{package_name}}/models.py.jinja +7 -0
- flint/templates/fastapi/rest-api/db-sqlmodel/src/{{package_name}}/routes/items.py.jinja +41 -0
- flint/templates/fastapi/rest-api/db-sqlmodel/tests/conftest.py.jinja +45 -0
- flint/templates/fastapi/rest-api/db-sqlmodel/tests/test_main.py.jinja +29 -0
- flint/templates/fastapi/rest-api/docker/Dockerfile.jinja +19 -0
- flint/templates/fastapi/rest-api/docker/dockerignore.jinja +11 -0
- flint/templates/fastapi/rest-api/files/AGENTS.md.jinja +124 -0
- flint/templates/fastapi/rest-api/files/README.md.jinja +150 -0
- flint/templates/fastapi/rest-api/files/env.jinja +13 -0
- flint/templates/fastapi/rest-api/files/gitignore.jinja +9 -0
- flint/templates/fastapi/rest-api/files/pyproject.toml.jinja +56 -0
- flint/templates/fastapi/rest-api/files/src/{{package_name}}/__init__.py.jinja +1 -0
- flint/templates/fastapi/rest-api/files/src/{{package_name}}/core/__init__.py.jinja +0 -0
- flint/templates/fastapi/rest-api/files/src/{{package_name}}/core/config.py.jinja +22 -0
- flint/templates/fastapi/rest-api/files/src/{{package_name}}/main.py.jinja +61 -0
- flint/templates/fastapi/rest-api/files/src/{{package_name}}/routes/__init__.py.jinja +0 -0
- flint/templates/fastapi/rest-api/files/src/{{package_name}}/routes/items.py.jinja +39 -0
- flint/templates/fastapi/rest-api/files/src/{{package_name}}/schemas.py.jinja +12 -0
- flint/templates/fastapi/rest-api/files/tests/test_main.py.jinja +31 -0
- flint/templates/fastapi/rest-api/migrations-sqlalchemy/alembic/env.py.jinja +50 -0
- flint/templates/fastapi/rest-api/migrations-sqlalchemy/alembic/script.py.mako +27 -0
- flint/templates/fastapi/rest-api/migrations-sqlalchemy/alembic/versions/gitkeep.jinja +0 -0
- flint/templates/fastapi/rest-api/migrations-sqlalchemy/alembic.ini.jinja +37 -0
- flint/templates/fastapi/rest-api/migrations-sqlmodel/alembic/env.py.jinja +51 -0
- flint/templates/fastapi/rest-api/migrations-sqlmodel/alembic/script.py.mako +28 -0
- flint/templates/fastapi/rest-api/migrations-sqlmodel/alembic/versions/gitkeep.jinja +0 -0
- flint/templates/fastapi/rest-api/migrations-sqlmodel/alembic.ini.jinja +37 -0
- flint/templates/fastapi/rest-api/redis/src/{{package_name}}/core/redis.py.jinja +5 -0
- flint/templates/fastapi/rest-api/template.json +91 -0
- flint/templates/fastapi/rest-api/worker-celery/src/{{package_name}}/tasks/__init__.py.jinja +0 -0
- flint/templates/fastapi/rest-api/worker-celery/src/{{package_name}}/tasks/example.py.jinja +6 -0
- flint/templates/fastapi/rest-api/worker-celery/src/{{package_name}}/worker.py.jinja +16 -0
- flint/templates/fastapi/rest-api/worker-taskiq/src/{{package_name}}/tasks/__init__.py.jinja +0 -0
- flint/templates/fastapi/rest-api/worker-taskiq/src/{{package_name}}/tasks/example.py.jinja +6 -0
- flint/templates/fastapi/rest-api/worker-taskiq/src/{{package_name}}/worker.py.jinja +19 -0
- flint/templates/fastapi/template.json +7 -0
- flint/templates/flask/hello-world/README.md +142 -0
- flint/templates/flask/hello-world/config/env.jinja +2 -0
- flint/templates/flask/hello-world/config/src/{{package_name}}/core/__init__.py.jinja +0 -0
- flint/templates/flask/hello-world/config/src/{{package_name}}/core/config.py.jinja +11 -0
- flint/templates/flask/hello-world/config/src/{{package_name}}/main.py.jinja +12 -0
- flint/templates/flask/hello-world/docker/Dockerfile.jinja +19 -0
- flint/templates/flask/hello-world/docker/dockerignore.jinja +8 -0
- flint/templates/flask/hello-world/files/AGENTS.md.jinja +58 -0
- flint/templates/flask/hello-world/files/README.md.jinja +46 -0
- flint/templates/flask/hello-world/files/gitignore.jinja +6 -0
- flint/templates/flask/hello-world/files/pyproject.toml.jinja +26 -0
- flint/templates/flask/hello-world/files/src/{{package_name}}/__init__.py.jinja +1 -0
- flint/templates/flask/hello-world/files/src/{{package_name}}/main.py.jinja +8 -0
- flint/templates/flask/hello-world/files/tests/test_main.py.jinja +8 -0
- flint/templates/flask/hello-world/template.json +23 -0
- flint/templates/flask/rest-api/README.md +343 -0
- flint/templates/flask/rest-api/db-flask-sqlalchemy/src/{{package_name}}/core/db.py.jinja +32 -0
- flint/templates/flask/rest-api/db-flask-sqlalchemy/src/{{package_name}}/models.py.jinja +9 -0
- flint/templates/flask/rest-api/db-flask-sqlalchemy/src/{{package_name}}/routes/items.py.jinja +44 -0
- flint/templates/flask/rest-api/db-flask-sqlalchemy/tests/conftest.py.jinja +13 -0
- flint/templates/flask/rest-api/db-flask-sqlalchemy/tests/test_main.py.jinja +24 -0
- flint/templates/flask/rest-api/db-sqlalchemy/src/{{package_name}}/core/db.py.jinja +52 -0
- flint/templates/flask/rest-api/db-sqlalchemy/src/{{package_name}}/models.py.jinja +13 -0
- flint/templates/flask/rest-api/db-sqlalchemy/src/{{package_name}}/routes/items.py.jinja +49 -0
- flint/templates/flask/rest-api/db-sqlalchemy/tests/conftest.py.jinja +13 -0
- flint/templates/flask/rest-api/db-sqlalchemy/tests/test_main.py.jinja +24 -0
- flint/templates/flask/rest-api/docker/Dockerfile.jinja +20 -0
- flint/templates/flask/rest-api/docker/dockerignore.jinja +12 -0
- flint/templates/flask/rest-api/files/AGENTS.md.jinja +139 -0
- flint/templates/flask/rest-api/files/README.md.jinja +151 -0
- flint/templates/flask/rest-api/files/env.jinja +13 -0
- flint/templates/flask/rest-api/files/gitignore.jinja +10 -0
- flint/templates/flask/rest-api/files/pyproject.toml.jinja +43 -0
- flint/templates/flask/rest-api/files/src/{{package_name}}/__init__.py.jinja +1 -0
- flint/templates/flask/rest-api/files/src/{{package_name}}/core/__init__.py.jinja +0 -0
- flint/templates/flask/rest-api/files/src/{{package_name}}/core/config.py.jinja +22 -0
- flint/templates/flask/rest-api/files/src/{{package_name}}/main.py.jinja +50 -0
- flint/templates/flask/rest-api/files/src/{{package_name}}/routes/__init__.py.jinja +0 -0
- flint/templates/flask/rest-api/files/src/{{package_name}}/routes/items.py.jinja +46 -0
- flint/templates/flask/rest-api/files/src/{{package_name}}/schemas.py.jinja +14 -0
- flint/templates/flask/rest-api/files/tests/test_main.py.jinja +30 -0
- flint/templates/flask/rest-api/migrations-flask-sqlalchemy/migrations/README +1 -0
- flint/templates/flask/rest-api/migrations-flask-sqlalchemy/migrations/alembic.ini +50 -0
- flint/templates/flask/rest-api/migrations-flask-sqlalchemy/migrations/env.py +113 -0
- flint/templates/flask/rest-api/migrations-flask-sqlalchemy/migrations/script.py.mako +24 -0
- flint/templates/flask/rest-api/migrations-flask-sqlalchemy/migrations/versions/gitkeep.jinja +0 -0
- flint/templates/flask/rest-api/migrations-flask-sqlalchemy/src/{{package_name}}/core/db.py.jinja +39 -0
- flint/templates/flask/rest-api/migrations-sqlalchemy/alembic/env.py.jinja +44 -0
- flint/templates/flask/rest-api/migrations-sqlalchemy/alembic/script.py.mako +27 -0
- flint/templates/flask/rest-api/migrations-sqlalchemy/alembic/versions/gitkeep.jinja +0 -0
- flint/templates/flask/rest-api/migrations-sqlalchemy/alembic.ini.jinja +37 -0
- flint/templates/flask/rest-api/redis/src/{{package_name}}/core/redis.py.jinja +5 -0
- flint/templates/flask/rest-api/template.json +89 -0
- flint/templates/flask/rest-api/worker-celery/src/{{package_name}}/tasks/__init__.py.jinja +0 -0
- flint/templates/flask/rest-api/worker-celery/src/{{package_name}}/tasks/example.py.jinja +6 -0
- flint/templates/flask/rest-api/worker-celery/src/{{package_name}}/worker.py.jinja +16 -0
- flint/templates/flask/template.json +7 -0
- flint_kit-0.14.0.dist-info/METADATA +120 -0
- flint_kit-0.14.0.dist-info/RECORD +193 -0
- flint_kit-0.14.0.dist-info/WHEEL +4 -0
- flint_kit-0.14.0.dist-info/entry_points.txt +3 -0
flint/__init__.py
ADDED
flint/__main__.py
ADDED
flint/cli.py
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"""The `flint` command-line app.
|
|
2
|
+
|
|
3
|
+
See PRODUCT_FLOW.md §1 for the entry-point table this implements: bare
|
|
4
|
+
`flint` behaves like `flint new` (an interactive wizard), `flint new
|
|
5
|
+
NAME --flags...` skips prompts for anything a flag already answers, and
|
|
6
|
+
`--yes` / a non-TTY stdin skips prompting entirely.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import sys
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Annotated, Optional
|
|
14
|
+
|
|
15
|
+
import typer
|
|
16
|
+
from rich.console import Console
|
|
17
|
+
from rich.table import Table
|
|
18
|
+
|
|
19
|
+
from flint import __version__
|
|
20
|
+
from flint import generator, postgen, prefs, prompts
|
|
21
|
+
from flint.errors import FlintUserError
|
|
22
|
+
|
|
23
|
+
app = typer.Typer(add_completion=False, no_args_is_help=False)
|
|
24
|
+
console = Console()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@app.callback(invoke_without_command=True)
|
|
28
|
+
def root(
|
|
29
|
+
ctx: typer.Context,
|
|
30
|
+
version: Annotated[
|
|
31
|
+
bool, typer.Option("--version", help="Show the version and exit.")
|
|
32
|
+
] = False,
|
|
33
|
+
) -> None:
|
|
34
|
+
"""Flint — interactive project scaffolding for Python frameworks."""
|
|
35
|
+
if version:
|
|
36
|
+
console.print(f"flint {__version__}")
|
|
37
|
+
raise typer.Exit()
|
|
38
|
+
if ctx.invoked_subcommand is None:
|
|
39
|
+
_run_new(
|
|
40
|
+
name=None,
|
|
41
|
+
framework=None,
|
|
42
|
+
template=None,
|
|
43
|
+
option=[],
|
|
44
|
+
docker=None,
|
|
45
|
+
git=None,
|
|
46
|
+
install=None,
|
|
47
|
+
yes=False,
|
|
48
|
+
force=False,
|
|
49
|
+
remember=True,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@app.command()
|
|
54
|
+
def new(
|
|
55
|
+
name: Annotated[
|
|
56
|
+
Optional[str], typer.Argument(help="Project name, e.g. 'my-api'.")
|
|
57
|
+
] = None,
|
|
58
|
+
framework: Annotated[
|
|
59
|
+
Optional[str], typer.Option(help="Framework to use, e.g. 'fastapi'.")
|
|
60
|
+
] = None,
|
|
61
|
+
template: Annotated[
|
|
62
|
+
Optional[str],
|
|
63
|
+
typer.Option(help="Template variant within the framework, e.g. 'hello-world'."),
|
|
64
|
+
] = None,
|
|
65
|
+
option: Annotated[
|
|
66
|
+
list[str],
|
|
67
|
+
typer.Option(
|
|
68
|
+
"--option",
|
|
69
|
+
"-o",
|
|
70
|
+
help=(
|
|
71
|
+
"Set a template-specific option as key=value (repeatable), "
|
|
72
|
+
"e.g. -o database=postgres -o orm=sqlmodel. See the wizard "
|
|
73
|
+
"or the template's README for available keys."
|
|
74
|
+
),
|
|
75
|
+
),
|
|
76
|
+
] = [], # noqa: B006 - Typer's documented pattern for repeatable options
|
|
77
|
+
docker: Annotated[
|
|
78
|
+
Optional[bool],
|
|
79
|
+
typer.Option("--docker/--no-docker", help="Add a Dockerfile."),
|
|
80
|
+
] = None,
|
|
81
|
+
git: Annotated[
|
|
82
|
+
Optional[bool],
|
|
83
|
+
typer.Option("--git/--no-git", help="Initialize a git repository."),
|
|
84
|
+
] = None,
|
|
85
|
+
install: Annotated[
|
|
86
|
+
Optional[bool],
|
|
87
|
+
typer.Option("--install/--no-install", help="Install dependencies with uv."),
|
|
88
|
+
] = None,
|
|
89
|
+
yes: Annotated[
|
|
90
|
+
bool,
|
|
91
|
+
typer.Option(
|
|
92
|
+
"--yes", "-y", help="Accept defaults for anything not given as a flag."
|
|
93
|
+
),
|
|
94
|
+
] = False,
|
|
95
|
+
force: Annotated[
|
|
96
|
+
bool,
|
|
97
|
+
typer.Option(
|
|
98
|
+
"--force", help="Generate even if the target directory already exists."
|
|
99
|
+
),
|
|
100
|
+
] = False,
|
|
101
|
+
remember: Annotated[
|
|
102
|
+
bool,
|
|
103
|
+
typer.Option(
|
|
104
|
+
"--remember/--no-remember",
|
|
105
|
+
help=(
|
|
106
|
+
"Remember these choices in ~/.flint/last.json as the "
|
|
107
|
+
"default for next time (framework/template + per-template "
|
|
108
|
+
"options, docker, git, install). Explicit flags/--option "
|
|
109
|
+
"always win regardless."
|
|
110
|
+
),
|
|
111
|
+
),
|
|
112
|
+
] = True,
|
|
113
|
+
) -> None:
|
|
114
|
+
"""Generate a new project."""
|
|
115
|
+
_run_new(
|
|
116
|
+
name=name,
|
|
117
|
+
framework=framework,
|
|
118
|
+
template=template,
|
|
119
|
+
option=option,
|
|
120
|
+
docker=docker,
|
|
121
|
+
git=git,
|
|
122
|
+
install=install,
|
|
123
|
+
yes=yes,
|
|
124
|
+
force=force,
|
|
125
|
+
remember=remember,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@app.command("list-templates")
|
|
130
|
+
def list_templates_cmd() -> None:
|
|
131
|
+
"""List available framework/template combinations."""
|
|
132
|
+
table = Table()
|
|
133
|
+
table.add_column("Framework")
|
|
134
|
+
table.add_column("Template")
|
|
135
|
+
table.add_column("Description")
|
|
136
|
+
table.add_column("Docker", justify="center")
|
|
137
|
+
|
|
138
|
+
for framework in generator.list_frameworks():
|
|
139
|
+
framework_label = (
|
|
140
|
+
framework.label if framework.enabled else f"{framework.label} (coming soon)"
|
|
141
|
+
)
|
|
142
|
+
for template in generator.list_templates(framework.id):
|
|
143
|
+
template_label = (
|
|
144
|
+
template.label if template.enabled else f"{template.label} (coming soon)"
|
|
145
|
+
)
|
|
146
|
+
table.add_row(
|
|
147
|
+
framework_label,
|
|
148
|
+
template_label,
|
|
149
|
+
template.description,
|
|
150
|
+
"✓" if template.supports_docker else "—",
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
console.print(table)
|
|
154
|
+
console.print(
|
|
155
|
+
"\nPass a pair with e.g. [bold]flint new my-api --framework fastapi "
|
|
156
|
+
"--template rest-api[/bold]. Templates with their own choices "
|
|
157
|
+
"(database, ORM, ...) take -o key=value — see the wizard or each "
|
|
158
|
+
"template's README for available keys."
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _run_new(
|
|
163
|
+
*,
|
|
164
|
+
name: Optional[str],
|
|
165
|
+
framework: Optional[str],
|
|
166
|
+
template: Optional[str],
|
|
167
|
+
option: list[str],
|
|
168
|
+
docker: Optional[bool],
|
|
169
|
+
git: Optional[bool],
|
|
170
|
+
install: Optional[bool],
|
|
171
|
+
yes: bool,
|
|
172
|
+
force: bool,
|
|
173
|
+
remember: bool,
|
|
174
|
+
) -> None:
|
|
175
|
+
interactive = sys.stdin.isatty() and not yes
|
|
176
|
+
stored_prefs = prefs.load_prefs() if remember else {}
|
|
177
|
+
|
|
178
|
+
try:
|
|
179
|
+
project_name, slug, package_name = prompts.prompt_project_name(name, interactive)
|
|
180
|
+
|
|
181
|
+
target_dir = Path.cwd() / slug
|
|
182
|
+
if target_dir.exists() and any(target_dir.iterdir()):
|
|
183
|
+
force = prompts.prompt_force_overwrite(slug, force, interactive)
|
|
184
|
+
if not force:
|
|
185
|
+
raise FlintUserError(
|
|
186
|
+
f"Directory '{slug}' already exists and is not empty. "
|
|
187
|
+
"Use --force to generate into it anyway."
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
frameworks = generator.list_frameworks()
|
|
191
|
+
framework_id = prompts.prompt_framework(
|
|
192
|
+
framework,
|
|
193
|
+
frameworks,
|
|
194
|
+
interactive,
|
|
195
|
+
default_id=prefs.get_last_framework(stored_prefs),
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
chosen_framework = generator.get_framework(framework_id)
|
|
199
|
+
|
|
200
|
+
templates = generator.list_templates(framework_id)
|
|
201
|
+
template_id = prompts.prompt_template(
|
|
202
|
+
template,
|
|
203
|
+
templates,
|
|
204
|
+
interactive,
|
|
205
|
+
default_id=prefs.get_last_template(stored_prefs, framework_id),
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
chosen_template = generator.get_template(framework_id, template_id)
|
|
209
|
+
remembered = prefs.get_template_prefs(stored_prefs, chosen_template.full_id)
|
|
210
|
+
|
|
211
|
+
provided_options = prompts.parse_option_flags(option)
|
|
212
|
+
resolved_options = prompts.prompt_template_options(
|
|
213
|
+
chosen_template, provided_options, interactive, last=remembered["options"]
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
docker_requested = prompts.prompt_docker(
|
|
217
|
+
docker, interactive, remembered=remembered["docker"]
|
|
218
|
+
)
|
|
219
|
+
if docker_requested and not chosen_template.supports_docker:
|
|
220
|
+
console.print(
|
|
221
|
+
f"[yellow]![/yellow] {chosen_template.full_id} doesn't support "
|
|
222
|
+
"--docker yet — skipping the Dockerfile."
|
|
223
|
+
)
|
|
224
|
+
docker_requested = False
|
|
225
|
+
|
|
226
|
+
if interactive:
|
|
227
|
+
console.print("Using uv to manage dependencies.")
|
|
228
|
+
|
|
229
|
+
git_init = prompts.prompt_git_init(git, interactive, remembered=remembered["git_init"])
|
|
230
|
+
install_now = prompts.prompt_install(install, interactive, remembered=remembered["install"])
|
|
231
|
+
|
|
232
|
+
answers = generator.Answers(
|
|
233
|
+
project_name=project_name,
|
|
234
|
+
slug=slug,
|
|
235
|
+
package_name=package_name,
|
|
236
|
+
framework=framework_id,
|
|
237
|
+
template=template_id,
|
|
238
|
+
git_init=git_init,
|
|
239
|
+
install=install_now,
|
|
240
|
+
docker=docker_requested,
|
|
241
|
+
options=resolved_options,
|
|
242
|
+
)
|
|
243
|
+
created = generator.render(framework_id, template_id, target_dir, answers, force=force)
|
|
244
|
+
|
|
245
|
+
if remember:
|
|
246
|
+
prefs.save_prefs(
|
|
247
|
+
prefs.record_run(
|
|
248
|
+
stored_prefs,
|
|
249
|
+
framework_id=framework_id,
|
|
250
|
+
template_id=template_id,
|
|
251
|
+
full_id=chosen_template.full_id,
|
|
252
|
+
options=resolved_options,
|
|
253
|
+
docker=docker_requested,
|
|
254
|
+
git_init=git_init,
|
|
255
|
+
install=install_now,
|
|
256
|
+
)
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
git_ok = postgen.git_init(target_dir) if git_init else False
|
|
260
|
+
installed_ok = postgen.install_dependencies(target_dir) if install_now else False
|
|
261
|
+
|
|
262
|
+
postgen.print_summary(
|
|
263
|
+
project_name=project_name,
|
|
264
|
+
slug=slug,
|
|
265
|
+
template_full_id=chosen_template.full_id,
|
|
266
|
+
target_dir=target_dir,
|
|
267
|
+
created=created,
|
|
268
|
+
git_ok=git_ok,
|
|
269
|
+
installed_ok=installed_ok,
|
|
270
|
+
installed_requested=install_now,
|
|
271
|
+
run_command=chosen_framework.run_command.format(package_name=package_name),
|
|
272
|
+
options=resolved_options,
|
|
273
|
+
)
|
|
274
|
+
except FlintUserError as exc:
|
|
275
|
+
console.print(f"[red]Error:[/red] {exc}")
|
|
276
|
+
raise typer.Exit(1) from None
|
|
277
|
+
except typer.Exit:
|
|
278
|
+
raise
|
|
279
|
+
except Exception as exc: # noqa: BLE001 - top-level safety net, see errors.py
|
|
280
|
+
console.print(f"[red]Unexpected error:[/red] {exc}")
|
|
281
|
+
raise typer.Exit(2) from None
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def main() -> None:
|
|
285
|
+
app()
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
if __name__ == "__main__":
|
|
289
|
+
main()
|
flint/errors.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Flint's error hierarchy.
|
|
2
|
+
|
|
3
|
+
`FlintUserError` maps to exit code 1 (the user did something we can
|
|
4
|
+
explain, e.g. an existing directory or an invalid name). Anything else
|
|
5
|
+
bubbles up and is treated as exit code 2 (unexpected/internal error), per
|
|
6
|
+
PRODUCT_FLOW.md §5.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class FlintError(Exception):
|
|
13
|
+
"""Base class for all Flint-raised errors."""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class FlintUserError(FlintError):
|
|
17
|
+
"""An error caused by user input/environment, not a Flint bug."""
|