Metadata-Version: 2.4
Name: fastapi_di
Version: 0.1.0
Summary: Depends で依存を明示する汎用 DI。解決戦略（リゾルバ）を差し替えられる。
Project-URL: Repository, https://github.com/sasano8/fastapi_di
Author-email: sasano8 <y-sasahara@ys-method.com>
License-Expression: MIT
License-File: LICENSE
Requires-Python: >=3.14
Description-Content-Type: text/markdown

# fastapi_di

`Depends` で依存を明示する汎用 DI。解決戦略（リゾルバ）を差し替えられる。

名前のとおり **FastAPI の DI に似せて**いる。`Depends` / `Dependant` /
`solve_dependencies` の設計を踏襲しつつ、HTTP 依存（Request、query/path/body、
pydantic 検証）を全て剥がした。FastAPI には依存せず（依存パッケージなし）、
Web に限らずパイプライン処理・バッチ・CLI・ライブラリ内部の配線に使える。

- **依存は明示 `Depends` のみ**。型アノテーションによる自動ワイヤリングはしない。
  書いていないものは注入されない。
- **解析と実行が分かれている**。シグネチャを辿るだけで依存グラフが得られる（実行しない）。
- **参照の意味を差し替えられる**。`Depends("name")` の `"name"` が何を指すかは
  リゾルバが決める。既定は完全一致だが、チェーンや凍結表に差し替えられる。

要件: Python >= 3.14 / 依存パッケージなし。

## インストール

```bash
uv add fastapi_di
```

## 使う

```python
import asyncio
from typing import Annotated

from fastapi_di import Depends, Injector


def get_settings() -> dict:
    return {"dsn": "postgres://localhost"}


def get_dsn(settings: Annotated[dict, Depends(get_settings)]) -> str:
    return settings["dsn"]


class Database:
    def __init__(self, dsn: str = Depends(get_dsn)) -> None:
        self.dsn = dsn


async def main() -> None:
    injector = Injector()
    async with injector as scope:            # スコープ = 依存の生存期間
        db = await scope.inject(Database)    # Database -> get_dsn -> get_settings
        print(db.dsn)


asyncio.run(main())
```

### クラス内の派生値（depend_property）

`depend_property` は getter の引数を**同じクラスのメンバ**から解決するプロパティ。
Injector とは無縁の同期ディスクリプタで、`Depends` の構文だけを共有する。
既定は**キャッシュしない**（毎回再計算）。`cache="auto"` にすると依存から戦略が
伝搬し（`cached_property` だけに依存すれば自分もキャッシュ、`property` が混ざれば
毎回再計算）、`cache=True` で強制キャッシュする。

```python
from functools import cached_property

from fastapi_di import Depends, depend_property


class Service:
    @cached_property
    def user(self) -> dict:
        return {"name": "alice", "deleted_at": None}

    @depend_property(cache="auto")
    def is_active_user(self, user=Depends(user)) -> bool:
        return user["deleted_at"] is None


print(Service().is_active_user)  # True（user が cached かつ cache="auto" なのでキャッシュされる）
```

詳細は [使う](docs/usage.md) を参照。

## ビジョン

狙いは**依存性の解決を標準化する**こと。三段階で考えている。

1. **小回りの利く小さな DI** — 数十行のスクリプトから使える。フレームワークを
   持ち込まず、`Depends` を書くだけ。今ここ。
2. **パイプライン処理などへの展開** — 依存グラフは DAG そのもの。同じ宣言から
   実行順序・並列性・キャッシュ境界を導ける。DI とワークフローを別物にしない。
3. **依存関係の定義フォーマット** — OpenAPI が HTTP インターフェースに対して
   果たした役割を、依存関係に対して果たすもの。何が何に依存しているかを
   機械可読な形で宣言・可視化・検証でき、言語や実装をまたいで共有できる形式。

`graph.to_json()` と `bindings.json` は、その 3 番目に向けた最初の一歩。
前者が「どう繋がっているか」、後者が「なぜそう繋がったか」を外に出す。

## ドキュメント

- [使う](docs/usage.md) — スコープ、setup/teardown、差し替え、名前での参照
- [依存グラフを取り出す](docs/graph.md) — `to_json`、層タグ、`visualize`（Mermaid / LLM テキスト）
- [リゾルバとマニフェスト](docs/resolver.md) — 解決戦略の差し替え、配線の凍結
- [開発](docs/development.md) — make タスク、examples
