2 minute read

Repo   Documentation

Introduction

coma makes it easy to build configurable command-based programs in Python.

Commands (also known as sub-commands) are the commit and pull part of git commit and git pull (whereas git is the program). Commands can be seen as command-line meta-arguments: They drastically affect not only the behavior of the program, but also what additional command-line line arguments and flags are accepted.

coma goes one step further. Each command implicitly determines which configuration files are loaded, enabling command-specific configs that don’t affect the whole program.

Why coma?

coma has three key features that enable rapid prototyping while maintaining rich flexibility:

  • Rich configuration awareness. Configurations leverage omegaconf’s extremely rich and powerful configuration management features.
  • Abstracted boilerplate. Command selection abstracts away the argparse boilerplate, while retaining full interoperability for complex use cases.
  • Plug-and-play extensibility. Hooks enable plug-and-play flexibility to tweak, replace, or extend coma’s default behavior.

Why choose coma over another solution, like hydra? hydra, specifically, has the following limitations (all of which are features that coma supports!):

  • Commands. hydra lacks commands. Related functionality must be implemented as separate programs. coma naturally encourages component reuse.
  • Command-line arguments and flags. hydra requires that all program data be provided through configs, whereas coma natively supports command-line integration in addition to configs.
  • Parallel and independent configs. All hydra configs must be hierarchical, whereas coma encourages greater flexibility with its config structure.

Installation

pip install coma

Testimonial

I developed coma to rapidly prototype and iterate my deep learning research projects. I have been using it for every project since. I use coma to split my workflow into discrete steps, such as preprocess, train, and eval. Each step maps cleanly onto a coma command, which only loads configs necessary to that step. This separation of concerns enables me to focus of producing impactful research, instead of getting bogged down in the minutiae of implementation details.

For example, my project codebase might have the following structure:

src/
|-- my_proj/
|   |-- preprocess.py
|   |-- train.py
|   `-- eval.py
`-- main.py

Inside each file in my_proj, I’ll define a command and a config for that step in my workflow. For example, train.py might have:

from dataclasses import dataclass

@dataclass
class TrainConfig:
    # Define a feature rich config that is fully omegaconf-compatible.
    lr: float = 1e-05  # For example, a default learning rate for training.

def train(cfg: TrainConfig):
    ... # Use `cfg' in your training code (e.g., setting the learning rate).

In main.py, only a single line of code is required to enable each command to access all the rich functionality of coma:

from my_proj import preprocess, PreprocessConfig, train, TrainConfig, eval, EvalConfig
import coma

if __name__ == "__main__":
    coma.register("preprocess", preprocess, PreprocessConfig)
    coma.register("train", train, TrainConfig)
    coma.register("eval", eval, EvalConfig)
    coma.wake()

For example, we can change the learning rate to 1e-06 with a simple command-line override:

python main.py train lr=1e-06

No need for argparse boilerplate! coma handles everything in the background.

Getting Started

Excited to learn more? Jump straight into the short introductory tutorial.

Updated: