Dispatching requests

Transporting requests to their destined handlers.

Buses

The Bus protocol wraps a command dispatcher and message bus implementation. The bus routes requests to their subscribed handlers via a composable chain of middleware.

Subscriptions

A bus must be able to find the handlers for a request to dispatch it. Subscriptions hold the connection from a handler to a request.

The Registry manages subscriptions, and provides them via the HandlerLocator protocol.

Subscribe a handler to a request via a manual definition or using a decorator.

import banshee

registry = banshee.Registry()
registry.subscribe(do_greeting, to=GreetCommand)

# or via a decorator

@registry.subscribe_to(GreetCommand)
async def do_greeting(command: GreetCommand) -> None:
    print(f"Hello {command.name}!")

Building a bus

You use the Builder class to construct a bus instance. Builder is an example of the builder pattern and uses a fluent interface to define a bus.

import banshee

bus = (
  banshee.Builder()
  .with_locator(registry)
  .build()
)

Sending a request

Once you have registered your handlers, you can dispatch requests to them via the bus.

await bus.handle(GreetCommand(name="Joe"))

Querying

You can get a result back from a handler by sending a query.

@dataclasses.dataclass(freeze=True)
class GetUserQuery:
   user_id: int

@registry.subscribe_to(GetUserQuery)
async def do_greeting(query: GetUserQuery) -> User:
    return user_store.get(query.user_id)

user = await bus.query(GetUserQuery(user_id=1))

Reference

class banshee.Builder(middleware=<factory>, locator=None, factory=None)

Bases: object

Builder.

Provides a chained interface to construct an instance of {class}`banshee.Bus`.

Parameters:
build()

Build.

Assemble the message bus instance

Returns:

a message bus instance

Raises:

ConfigurationError – when bus is incorrectly configured

Return type:

Bus

with_factory(factory)

With factory.

Set factory for the message bus.

Parameters:

factory (HandlerFactory) – handler factory instance

Returns:

builder instance with factory

Return type:

Builder

with_locator(locator)

With locator.

Set locator for the message bus.

Parameters:

locator (HandlerLocator) – handler locator instance

Returns:

builder instance with locator

Return type:

Builder

with_middleware(middleware)

With middleware.

Add a middleware to the message bus.

Parameters:

middleware (Middleware) – middleware instance

Returns:

builder instance with middleware added

Return type:

Builder

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

Bases: Protocol

Bus protocol.

An command/event/query bus for dispatching requests to handlers.

abstract async handle(request, contexts=None)

Handle.

Process a message.

Parameters:
  • request (T | Message[T]) – request instance

  • contexts (Iterable[Any] | None) – additional context objects

Return type:

Message[T]

async query(query, contexts=None)

Query.

Send a query to the message bus and return the result.

Parameters:
  • query (Any) – query instance

  • contexts (Iterable[Any] | None) – additional context objects

Returns:

the result of the query

Raises:

ConfigurationError – query does not map to exactly one handler

Return type:

Any

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

Bases: Protocol

Handler locator protocol.

Locates handlers that can process the messages request.

abstract subscribers_for(message)

Get subscribers for message.

Returns references to handlers for the passed messages request.

Parameters:

message (Message[T]) – message of request

Returns:

list of references to handlers for the message

Return type:

Iterable[HandlerReference[T]]

class banshee.HandlerReference(name, handler)

Bases: Generic[T]

Handler reference.

A reference to a type or callable, including a unique name for said type or callable, that when passed to a HandlerFactory will result in an instantiated Handler.

Parameters:
  • name (str) – unique handler name

  • handler (type | Callable[[...], Any]) – handler type or callable

class banshee.Registry

Bases: HandlerLocator

Registry.

Provides a means to register and lookup objects.

subscribe(handler, to, name=None)

Subscribe.

Add a subscription for a function or class to a request class.

When no name is provided, then the registry will try and infer a unique name based on the module and name of the decorated function or class.

Parameters:
  • handler (type | Callable[[...], Any]) – callable or type to act as subscriber

  • to (type) – type of request for subscription

  • name (str | None) – optional unique name for the handler

Return type:

None

subscribe_to(to, name=None)

Subscribe to.

A decorator to subscribe the decorated function or class to requests of the specified type.

When no name is provided, then the registry will try and infer a unique name based on the module and name of the decorated function or class.

Parameters:
  • to (type) – type of request for subscription

  • name (str | None) – optional unique name for the handler

Returns:

decorator function

Return type:

Callable[[H], H]

subscribers_for(message)

Get subscribers for message.

Returns references to handlers for the passed messages request.

Parameters:

message (Message[T]) – message of request

Returns:

list of references to handlers for the message

Return type:

Iterable[HandlerReference[T]]

exception banshee.ConfigurationError(message)

Configuration error.

There was an error in how banshee was configured.

exception banshee.MultipleErrors(message, exceptions)

Multiple errors.

Multiple errors were produced.