argdantic 1.3.3
Typed command line interfaces with argparse and pydantic

# argdantic
Typed command line interfaces with [`argparse`](https://docs.python.org/3/library/argparse.html) and [`pydantic`](https://github.com/pydantic/pydantic).

[![test passing](https://img.shields.io/github/actions/workflow/status/edornd/argdantic/test.yml?branch=main)](https://github.com/edornd/argdantic)
[![coverage](https://img.shields.io/codecov/c/gh/edornd/argdantic)](https://codecov.io/gh/edornd/argdantic)
[![pypi version](https://img.shields.io/pypi/v/argdantic)](https://pypi.org/project/argdantic/)
[![python versions](https://img.shields.io/pypi/pyversions/argdantic)](https://github.com/edornd/argdantic)

[![license](https://img.shields.io/github/license/edornd/argdantic)](https://github.com/edornd/argdantic)
[![documentation](https://img.shields.io/badge/documentation-%F0%9F%93%9A-blue)](https://edornd.github.io/argdantic/)
---

## Features

`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.

## Quickstart

### Installation
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:

```console
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
```

### A Simple Example

Creating a CLI with `argdantic` can be as simple as:

```python
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:

```console
$ 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:

```console
$ python cli.py --name apples --quantity 10 --price 3.4
> Bought 10 apples at $3.40.
```

### Using Models

Plain arguments and `pydantic` models can be mixed together:

```python
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:

```console
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
```

### Arguments From Different Sources

`argdantic` supports several inputs:
- **`.env` files**, environment variables, and secrets thanks to *pydantic*.
- **JSON files**, using either the standard `json` library, or `orjson` 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:

```python
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](https://edornd.github.io/argdantic/).

## Contributing

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.