Metadata-Version: 2.5
Name: neva-fastapi
Version: 1.1.2
Summary: Add your description here
Requires-Python: >=3.12
Requires-Dist: annotated-doc>=0.0.4
Requires-Dist: dishka>=1.10.0
Requires-Dist: fastapi>=0.129.0
Requires-Dist: python-neva>=3.6.0
Requires-Dist: starlette>=0.41
Requires-Dist: typing-extensions>=4.5
Provides-Extra: testing
Requires-Dist: httpx>=0.27; extra == 'testing'
Requires-Dist: pytest>=9.0.2; extra == 'testing'
Description-Content-Type: text/markdown

# neva-fastapi

The **FastAPI integration** for the [Neva](https://pypi.org/project/python-neva/)
framework. It marries FastAPI to neva's dishka-based dependency-injection
container so that route handlers resolve injected services, while keeping all
HTTP concerns out of the framework-agnostic `python-neva` core.

Most consumers pull this in transitively via the `python-neva[fastapi]` extra
rather than depending on it directly.

## What it provides

Everything is re-exported from `neva.fastapi`:

- **`App`** — a subclass of `fastapi.FastAPI` that owns a `neva.arch.Application`
  (the DI container) and drives it: builds the container, reads app metadata
  from neva config (`app.*`), wires neva providers' `lifespan()` hooks, and
  installs per-request DI scoping middleware.
- **`APIRouter`** — a subclass of `fastapi.APIRouter` that forces
  `route_class=DishkaRoute` so injection works inside handlers.
- **`Inject`** — a re-export of `dishka.FromDishka`. Annotate handler params as
  `Inject[SomeService]`. **`Inject` only works on this package's `APIRouter`
  (or `App`'s routes)** — a vanilla `fastapi.APIRouter` lacks `DishkaRoute` and
  injection silently won't happen.

`neva` is a **namespace package** (no top-level `neva/__init__.py`); this repo
owns `neva/fastapi/` and shares the `neva.*` namespace with `python-neva`.

## Testing helpers

Fixtures in `neva/fastapi/fixtures.py` ship as a pytest plugin — opt in with
`pytest_plugins = ["neva.fastapi.fixtures"]` in your `conftest.py`. It provides
`webapp` (an `App` built from a `test_config` path you supply) and `http_client`
(an `httpx.AsyncClient` wired to the app via `ASGITransport`).

## Develop

```bash
uv sync          # install/refresh deps
poe lint         # ruff check
poe fmt          # ruff format
poe tc           # pyrefly check
poe test         # pytest
poe test-cov     # pytest --cov=neva --cov-report=term-missing
```

`asyncio_mode = "auto"` is set, so async tests need no `@pytest.mark.asyncio`.

## Contributing

**Commits** follow [Conventional Commits](https://www.conventionalcommits.org/)
with [gitmoji](https://gitmoji.dev/) prefixes, enforced by
[`cz_gitmoji`](https://github.com/ljnsn/cz-conventional-gitmoji). Commitizen is
a dev dependency — run `cz commit` for the guided wizard, or format manually as
`:gitmoji: type(scope): subject`.

**Releases** are cut per-repo with commitizen from the repo root:

```bash
cz bump                          # bump version in pyproject, write CHANGELOG, tag v<version>
git push --follow-tags origin main
uv build && uv publish           # build + publish the wheel/sdist
```

`cz bump` derives the level from the commits since the last tag, updates
`CHANGELOG.md`, and runs `scripts/retag-with-changelog.sh` to rewrite the new
tag with the rendered changelog as its annotation.
