Skip to content

Commit dfac596

Browse files
committed
feat: add Copilot instructions for project context and guidelines
Add comprehensive Copilot instructions documenting tech stack, architecture patterns, coding standards, and common workflows for the FastAPI project.
1 parent 4c881cc commit dfac596

File tree

1 file changed

+222
-0
lines changed

1 file changed

+222
-0
lines changed

.github/copilot-instructions.md

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
# Copilot Instructions for python-samples-fastapi-restful
2+
3+
## Project Overview
4+
5+
This is a RESTful API proof of concept built with **Python 3.13** and **FastAPI**. The application manages football player data with full CRUD operations, featuring async SQLAlchemy ORM, in-memory caching, and SQLite database storage.
6+
7+
## Tech Stack
8+
9+
- **Framework**: FastAPI 0.123.0 with standard dependencies
10+
- **Database**: SQLite with async support (`aiosqlite 0.21.0`)
11+
- **ORM**: SQLAlchemy 2.0.44 (async)
12+
- **Caching**: aiocache 0.12.3 (SimpleMemoryCache)
13+
- **Testing**: pytest 9.0.1, pytest-cov 7.0.0, pytest-sugar 1.1.1, gevent 25.9.1
14+
- **Linting**: flake8 7.3.0, black 25.11.0
15+
- **Python Version**: 3.13.3 (see `.python-version`)
16+
- **Server**: uvicorn (included in FastAPI standard dependencies)
17+
- **Container**: Docker with multi-stage builds, Docker Compose
18+
19+
## Project Structure
20+
21+
```
22+
├── main.py # FastAPI app entry point, lifespan handler, router registration
23+
├── databases/
24+
│ └── player_database.py # Async engine, sessionmaker, Base, session generator
25+
├── models/
26+
│ └── player_model.py # Pydantic models for API request/response validation
27+
├── schemas/
28+
│ └── player_schema.py # SQLAlchemy ORM table schema definitions
29+
├── routes/
30+
│ ├── player_route.py # Player CRUD endpoints with caching
31+
│ └── health_route.py # Health check endpoint
32+
├── services/
33+
│ └── player_service.py # Async database CRUD operations
34+
├── tests/
35+
│ ├── conftest.py # pytest fixtures (TestClient)
36+
│ ├── test_main.py # Test suite for all endpoints
37+
│ └── player_stub.py # Test data stubs
38+
├── storage/ # SQLite database file (seeded)
39+
├── scripts/
40+
│ ├── entrypoint.sh # Docker entrypoint for DB initialization
41+
│ └── healthcheck.sh # Docker health check script
42+
└── postman_collections/ # Postman collection for API testing
43+
```
44+
45+
## Key Architectural Patterns
46+
47+
1. **Layered Architecture**: Routes → Services → Database
48+
2. **Dependency Injection**: `AsyncSession` via `Depends(generate_async_session)`
49+
3. **Pydantic for Validation**: `PlayerModel` with camelCase aliasing (`to_camel`)
50+
4. **SQLAlchemy ORM**: `Player` schema mapped to `players` table
51+
5. **Caching**: In-memory cache (10 min TTL) with `X-Cache` headers (HIT/MISS)
52+
6. **Async/Await**: All database operations are async
53+
54+
## Coding Guidelines
55+
56+
### Python Style (Strict Enforcement)
57+
58+
- **Formatter**: Black (line length: 88, target: Python 3.13)
59+
- **Linter**: flake8 (max-complexity: 10, ignores: E203, W503)
60+
- **Run Before Commit**: `black .` and `flake8`
61+
- **Imports**: SQLAlchemy 2.0+ style (use `select()` not legacy `Query`)
62+
- **Docstrings**: Google-style docstrings for all modules, classes, and functions
63+
- **Type Hints**: Use type annotations for function parameters and return values
64+
65+
### File Exclusions
66+
67+
Black and flake8 exclude:
68+
- `.venv`, `.git`, `.github`, `.pytest_cache`, `__pycache__`
69+
- `assets/`, `htmlcov/`, `postman_collections/`, `scripts/`, `storage/`
70+
- Exception: `tests/test_main.py` allows E501 (long lines for test names)
71+
72+
### Commit Conventions
73+
74+
Follow **Conventional Commits** (enforced by commitlint):
75+
- `feat:` for new features
76+
- `fix:` for bug fixes
77+
- `chore:` for maintenance/tooling
78+
- Max header length: 80 characters
79+
- Max body line length: 80 characters
80+
81+
## Common Commands
82+
83+
### Local Development
84+
85+
```bash
86+
# Install dependencies
87+
pip install -r requirements.txt
88+
pip install -r requirements-lint.txt
89+
pip install -r requirements-test.txt
90+
91+
# IMPORTANT: Activate virtual environment before running commands
92+
source .venv/bin/activate
93+
94+
# Start server (auto-reload on port 9000)
95+
uvicorn main:app --reload --port 9000
96+
97+
# Access interactive API docs
98+
# http://localhost:9000/docs
99+
100+
# Format code (must run from venv)
101+
black .
102+
103+
# Lint code (must run from venv)
104+
flake8 .
105+
106+
# Run tests
107+
pytest -v
108+
109+
# Run tests with coverage
110+
pytest --cov=./ --cov-report=xml --cov-report=term
111+
```
112+
113+
### Docker
114+
115+
```bash
116+
# Build image
117+
docker compose build
118+
119+
# Start app (initializes DB from seed on first run)
120+
docker compose up
121+
122+
# Stop app
123+
docker compose down
124+
125+
# Reset database (removes volume)
126+
docker compose down -v
127+
```
128+
129+
## Database Details
130+
131+
- **Path**: Controlled by `STORAGE_PATH` env var (default: `./storage/players-sqlite3.db`)
132+
- **Docker Volume**: Persistent volume at `/storage/` in container
133+
- **Initialization**: On first Docker run, `entrypoint.sh` copies seed DB from `/app/hold/` to `/storage/`
134+
- **Schema**: Single `players` table with columns: id (PK), firstName, middleName, lastName, dateOfBirth, squadNumber (unique), position, abbrPosition, team, league, starting11
135+
136+
## API Endpoints
137+
138+
| Method | Path | Description | Cache |
139+
|--------|-------------------------------------|------------------------------|-------|
140+
| GET | `/health` | Health check | No |
141+
| GET | `/players/` | Get all players | Yes |
142+
| GET | `/players/{player_id}` | Get player by ID | No |
143+
| GET | `/players/squadnumber/{squad_number}` | Get player by squad number | No |
144+
| POST | `/players/` | Create new player | Clears|
145+
| PUT | `/players/{player_id}` | Update existing player | Clears|
146+
| DELETE | `/players/{player_id}` | Delete player | Clears|
147+
148+
**Cache Notes**:
149+
- Cache key: `"players"`, TTL: 600s (10 min)
150+
- Cache is cleared on POST/PUT/DELETE operations
151+
- Response header `X-Cache: HIT` or `MISS` indicates cache status
152+
153+
## Testing
154+
155+
- **Framework**: pytest with `TestClient` from FastAPI
156+
- **Fixture**: `client` fixture in `conftest.py` (function scope for test isolation)
157+
- **Coverage Target**: 80% (configured in `codecov.yml`)
158+
- **Test Data**: Use stubs from `tests/player_stub.py`
159+
- **Warnings**: DeprecationWarning from httpx is suppressed in conftest
160+
161+
## CI/CD Pipeline
162+
163+
GitHub Actions workflow (`.github/workflows/python-app.yml`):
164+
1. **Lint Job**: Commitlint → Flake8 → Black (check mode)
165+
2. **Test Job**: pytest with coverage report generation
166+
3. **Coverage Job**: Upload to Codecov and Codacy (only for same-repo PRs)
167+
168+
**All PRs must pass CI checks before review.**
169+
170+
## Common Pitfalls & Solutions
171+
172+
1. **Virtual Environment**: Always activate `.venv` before running black, flake8, or pytest:
173+
```bash
174+
source .venv/bin/activate
175+
```
176+
177+
2. **FastAPI Route Ordering**: Static routes MUST be defined before dynamic path parameters. Place `/players/statistics` before `/players/{player_id}`, or FastAPI will try to parse "statistics" as a player_id.
178+
```python
179+
# CORRECT order:
180+
@api_router.get("/players/statistics") # Static route first
181+
@api_router.get("/players/{player_id}") # Dynamic route after
182+
```
183+
184+
3. **SQLAlchemy 2.0 Migration**: Use `select()` not `session.query()`. Example:
185+
```python
186+
statement = select(Player).where(Player.id == player_id)
187+
result = await async_session.execute(statement)
188+
```
189+
190+
4. **Async Session Usage**: Always use `Depends(generate_async_session)` in routes, never create sessions manually.
191+
192+
5. **Cache Invalidation**: Remember to call `await simple_memory_cache.clear(CACHE_KEY)` after mutations (POST/PUT/DELETE).
193+
194+
6. **Pydantic Model Conversion**: Use `player_model.model_dump()` to convert Pydantic to dict for SQLAlchemy:
195+
```python
196+
player = Player(**player_model.model_dump())
197+
```
198+
199+
7. **Database Path in Docker**: Use `STORAGE_PATH` env var, not hardcoded paths.
200+
201+
8. **Port Conflicts**: Default port is 9000. If occupied, use `--port` flag with uvicorn.
202+
203+
## VS Code Configuration
204+
205+
Recommended extensions (`.vscode/extensions.json`):
206+
- `ms-python.python`, `ms-python.flake8`, `ms-python.black-formatter`
207+
- `github.vscode-pull-request-github`, `github.vscode-github-actions`
208+
- `ms-azuretools.vscode-containers`, `sonarsource.sonarlint-vscode`
209+
210+
Settings (`.vscode/settings.json`):
211+
- Auto-format on save with Black
212+
- Pytest enabled (not unittest)
213+
- Flake8 integration with matching CLI args
214+
- Editor ruler at column 88
215+
216+
## Additional Resources
217+
218+
- **Postman Collection**: `postman_collections/python-samples-fastapi-restful.postman_collection.json`
219+
- **Architecture Diagram**: `assets/images/structure.svg`
220+
- **FastAPI Docs**: https://fastapi.tiangolo.com/
221+
- **SQLAlchemy 2.0**: https://docs.sqlalchemy.org/en/20/
222+
- **Conventional Commits**: https://www.conventionalcommits.org/

0 commit comments

Comments
 (0)