Skip to content

Commit 13e566d

Browse files
committed
[FEAT] enhance FastAPI-fastkit project templates and detection
- Updated project templates to include a standardized description marker "[FastAPI-fastkit templated]" in pyproject.toml files for better identification. - Introduced a new section `[tool.fastapi-fastkit]` in pyproject.toml to indicate managed projects. - Enhanced the `is_fastkit_project` function to detect FastAPI-fastkit projects using both the new TOML structure and legacy setup.py markers. - Added comprehensive tests for the new detection logic, ensuring compatibility with existing templates and structures. - Improved error handling and validation for project structure checks, particularly for templates with missing metadata files. - Refactored tests to ensure clarity and maintainability, including checks for dependencies in pyproject.toml and legacy setup.py files.
1 parent f89c86a commit 13e566d

39 files changed

Lines changed: 959 additions & 159 deletions

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
11
# Changelog
22

3+
## v1.3.0 (Unreleased)
4+
5+
### Maintenances
6+
7+
- **Modernize template contract to be pyproject-first** (#42)
8+
- Template inspection now accepts `pyproject.toml-tpl` (PEP 621) as the primary metadata file. A template passes structural validation as long as `tests/`, `README.md-tpl`, and **at least one** of `pyproject.toml-tpl` / `setup.py-tpl` are present. `requirements.txt-tpl` is no longer strictly required when `pyproject.toml-tpl` declares `[project].dependencies`.
9+
- `_check_dependencies` consults every available source (`requirements.txt-tpl`, `pyproject.toml-tpl` `[project].dependencies`, `setup.py-tpl` `install_requires`) independently and passes if **any** one declares `fastapi`. A template with a stale `requirements.txt-tpl` is no longer rejected when its `pyproject.toml-tpl` is authoritative.
10+
- `read_template_stack` gained a pyproject fallback between the existing `requirements.txt-tpl` and `setup.py-tpl` paths; legacy templates resolve dependencies exactly as before.
11+
- Template authoring docs (`src/fastapi_fastkit/fastapi_project_template/README.md`, `docs/en/contributing/template-creation-guide.md`, `docs/en/reference/template-quality-assurance.md`) now describe required vs optional files under the pyproject-first contract.
12+
- Added focused tests for the new rules (pyproject-only structural and dependency validation, setup.py-only legacy validation, pyproject fallback in `read_template_stack`, and `requirements.txt-tpl`-over-pyproject precedence).
13+
- **Pyproject-aware project identity detection** (follow-up to #42)
14+
- `is_fastkit_project()` now recognises FastAPI-fastkit-managed projects from `pyproject.toml` in addition to legacy `setup.py`. Detection precedence: `[tool.fastapi-fastkit].managed = true``[project].description` contains the `[FastAPI-fastkit templated]` marker (case-insensitive) → `setup.py` contains `fastapi-fastkit` (case-insensitive). Keeps the pre-existing setup.py path working; the new pyproject paths prevent pyproject-only templates from being mistaken for unrelated FastAPI projects in the user's workspace.
15+
- Canonical marker strings (`[FastAPI-fastkit templated]` and the `[tool.fastapi-fastkit]` table key `fastapi-fastkit`) are exported from `fastapi_fastkit.utils.main` as `FASTKIT_DESCRIPTION_MARKER` / `FASTKIT_TOOL_SECTION` so templates, metadata injection, and detection all reference the same strings.
16+
- `_process_pyproject_file` idempotently ensures the description marker and `[tool.fastapi-fastkit]\nmanaged = true` section are present after placeholder replacement, providing a safety net when a template author forgets to include them.
17+
- All 8 shipped `pyproject.toml-tpl` templates updated to carry `description = "[FastAPI-fastkit templated] <description>"` and a `[tool.fastapi-fastkit]` table with `managed = true`.
18+
319
## v1.2.1 (2026-04-17)
420

521
### Fixes

docs/en/contributing/template-creation-guide.md

Lines changed: 81 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -96,14 +96,29 @@ fastapi-{template-name}/
9696
│ ├── lint.sh-tpl # Linting
9797
│ ├── run-server.sh-tpl # Server execution
9898
│ └── test.sh-tpl # Test execution
99-
├── requirements.txt-tpl # ✅ Dependencies (required)
100-
├── setup.py-tpl # ✅ Package setup (required)
99+
├── pyproject.toml-tpl # ✅ Primary metadata (PEP 621, preferred)
100+
├── setup.py-tpl # 🟡 Legacy metadata (accepted for back-compat)
101+
├── requirements.txt-tpl # 🟡 Optional when pyproject declares deps
101102
├── setup.cfg-tpl # Development tools configuration
102103
├── README.md-tpl # ✅ Project documentation (required)
103104
├── .env-tpl # Environment variables template
104105
└── .gitignore-tpl # Git ignore file
105106
```
106107

108+
**Minimum required files.** A template must provide:
109+
110+
- `tests/` directory
111+
- `README.md-tpl`
112+
- At least one metadata file: `pyproject.toml-tpl` (preferred, PEP 621) or
113+
`setup.py-tpl` (legacy, still accepted)
114+
- A declaration of `fastapi` as a dependency in at least one of:
115+
`pyproject.toml-tpl` `[project].dependencies`, `requirements.txt-tpl`, or
116+
`setup.py-tpl` `install_requires`
117+
118+
`requirements.txt-tpl` is no longer strictly required when `pyproject.toml-tpl`
119+
declares `[project].dependencies`. Modern templates SHOULD adopt
120+
`pyproject.toml-tpl` as their primary metadata file.
121+
107122
### File Writing Guide
108123

109124
#### 1. Writing main.py-tpl
@@ -154,7 +169,58 @@ if __name__ == "__main__":
154169
uvicorn.run(app, host="0.0.0.0", port=8000)
155170
```
156171

157-
#### 2. Writing requirements.txt-tpl
172+
#### 2. Writing pyproject.toml-tpl (preferred)
173+
174+
Modern templates should declare metadata and dependencies with a PEP 621
175+
`pyproject.toml-tpl`. At minimum the file must expose a `[project]` section with
176+
`name`, `version`, a `description`, and a `dependencies` list that includes
177+
`fastapi`. Templates must also carry two FastAPI-fastkit identity markers so
178+
`is_fastkit_project()` can tell generated projects apart from unrelated FastAPI
179+
projects in the user's workspace:
180+
181+
- `[FastAPI-fastkit templated]` prefix in `description`
182+
- A dedicated `[tool.fastapi-fastkit]` table with `managed = true`
183+
184+
Detection accepts either marker (matching is case-insensitive). Metadata
185+
injection will add both at project generation time if a template omits them,
186+
but authors should include them explicitly.
187+
188+
```toml
189+
[project]
190+
name = "<project_name>"
191+
version = "0.1.0"
192+
description = "[FastAPI-fastkit templated] <description>"
193+
authors = [
194+
{name = "<author>", email = "<author_email>"},
195+
]
196+
readme = "README.md"
197+
requires-python = ">=3.12"
198+
dependencies = [
199+
"fastapi>=0.115.0",
200+
"uvicorn[standard]>=0.34.0",
201+
"pydantic>=2.10.0",
202+
"pydantic-settings>=2.7.0",
203+
"python-dotenv>=1.0.0",
204+
]
205+
206+
[project.optional-dependencies]
207+
dev = [
208+
"pytest>=8.0.0",
209+
"httpx>=0.28.0",
210+
]
211+
212+
[tool.fastapi-fastkit]
213+
managed = true
214+
215+
[build-system]
216+
requires = ["hatchling"]
217+
build-backend = "hatchling.build"
218+
```
219+
220+
#### 3. Writing requirements.txt-tpl (optional)
221+
222+
Optional when `pyproject.toml-tpl` declares `[project].dependencies`. Still
223+
useful for templates that prefer `pip`-only workflows.
158224

159225
```txt
160226
# FastAPI core dependencies (required)
@@ -183,7 +249,10 @@ isort==5.12.0
183249
mypy==1.7.1
184250
```
185251

186-
#### 3. Writing setup.py-tpl
252+
#### 4. Writing setup.py-tpl (legacy — optional when pyproject is present)
253+
254+
Retained for legacy templates. New templates do not need this file if they
255+
ship `pyproject.toml-tpl`.
187256

188257
```python
189258
"""
@@ -205,7 +274,7 @@ install_requires: list[str] = [
205274
setup(
206275
name="<project_name>",
207276
version="1.0.0",
208-
description="[fastapi-fastkit templated] <description>", # Required keyword
277+
description="[FastAPI-fastkit templated] <description>", # Identity marker used by is_fastkit_project()
209278
long_description=open("README.md").read(),
210279
long_description_content_type="text/markdown",
211280
author="<author>",
@@ -227,7 +296,7 @@ setup(
227296
)
228297
```
229298

230-
#### 4. Writing Test Files
299+
#### 5. Writing Test Files
231300

232301
```python
233302
# tests/test_items.py-tpl
@@ -299,9 +368,9 @@ The inspector automatically validates the following items:
299368
#### ✅ File Structure Validation
300369

301370
- [ ] `tests/` directory exists
302-
- [ ] `requirements.txt-tpl` file exists
303-
- [ ] `setup.py-tpl` file exists
304371
- [ ] `README.md-tpl` file exists
372+
- [ ] At least one of `pyproject.toml-tpl` (preferred) or `setup.py-tpl`
373+
(legacy) exists
305374

306375
#### ✅ File Extension Validation
307376

@@ -310,9 +379,10 @@ The inspector automatically validates the following items:
310379

311380
#### ✅ Dependencies Validation
312381

313-
- [ ] `requirements.txt-tpl` includes `fastapi`
314-
- [ ] `setup.py-tpl`'s `install_requires` includes `fastapi`
315-
- [ ] `setup.py-tpl`'s description includes `[fastapi-fastkit templated]`
382+
- [ ] `fastapi` is declared in at least one of:
383+
- [ ] `pyproject.toml-tpl` under `[project].dependencies` (preferred)
384+
- [ ] `requirements.txt-tpl`
385+
- [ ] `setup.py-tpl` under `install_requires`
316386

317387
#### ✅ FastAPI Implementation Validation
318388

docs/en/reference/template-quality-assurance.md

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,14 +188,34 @@ For a template to pass inspection, it must meet these requirements:
188188
### File Structure
189189
- Must contain a `src/` directory with Python source files
190190
- Python files must use `.py-tpl` extension
191-
- Must include a `requirements.txt-tpl` file
192-
- Must include a `setup.py-tpl` file
191+
- Must include a `tests/` directory and a `README.md-tpl` file
192+
- Must include **at least one** metadata file:
193+
- `pyproject.toml-tpl` (preferred, PEP 621), or
194+
- `setup.py-tpl` (legacy, still accepted)
195+
- `requirements.txt-tpl` is optional when `pyproject.toml-tpl` declares
196+
`[project].dependencies`
193197

194198
### FastAPI Requirements
195199
- Must contain FastAPI app initialization
196-
- Must include proper dependency declarations
200+
- Must declare `fastapi` as a dependency in at least one of
201+
`pyproject.toml-tpl` `[project].dependencies`, `requirements.txt-tpl`, or
202+
`setup.py-tpl` `install_requires`
197203
- Must have valid Python syntax in all template files
198204

205+
### Identity Markers
206+
Templates should carry FastAPI-fastkit identity markers so generated projects
207+
are distinguishable from unrelated FastAPI projects in the user's workspace:
208+
209+
- `pyproject.toml-tpl` — both a `[FastAPI-fastkit templated]` prefix in
210+
`description` and a `[tool.fastapi-fastkit]` table with `managed = true`.
211+
- `setup.py-tpl``[FastAPI-fastkit templated]` prefix in the `description`
212+
argument to `setup()`.
213+
214+
`is_fastkit_project()` accepts any one of these (pyproject takes precedence,
215+
setup.py is the legacy fallback; matching is case-insensitive). Metadata
216+
injection ensures the markers end up in generated projects even if a template
217+
forgets them.
218+
199219
### Quality Standards
200220
- All template files must be syntactically correct
201221
- Dependencies must be properly specified

scripts/inspect-changed-templates.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@
88
from pathlib import Path
99
from typing import List, Set
1010

11+
from fastapi_fastkit.backend.inspector import inspect_fastapi_template
12+
1113
# Ensure src is importable
1214
PROJECT_ROOT = Path(__file__).parent.parent
1315
sys.path.insert(0, str(PROJECT_ROOT / "src"))
1416

15-
from fastapi_fastkit.backend.inspector import inspect_fastapi_template
16-
1717
TEMPLATE_DIR = PROJECT_ROOT / "src" / "fastapi_fastkit" / "fastapi_project_template"
1818

1919

scripts/inspect-templates.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@
1717
from pathlib import Path
1818
from typing import Any, Dict, List
1919

20+
from fastapi_fastkit.backend.inspector import inspect_fastapi_template
21+
2022
# Add src to path to import fastapi_fastkit modules
2123
project_root = Path(__file__).parent.parent
2224
sys.path.insert(0, str(project_root / "src"))
2325

24-
from fastapi_fastkit.backend.inspector import inspect_fastapi_template
25-
2626

2727
def get_templates_to_inspect(specific_templates: str = "") -> List[str]:
2828
"""Get list of templates to inspect."""
@@ -119,7 +119,7 @@ def main() -> None:
119119
print(f"📋 Found {len(templates)} templates to inspect: {', '.join(templates)}")
120120
if args.verbose:
121121
print(f"🔧 Working directory: {os.getcwd()}")
122-
print(f"📁 Template directory: src/fastapi_fastkit/fastapi_project_template/")
122+
print("📁 Template directory: src/fastapi_fastkit/fastapi_project_template/")
123123
print("=" * 60)
124124

125125
# Inspect each template

scripts/translate.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ def _translate_single_chunk(
302302
def translate(self, text: str, target_lang: str, source_lang: str = "en") -> str:
303303
"""Translate text, automatically chunking if necessary."""
304304
try:
305-
import openai
305+
import openai # noqa
306306
except ImportError:
307307
logger.error("OpenAI package not installed. Run: pip install openai")
308308
raise
@@ -316,7 +316,7 @@ def translate(self, text: str, target_lang: str, source_lang: str = "en") -> str
316316
raise
317317

318318
# If payload too large, split into chunks and translate each
319-
logger.info(f"Document too large, splitting into chunks for translation...")
319+
logger.info("Document too large, splitting into chunks for translation...")
320320
chunks = self._split_text_into_chunks(text)
321321
logger.info(f"Split into {len(chunks)} chunks")
322322

0 commit comments

Comments
 (0)