Consolidate Python ignore rules into root gitignore

This commit is contained in:
Hiroaki86
2026-05-27 23:01:28 +09:00
commit fa3394415d
399 changed files with 509103 additions and 0 deletions
@@ -0,0 +1,197 @@
---
name: fastapi-api
description: FastAPIでAPIRouter、Pydanticスキーマ、Service、Repository、Depends、SQLAlchemy、APIテストを分離して実装・修正するときに使用する。
---
# FastAPI API Skill
## When to use
- FastAPIのエンドポイントを新規作成するとき
- 既存APIを修正するとき
- API層、Service層、Repository層を分離するとき
- SQLAlchemyを使用してDBアクセスを実装するとき
- ORMだけでは複雑になる処理を `sqlalchemy.text()` またはSQLファイルで実装するとき
- Pydanticのリクエスト・レスポンススキーマを作成するとき
- FastAPIの依存関係を `Depends` で整理するとき
- FastAPIのAPIテストを追加するとき
## 基本方針
- FastAPIはバックエンドAPIとして使用する。
- API層、Service層、Repository層を分離する。
- エンドポイント関数は薄く保つ。
- エンドポイント関数にDBアクセス、ファイルI/O、外部システム連携、重い処理を直接書かない。
- FastAPIでDBアクセスを行う場合は、原則としてSQLAlchemyを使用する。
- 単純なCRUDはSQLAlchemy ORMを優先する。
- 複雑なJOIN、集計、ウィンドウ関数、帳票用SQL、大量データ取得、N+1問題の回避でORM記述が複雑になる場合は、`sqlalchemy.text()` または `.sql` ファイルのSQLを使用する。
- 既存の設計、命名、ディレクトリ構成を優先する。
- 新規ファイルを作る場合は、既存の類似機能と同じ配置・命名に合わせる。
## Standard files
FastAPIの新規APIを追加する場合は、原則として以下のファイルを作成・修正する。
```text
src/app/api/routers/<feature>.py
src/app/schemas/<feature>.py
src/app/services/<feature>_service.py
src/app/repositories/<feature>_repository.py
src/app/api/dependencies.py
tests/api/test_<feature>.py
```
既存の構成がある場合は、新規構成を勝手に作らず、既存構成を優先する。
## Router
- `APIRouter` を使用する。
- `prefix``tags` を適切に設定する。
- ルーターは機能単位で分割する。
- エンドポイント関数では以下のみを行う。
- リクエストデータの受け取り
- `Depends` による依存関係の受け取り
- Serviceの呼び出し
- レスポンスの返却
- DB処理はRepository層に書く。
- 業務ロジックはService層に書く。
## Request / Response
- リクエストボディはPydanticモデルで定義する。
- レスポンスはPydanticモデルで定義する。
- 可能な限り `response_model` を指定する。
- DBモデル、DB行、内部オブジェクトをそのままAPIレスポンスとして返さない。
- APIレスポンスには、外部に公開してよい項目だけを含める。
- Pydantic v2を前提にする場合は、ORMオブジェクト変換に `ConfigDict(from_attributes=True)``model_validate()` を使用する。
## Service
- 業務ロジックはService層に書く。
- ServiceはRepositoryを呼び出して必要なデータを取得・保存する。
- ServiceはFastAPI固有の `Request``Response` に依存しすぎない。
- Service内で例外を握りつぶさない。
- 必要に応じて独自例外に変換して呼び出し元へ伝える。
- トランザクション境界をService層で管理する場合は、成功時commit、失敗時rollbackを明確にする。
## Repository
- DBアクセスはRepository層に書く。
- FastAPIでDBアクセスを行う場合は、原則としてSQLAlchemyを使用する。
- RepositoryはSQLAlchemyの `Session` またはDB接続を受け取る。
- Router層から直接SQLAlchemyのクエリを組み立てない。
- 単純なCRUD、主キー検索、単純な条件検索はSQLAlchemy ORMを優先する。
- SQL Server用RepositoryとOracleDB用Repositoryを分離する。
- OracleDBは参照専用とし、原則 `SELECT` のみ実行する。
- OracleDBに対して `INSERT` / `UPDATE` / `DELETE` / `MERGE` / `CREATE` / `ALTER` / `DROP` は実行しない。
- SQL Server用SQLとOracleDB用SQLを混在させない。
- 認証情報、接続文字列、ユーザー名、パスワードをコードに直書きしない。
## SQLAlchemy / SQL
- SQLAlchemyは可能な限り2.x系の記述スタイルを優先する。
- ORMモデルはDBテーブル構造を表すものとして扱い、APIレスポンスにはPydanticモデルを使用する。
- ORMのリレーションを使用する場合は、N+1問題に注意する。
- N+1問題が発生する可能性がある場合は、`selectinload()``joinedload()`、明示的な `join()`、またはSQLを使用する。
- 複雑なJOIN、集計、ウィンドウ関数、帳票用SQL、大量データ取得、性能要件が強い処理は、無理にORMだけで書かない。
- ORMで記述すると可読性や性能が悪くなる場合は、`sqlalchemy.text()` を使用してSQLを明示的に記述する。
- 長いSQL、再利用するSQL、DBごとに差があるSQLは `.sql` ファイルに分離する。
- `text()` を使用する場合も、ユーザー入力値をSQL文字列へ直接埋め込まない。
- `text()` では必ずバインドパラメータを使用する。
- 文字列連結でSQLを組み立てない。
- RepositoryはDBアクセス結果をServiceが扱いやすい形で返す。
- レスポンス生成時に遅延ロードが発生しないよう、Repository層で必要なデータを取得しきる。
## Dependency Injection
- FastAPIの `Depends` を使用する。
- Settings、SQLAlchemy `Session`、DB接続、Repository、Service は依存関係として注入できる形にする。
- SQLAlchemy `Session` はリクエスト単位で生成・終了する。
- Repository生成時にSQLAlchemy `Session` を渡し、Repository内部でグローバルなSessionを直接参照しない。
- テストで差し替えやすいように、依存関係は関数化する。
## Error Handling
- 入力不正は `HTTPException` で適切なHTTPステータスコードを返す。
- リソースが存在しない場合は `404` を返す。
- 内部例外の詳細をAPIレスポンスにそのまま返さない。
- 内部エラーは `logging` で記録する。
- クライアント向けレスポンスと内部ログを分離する。
- 必要に応じて独自例外をService層で発生させ、Router層でHTTPレスポンスへ変換する。
## Async / Sync
- `async def``def` は処理内容に応じて使い分ける。
- 同期DBドライバや同期SQLAlchemy Sessionを使う場合は、無理に `async def` にしない。
- ブロッキングI/Oを `async def` の中で直接実行しない。
- 長時間処理はAPIリクエスト内で完結させず、WorkerやJob Queueへの分離を検討する。
## Testing
- FastAPIのAPIテストでは `TestClient` を使用する。
- DB、外部API、ファイルI/Oはmockまたはdependency overrideで差し替える。
- Repositoryのテストでは、ORMで取得するケースと `text()` で取得するケースを必要に応じて分ける。
- 正常系、異常系、バリデーションエラー、権限エラーをテストする。
- dependency override を使用した場合は、テスト後に `app.dependency_overrides.clear()` を実行する。
## Output checklist
FastAPI APIを作成・修正する場合は、必要に応じて以下をセットで検討する。
1. Router
2. Schema
3. Service
4. Repository
5. SQLAlchemy Session / Dependency
6. ORMモデルまたはSQLファイル
7. Test
8. 実行・確認コマンド
## Example
### ORMを使用するRepository
```python
from sqlalchemy import select
from sqlalchemy.orm import Session
from app.models.job import Job
class JobRepository:
def __init__(self, session: Session) -> None:
self._session = session
def fetch_job(self, job_id: int) -> Job | None:
stmt = select(Job).where(Job.job_id == job_id)
return self._session.scalars(stmt).first()
```
### text() を使用するRepository
```python
from sqlalchemy import text
from sqlalchemy.orm import Session
class JobRepository:
def __init__(self, session: Session) -> None:
self._session = session
def fetch_job_summary(self, job_id: int) -> dict[str, object] | None:
stmt = text("""
SELECT
J.JOB_ID,
J.STATUS,
COUNT(S.SLOT_ID) AS SLOT_COUNT
FROM dbo.SAW6_JOB_QUEUE AS J
LEFT JOIN dbo.SAW6_JOB_QUEUE_SLOT AS S
ON S.JOB_ID = J.JOB_ID
WHERE J.JOB_ID = :job_id
GROUP BY
J.JOB_ID,
J.STATUS
""")
row = self._session.execute(stmt, {"job_id": job_id}).mappings().first()
return dict(row) if row is not None else None
```
@@ -0,0 +1,254 @@
---
name: oracle-select
description: Oracle Database から SELECT 文でデータを取得する処理、SQL作成、Python連携コードを作成・修正するときに使用する。Oracleは参照専用とし、INSERT、UPDATE、DELETE、MERGE、DDLは扱わない。
---
# Oracle SELECT Skill
## When to use
- Oracle Database から `SELECT` でデータを取得するとき
- Oracle用の参照SQLを作成・修正するとき
- Python から Oracle Database に接続して読み取り処理を実装するとき
- Oracle のテーブル・ビューから取得したデータを pandas DataFrame に変換するとき
- SQL Server や他DBではなく、Oracle固有のSQL構文に合わせる必要があるとき
## Scope
このSkillでは Oracle Database に対する読み取り専用処理のみを扱う。
許可する操作:
- `SELECT`
- `WITH`
- `JOIN`
- `WHERE`
- `GROUP BY`
- `HAVING`
- `ORDER BY`
- `FETCH FIRST n ROWS ONLY`
- Pythonからの読み取り処理
- pandas DataFrame への変換
扱わない操作:
- `INSERT`
- `UPDATE`
- `DELETE`
- `MERGE`
- `CREATE`
- `ALTER`
- `DROP`
- `TRUNCATE`
- `GRANT`
- `COMMIT`
- `ROLLBACK`
## Rules
- Oracle Database 用の `SELECT` 文として作成する。
- 参照専用を前提にし、データ更新系SQLは作成しない。
- 認証情報、接続文字列、パスワードをコードに直書きしない。
- 接続情報は `.env`、環境変数、Settings クラスなどから取得する。
- SQLには必要な列を明示し、原則 `SELECT *` は避ける。
- 条件値は文字列連結せず、バインド変数を使用する。
- Pythonで実行する場合は `oracledb` の使用を基本にする。
- pandasで取得する場合は `pd.read_sql_query()` または `pd.read_sql()` を使用する。
- 大量データ取得が想定される場合は、取得期間、キー条件、件数制限を検討する。
- 日付条件では `TO_DATE()``TRUNC()`、バインド変数の型に注意する。
- Oracleの文字列結合は `||` を使用する。
- SQL Server 固有構文は使用しない。
- `TOP`
- `GETDATE()`
- `ISNULL()`
- `LEN()`
- `DATEADD()`
- `DATEDIFF()`
- `[]` による識別子囲み
- 件数制限は Oracle 12c 以降では `FETCH FIRST n ROWS ONLY` を優先する。
- 古いOracle互換が必要な場合のみ `ROWNUM` を使用する。
- テーブル名・カラム名は既存DBの定義に合わせる。
- 別名を付ける場合は読みやすい名前にする。
- SQLは保守しやすいように整形する。
- エラー時はユーザー向けメッセージと詳細ログを分ける。
## SQL Style
### 基本
```sql
SELECT
T.TOOL_ID,
T.PROCESS_DATE,
T.STATUS_CODE,
T.CREATED_AT
FROM
SAMPLE_TABLE T
WHERE
T.TOOL_ID = :tool_id
AND T.PROCESS_DATE >= :start_date
AND T.PROCESS_DATE < :end_date
ORDER BY
T.PROCESS_DATE DESC
```
### 件数制限
```sql
SELECT
T.TOOL_ID,
T.PROCESS_DATE,
T.STATUS_CODE
FROM
SAMPLE_TABLE T
WHERE
T.TOOL_ID = :tool_id
ORDER BY
T.PROCESS_DATE DESC
FETCH FIRST 100 ROWS ONLY
```
### WITH句
```sql
WITH TARGET_DATA AS (
SELECT
T.TOOL_ID,
T.PROCESS_DATE,
T.STATUS_CODE
FROM
SAMPLE_TABLE T
WHERE
T.PROCESS_DATE >= :start_date
AND T.PROCESS_DATE < :end_date
)
SELECT
TOOL_ID,
STATUS_CODE,
COUNT(*) AS CNT
FROM
TARGET_DATA
GROUP BY
TOOL_ID,
STATUS_CODE
ORDER BY
TOOL_ID,
STATUS_CODE
```
## Python Rules
- Oracle接続は `oracledb` を基本にする。
- 接続情報は環境変数やSettingsから取得する。
- SQLとパラメータは分離する。
- SQL文字列へユーザー入力を直接埋め込まない。
- DataFrame取得時は `params` を使用する。
- 接続とカーソルは `with` で管理する。
- SELECT専用の処理として実装し、更新処理は追加しない。
## Python Example
```python
from __future__ import annotations
import os
from datetime import datetime
import oracledb
import pandas as pd
def get_oracle_connection() -> oracledb.Connection:
user = os.environ["ORACLE_USER"]
password = os.environ["ORACLE_PASSWORD"]
dsn = os.environ["ORACLE_DSN"]
return oracledb.connect(
user=user,
password=password,
dsn=dsn,
)
def fetch_sample_data(
tool_id: str,
start_date: datetime,
end_date: datetime,
) -> pd.DataFrame:
sql = '''
SELECT
T.TOOL_ID,
T.PROCESS_DATE,
T.STATUS_CODE,
T.CREATED_AT
FROM
SAMPLE_TABLE T
WHERE
T.TOOL_ID = :tool_id
AND T.PROCESS_DATE >= :start_date
AND T.PROCESS_DATE < :end_date
ORDER BY
T.PROCESS_DATE DESC
'''
params = {
"tool_id": tool_id,
"start_date": start_date,
"end_date": end_date,
}
with get_oracle_connection() as conn:
return pd.read_sql_query(sql, conn, params=params)
```
## Output Format
1. 目的
2. Oracle SELECT SQL
3. Pythonから実行する場合のコード
4. バインド変数の説明
5. 注意点
## Example Output
### 目的
指定した装置IDと処理期間に一致するOracle上の測定結果を取得する。
### Oracle SELECT SQL
```sql
SELECT
T.TOOL_ID,
T.PROCESS_DATE,
T.STATUS_CODE,
T.RESULT_VALUE
FROM
MEASURE_RESULT T
WHERE
T.TOOL_ID = :tool_id
AND T.PROCESS_DATE >= :start_date
AND T.PROCESS_DATE < :end_date
ORDER BY
T.PROCESS_DATE DESC
```
### Python Code
```python
params = {
"tool_id": tool_id,
"start_date": start_date,
"end_date": end_date,
}
with get_oracle_connection() as conn:
df = pd.read_sql_query(sql, conn, params=params)
```
### 注意点
- `:tool_id``:start_date``:end_date` はバインド変数として渡す。
- `SELECT *` は避け、必要な列だけ取得する。
- 大量データになる場合は、期間条件や件数制限を追加する。
- このSkillでは更新系SQLは作成しない。
@@ -0,0 +1,47 @@
---
name: python-base
description: Python 3.13プロジェクトで基本的なコーディング、型ヒント、pathlib、例外処理、関数分割を行うときに使用する。
---
# Python Base Skill
## When to use
- Pythonコードを新規作成するとき
- 既存コードをリファクタリングするとき
- 関数分割、型ヒント追加、pathlib化を行うとき
- UI、DB、I/O、ビジネスロジックを分離するとき
## Rules
- Python 3.13 を前提にする。
- すべての関数に引数と戻り値の型ヒントを付ける。
- 戻り値がない関数には `-> None` を付ける。
- `Any` は必要最小限にする。
- `os.path` ではなく `pathlib.Path` を使用する。
- `print` ではなく `logging` を使用する。
- magic number は定数化する。
- 例外は握りつぶさない。
- UI、DB、I/O、ビジネスロジックを分離する。
- 副作用がある関数は、関数名から意図が分かるようにする。
## Example
```python
from __future__ import annotations
from pathlib import Path
def load_text(path: Path) -> str:
return path.read_text(encoding="utf-8")
```
## Checklist
- [ ] 型ヒントがある
- [ ] 戻り値型がある
- [ ] `Path` を使用している
- [ ] `print` を使用していない
- [ ] 例外を握りつぶしていない
- [ ] 1関数1責務になっている
@@ -0,0 +1,62 @@
---
name: python-logging
description: Pythonプロジェクトでlogging設定、TOML設定、logger分離、例外ログを実装・修正するときに使用する。
---
# Python Logging Skill
## When to use
- logging設定を新規作成するとき
- TOMLからlogging設定を読み込むとき
- `print` を logging に置き換えるとき
- SQL、アプリ、Workerなどloggerを分離するとき
- 例外ログの出力を修正するとき
## Rules
- `print` は使用しない。
- `logging` を使用する。
- logging設定は TOML で管理する。
- logger は用途別に分離する。
- SQL、アプリ、バッチ、Worker、外部I/Oは必要に応じて別loggerにする。
- 例外発生時は `logger.exception(...)` を優先する。
- ユーザー向けメッセージと内部ログを分離する。
## Example
```python
from __future__ import annotations
import logging
import logging.config
import tomllib
from pathlib import Path
def setup_logging(toml_path: Path) -> None:
with toml_path.open("rb") as f:
config = tomllib.load(f)
logging.config.dictConfig(config)
logger = logging.getLogger(__name__)
```
## Exception Example
```python
try:
run_task()
except Exception:
logger.exception("Task failed")
raise
```
## Checklist
- [ ] `print` を使用していない
- [ ] `logger = logging.getLogger(__name__)` を使っている
- [ ] 例外時に `logger.exception` を使っている
- [ ] ユーザー表示と内部ログが分離されている
@@ -0,0 +1,59 @@
---
name: python-settings
description: Pythonプロジェクトでpydantic-settingsを使った環境変数、.env.dev、設定クラスを実装・修正するときに使用する。
---
# Python Settings Skill
## When to use
- `settings.py` を新規作成するとき
- pydantic-settings で設定クラスを作るとき
- `.env.dev` とOS環境変数の切り替えを実装するとき
- DB接続情報、ログ出力先、共有フォルダなどを設定化するとき
## Rules
- 設定は `pydantic_settings.BaseSettings` で一元管理する。
- 開発環境は `.env.dev` を使用する。
- テスト環境と本番環境はOS環境変数を使用する。
- パス系の設定値は `pathlib.Path` で扱う。
- DB接続情報、APIキー、パスワードをコードに直書きしない。
- Settingsクラスはアプリ起動時に1回読み込む。
- 必須設定が不足した場合は、明確なValidationErrorとして扱う。
## Example
```python
from __future__ import annotations
from pathlib import Path
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(
env_prefix="APP_",
env_file=".env.dev",
env_file_encoding="utf-8",
)
db_server: str
db_database: str
db_username: str | None = None
db_password: str | None = None
log_root: Path
output_root: Path
def get_settings() -> Settings:
return Settings()
```
## Checklist
- [ ] 秘密情報をコードに直書きしていない
- [ ] 環境変数prefixが統一されている
- [ ] パス系設定に `Path` を使っている
- [ ] 必須設定の不足を隠していない
@@ -0,0 +1,74 @@
---
name: python-testing
description: Pythonプロジェクトでpytestの単体テスト、例外テスト、fixture、parametrize、integration testを作成・修正するときに使用する。
---
# Python Testing Skill
## When to use
- pytest のテストを新規作成するとき
- 既存ロジックのテストを追加するとき
- リファクタリング前に仕様を固定するとき
- 例外・境界値・異常系をテストするとき
- DB / I/O を含む処理をmockまたはintegration testに分離するとき
## Purpose
- 仕様をコードとして固定する。
- 変更によるデグレードを防ぐ。
- ロジックを安全にリファクタリングできる状態を作る。
## Rules
- AAA パターンを使用する。
- 1テスト1Actにする。
- テスト名は `test_<対象>_<条件>_<期待結果>` にする。
- fixture は前提条件の名前にする。
- fixture には assert を書かない。
- 分岐パターンは `pytest.mark.parametrize` で表現する。
- 例外は型とメッセージの両方を確認する。
- DB、I/O、現在時刻、外部APIはUnit Testではmockする。
- Integration Testには `@pytest.mark.integration` を付ける。
## Example
```python
import pytest
@pytest.mark.parametrize(
("input_value", "expected"),
[
("OK", True),
("NG", False),
("", False),
],
)
def test_judge_status_various_inputs_returns_expected(
input_value: str,
expected: bool,
) -> None:
assert judge_status(input_value) is expected
def test_validate_input_missing_column_raises_value_error() -> None:
with pytest.raises(ValueError, match="missing column"):
validate_input(df)
```
## Commands
```bash
uv run pytest
uv run pytest -m "not integration"
uv run pytest -q
```
## Checklist
- [ ] AAAパターンになっている
- [ ] 1テスト1Actになっている
- [ ] テスト名で条件と期待結果が分かる
- [ ] 境界値と異常系が含まれている
- [ ] 外部依存がUnit Testに混ざっていない
@@ -0,0 +1,48 @@
---
name: sqlserver-ddl
description: SQL Server の CREATE TABLE、INDEX、UNIQUE制約、DDL修正、SQLファイル作成を行うときに使用する。
---
# SQL Server DDL Skill
## When to use
- SQL Server のテーブルを新規作成するとき
- CREATE TABLE を修正するとき
- INDEX、UNIQUE制約、FOREIGN KEY を追加するとき
- SQL Server用DDLをMarkdownやSQLファイルに整理するとき
## Rules
- SQL Server 用のDDLとして作成する。
- テーブル名・カラム名は既存ルールに合わせる。
- 主キーは原則 `ID INT IDENTITY(1,1) NOT NULL` を基本にする。
- 日時型は `DATETIME2(0)` を優先する。
- 作成日時は `CREATED_AT DATETIME2(0) NOT NULL DEFAULT SYSDATETIME()` を基本にする。
- 更新日時が必要な場合は `UPDATED_AT DATETIME2(0) NULL` を使用する。
- 検索条件に使う列には INDEX を検討する。
- 重複防止が必要な組み合わせには UNIQUE 制約を作成する。
- DDL、INDEX、補足説明をセットで出力する。
## Output Format
1. CREATE TABLE
2. CREATE INDEX
3. UNIQUE制約
4. 補足説明
## Example
```sql
CREATE TABLE dbo.SAMPLE_TABLE (
ID INT IDENTITY(1,1) NOT NULL,
TOOL_ID NVARCHAR(50) NOT NULL,
STATUS_CODE NVARCHAR(20) NOT NULL,
CREATED_AT DATETIME2(0) NOT NULL DEFAULT SYSDATETIME(),
UPDATED_AT DATETIME2(0) NULL,
CONSTRAINT PK_SAMPLE_TABLE PRIMARY KEY CLUSTERED (ID)
);
CREATE INDEX IX_SAMPLE_TABLE_TOOL_ID
ON dbo.SAMPLE_TABLE (TOOL_ID);
```
@@ -0,0 +1,43 @@
---
name: streamlit-page
description: Streamlitページからロジックを分離し、UI、session_state、service、repositoryに整理するときに使用する。
---
# Streamlit Page Refactor Skill
## When to use
- Streamlitページの処理が長くなったとき
- UIとロジックを分離したいとき
- `st.session_state` の管理を専用クラスに寄せたいとき
- DB処理、ファイルI/O、集計処理を `src/` 側へ移したいとき
## Rules
- ページファイルには画面表示、入力受付、結果表示を中心に残す。
- ビジネスロジックは service 層へ移す。
- DBアクセスは repository 層へ移す。
- DataFrame加工は pure function として分離する。
- `st.session_state` は専用クラスまたは専用関数で管理する。
- ユーザー向けエラーと内部ログを分離する。
- 長時間処理はJob Queue / Workerへの分離を検討する。
## Suggested Structure
```text
src/
├─ pages/
├─ services/
├─ repositories/
├─ models/
├─ settings.py
└─ exceptions.py
```
## Checklist
- [ ] ページファイルにDB処理が残っていない
- [ ] ページファイルに重い処理が残っていない
- [ ] session_stateのキーが散らばっていない
- [ ] ユーザー表示エラーと内部ログが分かれている
- [ ] service / repository / utility の責務が分かれている