Usage¶
Convert dataclasses into a command-line interface.
Quickstart¶
To install, use
pip install clout
Define some dataclasses and convert them into a command-line interface.
import attr
import click
import clout
@attr.dataclass
class DB:
host: str
port: int
@attr.dataclass
class Config:
db: DB
dry_run: bool
cli = clout.Command(Config)
print(cli.build())
$ myapp --dry-run db --host example.com --port 9999
Config(db=DB(host='example.com', port=9999), dry_run=True)
A decorator¶
To run a function with the built object, you can use a decorator, clout.command()
.
Using clout.Command.main()
, the program will exit immediately afterwards.
import attr
import clout
@attr.dataclass
class Person:
name: str = attr.ib(metadata={"clout": {"cli": dict(envvar="EXAMPLE_NAME")}})
age: int
@clout.command(Person)
def greet(person):
print(f"Hello, {person.name}!")
if __name__ == "__main__":
print(greet.main())
$ EXAMPLE_NAME=Alice python examples/decorator.py --age 21
Hello, Alice!
A callback¶
If you don’t want to use a decorator, you can use clout.Command()
directly, and
pass the callback=
argument.
import attr
import click
import clout
@attr.dataclass
class Person:
name: str
age: int
def greet(person):
print(f"Hello, {person.name}!")
cli = clout.Command(Person, callback=greet)
if __name__ == "__main__":
cli.main()
Extended example¶
Now for a longer example.
- Define a nested configuration object.
- Load the object from the CLI, getting missing values from environment variables,
configuration file, and dataclass
default=
values.
Here we define the config file in the Freedesktop standard directory.
# ~/.config/myapp/config.toml
[config]
dry_run=true
Set an environment variable and run the app.
% MYAPP_PRIORITY=2 python examples/long.py --debug user --name Alice db --host example.com --port 9999 user --name Bob
Config(db=DB(host='example.com', port=9999, user=User(name='Bob')), debug=True, dry_run=True, user=User(name='Alice'), priority=2.0, logging=True)
The code:
import pathlib
import appdirs
import attr
import click
import toml
import clout
@attr.dataclass
class User:
name: str
@attr.dataclass
class DB:
host: str
port: int
user: User
@attr.dataclass
class Config:
db: DB
debug: bool
dry_run: bool
user: User
priority: float = attr.ib(
default=0,
metadata={
"clout": {
"cli": dict(param_decls=["--priority"], help="App priority value")
}
},
)
logging: bool = True
APP_NAME = "myapp"
# Read config file.
with open("examples/config.toml") as f:
CONFIG_FILE_DATA = toml.load(f)
# Read from environment_variables prefixed `MYAPP_`,
# such as MYAPP_PRIORITY=10.
ENVVAR_DATA = clout.load_env(Config, prefix=APP_NAME)
# Combine config file and envvars to set CLI defaults.
CONTEXT_SETTINGS = dict(default_map=clout.DeepChainMap(ENVVAR_DATA, CONFIG_FILE_DATA))
# Define the CLI.
commands = [
clout.Command(
name="config",
type=Config,
context_settings=CONTEXT_SETTINGS,
help="Run the app with given configuration object.",
)
]
cli = click.Group(commands={c.name: c for c in commands})
if __name__ == "__main__":
# Run the CLI.
print(cli.main(standalone_mode=False))