Skip to main content

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 every get)

  • factory(service: Union[Type, Callable[..., Instance]], name?: str)
    Adds a service as a factory into container. (Returns a fresh object on every get)

  • instance(service: object, name?: str)
    Adds a service as an instance into container. (Returns the same object on every get, 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.

Metadata

Release files for autocontainer 1.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for autocontainer 1.1.0
File Size Uploaded Provenance Actions
autocontainer-1.1.0.tar.gz 5.6 kB Without provenance Details for autocontainer-1.1.0.tar.gz

Release files / autocontainer-1.1.0.tar.gz

Download URL autocontainer-1.1.0.tar.gz
Size 5.6 kB
Tags Source
SHA-256 checksum
How to use checksums
1766d5de3ac670fb560cdbd4d52249f608081f93d6d88cf9bc65c134f7915c26
BLAKE2b-256 checksum
How to use checksums
d1fa5b6afb32547db34c2844832f36006acc164cd8e7cc7c594a712af0cb0005
Upload date
Uploaded using Trusted Publishing?
What is 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

Security for autocontainer 1.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page