Typed command line interfaces with argparse
and pydantic
.
argdantic
provides a thin boilerplate layer to provide a modern CLI experience, including:
- Typed arguments: arguments require full typing by default, enforcing clarity and help your editor provide better support (linting, hinting).
- Nested models: exploit
pydantic
models to scale from simple primitives to complex nested configurations with little effort. - Nested commands: combine commands and build complex hierarchies to build complex interfaces.
- Validation by default: thanks to
pydantic
, field validation is provided by default, with the desired complexity. - Multiple sources: arguments can be provided from multiple sources, including environment variables, JSON, TOML and YAML files.
Installing argdantic
can be done from source, or simply using pip
.
The only required dependency is, of course, pydantic, while the remaining can be selected depending on your needs:
recommended choice: install everything
this includes orjson, pyyaml, tomli, python-dotenv
user@pc:~$ pip install argdantic[all]
env, json, toml or yaml dependencies
user@pc:~$ pip install argdantic[env|json|toml|yaml]
minimum requirement, only pydantic included
user@pc:~$ pip install argdantic
Creating a CLI with argdantic
can be as simple as:
from argdantic import ArgParser
# 1. create a CLI instance
parser = ArgParser()
# 2. decorate the function to be called
@parser.command()
def buy(name: str, quantity: int, price: float):
print(f"Bought {quantity} {name} at ${price:.2f}.")
# 3. Use your CLI by simply calling it
if __name__ == "__main__":
parser()
Then, in a terminal, the help
command can provide the usual information:
$ python cli.py --help
> usage: buy [-h] --name TEXT --quantity INT --price FLOAT
>
> optional arguments:
> -h, --help show this help message and exit
> --name TEXT
> --quantity INT
> --price FLOAT
This gives us the required arguments for the execution:
$ python cli.py --name apples --quantity 10 --price 3.4
> Bought 10 apples at $3.40.
Plain arguments and pydantic
models can be mixed together:
from argdantic import ArgParser
from pydantic import BaseModel
parser = ArgParser()
class Item(BaseModel):
name: str
price: float
@parser.command()
def buy(item: Item, quantity: int):
print(f"Bought {quantity} X {item.name} at ${item.price:.2f}.")
if __name__ == "__main__":
parser()
This will produce the following help:
usage: cli.py [-h] --item.name TEXT --item.price FLOAT --quantity INT
optional arguments:
-h, --help show this help message and exit
--item.name TEXT
--item.price FLOAT
--quantity INT
argdantic
supports several inputs:
.env
files, environment variables, and secrets thanks to pydantic.- JSON files, using either the standard
json
library, ororjson
if available. - YAML files, using the
pyyaml
library. - TOML files, using the lightweight
tomli
library.
Sources can be imported and added to each command independently, as such:
from argdantic import ArgParser
from argdantic.sources import EnvSettingsSource, JsonSettingsSource
parser = ArgParser()
@parser.command(
sources=[
EnvSettingsSource(env_file=".env", env_file_encoding="utf-8"),
JsonSettingsSource(path="settings.json"),
]
)
def sell(item: str, quantity: int, value: float):
print(f"Selling: {item} x {quantity}, {value:.2f}$")
if __name__ == "__main__":
parser()
This is just a brief introduction to the library, more examples and details can be found in the documentation.
Contributions are welcome! You can open a new issue to report bugs, or suggest new features. If you're brave enough, pull requests are also welcome.