Applications

HARP applications are Python packages that integrate with the system through a standard protocol. Both core features and third-party extensions use the same protocol.

An application is a Python package with an __app__.py file that defines how it hooks into the system.

Basic Structure

To create a HARP application, add an __app__.py file to your Python package:

from harp.config import Application

application = Application()

Applications usually also need settings.

Configuring Settings

Define your settings in a settings.py file:

from harp.config import ApplicationSettingsMixin, Configurable


class AcmeSettings(ApplicationSettingsMixin, Configurable):
    owner: str = "Joe"

Added in version 0.10: All application settings must inherit from ApplicationSettingsMixin.

Then reference them in __app__.py:

from harp.config import Application
from .settings import AcmeSettings


application = Application(
    settings_type=AcmeSettings,
)

Test your settings:

from harp.config.asdict import asdict

from ..settings import AcmeSettings


def test_default_settings():
    settings = AcmeSettings()
    assert asdict(settings) == {}
    assert asdict(settings, verbose=True) == {"enabled": True, "owner": "Joe"}


def test_custom_settings():
    settings = AcmeSettings(owner="Alice")
    assert asdict(settings) == {"owner": "Alice"}
    assert asdict(settings, verbose=True) == {"enabled": True, "owner": "Alice"}

Important

ApplicationSettingsMixin must be the first base class in the inheritance chain.

Declaring Dependencies

Added in version 0.10: Applications can declare dependencies to ensure correct initialization order.

Applications declare dependencies as a list. At startup, the system validates all dependencies exist, detects cycles, and initializes applications in topological order.

from harp.config import Application
from .settings import ProxySettings

application = Application(
    settings_type=ProxySettings,
    dependencies=["http_client"],  # Simple list of app names
)

Best practices:

  • Declare only direct dependencies (transitive dependencies are resolved automatically)

  • Use simple names (storage, not harp_apps.storage)

  • Applications without dependencies work unchanged

Error examples:

# Missing dependency
MissingDependencyError: Application 'dashboard' requires 'storage' but it is not enabled

# Circular dependency
CircularDependencyError: Circular dependency detected: a → b → c → a

Testing with partial systems:

Tests building incomplete systems can bypass validation with validate_dependencies=False:

system = await ConfigurationBuilder(
    {"applications": ["http_client", "storage"]},
    use_default_applications=False,
).abuild_system(validate_dependencies=False)  # Skip validation for tests

Warning

Only use validate_dependencies=False in tests. Production code should always validate (default behavior).

Application Lifecycle

Applications interact with the system through lifecycle hooks. All hooks are async functions that receive an event object.

On Bind

Called during system setup. Register services and dependencies here.

from harp.config import Application, OnBindEvent

async def on_bind(event: OnBindEvent):
    ...

application = Application(
    ...,
    on_bind=on_bind,
)

Reference: harp.config.OnBindEvent

On Bound

Called after services are registered. Access and configure service instances here.

from harp.config import Application, OnBoundEvent

async def on_bound(event: OnBoundEvent):
    ...

application = Application(
    ...,
    on_bound=on_bound,
)

Reference: harp.config.OnBoundEvent

On Ready

Called when the system starts. All services are ready.

from harp.config import Application, OnReadyEvent

async def on_ready(event: OnReadyEvent):
    ...

application = Application(
    ...,
    on_ready=on_ready,
)

Reference: harp.config.OnReadyEvent

On Shutdown

Called during system shutdown. Clean up resources here.

Shutdown events are dispatched in reverse order - the first application initialized is the last to shut down.

from harp.config import Application, OnShutdownEvent

async def on_shutdown(event: OnShutdownEvent):
    ...

application = Application(
    ...,
    on_shutdown=on_shutdown,
)

Reference: harp.config.OnShutdownEvent