Skip to main content

No project description provided

Project description

Dora Utils

This repository contains some simple utilities for using dora that improve QoL.

For simple usage examples, see dora_utils.examples.

The DoraNode class

The usual workflow in dora is to define a Node, which acts as an iterator over events. In this loop, you can then manually handle the events as they arrive in the node. We provide a convenient DoraNode convenience class to register callbacks on events in a more ROS-style fashion. Consider the talker.py and listener.py examples:

# talker.py
import pyarrow as pa

from dora_utils.node import DoraNode, on_event


class Talker(DoraNode):
    """Simple publisher node for a Dora tutorial."""

    @on_event("INPUT", "tick")
    def talk(self, event: dict) -> None:
        self.node.send_output("speech", pa.array(["Hello World"]))
# listener.py
from dora_utils.node import DoraNode, on_event


class Listener(DoraNode):
    """Simple publisher node for a Dora tutorial."""

    def __init__(
        self, node_id: str = "listener", max_workers: int | None = None, override_msg: str | None = None
    ) -> None:
        """Initialize the listener."""
        super().__init__(node_id=node_id, max_workers=max_workers)
        self.override_msg = override_msg

    @on_event("INPUT", "speech")
    def listen(self, event: dict) -> None:
        if self.override_msg is not None:
            message = self.override_msg
        else:
            message = event["value"][0].as_py()
        print(f"""I heard {message} from {event["id"]}""")

The on_event decorator provides a simple interface for registering callbacks to inputs specified into the node using dora's dataflow.yml. You can register the same callback to multiple inputs by instead passing a sequence of input names into on_event.

Hydra Configuration

One sharp edge regarding dora is node configuration. The typical workflow is writing a script-style program where you can manually configure nodes and spin them. This is a bit cumbersome once the system scales up to many nodes with many arguments that you might want to configure in a more organized way. We provide an example design pattern that combines the dataflow.yml configuration system from dora with hydra.

For an example, consider scripts/chatter.yaml, which both defines a simple "chatter" workflow between a talker and listener node.

# this section of the configuration specifies the dora dataflow
dataflow:
  nodes:
    - id: talker
      path: dynamic
      inputs:
        tick: dora/timer/millis/10
      outputs:
        - speech
    - id: listener
      path: dynamic
      inputs:
        speech: talker/speech

# everything else is used to instantiate and spin the nodes with hydra
node_definitions:
  talker:  # <--- this name doesn't matter
    _target_: dora_utils.examples.chatter.talker.Talker
    node_id: talker
    max_workers: null
  listener:  # <--- this name doesn't matter
    _target_: dora_utils.examples.chatter.listener.Listener
    node_id: listener
    max_workers: null
    override_msg: null

Everything under the dataflow section is the "standard" dora dataflow, which defines the nodes, their IDs, their inputs/outputs, etc. Under node_definitions is a list of hydra targets, so you can instantiate arbitrarily complicated nodes using hydra's composition API. Above, you'll see that there are comments saying "this name doesn't matter." We use this yaml syntax because we want to be able to use hydra's configuration override syntax to preserve default values. Because lists are atomic in hydra, we can't override single list elements, so in any file that specifies another configuration file as a default, we would have to copy and paste the entire node_definitions section. When we instead structure things like a dictionary, overrides work!

If we run

python scripts/run.py -cn chatter

we will start the chatter example, and we should see the following printed a lot:

I heard Hello World from speech
I heard Hello World from speech
I heard Hello World from speech

Now, if we modify chatter.yaml to instead show

node_definitions:
  talker:
    _target_: dora_utils.examples.chatter.talker.Talker
    node_id: talker
    max_workers: null
  listener:
    _target_: dora_utils.examples.chatter.listener.Listener
    node_id: listener
    max_workers: null
    override_msg: "override"  # change this field for this example!

and run the same command, we will see

I heard override from speech
I heard override from speech
I heard override from speech

When you import the dora_utils package to your own project, you can use the hydra configuration design pattern as in our scripts/run.py file:

from pathlib import Path

import hydra
from omegaconf import DictConfig

from dora_utils.launch.run import run

CONFIG_PATH = Path(__file__).parent / "configs"


@hydra.main(config_path=str(CONFIG_PATH), config_name="default", version_base="1.3")
def main(cfg: DictConfig) -> None:
    """Main function to run a dora stack via a hydra configuration yaml file."""
    run(cfg)


if __name__ == "__main__":
    main()

The main variables to change in your downstream project are the configuration path and configuration name. Note that these can easily be overwritten from the command line, e.g., as follows:

python scripts/run.py -cp <my_config_path> -cn <my_config_name>

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dora_utils-0.0.1-py3-none-any.whl (9.8 kB view details)

Uploaded Python 3

File details

Details for the file dora_utils-0.0.1-py3-none-any.whl.

File metadata

  • Download URL: dora_utils-0.0.1-py3-none-any.whl
  • Upload date:
  • Size: 9.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for dora_utils-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 6d36319f046d810a57a6a0c8e89007957b15cfc865aabddf7dbdc1694b5eb214
MD5 e7541503176469ebaee328ad849aa0fa
BLAKE2b-256 6749192a7d76d86fa99bbdf5e7ee111942c646f194f7f2865e42a061ebf54af3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page