diff --git a/burr/core/action.py b/burr/core/action.py index c92d3ce6d..0e7977df5 100644 --- a/burr/core/action.py +++ b/burr/core/action.py @@ -443,7 +443,7 @@ def bind(self, **kwargs: Any): def bind(self: FunctionRepresentingAction, **kwargs: Any) -> FunctionRepresentingAction: """Binds an action to the given parameters. This is functionally equivalent to functools.partial, but is more explicit and is meant to be used in the API. This only works with - the functional API for ``@action`` and not with the class-based API. + the :py:meth:`@action ` functional API and not with the class-based API. .. code-block:: python diff --git a/docs/concepts/actions.rst b/docs/concepts/actions.rst index aa6100a35..3f1e9a2d2 100644 --- a/docs/concepts/actions.rst +++ b/docs/concepts/actions.rst @@ -6,7 +6,7 @@ Actions Actions do the heavy-lifting in a workflow. They should contain all complex compute. You can define actions -either through a class-based or function-based API. If actions implement `async def run` then will be run in an +either through a class-based or function-based API. If actions implement ``async def run`` then will be run in an asynchronous context (and thus require one of the async application functions). Actions have two primary responsibilities: @@ -21,7 +21,7 @@ framework to optimize the execution of the workflow. We call (1) a ``Function`` .. _inputref: -------------- -Runtime inputs +Runtime Inputs -------------- Actions can declare inputs that are not part of the state. This is for the case that you want to pause workflow execution for human input. @@ -35,10 +35,10 @@ There are two APIs for defining actions: class-based and function-based. They ar - use the class-based API when you want to leverage inheritance or parameterize the action in more powerful ways ------------------- -Class-based actions +Class-Based Actions ------------------- -You can define an action by implementing the ``Action`` class: +You can define an action by implementing the :py:class:`Action ` class: .. code-block:: python @@ -59,7 +59,7 @@ You can define an action by implementing the ``Action`` class: def update(self, result: dict, state: State) -> State: return state.update(**result) -You then pass the action to the ``ApplicationBuilder``: +You then pass the action to the :py:class:`ApplicationBuilder `: .. code-block:: python @@ -70,7 +70,7 @@ You then pass the action to the ``ApplicationBuilder``: )... -Note that if the action has inputs, you have to define the optional `inputs` property: +Note that if the action has inputs, you have to define the optional ``inputs`` property: .. code-block:: python @@ -100,7 +100,7 @@ Note that if the action has inputs, you have to define the optional `inputs` pro Function-based actions ---------------------- -You can also define actions by decorating a function with the `@action` decorator: +You can also define actions by decorating a function with the :py:func:`@action ` decorator: .. code-block:: python @@ -115,7 +115,7 @@ You can also define actions by decorating a function with the `@action` decorato custom_action=custom_action )... -Function-based actions can take in parameters which are akin to passing in constructor parameters. This is done through the `bind` method: +Function-based actions can take in parameters which are akin to passing in constructor parameters. This is done through the :py:meth:`bind ` method: .. code-block:: python @@ -145,7 +145,7 @@ bound, they will be referred to as inputs. For example: Will require the inputs to be passed in at runtime. -Note that these combine the ``reduce`` and ``run`` methods into a single function, and they're both returned at the same time. +Note that these combine the ``update`` and ``run`` methods into a single function, and they're both executed at the same time. ----------- ``Inputs`` diff --git a/docs/concepts/hooks.rst b/docs/concepts/hooks.rst index a670eb2b9..940ef7697 100644 --- a/docs/concepts/hooks.rst +++ b/docs/concepts/hooks.rst @@ -19,7 +19,7 @@ To implement hooks, you subclass any number of the :ref:`available lifecycle hoo 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 `ApplicationBuilder` as a list of hooks. For instance, +To use them, you pass them into the :py:class:`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. @@ -40,7 +40,7 @@ We implement the pre/post run step hooks. ): print(f"Finishing action: {action.node.name}") -To include this in the application, you would pass it in as a list of hooks: +To include this in the application, you pass it into the :py:meth:`with_hooks ` method. .. code-block:: python diff --git a/docs/concepts/state-machine.rst b/docs/concepts/state-machine.rst index ca8360921..1afa033b1 100644 --- a/docs/concepts/state-machine.rst +++ b/docs/concepts/state-machine.rst @@ -5,12 +5,11 @@ Applications .. _applications: Applications form the core representation of the state machine. You build them with the ``ApplicationBuilder``. - -The ``ApplicationBuilder`` is a class that helps you build an application. Here is the minimum that is required: +Here is the minimum that is required: 1. A ``**kwargs`` of actions passed to ``with_actions(...)`` 2. Any relevant transitions (with conditions) -3. An entry point +3. An entry point -- this is the first action to execute This is shown in the example from :ref:`getting started ` @@ -122,7 +121,7 @@ Inspection You can ask various questions of the state machine using publicly-supported APIs: -- ``application.graph`` will give you a static reprsentation of the state machine with enough information to visualize -- ``application.state`` will give you the current state of the state machine. Note that if you modify it the results will not show up -- state is immutable! +- ``application.graph`` will give you a static representation of the state machine with enough information to visualize +- ``application.state`` will give you the current state of the state machine. Note that if you modify it the results will not show up -- state is immutable! Modify the state through actions. See the :ref:`application docs ` diff --git a/docs/concepts/state.rst b/docs/concepts/state.rst index 1490a59be..916b96b98 100644 --- a/docs/concepts/state.rst +++ b/docs/concepts/state.rst @@ -7,14 +7,16 @@ State The ``State`` class provides the ability to manipulate state for a given action. It is entirely immutable, meaning that you can only create new states from old ones, not modify them in place. -State manipulation is done through the ``State`` class. The most common write are: +State manipulation is done through calling methods on the ``State`` class. The most common write are: .. code-block:: python state.update(foo=bar) # update the state with the key "foo" set to "bar" state.append(foo=bar) # append "bar" to the list at "foo" + state.wipe(keep=["foo", "bar"]) # remove all keys except "foo" and "bar" + state.wipe(delete=["foo", "bar"]) # remove "foo" and "bar" from the state -The read operations extend from those in the [Mapping](https://docs.python.org/3/library/collections.abc.html#collections.abc.Mapping) +The read operations extend from those in the `Mapping `_ interface, but there are a few extra: .. code-block:: python diff --git a/docs/concepts/tracking.rst b/docs/concepts/tracking.rst index 0face0c60..17727b21b 100644 --- a/docs/concepts/tracking.rst +++ b/docs/concepts/tracking.rst @@ -11,7 +11,7 @@ both in development and production. Note this is a WIP. Tracking Client --------------- -When you use :py:meth:`burr.core.application.ApplicationBuilder.with_tracker`, you add a tracker to Burr. +When you use :py:meth:`with_tracker `, you add a tracker to Burr. This is a lifecycle hook that does the following: #. Logs the static representation of the state machine @@ -21,7 +21,7 @@ This is a lifecycle hook that does the following: - The state at time of execution - The timestamps -This currently defaults to (and only supports) the :py:class:`burr.tracking.LocalTrackingClient` class, which +This currently defaults to (and only supports) the :py:class:`LocalTrackingClient ` class, which writes to a local file system, althoguh we will be making it pluggable in the future. This will be used with the UI, which can serve out of the specified directory. More coming soon! diff --git a/docs/concepts/transitions.rst b/docs/concepts/transitions.rst index a207d78f2..4f304729c 100644 --- a/docs/concepts/transitions.rst +++ b/docs/concepts/transitions.rst @@ -29,8 +29,12 @@ Conditions have a few APIs, but the most common are the three convenience functi Conditions are evaluated in the order they are specified, and the first one that evaluates to True will be the transition that is selected -when determining which action to run next. +when determining which action to run next. If no condition evaluates to ``True``, the application execution will stop early. -Note that if no condition evaluates to ``True``, the application execution will stop early. +.. note:: + + The ``default`` condition is a special case, and will always evaluate to ``True``. It is useful for defining a "catch-all" transition + that will be selected if no other condition is met. If you pass a tuple of length 2 to :py:meth:`with_transitions `, the + default condition will be used. See the :ref:`transition docs ` for more information on the transition API.