Привет,
Я выпустил новую версию Dependency Injector 4.4. Она позволяет использовать Dependency Injector вместе с FastAPI. В этом посте покажу как это работает.
Основная задача интеграции: подружить директиву
Depends
FastAPI c маркерами Provide
и
Provider
Dependency Injector'a.Из коробки до версии DI 4.4 это не работало. FastAPI использует типизацию и Pydantic для валидации входных параметров и ответа. Маркеры Dependency Injector'а приводили его в недоумение.
Решение пришло после изучения внутренностей FastAPI. Пришлось сделать нескольких изменений в модуле связывания (wiring) Dependency Injector'а. Директива
Depends
теперь
работает вместе с маркерами Provide
и
Provider
.Пример
Создайте файл
fastapi_di_example.py
и поместите в него
следующие строки:
import sysfrom fastapi import FastAPI, Dependsfrom dependency_injector import containers, providersfrom dependency_injector.wiring import inject, Provideclass Service: async def process(self) -> str: return 'Ok'class Container(containers.DeclarativeContainer): service = providers.Factory(Service)app = FastAPI()@app.api_route('/')@injectasync def index(service: Service = Depends(Provide[Container.service])): result = await service.process() return {'result': result}container = Container()container.wire(modules=[sys.modules[__name__]])
Для того чтобы запустить пример установите зависимости:
pip install fastapi dependency-injector uvicorn
и запустите
uvicorn
:
uvicorn fastapi_di_example:app --reload
В терминале должно будет появится что-то вроде:
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)INFO: Started reloader process [11910] using watchgodINFO: Started server process [11912]INFO: Waiting for application startup.INFO: Application startup complete.
а
http://127.0.0.1:8000
должен возвращать:
{ "result": "Ok"}
Как тестировать?
Создайте рядом файл
tests.py
и поместите в него
следующие строки:
from unittest import mockimport pytestfrom httpx import AsyncClientfrom fastapi_di_example import app, container, Service@pytest.fixturedef client(event_loop): client = AsyncClient(app=app, base_url='http://test') yield client event_loop.run_until_complete(client.aclose())@pytest.mark.asyncioasync def test_index(client): service_mock = mock.AsyncMock(spec=Service) service_mock.process.return_value = 'Foo' with container.service.override(service_mock): response = await client.get('/') assert response.status_code == 200 assert response.json() == {'result': 'Foo'}
Для того чтобы запустить тесты установите зависимости:
pip install pytest pytest-asyncio httpx
и запустите
pytest
:
pytest tests.py
В терминале должно будет появится:
======= test session starts =======platform darwin -- Python 3.8.3, pytest-5.4.3, py-1.9.0, pluggy-0.13.1rootdir: ...plugins: asyncio-0.14.0collected 1 itemtests.py . [100%]======= 1 passed in 0.17s =======
Что дает интеграция?
FastAPI классный фреймворк для построения API. В него встроен базовый механизм dependency injection.
Эта интеграция улучшает работу dependency injection в FastAPI. Она позволяет использовать в нём провайдеры, переопредение, конфиг и ресурсы Dependency Injector'а.
Что дальше?
- Загляни на Github проекта
- Загляни в документацию
- Посмотри пример FastAPI с несколькими модулями