Getting Started with Clak
This guide will walk you through the basics of building command-line applications using Clak. We'll start with a minimal example and gradually explore more advanced features.
Base Concepts
Clak is a Python library that simplifies the creation of command-line applications by providing an elegant, class-based interface on top of Python's argparse. The main components are:
Parser: The base class for your applicationArgument: Class to define command-line argumentsCommand: (Not shown in basic examples) Used for subcommands
Minimal Example
Let's start with a minimal example called script1.py, that demonstrates the core concepts:
- Import
ParserandArgumentfromclak. - Root parser class — must inherit from
Parser. - Class docstring becomes the application help description.
- Optional argument (
--/-); same kwargs asargparse.add_argument(). - Positional argument (name without
-/--). cli_run()runs when the command is invoked; parameters match destinations.cli_exit(1)sets a non-zero exit code (see Parser API).- Instantiating the root parses argv and runs the command (auto-
dispatch()).
From this example, run it with the Python interpreter. Clak adds -h / --help automatically:
$ python script1.py --help
usage: script1.py [-h] [--config CONFIG] [NAME]
Demo application with two arguments.
positional arguments:
NAME First Name (default: None)
options:
-h, --help show this help message and exit
--config CONFIG, -c CONFIG Config file path (default: config.yaml)
Thus, we can try to call it:
$ python script1.py
No name provided, please provide a name as first argument.
$ python script1.py John
Store name 'John' in config file: config.yaml
$ python script1.py John Doe
usage: script1.py [-h] [--config CONFIG] [NAME]
script1.py: error: unrecognized arguments: Doe
It is also possible to run directly if you make your script executable:
$ chmod +x script1.py
# You can directly run it
$ ./script1.py --help
$ ./script1.py
$ ./script1.py John
$ ./script1.py John Doe
This example shows:
- Creating a basic application class that inherits from Parser
- Defining two arguments:
- An optional
--configargument with a default value - A positional
NAMEargument that's optional (nargs="?")
- An optional
- Implementing the
cli_runmethod that receives the parsed arguments
Advanced Arguments Example
Let's update our previous example with more fields:
-
Import
ParserandArgumentclasses from clak -
Boolean Flags (
force):- Uses
action="store_true"to create a flag - No value needed, just presence/absence
- Use both long and short form
- Uses
-
String Options (
config):- Basic string argument with default value
- Can be specified with
-c - Use short form only
-
Choice Options (
color):- Restricts input to predefined choices
- Provides validation out of the box
- Can be specified with
--color - Use long form only
-
List Arguments (
items):- Uses
action="append"to collect multiple values - Can be specified multiple times:
-m item1 -m item2
- Uses
-
Positional Arguments:
- Required (
name): Must be provided
- Required (
-
Optional Positional Arguments (
surname):- Optional with default (
surname): Usesnargs="?"anddefault
- Optional with default (
-
Variable number of arguments (
aliases):- Variable number (
aliases): Usesnargs="*"for zero or more
- Variable number (
-
Run method (
cli_run):- This method is executed when the command is run.
- It receives the parsed arguments as keyword arguments.
- You can directly use them in your code.
-
Instantiate App (
AppMain):- Instantiating the root starts parsing and runs the matched command
When executed with -h or --help:
$ python script2.py -h
usage: script2.py [-h] [--force] [-c CONFIG] [--color {red,green,blue,unknown}] [--items ITEMS]
NAME [SURNAME] [ALIAS ...]
Demo application with many arguments.
positional arguments:
NAME First Name
SURNAME Last Name (default: Doe)
ALIAS Aliases (default: ['Bond', 'agent 007'])
options:
-h, --help show this help message and exit
--force, -f Force mode (default: False)
-c CONFIG Config file path (default: config.yaml)
--color {red,green,blue,unknown}
Favorite color (default: unknown)
--items ITEMS, -m ITEMS Preferred items (default: None)
Example Usage
# Basic usage
python script2.py John Smith
# With options
python script2.py --force --config custom.yaml John Smith
# With color choice
python script2.py --color red John Smith
# With multiple items
python script2.py --items apple --items banana John Smith
# With aliases
python script2.py John Smith nickname1 nickname2 nickname3
# Full example
python script2.py --force --config custom.yaml --color blue \
--items apple --items banana \
John Smith nickname1 nickname2
Optional Arg and Opt helpers
Argument is the canonical descriptor and still accepts both flags and
positionals. Opt and Arg are optional sugar if you want that
difference checked when the class is defined:
Opt(...): option flags only (-/--); emptyOpt()derives--attrfrom the field nameArg(...): positional names only (no leading-); at least one name is required
Mixing names raises ValueError (Arg("--flag"), Opt("NAME"), or both
kinds in one call). Same kwargs as Argument / add_argument(). You never
have to use them.
The previous example, rewritten:
from clak import Arg, Opt, Parser
class AppMain(Parser):
"""Demo application with many arguments."""
force = Opt("--force", "-f", action="store_true", help="Force mode")
config = Opt("-c", help="Config file path", default="config.yaml")
color = Opt(
"--color",
choices=["red", "green", "blue", "unknown"],
default="unknown",
help="Favorite color",
)
items = Opt("--items", "-m", action="append", help="Preferred items")
name = Arg("NAME", help="First Name")
surname = Arg("SURNAME", nargs="?", default="Doe", help="Last Name")
aliases = Arg(
"ALIAS", nargs="*", default=["Bond", "agent 007"], help="Aliases"
)
def cli_run(self, name=None, surname=None, **_):
print(f"Identity: {name} {surname}")
Next Steps
Next: Nested command-line applications — subcommands and
multi-level trees like git or docker.