coma: Configurable command management for humans.
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
argparseboilerplate, 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.
hydralacks commands. Related functionality must be implemented as separate programs.comanaturally encourages component reuse. - Command-line arguments and flags.
hydrarequires that all program data be provided through configs, whereascomanatively supports command-line integration in addition to configs. - Parallel and independent configs. All
hydraconfigs must be hierarchical, whereascomaencourages 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.