Skip to content

Commit

Permalink
docs: application protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
hartym committed Jul 25, 2024
1 parent 2b2f3d9 commit 9523491
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 14 deletions.
148 changes: 147 additions & 1 deletion docs/contribute/applications/application-protocol.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,150 @@
Application Protocol
====================

.. todo:: document this
The HARP Application Protocol enables writing plug-and-play Python packages to enhance core functionalities.

An application is essentially a Python package with additional files that integrate it with the HARP framework.

Basic Structure
:::::::::::::::

A standard Python package has a directory containing an ``__init__.py`` file. To transform this package into a HARP
application, you need to add an ``__app__.py`` file at the root. This file contains the application's definition.

Example ``__app__.py``:

.. code-block:: python
from harp.config import Application
application = Application()
This setup is the bare minimum. However, applications usually require more features, such as settings.


Configuring Settings
::::::::::::::::::::

Applications often need custom settings. You can define these settings in a class, typically stored in a ``settings.py``
file at the package root.

1. Define your settings class in settings.py.
2. Include this class in your application definition in ``__app__.py``.

Example ``settings.py``:

.. literalinclude:: ../../../harp_apps/acme/settings.py

Example ``__app__.py`` update:

.. code-block:: python
from harp.config import Application
from .settings import AcmeSettings
application = Application(
settings_type=AcmeSettings,
)
The settings class should:

- Be instantiable without arguments for default settings.
- Accept keyword arguments for custom settings.
- Convert to a dictionary via :func:`harp.config.asdict`.

Let's write a simple test to check that.

.. literalinclude:: ../../../harp_apps/acme/tests/test_settings.py


Application Lifecycle
:::::::::::::::::::::

To have a real purpose, an application should interact with the core system through lifecycle hooks.

All hooks are python coroutines, taking a specific :class:`whistle.Event` instance as argument.

Hooks must be registered in the application definition.


On Bind
-------

Triggered during system setup but before service instances are created. Ideal for defining services and dependencies.

.. code-block:: python
from harp.config import OnBindEvent
async def on_bind(event: OnBindEvent):
...
Reference: :class:`harp.config.OnBindEvent`


On Bound
--------

Occurs when the system can instantiate services. Use this to access and manipulate service instances.

.. code-block:: python
from harp.config import OnBoundEvent
async def on_bound(event: OnBoundEvent):
...
Reference: :class:`harp.config.OnBoundEvent`


On Ready
--------

Called when the system starts, after all services are ready. A good place to add ASGI middlewares.

.. code-block:: python
from harp.config import OnReadyEvent
async def on_ready(event: OnReadyEvent):
...
Reference: :class:`harp.config.OnReadyEvent`


On Shutdown
-----------

Invoked during system shutdown, allowing for cleanup and resource release.

Unlike other events, the shutdown events will be dispatched in applications **reverse** order, so that the first
initialized application is the last to be shutdown.

.. code-block:: python
from harp.config import OnShutdownEvent
async def on_shutdown(event: OnShutdownEvent):
...
Reference: :class:`harp.config.OnShutdownEvent`


Full Example
::::::::::::

``__app__.py``
--------------

.. literalinclude:: ../../../harp_apps/acme/__app__.py

``settings.py``
---------------

.. literalinclude:: ../../../harp_apps/acme/settings.py

``tests/test_settings.py``
--------------------------

.. literalinclude:: ../../../harp_apps/acme/tests/test_settings.py
1 change: 1 addition & 0 deletions docs/contribute/changelogs/unreleased.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ Changed
bootstrapping (internal) API has changed and there is no more "kernel factory" or complex "Config" object. Instead,
we have a ConfigurationBuilder and a SystemBuilder that will expose a simple and understandable output api.
* Storage: removed unused storage "type" where the only valid value in the foreseeable future was "sqlalchemy".
* Applications: simpler and cleaner interface for defining applications and their configuration.
9 changes: 0 additions & 9 deletions docs/reference/apps/harp_apps.storage.lifecycle.rst

This file was deleted.

1 change: 0 additions & 1 deletion docs/reference/apps/harp_apps.storage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ Submodules
harp_apps.storage.conftest
harp_apps.storage.constants
harp_apps.storage.factories
harp_apps.storage.lifecycle
harp_apps.storage.models
harp_apps.storage.optionals
harp_apps.storage.services
Expand Down
6 changes: 3 additions & 3 deletions harp/config/builders/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@ async def abuild(self) -> System:
provider = container.build_provider()
await self.dispatch_bound_event(dispatcher, provider, controller_resolver)

# Build the kernel and dispatch «build» event.
event = await self.dispatch_build_event(dispatcher, provider, controller_resolver)
# Build the kernel and dispatch «ready» event.
event = await self.dispatch_ready_event(dispatcher, provider, controller_resolver)

# Send back a coherent view of the system.
return System(config, dispatcher, provider, kernel=event.kernel, binds=event.binds)
Expand Down Expand Up @@ -206,7 +206,7 @@ async def dispatch_bound_event(
logger.fatal("💣 Fatal while dispatching «%s» event: %s", EVENT_BOUND, exc)
raise

async def dispatch_build_event(
async def dispatch_ready_event(
self,
dispatcher: IAsyncEventDispatcher,
provider: Services,
Expand Down
File renamed without changes.
13 changes: 13 additions & 0 deletions harp_apps/acme/tests/test_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from harp.config import asdict

from ..settings import AcmeSettings


def test_default_settings():
settings = AcmeSettings()
assert asdict(settings) == {"owner": "Joe"}


def test_custom_settings():
settings = AcmeSettings(owner="Alice")
assert asdict(settings) == {"owner": "Alice"}

0 comments on commit 9523491

Please sign in to comment.