paststack 0.1.2__tar.gz → 0.1.4__tar.gz

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 (47) hide show
  1. {paststack-0.1.2 → paststack-0.1.4}/PKG-INFO +2 -2
  2. {paststack-0.1.2 → paststack-0.1.4}/README.md +1 -1
  3. paststack-0.1.4/paststack/__init__.py +1 -0
  4. {paststack-0.1.2 → paststack-0.1.4}/paststack/cli.py +10 -11
  5. paststack-0.1.4/paststack/prompts.py +180 -0
  6. {paststack-0.1.2 → paststack-0.1.4}/paststack.egg-info/PKG-INFO +2 -2
  7. {paststack-0.1.2 → paststack-0.1.4}/pyproject.toml +1 -1
  8. {paststack-0.1.2 → paststack-0.1.4}/tests/test_cli_generation.py +1 -4
  9. paststack-0.1.2/paststack/__init__.py +0 -1
  10. paststack-0.1.2/paststack/prompts.py +0 -92
  11. {paststack-0.1.2 → paststack-0.1.4}/LICENSE +0 -0
  12. {paststack-0.1.2 → paststack-0.1.4}/paststack/__main__.py +0 -0
  13. {paststack-0.1.2 → paststack-0.1.4}/paststack/banner.py +0 -0
  14. {paststack-0.1.2 → paststack-0.1.4}/paststack/combinations.py +0 -0
  15. {paststack-0.1.2 → paststack-0.1.4}/paststack/models.py +0 -0
  16. {paststack-0.1.2 → paststack-0.1.4}/paststack/templates/base/.env.example +0 -0
  17. {paststack-0.1.2 → paststack-0.1.4}/paststack/templates/base/.gitignore +0 -0
  18. {paststack-0.1.2 → paststack-0.1.4}/paststack/templates/base/README.md +0 -0
  19. {paststack-0.1.2 → paststack-0.1.4}/paststack/templates/base/pyproject.toml +0 -0
  20. {paststack-0.1.2 → paststack-0.1.4}/paststack/templates/base/src/app/__init__.py +0 -0
  21. {paststack-0.1.2 → paststack-0.1.4}/paststack/templates/base/src/app/api/__init__.py +0 -0
  22. {paststack-0.1.2 → paststack-0.1.4}/paststack/templates/base/src/app/api/deps.py +0 -0
  23. {paststack-0.1.2 → paststack-0.1.4}/paststack/templates/base/src/app/api/router.py +0 -0
  24. {paststack-0.1.2 → paststack-0.1.4}/paststack/templates/base/src/app/api/routes/__init__.py +0 -0
  25. {paststack-0.1.2 → paststack-0.1.4}/paststack/templates/base/src/app/api/routes/health.py +0 -0
  26. {paststack-0.1.2 → paststack-0.1.4}/paststack/templates/base/src/app/core/__init__.py +0 -0
  27. {paststack-0.1.2 → paststack-0.1.4}/paststack/templates/base/src/app/core/config.py +0 -0
  28. {paststack-0.1.2 → paststack-0.1.4}/paststack/templates/base/src/app/database.py +0 -0
  29. {paststack-0.1.2 → paststack-0.1.4}/paststack/templates/base/src/app/main.py +0 -0
  30. {paststack-0.1.2 → paststack-0.1.4}/paststack/templates/base/src/app/models/__init__.py +0 -0
  31. {paststack-0.1.2 → paststack-0.1.4}/paststack/templates/base/src/app/schemas/__init__.py +0 -0
  32. {paststack-0.1.2 → paststack-0.1.4}/paststack/templates/database/postgres/docker-compose.yml +0 -0
  33. {paststack-0.1.2 → paststack-0.1.4}/paststack/templates/database/postgres/none/database.py +0 -0
  34. {paststack-0.1.2 → paststack-0.1.4}/paststack/templates/database/postgres/sqlmodel/api/deps.py +0 -0
  35. {paststack-0.1.2 → paststack-0.1.4}/paststack/templates/database/postgres/sqlmodel/database.py +0 -0
  36. {paststack-0.1.2 → paststack-0.1.4}/paststack/templates/database/postgres/sqlmodel/models/__init__.py +0 -0
  37. {paststack-0.1.2 → paststack-0.1.4}/paststack/templates/database/sqlite/none/database.py +0 -0
  38. {paststack-0.1.2 → paststack-0.1.4}/paststack/templates/database/sqlite/sqlmodel/api/deps.py +0 -0
  39. {paststack-0.1.2 → paststack-0.1.4}/paststack/templates/database/sqlite/sqlmodel/database.py +0 -0
  40. {paststack-0.1.2 → paststack-0.1.4}/paststack/templates/database/sqlite/sqlmodel/models/__init__.py +0 -0
  41. {paststack-0.1.2 → paststack-0.1.4}/paststack/templates/rate_limiting/src/app/core/rate_limit.py +0 -0
  42. {paststack-0.1.2 → paststack-0.1.4}/paststack.egg-info/SOURCES.txt +0 -0
  43. {paststack-0.1.2 → paststack-0.1.4}/paststack.egg-info/dependency_links.txt +0 -0
  44. {paststack-0.1.2 → paststack-0.1.4}/paststack.egg-info/entry_points.txt +0 -0
  45. {paststack-0.1.2 → paststack-0.1.4}/paststack.egg-info/requires.txt +0 -0
  46. {paststack-0.1.2 → paststack-0.1.4}/paststack.egg-info/top_level.txt +0 -0
  47. {paststack-0.1.2 → paststack-0.1.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: paststack
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: Opinionated CLI to generate production-ready FastAPI boilerplate with typing and linting.
5
5
  License-Expression: MIT
6
6
  Project-URL: Repository, https://github.com/initd-fr/paststack
@@ -25,7 +25,7 @@ pip install -U paststack
25
25
  paststack
26
26
  ```
27
27
 
28
- Si le terminal intégré **VS Code** n’affiche pas les questions, utilise **Terminal.app** / **iTerm**, ou : `python -m paststack` (même venv).
28
+ Les questions passent par **click** (terminal classique, VS Code, Cursor). Pour l’UI **questionary** à la place : `PASTSTACK_USE_QUESTIONARY=1 paststack`.
29
29
 
30
30
  ## Develop
31
31
 
@@ -9,7 +9,7 @@ pip install -U paststack
9
9
  paststack
10
10
  ```
11
11
 
12
- Si le terminal intégré **VS Code** n’affiche pas les questions, utilise **Terminal.app** / **iTerm**, ou : `python -m paststack` (même venv).
12
+ Les questions passent par **click** (terminal classique, VS Code, Cursor). Pour l’UI **questionary** à la place : `PASTSTACK_USE_QUESTIONARY=1 paststack`.
13
13
 
14
14
  ## Develop
15
15
 
@@ -0,0 +1 @@
1
+ __version__ = "0.1.4"
@@ -6,7 +6,6 @@ from pathlib import Path
6
6
  from shutil import copyfile, which
7
7
 
8
8
  import click
9
- import questionary
10
9
  from rich.progress import (
11
10
  BarColumn,
12
11
  Progress,
@@ -17,7 +16,7 @@ from rich.progress import (
17
16
 
18
17
  from paststack.banner import display_banner
19
18
  from paststack.models import Database, Orm, Project
20
- from paststack.prompts import ask_questions, show_summary
19
+ from paststack.prompts import ask_questions, confirm, show_summary
21
20
 
22
21
  TEMPLATES_ROOT = Path(__file__).resolve().parent / "templates"
23
22
  BASE = TEMPLATES_ROOT / "base"
@@ -154,7 +153,7 @@ def main() -> None:
154
153
  while True:
155
154
  project = ask_questions()
156
155
  show_summary(project)
157
- if questionary.confirm("Proceed with project setup ?").unsafe_ask():
156
+ if confirm("Proceed with project setup ?", default=True):
158
157
  break
159
158
  setup_project(project)
160
159
  except KeyboardInterrupt:
@@ -327,21 +326,21 @@ def setup_project(project: Project) -> None:
327
326
  copyfile(env_example, env_dest)
328
327
  click.echo("Fichier .env créé à partir de .env.example.")
329
328
 
330
- start_uvicorn = project.run_install and questionary.confirm(
329
+ start_uvicorn = project.run_install and confirm(
331
330
  "Démarrer l’API maintenant (uvicorn --reload) ?",
332
331
  default=True,
333
- ).unsafe_ask()
332
+ )
334
333
 
335
334
  if start_uvicorn and project.database == Database.POSTGRES:
336
- if questionary.confirm(
335
+ if confirm(
337
336
  "Lancer PostgreSQL avec Docker (`docker compose up -d`) ?",
338
337
  default=True,
339
- ).unsafe_ask():
338
+ ):
340
339
  if not _docker_compose_up_postgres(main_directory):
341
- start_uvicorn = questionary.confirm(
340
+ start_uvicorn = confirm(
342
341
  "Lancer uvicorn quand même ? (l’API échouera si Postgres n’est pas joignable.)",
343
342
  default=False,
344
- ).unsafe_ask()
343
+ )
345
344
  else:
346
345
  time.sleep(2)
347
346
  else:
@@ -352,10 +351,10 @@ def setup_project(project: Project) -> None:
352
351
  fg="yellow",
353
352
  )
354
353
  )
355
- start_uvicorn = questionary.confirm(
354
+ start_uvicorn = confirm(
356
355
  "Lancer uvicorn quand même ?",
357
356
  default=False,
358
- ).unsafe_ask()
357
+ )
359
358
 
360
359
  if start_uvicorn:
361
360
  click.echo(
@@ -0,0 +1,180 @@
1
+ import os
2
+
3
+ import click
4
+ import questionary
5
+
6
+ from paststack.models import Database, Orm, Project
7
+
8
+
9
+ def use_simple_prompts() -> bool:
10
+ """Par défaut : prompts `click` (fiables partout, dont terminal intégré VS Code / Cursor).
11
+
12
+ Questionary (prompt_toolkit) uniquement si tu choisis explicitement :
13
+ `PASTSTACK_USE_QUESTIONARY=1 paststack`
14
+ """
15
+ if os.environ.get("PASTSTACK_USE_QUESTIONARY", "").strip().lower() in ("1", "true", "yes", "on"):
16
+ return False
17
+ return True
18
+
19
+
20
+ def confirm(message: str, default: bool = False) -> bool:
21
+ if use_simple_prompts():
22
+ return click.confirm(message, default=default)
23
+ out = questionary.confirm(message, default=default).unsafe_ask()
24
+ return bool(out)
25
+
26
+
27
+ def _pick_choice(label: str, values: list[str], *, default_value: str) -> str:
28
+ click.echo(f"\n{label}")
29
+ default_idx = values.index(default_value) + 1 if default_value in values else 1
30
+ for i, v in enumerate(values, start=1):
31
+ mark = "*" if i == default_idx else " "
32
+ click.echo(f" [{i}]{mark} {v}")
33
+ choice = click.prompt(
34
+ "Numéro",
35
+ type=click.IntRange(1, len(values)),
36
+ default=default_idx,
37
+ show_default=True,
38
+ )
39
+ return values[int(choice) - 1]
40
+
41
+
42
+ def _ask_questions_click() -> Project:
43
+ click.echo(click.style("(saisie clavier — Entrée = défaut à chaque question)", fg="dim"))
44
+ project_name = click.prompt("Project name", default="my_fastapi_app", show_default=True)
45
+ raw_allowed_origins = click.prompt(
46
+ "Allowed origins (CORS), séparés par des virgules [vide = désactivé]",
47
+ default="",
48
+ show_default=False,
49
+ )
50
+ allowed_origins = [o.strip() for o in raw_allowed_origins.split(",") if o.strip()]
51
+
52
+ db_values = [e.value for e in Database]
53
+ raw_database = _pick_choice("Base de données", db_values, default_value="none")
54
+
55
+ raw_orm = "none"
56
+ if raw_database != "none":
57
+ orm_values = [e.value for e in Orm]
58
+ raw_orm = _pick_choice(
59
+ "Couche données (sqlmodel = ORM + Pydantic, none = driver seul)",
60
+ orm_values,
61
+ default_value="sqlmodel",
62
+ )
63
+
64
+ rate_limiting = click.confirm("Activer le rate limiting ?", default=False)
65
+ run_install = click.confirm("Lancer l’installation (uv sync) après la génération ?", default=True)
66
+ git = click.confirm("Initialiser un dépôt git dans le nouveau projet ?", default=True)
67
+ git_z = False
68
+ if git:
69
+ git_z = click.confirm(
70
+ "Lancer git z init (git-z) ? — https://github.com/ejpcmac/git-z",
71
+ default=False,
72
+ )
73
+
74
+ return Project(
75
+ project_name=project_name,
76
+ package_manager="uv",
77
+ use_typing=True,
78
+ use_ruff=True,
79
+ enable_cors=bool(allowed_origins),
80
+ allowed_origins=allowed_origins,
81
+ database=Database(raw_database),
82
+ orm=Orm(raw_orm),
83
+ rate_limiting=rate_limiting,
84
+ config=True,
85
+ git=git,
86
+ git_z=git_z,
87
+ run_install=run_install,
88
+ )
89
+
90
+
91
+ def _ask_questions_questionary() -> Project:
92
+ project_name = questionary.text(
93
+ "Project name",
94
+ default="my_fastapi_app",
95
+ ).unsafe_ask()
96
+
97
+ raw_allowed_origins: str = questionary.text(
98
+ "Allowed origins (CORS) [comma-separated, empty = disabled]",
99
+ default="",
100
+ ).unsafe_ask()
101
+
102
+ allowed_origins: list[str] = [
103
+ origin.strip() for origin in raw_allowed_origins.split(",") if origin.strip()
104
+ ]
105
+
106
+ raw_database: str = questionary.select(
107
+ "Database",
108
+ choices=[e.value for e in Database],
109
+ default="none",
110
+ ).unsafe_ask()
111
+
112
+ raw_orm: str = "none"
113
+ if raw_database != "none":
114
+ raw_orm = questionary.select(
115
+ "Data layer (SQLModel = ORM + Pydantic, none = driver only)",
116
+ choices=[e.value for e in Orm],
117
+ default="sqlmodel",
118
+ ).unsafe_ask()
119
+
120
+ rate_limiting: bool = questionary.confirm(
121
+ "Enable rate limiting",
122
+ default=False,
123
+ ).unsafe_ask()
124
+
125
+ run_install: bool = questionary.confirm(
126
+ "Run install after setup",
127
+ default=True,
128
+ ).unsafe_ask()
129
+
130
+ git: bool = questionary.confirm(
131
+ "Initialize git repository in the new project?",
132
+ default=True,
133
+ ).unsafe_ask()
134
+
135
+ git_z: bool = False
136
+ if git:
137
+ git_z = questionary.confirm(
138
+ "Run git z init (git-z commit wizard)? Requires git-z — https://github.com/ejpcmac/git-z",
139
+ default=False,
140
+ ).unsafe_ask()
141
+
142
+ return Project(
143
+ project_name=project_name or "my_fastapi_app",
144
+ package_manager="uv",
145
+ use_typing=True,
146
+ use_ruff=True,
147
+ enable_cors=bool(allowed_origins),
148
+ allowed_origins=allowed_origins,
149
+ database=Database(raw_database or "none"),
150
+ orm=Orm(raw_orm or "none"),
151
+ rate_limiting=bool(rate_limiting),
152
+ config=True,
153
+ git=bool(git),
154
+ git_z=bool(git_z),
155
+ run_install=bool(run_install),
156
+ )
157
+
158
+
159
+ def ask_questions() -> Project:
160
+ if use_simple_prompts():
161
+ return _ask_questions_click()
162
+ return _ask_questions_questionary()
163
+
164
+
165
+ def show_summary(project: Project) -> None:
166
+ click.echo()
167
+ click.echo(f"Project : {project.project_name}")
168
+ click.echo(f"Package Manager : {project.package_manager}")
169
+
170
+ click.echo(f"CORS enabled : {'yes' if project.enable_cors else 'no'}")
171
+ if project.enable_cors:
172
+ click.echo(f"Allowed origins : {project.allowed_origins}")
173
+
174
+ click.echo(f"Database : {project.database.value}")
175
+ click.echo(f"ORM : {project.orm.value}")
176
+ click.echo(f"Rate limiting : {'yes' if project.rate_limiting else 'no'}")
177
+ click.echo(f"Run install : {'yes' if project.run_install else 'no'}")
178
+ click.echo(f"Git init : {'yes' if project.git else 'no'}")
179
+ click.echo(f"git-z init : {'yes' if project.git_z else 'no'}")
180
+ click.echo()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: paststack
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: Opinionated CLI to generate production-ready FastAPI boilerplate with typing and linting.
5
5
  License-Expression: MIT
6
6
  Project-URL: Repository, https://github.com/initd-fr/paststack
@@ -25,7 +25,7 @@ pip install -U paststack
25
25
  paststack
26
26
  ```
27
27
 
28
- Si le terminal intégré **VS Code** n’affiche pas les questions, utilise **Terminal.app** / **iTerm**, ou : `python -m paststack` (même venv).
28
+ Les questions passent par **click** (terminal classique, VS Code, Cursor). Pour l’UI **questionary** à la place : `PASTSTACK_USE_QUESTIONARY=1 paststack`.
29
29
 
30
30
  ## Develop
31
31
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "paststack"
7
- version = "0.1.2"
7
+ version = "0.1.4"
8
8
  description = "Opinionated CLI to generate production-ready FastAPI boilerplate with typing and linting."
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -34,10 +34,7 @@ def no_subprocess(monkeypatch: pytest.MonkeyPatch) -> None:
34
34
 
35
35
  @pytest.fixture
36
36
  def no_server_prompt(monkeypatch: pytest.MonkeyPatch) -> None:
37
- monkeypatch.setattr(
38
- "paststack.cli.questionary.confirm",
39
- _stub_confirm,
40
- )
37
+ monkeypatch.setattr("paststack.cli.confirm", lambda *_a, **_k: False)
41
38
 
42
39
 
43
40
  def test_combination_count_matches_iter() -> None:
@@ -1 +0,0 @@
1
- __version__ = "0.1.2"
@@ -1,92 +0,0 @@
1
- import click
2
- import questionary
3
-
4
- from paststack.models import Database, Orm, Project
5
-
6
-
7
- def ask_questions() -> Project:
8
- project_name = questionary.text(
9
- "Project name",
10
- default="my_fastapi_app",
11
- ).unsafe_ask()
12
-
13
- raw_allowed_origins: str = questionary.text(
14
- "Allowed origins (CORS) [comma-separated, empty = disabled]",
15
- default="",
16
- ).unsafe_ask()
17
-
18
- allowed_origins: list[str] = [
19
- origin.strip() for origin in raw_allowed_origins.split(",") if origin.strip()
20
- ]
21
-
22
- raw_database: str = questionary.select(
23
- "Database",
24
- choices=[e.value for e in Database],
25
- default="none",
26
- ).unsafe_ask()
27
-
28
- raw_orm: str = "none"
29
- if raw_database != "none":
30
- raw_orm = questionary.select(
31
- "Data layer (SQLModel = ORM + Pydantic, none = driver only)",
32
- choices=[e.value for e in Orm],
33
- default="sqlmodel",
34
- ).unsafe_ask()
35
-
36
- rate_limiting: bool = questionary.confirm(
37
- "Enable rate limiting",
38
- default=False,
39
- ).unsafe_ask()
40
-
41
- run_install: bool = questionary.confirm(
42
- "Run install after setup",
43
- default=True,
44
- ).unsafe_ask()
45
-
46
- git: bool = questionary.confirm(
47
- "Initialize git repository in the new project?",
48
- default=True,
49
- ).unsafe_ask()
50
-
51
- git_z: bool = False
52
- if git:
53
- git_z = questionary.confirm(
54
- "Run git z init (git-z commit wizard)? Requires git-z — https://github.com/ejpcmac/git-z",
55
- default=False,
56
- ).unsafe_ask()
57
-
58
- project = Project(
59
- project_name=project_name,
60
- package_manager="uv",
61
- use_typing=True,
62
- use_ruff=True,
63
- enable_cors=bool(allowed_origins),
64
- allowed_origins=allowed_origins,
65
- database=Database(raw_database),
66
- orm=Orm(raw_orm),
67
- rate_limiting=rate_limiting,
68
- config=True,
69
- git=git,
70
- git_z=git_z,
71
- run_install=run_install,
72
- )
73
-
74
- return project
75
-
76
-
77
- def show_summary(project: Project) -> None:
78
- click.echo()
79
- click.echo(f"Project : {project.project_name}")
80
- click.echo(f"Package Manager : {project.package_manager}")
81
-
82
- click.echo(f"CORS enabled : {'yes' if project.enable_cors else 'no'}")
83
- if project.enable_cors:
84
- click.echo(f"Allowed origins : {project.allowed_origins}")
85
-
86
- click.echo(f"Database : {project.database.value}")
87
- click.echo(f"ORM : {project.orm.value}")
88
- click.echo(f"Rate limiting : {'yes' if project.rate_limiting else 'no'}")
89
- click.echo(f"Run install : {'yes' if project.run_install else 'no'}")
90
- click.echo(f"Git init : {'yes' if project.git else 'no'}")
91
- click.echo(f"git-z init : {'yes' if project.git_z else 'no'}")
92
- click.echo()
File without changes
File without changes
File without changes
File without changes