Skip to content

Easy dependency injection without wiring, works with Python 3.8-3.12. Easy replacing for dependency-injector

License

Notifications You must be signed in to change notification settings

nightblure/injection

Repository files navigation

Injection

PyPI - Version GitHub License

PyPI - Python Version

Latest Release Documentation Status

Tests And Linting codecov

Ruff Hatch project pdm-managed MyPy Strict

Maintainability GitHub Downloads (all assets, all releases)

PyPI - Month Downloads GitHub Repo stars


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;
  • support dependency injection via Annotated in FastAPI;
  • the code is fully typed and checked with mypy;
  • no third-party dependencies;
  • multiple containers;
  • overriding dependencies for tests without wiring;
  • 100% code coverage and very simple code;
  • good documentation;
  • intuitive and almost identical api with dependency-injector, which will allow you to easily migrate to injection (see migration from dependency injector);

Installation

pip install deps-injection

Using example

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[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: RedisDependency):
    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"