This is a package to define click command line options from a python dataclass
.
You can define a python dataclass
from dataclasses
with fields that have
proper types when assigned by click parsing library. Then each field is
initialized with a option, argument, command or group from clickdc
library.
from dataclasses import dataclass
import clickdc
import click
@dataclass
class Args:
option: bool = clickdc.option(is_flag=True, help="This is an option")
command: int = clickdc.argument(type=int)
When the dataclass
is decorated with clickdc.adddc
to a function, this
library collects all options and arguments from the dataclass
definition and
decorates the function. Then upon calling the function, this library
reconstructs the object using only arguments with names equal to the fields in
the dataclass
, removing the arguments in the process.
@click.command(help="This is a command")
@clickdc.adddc("args", Args)
def cli(args: Args):
print(args)
print(clickdc.to_args(args)) # converts dataclass back to string '--option command'
If a keyword argument clickdc
is missing, the field name is added with
an underscore replaced by dashes with two front dashes in front, and the
argument is added as a string as positional argument to click.argument
call. If the argument already exists, it is not added twice, for ease
of porting.
long_option: Optional[str] = clickdc.option("-o", help="--long-option is added automatically")
args: str = clickdc.option(help="Positional argument 'args' is added automatically")
Additionally, some keyword arguments to the underlying click.option
or
click.argument
are inferred from the dataclass
field type depending on
on the following conditions. Use field type Any
or add type=
, for example
type=str
, to ad-hoc disable the mechanisms.
- If the field:
- is initialized using an
option
orargument
, - does not have the type
Any
, - does not have any keyword argument
type required is_flag default nargs count flag_value
- and does not have an argument
clickdc
passed withNone
- is initialized using an
- Then:
- if the field is an option, then if the field type
- is
bool
, addis_flag=True
- is
Optional[T]
, addtype=T
- is
Tuple[T, ...]
, add type=T, multiple=True, - is any other type
T
, addtype=T, required=True
- is
- if the field is an argument, then if the field type:
- is
Tuple[T, ...]
, addtype=T, nargs=-1
- is any other type
T
, addtype=T
- is
- if the field is an option, then if the field type
The correct type for multiple arguments returned by click
is Tuple[T, ...]
.
custom: Tuple[float, ...] = clickdc.option(type=float, multiple=True)
moreargs: Tuple[int, ...] = clickdc.argument(type=int, nargs=5)
options: Tuple[float, ...] = clickdc.option()
arguments: Tuple[int, ...] = clickdc.argument()
The dataclass
field initializes functions - command, group, option, argument - take the
same options as their click counterparts with one additional positional
argument called clickdc
for this module options. The clickdc
can be assigned
just to None
to disable automatic adding of the options and keyword
arguments by this module.
option: bool = clickdc.option("--option", is_flag=True, clickdc=None)
command: int = clickdc.argument("command", type=int, clickdc=None)
You can use from pydantic.dataclasses import dataclass
to have type checking
for your dataclass
.
You can safely mix any clickdc
options with click
options. Typically, I
would do:
@dataclass
class Args:
options: bool = clickdc.option(help="Options here")
@click.command(help="But command here")
@clickdc.adddc("args", Args)
@click.option("-v", "--verbose", count=True)
def cli(args: Args, verbose: int):
print(args)
print(verbose)
You can inherit dataclasses
and decorate using multiple. It works just by
decorating the function with the proper click.
function inferred from the
field type.
dataclasses(default, default_factory)
require some questionable polishing.
Written by Kamil Cukrowski