Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/pilagod/deppy
Dependency injection framework for Python
https://github.com/pilagod/deppy
dependency-injection di di-container di-framework inversion-of-control ioc ioc-container ioc-framework python python3
Last synced: 4 days ago
JSON representation
Dependency injection framework for Python
- Host: GitHub
- URL: https://github.com/pilagod/deppy
- Owner: pilagod
- License: mit
- Created: 2021-03-02T02:05:44.000Z (almost 4 years ago)
- Default Branch: master
- Last Pushed: 2021-03-08T08:17:52.000Z (almost 4 years ago)
- Last Synced: 2024-12-05T13:07:57.963Z (about 1 month ago)
- Topics: dependency-injection, di, di-container, di-framework, inversion-of-control, ioc, ioc-container, ioc-framework, python, python3
- Language: Python
- Homepage:
- Size: 17.6 KB
- Stars: 7
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# deppy [![Build Status](https://travis-ci.com/pilagod/deppy.svg?branch=master)](https://travis-ci.com/pilagod/deppy) [![Coverage Status](https://coveralls.io/repos/github/pilagod/deppy/badge.svg?branch=master)](https://coveralls.io/github/pilagod/deppy?branch=master)
Dependency injection framework for Python
## Installation
```shell
$ pip install deppy
```## Usage
### Concrete class
```python
from deppy import container, injectable@injectable()
class Greeter:
def greet(self) -> str:
return "Hello"g = container.get(Greeter)
g.greet() # Hello
```### Implementation for abstract class
```python
import abcfrom deppy import container, injectable
# Should raise NotImplementedError instead of abc.abstractmethod decorator for abstract method due to following mypy issue:
# https://github.com/python/mypy/issues/5374#issuecomment-582093112
class Greeter(abc.ABC):
def greeter(self) -> str:
raise NotImplementedError()@injectable(Greeter)
class GreeterImpl(Greeter):
def greeter(self) -> str:
return "Hello"g = container.get(Greeter)
g.greet() # Hello# If class is on behalf of abstract class, the class cannot get by itself from container
# check `Multiple implementations` section for more complex use case.
container.get(GreeterImpl) # KeyError
```### Multiple implementations
```python
import abcfrom enum import Enum
from deppy from container, injectable
class Greeter(abc.ABC):
def greet(self) -> str:
raise NotImplementedError()class GreeterType(Enum):
A = "A"
B = "B"@injectable(Greeter, tag=GreeterType.A)
class GreeterA(Greeter):
def greet(self) -> str:
return "A"@injectable(Greeter, tag=GreeterType.B)
class GreeterB(Greeter):
def greet(self) -> str:
return "B"a = container.get(Greeter, tag=GreeterType.A)
a.greet() # Ab = container.get(Greeter, tag=GreeterType.B)
b.greet() # B
```### Dependency injection
```python
from deppy import container, injectable@injectable()
class Greeter:
def greet(self) -> str:
return "Hello"@injectable()
class GreeterWorld:
def __init__(self, greeter: Greeter):
self._greeter = greeter
def greet(self) -> str:
return self._greeter.greet() + " World"gw = container.get(GreeterWorld)
gw.greet() # Hello World
```### Dependency injection with selected implementation
```python
import abcfrom enum import Enum
from deppy import container, injectable
class Greeter(abc.ABC):
def greet(self) -> str:
raise NotImplementedError()class GreeterType(Enum):
A = "A"
B = "B"
@injectable(Greeter, tag=GreeterType.A)
class GreeterA(Greeter):
def greet(self) -> str:
return "A"@injectable(Greeter, tag=GreeterType.B)
class GreeterB(Greeter):
def greet(self) -> str:
return "B"@injectable(
params={
"a": GreeterType.A,
"b": GreeterType.B
}
)
class GreeterManager:
def __init__(self, a: Greeter, b: Greeter):
self._a = a
self._b = bdef greet(self) -> str:
return self._a.greet() + self._b.greet()gm = container.get(GreeterManager)
gm.greet() # AB
```## Caveat
In order to avoid circular import, it is common to separate interfaces and implementations into different packages.
Package comsumers usually depend only on interfaces,
resulting in no one to import implementations and thus `injectable` is never called.For me, I usually put implemetations under `impls` directory besides interfaces. Implemetations can be registered into container by an independent composition root, and just import it and initialize it on application startup:
```python
# composition_root.pyimport glob
import importlibdef init():
impls = glob.glob("**/impls", recursive=True)
for impl in impls:
importlib.import_module(impl.replace("/", ".").replace("\\", "."))```
## License
© Cyan Ho (pilagod), 2021-NOW
Released under the [MIT License](https://github.com/pilagod/deppy/blob/master/LICENSE)