Dependency injection

Wiring dependencies to handlers for extra handling.

Handler factories

Subscribing to a request will create a handler reference. The reference is an instance of the HandlerReference class. A handler factory creates a concrete handler from the reference. The HandlerFactory protocol defines this contract.

The reference will be to the class or function used in the subscription. Which may or may not be a handler. The factory tries to coerce these classes and functions into the Handler protocol.

Providing dependencies

To perform the business logic a handler represents, it often needs other dependencies.

Banshee has no opinions about on how you manage your dependencies. You can use a custom factory to provide them to handlers any way you like.

import functools
import inspect
import typing

import banshee

T = typing.TypeVar("T")


DEPENDENCIES = {
   "user_repository": UserRepository()
}

def kwargs_factory(self, reference: HandlerReference[T]) -> Handler[T]:
   expected = inspect.signature(reference.handler).parameters.keys()

   kwargs = {k: v for k, v in DEPENDENCIES.items() if k in keys}

   return functools.partial(reference.handler, **kwargs)

The above uses values based on argument name. It is a crude example. In production we recommend that you consider using a proper dependency injection framework. See our injector integration for example.

Default behaviour

The SimpleHandlerFactory is the default used when a factory is not provided. It will return a function as is, and try to create an instance for class based handlers.

Reference

class banshee.HandlerFactory(*args, **kwargs)

Bases: Protocol

Handler factory protocol.

Returns a concrete handler for the given reference.

abstract __call__(reference, /)

Get handler.

Returns a concrete handler for the given reference.

Parameters

reference (HandlerReference[T]) – reference to a handler

Returns

concrete handler instance

Return type

Handler[T]

class banshee.SimpleHandlerFactory(*args, **kwargs)

Bases: HandlerFactory

Simple handler factory.

This factory assumes that the reference is itself a valid handler, allowing handlers to be directly registered with the registry.

@registry.subscribe_to(GreetCommand)
def do_greet(command: GreetCommand):
    print(f"hello {command.name}!")
__call__(reference, /)

Get handler.

Returns a concrete handler for the given reference.

Parameters

reference (HandlerReference[T]) – reference to a handler

Returns

concrete handler instance

Return type

Handler[T]