Event Bus & Hooks API
Loose coupling through events and lightweight hooks.
Event Bus
class whoosh.event_bus.EventBus
Publish/subscribe event system supporting both synchronous and asynchronous
listeners. The module-level singleton event_bus is available for use:
from whoosh.event_bus import event_bus
Methods
subscribe()
@event_bus.subscribe(DocumentIndexed)
async def handler(event: DocumentIndexed):
print(f"Indexed document: {event.document_id}")
# Or without decorator
event_bus.subscribe(DocumentIndexed)(handler)
Register a handler function (synchronous or async) for a specific event type. The decorator takes the event class as an argument. Returns the handler unchanged so it can be used normally.
publish()
from whoosh.event_bus import event_bus, DocumentIndexed
event_bus.publish(DocumentIndexed(document_id="doc123"))
Publish an event to all subscribers. If listeners are async coroutines and no
event loop is running, they are executed via asyncio.run(). If an event
loop is running, tasks are scheduled on it. Exceptions in listeners are
swallowed.
clear()
event_bus.clear()
Remove all subscribers.
Events
Events are immutable dataclasses.
Event
@dataclass(frozen=True)
class Event:
pass
Base class for all events.
DocumentIndexed
@dataclass(frozen=True)
class DocumentIndexed(Event):
document_id: str
Published when a document is indexed. Contains the document ID.
SearchExecuted
@dataclass(frozen=True)
class SearchExecuted(Event):
query: str
Published after a search is executed. Contains the query string.
Hooks
Hook system for cross-cutting concerns. Hooks are registered globally using a module-level registry.
hookimpl
from whoosh.hooks import hookimpl
@hookimpl
def before_search(context):
context.query = optimize_query(context.query)
return context
Decorator that marks a function as a hook implementation. Returns a HookImpl
wrapper.
register_hook()
from whoosh.hooks import register_hook, hookimpl
@hookimpl
def before_search(context):
...
register_hook("before_search", before_search)
Register a HookImpl under a named hook. Multiple hooks can be registered
per name; they are called in registration order.
call_hook()
from whoosh.hooks import call_hook
results = await call_hook("before_search", context)
Async function that calls all hooks registered under the given name. Returns a list of results from each hook's execution. Exceptions in individual hooks are logged but do not stop execution.
Example: Event Bus
from whoosh.event_bus import event_bus, DocumentIndexed, SearchExecuted
@event_bus.subscribe(DocumentIndexed)
async def on_document_indexed(event: DocumentIndexed):
print(f"Document indexed: {event.document_id}")
@event_bus.subscribe(SearchExecuted)
async def on_search_executed(event: SearchExecuted):
print(f"Search executed: {event.query}")
# Publish events
event_bus.publish(DocumentIndexed(document_id="doc123"))
event_bus.publish(SearchExecuted(query="hello world"))
Example: Hooks
from whoosh.hooks import hookimpl, register_hook, call_hook
import asyncio
@hookimpl
def before_search(context):
print(f"Searching for: {context['query']}")
return context
register_hook("before_search", before_search)
# Call hooks
context = {"query": "hello"}
results = asyncio.run(call_hook("before_search", context))