sprout-template 1.3.0__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 zigai
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,394 @@
1
+ Metadata-Version: 2.4
2
+ Name: sprout-template
3
+ Version: 1.3.0
4
+ Summary: A Jinja2 project generator with Python-based configuration
5
+ Keywords: sprout
6
+ Author: zigai
7
+ Author-email: zigai <ziga.ivansek@gmail.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Environment :: Console
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3 :: Only
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Software Development
19
+ Classifier: Topic :: Software Development :: Code Generators
20
+ Classifier: Topic :: Utilities
21
+ Requires-Dist: interfacy>=0.6.7
22
+ Requires-Dist: jinja2>=3.0
23
+ Requires-Dist: prompt-toolkit>=3.0.52
24
+ Requires-Dist: rich>=13
25
+ Requires-Dist: pytest>=9.0.3 ; extra == 'dev'
26
+ Requires-Dist: coverage>=7.14.1 ; extra == 'dev'
27
+ Requires-Dist: ruff==0.15.16 ; extra == 'dev'
28
+ Requires-Dist: pre-commit>=4.6.0 ; extra == 'dev'
29
+ Requires-Dist: pyrefly>=1.0.0 ; extra == 'dev'
30
+ Requires-Dist: codespell>=2.4.2 ; extra == 'dev'
31
+ Requires-Dist: rattle-lint>=2.5.1 ; extra == 'dev'
32
+ Requires-Dist: pytest>=9.0.3 ; extra == 'test'
33
+ Requires-Dist: coverage>=7.14.1 ; extra == 'test'
34
+ Requires-Python: >=3.12
35
+ Project-URL: Repository, https://github.com/zigai/sprout
36
+ Project-URL: Issues, https://github.com/zigai/sprout/issues
37
+ Project-URL: Homepage, https://github.com/zigai/sprout
38
+ Provides-Extra: dev
39
+ Provides-Extra: test
40
+ Description-Content-Type: text/markdown
41
+
42
+ # sprout
43
+
44
+ Sprout is a Jinja2-based project generator with a Python manifest.
45
+
46
+ Instead of configuring prompts in
47
+ YAML, you write `sprout.py`:
48
+
49
+ ```python
50
+ from sprout import Question
51
+
52
+ questions = [
53
+ Question(key="project_name", prompt="Project name"),
54
+ ]
55
+ ```
56
+
57
+ That single manifest drives interactive prompts, CLI flags, validation and conditional questions.
58
+
59
+ Template files go in `template/`. `.jinja` files are rendered, everything else
60
+ is copied.
61
+
62
+ Works with local templates, Git repos, or `owner/repo` GitHub shorthand.
63
+
64
+ Every question becomes a CLI flag so you can script it too:
65
+
66
+ ```bash
67
+ sprout new <template-path> <project-path>
68
+ sprout new <template-path> <project-path> --project-name demo
69
+ ```
70
+
71
+ ## Install
72
+
73
+ ```bash
74
+ uv tool install sprout-template
75
+ ```
76
+
77
+ ## Usage
78
+
79
+ ```bash
80
+ sprout init [directory]
81
+ sprout add <template-source> [--name <trusted-name>]
82
+ sprout list
83
+ sprout new <template> <project-path> [--force] [--<question-flag> <value> ...]
84
+ ```
85
+
86
+ `new` accepts a local template path, Git URL, `owner/repo` GitHub shorthand, or a trusted name added
87
+ with `sprout add`. Pass values for question flags to skip those prompts:
88
+
89
+ ```bash
90
+ sprout new <template-path> <project-path> --project-name demo
91
+ ```
92
+
93
+ Use `sprout new <template> --help` to show template-specific flags.
94
+
95
+ ### Initialize a template
96
+
97
+ Create a minimal `sprout.py` and `template/README.md.jinja` scaffold in the current directory:
98
+
99
+ ```bash
100
+ sprout init
101
+ ```
102
+
103
+ Pass a directory to initialize it elsewhere. Existing scaffold files are never overwritten.
104
+
105
+ ### Trusted templates
106
+
107
+ Store a reusable name for any supported template source:
108
+
109
+ ```bash
110
+ sprout add zigai/python-project-template --name python
111
+ sprout new python ./my-project
112
+ sprout list
113
+ ```
114
+
115
+ ## Template structure
116
+
117
+ The source root must contain `sprout.py`.
118
+
119
+ The only required name is `questions`.
120
+
121
+ ```python
122
+ from sprout import Question
123
+
124
+ questions = [
125
+ Question(key="project_name", prompt="Project name"),
126
+ ]
127
+ ```
128
+
129
+ Optional names are `template_dir`, `style`, `extensions`, `title`, `cli_boolean_style`,
130
+ `should_skip_file(...)`, and `apply(context)`.
131
+
132
+ ## Question model
133
+
134
+ Each `Question` describes one answer:
135
+
136
+ ```python
137
+ from sprout import Question
138
+
139
+ Question(
140
+ key="project_name",
141
+ prompt="Project name",
142
+ help="Used for package metadata and generated paths",
143
+ default="demo",
144
+ )
145
+ ```
146
+
147
+ `key` is the answer dictionary key. It also becomes the CLI flag name.
148
+
149
+ ```text
150
+ project_name -> --project-name
151
+ ```
152
+
153
+ ## Choices
154
+
155
+ Use choices when the answer should come from a closed list:
156
+
157
+ ```python
158
+ from sprout import Question
159
+
160
+ questions = [
161
+ Question(
162
+ key="package_manager",
163
+ prompt="Package manager",
164
+ choices=[("uv", "uv"), ("pip", "pip")],
165
+ default="uv",
166
+ ),
167
+ ]
168
+ ```
169
+
170
+ ## Multiselect
171
+
172
+ ```python
173
+ from sprout import Question
174
+
175
+ questions = [
176
+ Question(
177
+ key="workflow",
178
+ prompt="Workflows",
179
+ choices=[("tests", "Tests"), ("lint", "Lint")],
180
+ multiselect=True,
181
+ ),
182
+ ]
183
+ ```
184
+
185
+ From the CLI:
186
+
187
+ ```bash
188
+ sprout new <template-path> <project-path> --workflow tests --workflow lint
189
+ ```
190
+
191
+ ## Booleans
192
+
193
+ Use the built-in yes/no helper:
194
+
195
+ ```python
196
+ from sprout import Question
197
+
198
+ questions = [
199
+ Question.yes_no(
200
+ key="git_init",
201
+ prompt="Initialize Git?",
202
+ default=True,
203
+ ),
204
+ ]
205
+ ```
206
+
207
+ By default, yes/no questions are exposed as Boolean CLI flags:
208
+
209
+ ```bash
210
+ sprout new <template-path> <project-path> --git-init
211
+ sprout new <template-path> <project-path> --no-git-init
212
+ ```
213
+
214
+ If a template should use explicit yes/no values instead, opt into that style in `sprout.py`:
215
+
216
+ ```python
217
+ cli_boolean_style = "yes-no"
218
+ ```
219
+
220
+ Then the CLI accepts values for yes/no questions:
221
+
222
+ ```bash
223
+ sprout new <template-path> <project-path> --git-init yes
224
+ sprout new <template-path> <project-path> --git-init no
225
+ ```
226
+
227
+ ## Conditional flow
228
+
229
+ `when` can be a boolean or a callable that receives the answers collected so far:
230
+
231
+ ```python
232
+ from sprout import Question
233
+
234
+ questions = [
235
+ Question.yes_no(
236
+ key="create_github_repo",
237
+ prompt="Create GitHub repository?",
238
+ default=False,
239
+ ),
240
+ Question(
241
+ key="github_repo_visibility",
242
+ prompt="GitHub repository visibility",
243
+ choices=[("private", "Private"), ("public", "Public")],
244
+ default="private",
245
+ when=lambda answers: bool(answers.get("create_github_repo")),
246
+ ),
247
+ ]
248
+ ```
249
+
250
+ ## Defaults, parsers, and validators
251
+
252
+ Defaults can be static values or callables. Use `default=""` for a text prompt that should accept
253
+ a blank answer.
254
+
255
+ ```python
256
+ from sprout import Question
257
+
258
+ questions = [
259
+ Question(
260
+ key="package_name",
261
+ prompt="Package name",
262
+ default=lambda answers: str(answers["project_name"]).replace("-", "_"),
263
+ ),
264
+ ]
265
+ ```
266
+
267
+ Validators return `(valid, message)`:
268
+
269
+ ```python
270
+ from sprout import Question, validate_repository_url
271
+
272
+ questions = [
273
+ Question(
274
+ key="repository_url",
275
+ prompt="Repository URL",
276
+ validators=[validate_repository_url],
277
+ ),
278
+ ]
279
+ ```
280
+
281
+ Sprout includes validators for repository URLs, GitHub repository URLs, repository names, npm package
282
+ names, and semantic versions.
283
+
284
+ ## Destination-aware questions
285
+
286
+ When question definitions need runtime context, make `questions` callable:
287
+
288
+ ```python
289
+ from pathlib import Path
290
+ from jinja2 import Environment
291
+ from sprout import Question
292
+
293
+
294
+ def questions(env: Environment, destination: Path) -> list[Question]:
295
+ return [
296
+ Question(
297
+ key="project_name",
298
+ prompt="Project name",
299
+ default=destination.name,
300
+ ),
301
+ ]
302
+ ```
303
+
304
+ The callable must accept exactly two positional parameters: `env` and `destination`.
305
+
306
+ ## Rendering
307
+
308
+ Default rendering uses `template_dir`, or `template` when no directory is declared.
309
+
310
+ ```python
311
+ template_dir = "template"
312
+ ```
313
+
314
+ ## Skipping files
315
+
316
+ `should_skip_file` receives a path relative to `template_dir` and the final answers:
317
+
318
+ ```python
319
+ from sprout import NO_LICENSE
320
+
321
+
322
+ def should_skip_file(relative_path: str, answers: dict[str, object]) -> bool:
323
+ return relative_path == "LICENSE.jinja" and answers.get("license") == NO_LICENSE
324
+ ```
325
+
326
+ ## Jinja environment
327
+
328
+ Set Jinja2 extension classes with `extensions`:
329
+
330
+ ```python
331
+ from sprout import CurrentYearExtension, GitDefaultsExtension
332
+
333
+ extensions = [GitDefaultsExtension, CurrentYearExtension]
334
+ ```
335
+
336
+ When `extensions` is omitted, the default environment includes Git defaults:
337
+
338
+ - `git_user_name`
339
+ - `git_user_email`
340
+ - `github_username`
341
+
342
+ Include `GitDefaultsExtension` explicitly when you provide a custom extension list and still want
343
+ those globals.
344
+
345
+ `CurrentYearExtension` exposes:
346
+
347
+ - `current_year`
348
+
349
+ ## Prompt title and style
350
+
351
+ ```python
352
+ title = "Generate a Python package"
353
+ ```
354
+
355
+ ```python
356
+ from sprout import ManifestContext
357
+
358
+
359
+ def title(context: ManifestContext) -> str | None:
360
+ return f"Generate project in {context.destination}"
361
+ ```
362
+
363
+ The title is evaluated before answers are collected. For prompt appearance, assign `style` to a
364
+ `sprout.Style` instance.
365
+
366
+ ## Custom generation
367
+
368
+ Most templates should use the default renderer, but you can define `apply(context)` when generation needs
369
+ custom file creation, post-processing, or post-generation actions.
370
+
371
+ ```python
372
+ from sprout import ManifestContext, render_templates
373
+
374
+
375
+ def apply(context: ManifestContext):
376
+ return render_templates(
377
+ context.env,
378
+ context.template_dir,
379
+ context.destination,
380
+ context.answers,
381
+ render_paths=True,
382
+ )
383
+ ```
384
+
385
+ `apply` must accept exactly one `context` parameter. It may return `None`, one path, or a sequence of
386
+ paths for the generated-files summary.
387
+
388
+ ## Examples
389
+
390
+ - [python-project-template](https://github.com/zigai/python-project-template).
391
+
392
+ ## License
393
+
394
+ [MIT License](https://github.com/zigai/sprout/blob/master/LICENSE)