Skip to content

Commit

Permalink
Merge pull request #1 from woltapp/small-adjustments
Browse files Browse the repository at this point in the history
Small adjustments
  • Loading branch information
jerry-git authored Mar 27, 2024
2 parents 6a04174 + 7fe09ec commit 3f80e4b
Show file tree
Hide file tree
Showing 17 changed files with 82 additions and 54 deletions.
2 changes: 1 addition & 1 deletion .cruft.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"cookiecutter": {
"author_name": "Nikita Zavadin",
"author_email": "[email protected]",
"github_username": "RB387",
"github_username": "woltapp",
"project_name": "magic-di",
"project_slug": "magic-di",
"package_name": "magic_di",
Expand Down
1 change: 0 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ repos:
types: [ python ]
- id: ruff
name: ruff
# Add --fix, in case you want it to autofix when this hook runs
entry: poetry run ruff check --fix --force-exclude
require_serial: true
language: system
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased
### Changed
- Initial version
2 changes: 1 addition & 1 deletion LICENCE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2021 Nikita Zavadin <[email protected]>
Copyright (c) 2024 Wolt Enterprises

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
Expand Down
53 changes: 35 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,9 @@

---

Dependency Injector that makes your life easier with built-in support of FastAPI, Celery (and it can be integrated with everything)

What are the problems with FastAPI’s dependency injector?
1) It forces you to use global variables.
2) You need to write an endless number of fabrics with startup logic
3) It makes your project highly dependent on FastAPI’s injector by using “Depends” everywhere.

To solve these problems, you can use this dead-simple Dependency Injector that will make development so much easier.

__Q: But why not to use [python-dependency-injector](https://github.com/ets-labs/python-dependency-injector) or other libs?__

A: The goal of this Dependency Injector is to __reduce the amount of code as much as possible__ and get rid of enterprise code with millions of configs, containers, and fabrics. That’s why python-dependency-injector and similar libraries are overkill. The philosophy of this injector is that clients know how to configure themselves and perform all startup routines.
Dependency Injector with minimal boilerplate code, built-in support for FastAPI and Celery, and seamless integration to basically anything.

## Contents
* [Install](#install)
* [Getting Started](#getting-started)
* [Clients Configuration](#clients-configuration)
Expand All @@ -41,14 +31,25 @@ A: The goal of this Dependency Injector is to __reduce the amount of code as muc
* [Testing](#testing)
* [Default simple mock](#default-simple-mock)
* [Custom mocks](#custom-mocks)
* [Alternatives](#alternatives)
* [Development](#development)


## Install
```bash
pip install magic-di
```

With FastAPI integration:
```bash
pip install 'magic-di[fastapi]'
```

With Celery integration:
```bash
pip install 'magic-di[celery]'
```

## Getting Started

```python
Expand Down Expand Up @@ -323,16 +324,32 @@ def client(injector: DependencyInjector, service_mock: Service):
yield client
```

## Alternatives

### [FastAPI's built-in dependency injection](https://fastapi.tiangolo.com/tutorial/dependencies/)

FastAPI's built-in DI is great, but it makes the project (and its business logic) dependent on FastAPI, `fastapi.Depends` specifically.

`magic-di` decouples DI from other dependencies while still offering seamless integration to FastAPI, for example.

### [python-dependency-injector](https://github.com/ets-labs/python-dependency-injector)

[python-dependency-injector](https://github.com/ets-labs/python-dependency-injector) is great, but it requires a notable amount of boilerplate code.

The goal of `magic-di` is to __reduce the amount of code as much as possible__ and get rid of enterprise code with countless configs, containers, and fabrics.
The philosophy of `magic-di` is that clients know how to configure themselves and perform all startup routines.


## Development

* Clone this repository
* Requirements:
* [Poetry](https://python-poetry.org/)
* Python 3.8+
* Python 3.10+
* Create a virtual environment and install the dependencies

```sh
poetry install
poetry install --all-extras
```

* Activate the virtual environment
Expand All @@ -349,17 +366,17 @@ pytest

### Documentation

The documentation is automatically generated from the content of the [docs directory](https://github.com/RB387/magic-di/tree/master/docs) and from the docstrings
The documentation is automatically generated from the content of the [docs directory](https://github.com/woltapp/magic-di/tree/master/docs) and from the docstrings
of the public signatures of the source code. The documentation is updated and published as a [Github Pages page](https://pages.github.com/) automatically as part each release.

### Releasing

Trigger the [Draft release workflow](https://github.com/RB387/magic-di/actions/workflows/draft_release.yml)
Trigger the [Draft release workflow](https://github.com/woltapp/magic-di/actions/workflows/draft_release.yml)
(press _Run workflow_). This will update the changelog & version and create a GitHub release which is in _Draft_ state.

Find the draft release from the
[GitHub releases](https://github.com/RB387/magic-di/releases) and publish it. When
a release is published, it'll trigger [release](https://github.com/RB387/magic-di/blob/master/.github/workflows/release.yml) workflow which creates PyPI
[GitHub releases](https://github.com/woltapp/magic-di/releases) and publish it. When
a release is published, it'll trigger [release](https://github.com/woltapp/magic-di/blob/master/.github/workflows/release.yml) workflow which creates PyPI
release and deploys updated documentation.

### Pre-commit
Expand Down
8 changes: 4 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ authors = [
license = "MIT"
readme = "README.md"

documentation = "https://RB387.github.io/magic-di"
homepage = "https://RB387.github.io/magic-di"
repository = "https://github.com/RB387/magic-di"
documentation = "https://woltapp.github.io/magic-di"
homepage = "https://woltapp.github.io/magic-di"
repository = "https://github.com/woltapp/magic-di"

classifiers = [
"Development Status :: 4 - Beta",
Expand Down Expand Up @@ -61,7 +61,7 @@ requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

[tool.ruff]
target-version = "py38" # The lowest supported version
target-version = "py310" # The lowest supported version
line-length = 100

[tool.ruff.lint]
Expand Down
4 changes: 3 additions & 1 deletion src/magic_di/_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
import inspect
from dataclasses import dataclass
from threading import Lock
from typing import TYPE_CHECKING, Any, Generic, Iterable, TypeVar, cast
from typing import TYPE_CHECKING, Any, Generic, TypeVar, cast

if TYPE_CHECKING:
from collections.abc import Iterable

from magic_di import ConnectableProtocol

T = TypeVar("T")
Expand Down
5 changes: 2 additions & 3 deletions src/magic_di/_injector.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@
TYPE_CHECKING,
Annotated,
Any,
Callable,
Iterable,
Iterator,
TypeVar,
cast,
get_origin,
Expand All @@ -27,6 +24,8 @@
from magic_di.exceptions import InjectionError, InspectionError

if TYPE_CHECKING:
from collections.abc import Callable, Iterable, Iterator

from magic_di._connectable import ConnectableProtocol

# flag to use in typing.Annotated
Expand Down
8 changes: 4 additions & 4 deletions src/magic_di/_utils.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
from __future__ import annotations

from typing import Any, TypeVar, Union, cast, get_args
from typing import Any, TypeVar, cast, get_args
from typing import get_type_hints as _get_type_hints

from magic_di import ConnectableProtocol

LegacyUnionType = type(Union[object, None])
LegacyUnionType = type(object | None)

try:
from types import UnionType # type: ignore[import-error,unused-ignore]
except ImportError:
UnionType = LegacyUnionType # type: ignore[misc, assignment]
UnionType = LegacyUnionType # type: ignore[misc]


T = TypeVar("T")
Expand Down Expand Up @@ -40,7 +40,7 @@ def get_cls_from_optional(cls: T) -> T:
Returns:
T: Extracted class
"""
if not isinstance(cls, (UnionType, LegacyUnionType)):
if not isinstance(cls, UnionType | LegacyUnionType):
return cls

args = get_args(cls)
Expand Down
5 changes: 4 additions & 1 deletion src/magic_di/celery/_async_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
import asyncio
import threading
from dataclasses import dataclass
from typing import Any, Coroutine, TypeVar
from typing import TYPE_CHECKING, Any, TypeVar

if TYPE_CHECKING:
from collections.abc import Coroutine

R = TypeVar("R")

Expand Down
4 changes: 3 additions & 1 deletion src/magic_di/celery/_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import asyncio
import os
import threading
from typing import TYPE_CHECKING, Any, Callable, Protocol, runtime_checkable
from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable

from celery import signals
from celery.loaders.app import AppLoader # type: ignore[import-untyped]
Expand All @@ -12,6 +12,8 @@
from magic_di.celery._async_utils import EventLoop, EventLoopGetter, run_in_event_loop

if TYPE_CHECKING:
from collections.abc import Callable

from celery.loaders.base import BaseLoader


Expand Down
5 changes: 4 additions & 1 deletion src/magic_di/celery/_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@

import inspect
from functools import wraps
from typing import Any, Callable, cast, get_type_hints
from typing import TYPE_CHECKING, Any, cast, get_type_hints

from celery.app.task import Task

from magic_di import Connectable, DependencyInjector
from magic_di.celery._async_utils import EventLoop, run_in_event_loop
from magic_di.celery._loader import InjectedCeleryLoaderProtocol

if TYPE_CHECKING:
from collections.abc import Callable


class BaseCeleryConnectableDeps(Connectable): ...

Expand Down
3 changes: 2 additions & 1 deletion src/magic_di/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import inspect
from typing import Any, Callable
from collections.abc import Callable
from typing import Any

from magic_di._signature import Signature
from magic_di._utils import get_type_hints
Expand Down
5 changes: 2 additions & 3 deletions src/magic_di/fastapi/_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@
TYPE_CHECKING,
Annotated,
Any,
AsyncIterator,
Callable,
Iterator,
Protocol,
get_origin,
runtime_checkable,
Expand All @@ -19,6 +16,8 @@
from magic_di._injector import DependencyInjector

if TYPE_CHECKING:
from collections.abc import AsyncIterator, Callable, Iterator

from fastapi import FastAPI, routing


Expand Down
4 changes: 2 additions & 2 deletions src/magic_di/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

import asyncio
import inspect
from typing import TYPE_CHECKING, Awaitable, TypeVar, overload
from typing import TYPE_CHECKING, TypeVar, overload

from magic_di import DependencyInjector

if TYPE_CHECKING:
from collections.abc import Callable
from collections.abc import Awaitable, Callable


T = TypeVar("T")
Expand Down
8 changes: 4 additions & 4 deletions tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def test_app_injection(injector: DependencyInjector) -> None:
app = inject_app(FastAPI(), injector=injector)

@app.get(path="/hello-world")
def hello_world(service: Provide[Service], some_query: str) -> dict[str, str | bool]: # noqa: FA102
def hello_world(service: Provide[Service], some_query: str) -> dict[str, str | bool]:
assert isinstance(service, Service)
return {"query": some_query, "is_alive": service.is_alive()}

Expand Down Expand Up @@ -42,7 +42,7 @@ def global_dependency(dep: Provide[GlobalConnect]) -> None:
class MiddlewareNonConnectable:
creds: str = "secret_creds"

def get_creds(self, value: str | None = None) -> str: # noqa: FA102
def get_creds(self, value: str | None = None) -> str:
return value or self.creds

class AnotherDatabase(Database): ...
Expand All @@ -65,7 +65,7 @@ def get_creds(
def hello_world(
service: Provide[Service],
creds: Annotated[str, Depends(get_creds)],
) -> dict[str, str | bool]: # noqa: FA102
) -> dict[str, str | bool]:
assert isinstance(service, Service)
return {"creds": creds, "is_alive": service.is_alive()}

Expand All @@ -90,7 +90,7 @@ def test_app_injection_clients_connect(
router = APIRouter()

@router.get(path="/hello-world")
def hello_world(service: Provide[Service]) -> dict[str, bool]: # noqa: FA102
def hello_world(service: Provide[Service]) -> dict[str, bool]:
assert service.workers

return {
Expand Down
Loading

0 comments on commit 3f80e4b

Please sign in to comment.