Skip to content

duho

duho is a declarative CLI framework for Python. You define a class, annotate its fields, and duho builds the argparse parser — type conversion, help text, subcommands, config layering, and shell completion included.

Named after the sacred Taíno ceremonial stool — a symbol of power and authority — duho is the foundation from which you command your application.

It has zero required runtime dependencies. Colored logging works without one; colorama is an optional extra that resolves named colors and patches a legacy Windows console, TOML config on Python 3.9/3.10 (tomli) and TOML writing are the second, and YAML config files are the third. Each is imported only when used.

Why duho

  • Declarative. The class is the CLI. A field's annotation gives its type, its docstring gives the help text, and a tuple literal gives its flags.
  • Type-driven. int, Literal, Enum, list[T], Optional, Union — the annotation decides how the CLI text is parsed and validated.
  • Not a walled garden. duho builds a real argparse.ArgumentParser. Any add_argument keyword is reachable via Arg[T, Meta(...)], and you can take the parser and do whatever you want with it.
  • Layered defaults. CLI args override environment variables, which override a config file, which overrides class defaults (JSON, TOML, YAML or INI) — with a helper that tells you which layer won.
  • Batteries included. Verbosity flags, --version, and static shell completion are one attribute each.

Install

pip install duho

Optional extras:

pip install duho[colorama]   # named log colors; legacy Windows consoles
pip install duho[config]     # TOML config files on Python 3.9/3.10; writing TOML
pip install duho[yaml]       # YAML config files

A first CLI

import duho
from duho import Cmd

class Greet(Cmd):
    """Print a greeting."""

    name: str = "world"
    "Who to greet"
    ("--name", "-n")

    count: int = 1
    "How many times"
    ("--count", "-c")

    def __call__(self):
        for _ in range(self.count):
            print(f"Hello, {self.name}!")

if __name__ == "__main__":
    raise SystemExit(duho.main(Greet))
$ python greet.py --name Alice -c 2
Hello, Alice!
Hello, Alice!

$ python greet.py --help
usage: greet [-h] [--name NAME] [--count COUNT]

Print a greeting.

options:
  -h, --help         show this help message and exit
  --name, -n NAME    Who to greet
  --count, -c COUNT  How many times

The --help text above is from Python 3.14; argparse words and lays out its help a little differently on older versions (optional arguments: on 3.9, and each flag repeats its metavar). A script run directly is named after its class, Greet → greet.

Where to next