A modern typing based service container and dependency injector.
Project description
AutoContainer
Python really needed a modern reflection based dependency injection container that "just works". Alas, welcome to AutoContainer for python. The dependency injection service contaienr that just works.
Features
- Direct class as service
- Separate provider functions
- Service Behaviors:
- Singleton
- Factory
- Assembler
- Instance
- Naming Services
- Container Bound Functions
- Dependency Graph
- Automatic Injection
- Service registration checking
- Inject typehint by name
- Primitive types by name
Installation
pip3 install autocontainer
Requirements:
- Python >= 3.5
Usage
It's all about types and hints, but first create the container
from autocontianer import Container
container = Container()
# Party Time
Classes & Injection
We'll use singleton as an example.
class A:
pass
class B:
def __init__(self, obj_a: A):
assert isinstance(obj_a, A)
# Order does not matter.
container.singleton(B)
container.singleton(A)
obj_b = container.get(B)
assert isinstance(obj_b, B)
Naming Services
class A:
pass
container.singleton(A, 'ayy')
obj_a = container.get(A)
obj_b = container.get('ayy')
assert obj_a is obj_b
Other ways to get
obj_a = container.get(A) # <--- Best IDE Support due to type hints.
obj_b = container.get('ayy')
obj_c = container.ayy # <--- the most concise way.
obj_d = container('ayy')
Builder Functions
You won't always put raw classes into the service container sometimes, it's necessary to write a function that custom initializes a class or object.
class A:
pass
class B:
def __init__(self):
self.fruit = 'tomato'
def makeB(obj_a: A) -> B: # Return type MUST be annotated
b = B()
b.fruit = 'mango'
return b
container.singleton(makeB)
obj_b = container(B)
assert obj_b.fruit == 'mango'
Factory
Factories can also take builder function as well as classes. The container returns a new instance every time.
class A:
pass
container.factory(A)
aa = container.get(A)
ab = container.get(A)
assert aa is not ab
assert isinstance(aa, A)
assert isinstance(ab, A)
Binding
This is the coolest feature, trust me. Imagine you have a function that needs both classes out of a container and vanilla arguments like int and str, this would be a pain to do manually. Unless...
class A:
pass
class B:
pass
container.singleton(A)
container.singleton(B)
def crazy_function(a: A, repeating: str, b: B, times: int):
assert isinstance(a, A)
assert isinstance(b, B)
return repeating * time
less_crazy_function = container.bind(crazy_function)
result = less_crazy_function("pew", 3)
assert result == 'pewpewpew'
Injecting
Same as binding but for simpler times.
class A:
pass
class B:
pass
container.singleton(A)
container.singleton(B)
def crazy_function(a: A, b: B):
assert isinstance(a, A)
assert isinstance(b, B)
return 'potato'
assert contianer.inject(crazy_function) == 'potato'
Specificity Injector
The container maintains an internal graph of dependencies that allows it to efficiently push instances of ancestor classes.
class A:
pass
class B:
pass
class C(A):
pass
class D(C, B):
pass
container.factory(A)
container.singleton(B)
container.factory(C)
container.singleton(D)
obj = container.get(A)
assert isinstance(obj, D)
assert isinstance(obj, A)
Hinting by Name
This is completely valid with the container
class A:
pass
container.singleton(A, 'apple')
def magic(ap: 'apple'):
assert isinstance(ap, A)
container.inject(magic)
Available Methods
-
get(service: Union[Type, str])
Retreives a service -
has(service: Union[Type, str])
Returns if a service exists -
singleton(service: Union[Type, Callable[..., Instance]], name?: str)
Adds a service as a singleton into container. (Returns the same object on everyget
) -
factory(service: Union[Type, Callable[..., Instance]], name?: str)
Adds a service as a factory into container. (Returns a fresh object on everyget
) -
instance(service: object, name?: str)
Adds a service as an instance into container. (Returns the same object on everyget
, but does not try to instantiate) -
assembler(service: Union[Type, Callable[..., Instance]], name?: str)
Adds a service such that on every get, the container returns a bound callable that produces a fresh object everytime. -
bind(func: Callable)
Returns a new callable but in which the arguments recognized by the container are automatically pushed when calling. (see examples below) -
inject(func: Callable)
Takes a callable and calls it by injecting all the services it requires and then returns the return value.
Running Tests
python -m unittest discover -s ./
License
MIT. Go crazy.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
File details
Details for the file autocontainer-1.1.0.tar.gz
.
File metadata
- Download URL: autocontainer-1.1.0.tar.gz
- Upload date:
- Size: 5.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.1.1 pkginfo/1.5.0.1 requests/2.21.0 setuptools/40.8.0 requests-toolbelt/0.9.1 tqdm/4.43.0 CPython/3.7.3
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 1766d5de3ac670fb560cdbd4d52249f608081f93d6d88cf9bc65c134f7915c26 |
|
MD5 | 1f861f5c5bd757370bd729e596916586 |
|
BLAKE2b-256 | d1fa5b6afb32547db34c2844832f36006acc164cd8e7cc7c594a712af0cb0005 |