Creating a custom provider

You can create a custom provider.

To create a custom provider you need to follow these rules:

  1. New provider class should inherit Provider.

  2. You need to implement the Provider._provide() method.

  3. You need to implement the Provider.__deepcopy__() method. It should return an equivalent copy of a provider. All providers must be copied with the deepcopy() function from the providers module. It’s essential to pass memo into deepcopy in order to keep the preconfigured args and kwargs of stored providers. After the a new provider object is created, use Provider._copy_overriding() method to copy all overriding providers. See the example below.

  4. If new provider has a __init__() method, it should call the parent Provider.__init__().

  5. If new provider stores any other providers, these providers should be listed in .related property. Property .related also should yield providers from parent .related property.

from dependency_injector import containers, providers


class CustomFactory(providers.Provider):

    __slots__ = ("_factory",)

    def __init__(self, provides, *args, **kwargs):
        self._factory = providers.Factory(provides, *args, **kwargs)
        super().__init__()

    def __deepcopy__(self, memo):
        copied = memo.get(id(self))
        if copied is not None:
            return copied

        copied = self.__class__(
            self._factory.provides,
            *providers.deepcopy(self._factory.args, memo),
            **providers.deepcopy(self._factory.kwargs, memo),
        )
        self._copy_overridings(copied, memo)

        return copied

    @property
    def related(self):
        """Return related providers generator."""
        yield from [self._factory]
        yield from super().related

    def _provide(self, args, kwargs):
        return self._factory(*args, **kwargs)


class Container(containers.DeclarativeContainer):

    factory = CustomFactory(object)


if __name__ == "__main__":
    container = Container()

    object1 = container.factory()
    assert isinstance(object1, object)

    object2 = container.factory()
    assert isinstance(object1, object)

    assert object1 is not object2

Note

  1. Prefer delegation over inheritance. If you choose between inheriting a Factory or inheriting a Provider and use Factory internally - the last is better.

  2. When creating a new provider follow the Factory-like injections style. Consistency matters.

  3. Use the __slots__ attribute to make sure nothing could be attached to your provider. You will also save some memory.

Note

If you don’t find needed provider in the providers module and experience troubles creating one by your own - open a Github Issue.

I’ll help you to resolve the issue if that’s possible. If the new provider can be useful for others I’ll include it into the providers module.