Metadata-Version: 2.4
Name: futile_di_azya0
Version: 0.1.1
Summary: A small dependency injection library
Project-URL: Homepage, https://github.com/azya0/futile_di
Project-URL: Issues, https://github.com/azya0/futile_di/issues
Author-email: azya0 <azyadr@yandex.ru>
License-Expression: MIT
License-File: LICENSE
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Requires-Python: >=3.12
Description-Content-Type: text/markdown

# futile_di

## Описание

**futile_di** - маленькая библиотека для **инъекции зависимостей**, не привязанной к реализации конкретного фреймворка (вроде Depends из FastAPI). 

Допустим у нас есть генератор для контроля цикла жизни сессии:

```python
def get_session():
    with create_session() as session:
        yield session
```

Тогда, чтобы получить сессию нам нужно:

* Создавать сильную зависимость
* Хранить созданный генератор на стеке функции-консьюмера
* Вручную вызывать ```next(...)``` для получения сессии

```python
def get_user(generator=get_session()):
    session = next(generator)

    ...
```

Вместо этого можно использовать механизм инъекции зависимости, который будет делать это "под капотом":

```python
def get_session() -> Session:
    with create_session() as session:
        yield session

@inject
def get_user(session: Session = Depends(get_session)):
    ...
```

На данный момент ```@inject``` и ```Depends``` поддерживают:
* синхронные функции
* асинхронные функции
* генераторы
* асинхронные генераторы

## Реализация

```inject``` - декоратор для синхронной/асинхронной функции. Он ищет класс ```Depends``` как среди параметров функции по умолчанию, так и среди переданных значений. Далее он обрабатывает его в зависимости от типа:

```python
class DependsType(Enum):
    VALUE = 0,
    SYNC = 1,
    ASYNC = 2,
    GENERATOR = 3,
    ASYNC_GENERATOR = 4,
```

Если это ```VALUE```, то он просто возвращает значение. Если это ```SYNC``` или ```ASYNC```, то он вызывает их, получает значение и возвращает его. Если это ```GENERATOR``` или ```ASYNC_GENERATOR```, то он получает первое значение, сохраняет генераторы до конца выполнения функции, а потом вызывает ещё один ```next(...)/await anext(...)```, для того, чтобы генератор мог завершить свой контекст. При этом, если исключение ```StopIteration/StopAsyncIteration``` не было получено, то исключения не будет. В конечном итоге генераторы удаляются со стека.

## Тесты

Для **futile_di** было написано несколько тестов. Запустить их можно через библиотеку ```pytest``` из корневой директории командой:

```shell
pytest -s 
```
