diff --git a/README.md b/README.md index 4404b78..586bbf8 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Injection ![PyPI - Version](https://img.shields.io/pypi/v/deps-injection?label=pypi%20version) -[![GitHub license](https://img.shields.io/github/license/nightblure/injection)](https://github.com/nightblure/injection/blob/main/LICENSE) +![GitHub License](https://img.shields.io/github/license/nightblure/injection?color=012111012) ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/deps-injection) @@ -13,14 +13,132 @@ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) [![Hatch project](https://img.shields.io/badge/%F0%9F%A5%9A-Hatch-4051b5.svg)](https://github.com/pypa/hatch) -![GitHub Repo stars](https://img.shields.io/github/stars/nightblure/injection) +[![Maintainability](https://api.codeclimate.com/v1/badges/1da49eb0b28eacae4624/maintainability)](https://codeclimate.com/github/nightblure/injection/maintainability) -![PyPI - Month Downloads](https://img.shields.io/pypi/dm/deps-injection?color=102255102) +![GitHub Repo stars](https://img.shields.io/github/stars/nightblure/injection) ![GitHub Downloads (all assets, all releases)](https://img.shields.io/github/downloads/nightblure/injection/total?color=102255102&label=Total%20downloads) +![PyPI - Month Downloads](https://img.shields.io/pypi/dm/deps-injection?color=102255102) --- -## Features +Easy dependency injection for all, works with Python 3.8-3.12. Main features and advantages: * support **Python 3.8-3.12**; * works with **FastAPI, Flask, Litestar** and **Django REST Framework**; +* dependency injection via `Annotated` in FastAPI; * **no third-party dependencies**; +* **multiple containers**; +* **overriding** dependencies for tests without wiring; +* **100%** code coverage and very simple code; +* good [documentation](https://injection.readthedocs.io/latest/). + +--- + +## Installation +```shell +pip install deps-injection +``` + +## Using example + +```python3 +import sys + +if sys.version_info >= (3, 9): + from typing import Annotated +else: + from typing import Annotated +from unittest.mock import Mock + +import pytest +from fastapi import APIRouter, Depends, FastAPI +from fastapi.testclient import TestClient +from injection import DeclarativeContainer, Provide, inject, providers + + +class Settings: + redis_url: str = "redis://localhost" + redis_port: int = 6379 + + +class Redis: + def __init__(self, *, url: str, port: int): + self.uri = url + ":" + str(port) + self.url = url + self.port = port + + def get(self, key): + return key + + +class Container(DeclarativeContainer): + settings = providers.Singleton(Settings) + redis = providers.Singleton( + Redis, + port=settings.provided.redis_port, + url=settings.provided.redis_url, + ) + + +router = APIRouter(prefix="/api") + + +def create_app(): + app = FastAPI() + app.include_router(router) + return app + + +RedisDependency = Annotated[Redis, Depends(Provide["redis"])] +RedisDependencyExplicit = Annotated[Redis, Depends(Provide[Container.redis])] + + +@router.get("/values") +@inject +def some_get_endpoint_handler(redis: RedisDependency): + value = redis.get(299) + return {"detail": value} + + +@router.post("/values") +@inject +async def some_get_async_endpoint_handler(redis: RedisDependencyExplicit): + value = redis.get(399) + return {"detail": value} + + +###################### TESTING ###################### +@pytest.fixture(scope="session") +def app(): + return create_app() + + +@pytest.fixture(scope="session") +def container(): + return Container.instance() + + +@pytest.fixture() +def test_client(app): + client = TestClient(app) + return client + + +def test_override_providers(test_client, container): + def mock_get_method(_): + return "mock_get_method" + + mock_redis = Mock() + mock_redis.get = mock_get_method + + providers_to_override = {"redis": mock_redis} + + with container.override_providers(providers_to_override): + response = test_client.get("/api/values") + + assert response.status_code == 200 + body = response.json() + assert body["detail"] == "mock_get_method" + +``` + +--- diff --git a/tests/test_integrations/test_fastapi/app.py b/tests/test_integrations/test_fastapi/app.py index 88dc32b..eb732c4 100644 --- a/tests/test_integrations/test_fastapi/app.py +++ b/tests/test_integrations/test_fastapi/app.py @@ -8,9 +8,6 @@ @asynccontextmanager async def lifespan_handler(_): yield - # Container.wire() - # yield - # Container.unwire() def create_app():