blob: dbd62f12fa2fd6ed25aae3858565b85f713d676f [file]
..
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
=====
Hooks
=====
.. _hooks:
.. note::
Hooks allow you to customize every aspect of Burr's execution, plugging in whatever tooling,
observability framework, or debugging tool you need.
Burr has a system of lifecycle adapters (adapted from the similar `Hamilton <https://github.com/apache/hamilton>`_ concept), which allow you to run tooling before and after
various places in a node's execution. For instance, you could:
1. Log every step as a trace in datadog
2. Add a time-delay to your steps to allow for rendering
3. Add a print statement to every step to see what happened (E.G. implement the printline in cowsay above)
4. Synchronize state/updates to an external database
5. Put results on a queue to feed to some monitoring system
Note some of the above are yet to be implemented.
To implement hooks, you subclass any number of the :ref:`available lifecycle hooks <hooksref>`.
These have synchronous and asynchronous versions, and your hook can subclass as many as you want
(as long as it doesn't do both the synchronous and asynchronous versions of the same hook).
To use them, you pass them into the :py:class:`ApplicationBuilder <burr.core.application.ApplicationBuilder>` as a ``*args`` list of hooks. For instance,
a hook that prints out the nodes name during execution looks like this.
We implement the pre/post run step hooks.
.. code-block:: python
class PrintLnHook(PostRunStepHook, PreRunStepHook):
def pre_run_step(self, *, state: "State", action: "Action", **future_kwargs: Any):
print(f"Starting action: {action.node.name}")
def post_run_step(
self,
*,
state: "State",
action: "Action",
result: Optional[dict],
sequence_id: int,
exception: Exception,
**future_kwargs: Any,
):
print(f"Finishing action: {action.node.name}")
To include this in the application, you pass it into the :py:meth:`with_hooks <burr.core.application.ApplicationBuilder.with_hooks>` method.
.. code-block:: python
app = (
ApplicationBuilder()
.with_hooks(PrintLnHook())
...
.build())
.. note::
There are synchronous and asynchronous hooks. Synchronous hooks will be called with both synchronous and asynchronous run methods
(all of ``step``, ``astep``, ``iterate``, ``aiterate``, ``run``, and ``arun``), whereas synchronous hooks will only be called with
the asynchronous methods (``astep``, ``aiterate``, ``arun``).
.. warning::
Hook order is currently undefined -- they happen to be called now in the order in which they are defined. We will likely
alter them to be called in the order they are defined (for `pre...`) hooks and in reverse order for `post...` hooks,
but this is not yet implemented.
Read more about the hook API in the :ref:`hooks section <hooksref>`.