create-awesome-python-app 0.2.0__tar.gz → 0.2.2__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.
- {create_awesome_python_app-0.2.0 → create_awesome_python_app-0.2.2}/PKG-INFO +2 -2
- create_awesome_python_app-0.2.2/assets/hero.svg +19 -0
- {create_awesome_python_app-0.2.0 → create_awesome_python_app-0.2.2}/pyproject.toml +2 -2
- {create_awesome_python_app-0.2.0 → create_awesome_python_app-0.2.2}/src/create_awesome_python_app/__init__.py +1 -1
- {create_awesome_python_app-0.2.0 → create_awesome_python_app-0.2.2}/src/create_awesome_python_app/catalog.py +3 -16
- {create_awesome_python_app-0.2.0 → create_awesome_python_app-0.2.2}/src/create_awesome_python_app/cli.py +0 -1
- {create_awesome_python_app-0.2.0 → create_awesome_python_app-0.2.2}/tests/test_catalog_resolve.py +25 -0
- {create_awesome_python_app-0.2.0 → create_awesome_python_app-0.2.2}/tests/test_interactive.py +65 -0
- create_awesome_python_app-0.2.0/assets/hero.svg +0 -4
- {create_awesome_python_app-0.2.0 → create_awesome_python_app-0.2.2}/.gitignore +0 -0
- {create_awesome_python_app-0.2.0 → create_awesome_python_app-0.2.2}/README.md +0 -0
- {create_awesome_python_app-0.2.0 → create_awesome_python_app-0.2.2}/src/create_awesome_python_app/cache.py +0 -0
- {create_awesome_python_app-0.2.0 → create_awesome_python_app-0.2.2}/tests/test_cache_cmds.py +0 -0
- {create_awesome_python_app-0.2.0 → create_awesome_python_app-0.2.2}/tests/test_cache_env.py +0 -0
- {create_awesome_python_app-0.2.0 → create_awesome_python_app-0.2.2}/tests/test_catalog.py +0 -0
- {create_awesome_python_app-0.2.0 → create_awesome_python_app-0.2.2}/tests/test_catalog_fetch.py +0 -0
- {create_awesome_python_app-0.2.0 → create_awesome_python_app-0.2.2}/tests/test_cli.py +0 -0
- {create_awesome_python_app-0.2.0 → create_awesome_python_app-0.2.2}/tests/test_cpa_templates_integration.py +0 -0
- {create_awesome_python_app-0.2.0 → create_awesome_python_app-0.2.2}/tests/test_flags.py +0 -0
- {create_awesome_python_app-0.2.0 → create_awesome_python_app-0.2.2}/tests/test_smoke.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: create-awesome-python-app
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Composable scaffolding CLI for production-ready Python apps
|
|
5
5
|
Project-URL: Homepage, https://github.com/Create-Python-App/create-python-app
|
|
6
6
|
Project-URL: Repository, https://github.com/Create-Python-App/create-python-app
|
|
@@ -8,7 +8,7 @@ Project-URL: Issues, https://github.com/Create-Python-App/create-python-app/issu
|
|
|
8
8
|
Project-URL: Changelog, https://github.com/Create-Python-App/create-python-app/blob/main/CHANGELOG.md
|
|
9
9
|
License-Expression: MIT
|
|
10
10
|
Requires-Python: >=3.12
|
|
11
|
-
Requires-Dist: create-python-app-core>=0.2.
|
|
11
|
+
Requires-Dist: create-python-app-core>=0.2.2
|
|
12
12
|
Requires-Dist: questionary>=2.1.1
|
|
13
13
|
Requires-Dist: rich>=15.0.0
|
|
14
14
|
Requires-Dist: typer>=0.27.0
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="200" role="img" aria-label="Create Awesome Python App">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="g" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
4
|
+
<stop offset="0%" stop-color="#3b82f6"/>
|
|
5
|
+
<stop offset="55%" stop-color="#2563eb"/>
|
|
6
|
+
<stop offset="100%" stop-color="#16a34a"/>
|
|
7
|
+
</linearGradient>
|
|
8
|
+
</defs>
|
|
9
|
+
<rect width="100%" height="100%" fill="#0f172a"/>
|
|
10
|
+
<circle cx="60" cy="70" r="10" fill="#3b82f6" fill-opacity="0.9"/>
|
|
11
|
+
<circle cx="120" cy="70" r="10" fill="#16a34a" fill-opacity="0.9"/>
|
|
12
|
+
<circle cx="90" cy="110" r="14" fill="url(#g)"/>
|
|
13
|
+
<circle cx="90" cy="155" r="8" fill="#16a34a" fill-opacity="0.75"/>
|
|
14
|
+
<line x1="68" y1="78" x2="82" y2="100" stroke="#3b82f6" stroke-opacity="0.5" stroke-width="3" stroke-linecap="round"/>
|
|
15
|
+
<line x1="112" y1="78" x2="98" y2="100" stroke="#16a34a" stroke-opacity="0.5" stroke-width="3" stroke-linecap="round"/>
|
|
16
|
+
<line x1="90" y1="124" x2="90" y2="147" stroke="#16a34a" stroke-opacity="0.45" stroke-width="3" stroke-linecap="round"/>
|
|
17
|
+
<text x="160" y="105" fill="url(#g)" font-size="36" font-weight="700" font-family="ui-sans-serif, system-ui, sans-serif">Create Awesome Python App</text>
|
|
18
|
+
<text x="160" y="145" fill="#94a3b8" font-size="18" font-family="ui-sans-serif, system-ui, sans-serif">One command. Any Python stack. · Beta</text>
|
|
19
|
+
</svg>
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "create-awesome-python-app"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.2"
|
|
4
4
|
description = "Composable scaffolding CLI for production-ready Python apps"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
7
7
|
license = "MIT"
|
|
8
8
|
dependencies = [
|
|
9
|
-
"create-python-app-core>=0.2.
|
|
9
|
+
"create-python-app-core>=0.2.2",
|
|
10
10
|
"questionary>=2.1.1",
|
|
11
11
|
"rich>=15.0.0",
|
|
12
12
|
"typer>=0.27.0",
|
|
@@ -20,14 +20,6 @@ from create_awesome_python_app import __version__
|
|
|
20
20
|
console = Console(stderr=True)
|
|
21
21
|
|
|
22
22
|
CUSTOM_TEMPLATE_SENTINEL = "__custom_template__"
|
|
23
|
-
_ANSI_RESET = "\033[0m"
|
|
24
|
-
_CATEGORY_PALETTE = (
|
|
25
|
-
"\033[33m", # yellow
|
|
26
|
-
"\033[32m", # green
|
|
27
|
-
"\033[36m", # cyan
|
|
28
|
-
"\033[35m", # magenta
|
|
29
|
-
"\033[34m", # blue
|
|
30
|
-
)
|
|
31
23
|
|
|
32
24
|
|
|
33
25
|
@dataclass(frozen=True)
|
|
@@ -172,13 +164,6 @@ def short_category_label(category_name: str) -> str:
|
|
|
172
164
|
return " ".join(words[:2]) or category_name
|
|
173
165
|
|
|
174
166
|
|
|
175
|
-
def _color_category(slug: str, label: str) -> str:
|
|
176
|
-
if os.environ.get("NO_COLOR"):
|
|
177
|
-
return label
|
|
178
|
-
idx = sum(ord(char) for char in slug) % len(_CATEGORY_PALETTE)
|
|
179
|
-
return f"{_CATEGORY_PALETTE[idx]}{label}{_ANSI_RESET}"
|
|
180
|
-
|
|
181
|
-
|
|
182
167
|
def _category_map(data: dict[str, Any]) -> dict[str, str]:
|
|
183
168
|
return {
|
|
184
169
|
str(category.get("slug", "")): str(category.get("name", ""))
|
|
@@ -256,8 +241,10 @@ def build_template_choices(data: dict[str, Any]) -> list[TemplateChoice]:
|
|
|
256
241
|
label_suffix = " · " + ", ".join(str(label) for label in labels[:3])
|
|
257
242
|
description = str(template.get("description", "")).strip()
|
|
258
243
|
description_suffix = f" — {description}" if description else ""
|
|
244
|
+
# Plain text only: questionary.autocomplete wraps choices in HTML for
|
|
245
|
+
# match highlighting, so ANSI / markup here raises XML parse errors.
|
|
259
246
|
title = (
|
|
260
|
-
f"{
|
|
247
|
+
f"{badge} "
|
|
261
248
|
f"{template.get('name', slug)} ({slug})"
|
|
262
249
|
f"{label_suffix}{description_suffix}"
|
|
263
250
|
)
|
{create_awesome_python_app-0.2.0 → create_awesome_python_app-0.2.2}/tests/test_catalog_resolve.py
RENAMED
|
@@ -102,12 +102,37 @@ def test_build_template_choices_are_searchable() -> None:
|
|
|
102
102
|
assert "FastAPI Starter" in first.title
|
|
103
103
|
assert "OpenAPI" in first.title
|
|
104
104
|
assert "uv" in first.title
|
|
105
|
+
assert "\033" not in first.title
|
|
105
106
|
assert "openapi" in first.search
|
|
106
107
|
assert "backend" in first.search
|
|
107
108
|
assert "uv" in first.search
|
|
108
109
|
assert choices[-1].value == CUSTOM_TEMPLATE_SENTINEL
|
|
109
110
|
|
|
110
111
|
|
|
112
|
+
def test_template_choice_titles_are_html_safe_for_questionary() -> None:
|
|
113
|
+
"""questionary.autocomplete formats choice text as HTML (match underline)."""
|
|
114
|
+
from prompt_toolkit.formatted_text import HTML
|
|
115
|
+
|
|
116
|
+
catalog = {
|
|
117
|
+
"categories": [
|
|
118
|
+
{"slug": "backend-applications", "name": "Backend Applications"}
|
|
119
|
+
],
|
|
120
|
+
"templates": [
|
|
121
|
+
{
|
|
122
|
+
"slug": "fastapi-starter",
|
|
123
|
+
"name": "FastAPI Starter",
|
|
124
|
+
"description": "Async API with OpenAPI docs",
|
|
125
|
+
"url": "file:///templates/fastapi",
|
|
126
|
+
"category": "backend-applications",
|
|
127
|
+
"labels": ["FastAPI"],
|
|
128
|
+
}
|
|
129
|
+
],
|
|
130
|
+
}
|
|
131
|
+
title = build_template_choices(catalog)[0].title
|
|
132
|
+
# Must not raise "not well-formed (invalid token)" from ANSI escapes.
|
|
133
|
+
HTML("{}<b><u>{}</u></b>{}").format(title[:3], title[3:6], title[6:])
|
|
134
|
+
|
|
135
|
+
|
|
111
136
|
def test_build_extension_choices_filters_by_template_type() -> None:
|
|
112
137
|
catalog = {
|
|
113
138
|
"categories": [
|
{create_awesome_python_app-0.2.0 → create_awesome_python_app-0.2.2}/tests/test_interactive.py
RENAMED
|
@@ -28,6 +28,71 @@ def test_in_ci_env(monkeypatch) -> None:
|
|
|
28
28
|
os.environ.pop("CI", None)
|
|
29
29
|
|
|
30
30
|
|
|
31
|
+
def test_interactive_template_autocomplete_omits_pointer(
|
|
32
|
+
tmp_path: Path, monkeypatch
|
|
33
|
+
) -> None:
|
|
34
|
+
"""questionary.autocomplete rejects ``pointer`` (PromptSession TypeError)."""
|
|
35
|
+
template_dir = tmp_path / "fastapi"
|
|
36
|
+
template_dir.mkdir()
|
|
37
|
+
(template_dir / "cpa.config.json").write_text(
|
|
38
|
+
json.dumps({"name": "fastapi-starter"}),
|
|
39
|
+
encoding="utf-8",
|
|
40
|
+
)
|
|
41
|
+
template_url = f"file://{template_dir}"
|
|
42
|
+
catalog = {
|
|
43
|
+
"categories": [{"slug": "backend-applications", "name": "Backend"}],
|
|
44
|
+
"templates": [
|
|
45
|
+
{
|
|
46
|
+
"slug": "fastapi-starter",
|
|
47
|
+
"name": "FastAPI Starter",
|
|
48
|
+
"url": template_url,
|
|
49
|
+
"type": "fastapi-backend",
|
|
50
|
+
"category": "backend-applications",
|
|
51
|
+
}
|
|
52
|
+
],
|
|
53
|
+
"extensions": [],
|
|
54
|
+
}
|
|
55
|
+
catalog_file = tmp_path / "templates.json"
|
|
56
|
+
catalog_file.write_text(json.dumps(catalog), encoding="utf-8")
|
|
57
|
+
monkeypatch.setenv("CPA_CATALOG_URL", f"file://{catalog_file}")
|
|
58
|
+
monkeypatch.setenv("CPA_NO_CATALOG_CACHE", "1")
|
|
59
|
+
monkeypatch.delenv("CI", raising=False)
|
|
60
|
+
|
|
61
|
+
captured_kwargs: dict[str, object] = {}
|
|
62
|
+
|
|
63
|
+
def fake_autocomplete(*_args, **kwargs):
|
|
64
|
+
captured_kwargs.update(kwargs)
|
|
65
|
+
choices = kwargs.get("choices") or []
|
|
66
|
+
# Return a mapped title so choice_by_title resolves to the template URL.
|
|
67
|
+
return FakePrompt(choices[0] if choices else template_url)
|
|
68
|
+
|
|
69
|
+
def fake_checkbox(*_args, **_kwargs):
|
|
70
|
+
return FakePrompt([])
|
|
71
|
+
|
|
72
|
+
captured: dict[str, object] = {}
|
|
73
|
+
|
|
74
|
+
async def fake_create_python_app(project_directory, options, *_args, **_kwargs):
|
|
75
|
+
captured["project_directory"] = project_directory
|
|
76
|
+
captured["options"] = options
|
|
77
|
+
|
|
78
|
+
monkeypatch.setattr("questionary.autocomplete", fake_autocomplete)
|
|
79
|
+
monkeypatch.setattr("questionary.checkbox", fake_checkbox)
|
|
80
|
+
monkeypatch.setattr(
|
|
81
|
+
"create_awesome_python_app.cli.create_python_app",
|
|
82
|
+
fake_create_python_app,
|
|
83
|
+
)
|
|
84
|
+
monkeypatch.setattr(
|
|
85
|
+
"create_awesome_python_app.cli.check_for_latest_version",
|
|
86
|
+
fake_check_for_latest_version,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
result = runner.invoke(app, ["--interactive", "--no-install", "api"])
|
|
90
|
+
|
|
91
|
+
assert result.exit_code == 0, result.stdout + result.stderr
|
|
92
|
+
assert "pointer" not in captured_kwargs
|
|
93
|
+
assert captured["project_directory"] == "api"
|
|
94
|
+
|
|
95
|
+
|
|
31
96
|
def test_interactive_extension_selection_passes_addon_urls(
|
|
32
97
|
tmp_path: Path, monkeypatch
|
|
33
98
|
) -> None:
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="200" role="img" aria-label="Create Awesome Python App">
|
|
2
|
-
<rect width="100%" height="100%" fill="#0f172a"/>
|
|
3
|
-
<text x="40" y="110" fill="#14b8a6" font-size="36" font-family="ui-sans-serif, system-ui, sans-serif">Create Awesome Python App</text>
|
|
4
|
-
</svg>
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{create_awesome_python_app-0.2.0 → create_awesome_python_app-0.2.2}/tests/test_cache_cmds.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{create_awesome_python_app-0.2.0 → create_awesome_python_app-0.2.2}/tests/test_catalog_fetch.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|