Metadata-Version: 2.1
Name: piou
Version: 0.1.13
Summary: A CLI toolkit
Home-page: https://github.com/andarius/piou
License: MIT
Keywords: cli
Author: Julien Brayere
Author-email: julien.brayere@gmail.com
Requires-Python: >=3.9,<4.0
Classifier: Environment :: Console
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.9
Requires-Dist: rich (>=10.11.0,<11.0.0)
Project-URL: Repository, https://github.com/andarius/piou
Description-Content-Type: text/markdown

# Piou

[![Python versions](https://img.shields.io/pypi/pyversions/piou)](https://pypi.python.org/pypi/piou)
[![Latest PyPI version](https://img.shields.io/pypi/v/piou?logo=pypi)](https://pypi.python.org/pypi/piou)
[![CircleCI](https://circleci.com/gh/Andarius/piou/tree/master.svg?style=shield)](https://app.circleci.com/pipelines/github/Andarius/piou?branch=master)
[![Latest conda-forge version](https://img.shields.io/conda/vn/conda-forge/piou?logo=conda-forge)](https://anaconda.org/conda-forge/piou)

A CLI tool to build beautiful command-line interfaces with type validation.

It is as simple as

```python
from piou import Cli, Option

cli = Cli(description='A CLI tool')


@cli.command(cmd='foo',
             help='Run foo command')
def foo_main(
        foo1: int = Option(..., help='Foo arguments'),
        foo2: str = Option(..., '-f', '--foo2', help='Foo2 arguments'),
        foo3: str = Option(None, '-g', '--foo3', help='Foo3 arguments'),
):
    pass


if __name__ == '__main__':
    cli.run()
```

The output will look like this:

![example](https://github.com/Andarius/piou/raw/master/docs/simple-output.png)

# Install

You can install `piou` with either:

- `pip install piou`
- `conda install piou -c conda-forge`

# Features

## Commands

```python
from piou import Cli, Option

cli = Cli(description='A CLI tool')


@cli.command(cmd='foo',
             help='Run foo command')
def foo_main(
        foo1: int = Option(..., help='Foo arguments'),
        foo2: str = Option(..., '-f', '--foo2', help='Foo2 arguments'),
        foo3: str = Option(None, '-g', '--foo3', help='Foo3 arguments'),
):
    pass


@cli.command(cmd='bar',
             help='Run bar command')
def bar_main(
        foo1: int = Option(..., help='Foo arguments'),
        foo2: str = Option(..., '-f', '--foo2', help='Foo2 arguments'),
        foo3: str = Option(None, '-g', '--foo3', help='Foo3 arguments'),
):
    pass


if __name__ == '__main__':
    cli.run()
```  

In this case, `foo1` is a positional argument while `foo2` and `foo3` are keyword arguments.

You can optionally specify global options that will be passed to all commands:

```python
cli = Cli(description='A CLI tool')

cli.add_option('-q', '--quiet', help='Do not output any message')
```

The **help** can also be extracted from the function docstring, both functions here have the same one.

```python
@cli.command(cmd='bar', help='Run foo command')
def bar_main():
    pass


@cli.command(cmd='bar2')
def bar_2_main():
    """
    Run foo command
    """
    pass
```

A command can also be asynchronous, it will be run automatically using `asyncio.run`.

```python
@cli.command(cmd='bar', help='Run foo command')
async def bar_main():
    pass
```

## Sub-commands

You can group commands into sub-commands:

```python
from piou import Cli, Option

cli = Cli(description='A CLI tool')

sub_cmd = cli.add_sub_parser(cmd='sub', description='A sub command')

sub_cmd.add_option('--dry-run', help='Run in dry run mode')


@sub_cmd.command(cmd='baz', help='Run baz command')
def baz_bar_main(**kwargs):
    pass


@sub_cmd.command(cmd='foo', help='Run foo command')
def toto_main(
        foo1: int = Option(..., help='Foo arguments'),
        foo2: str = Option(..., '-f', '--foo2', help='Foo2 arguments'),
):
    pass


if __name__ == '__main__':
    cli.run()

```

![example](https://github.com/Andarius/piou/raw/master/docs/sub-cmd-output.png)

## Options processor

Sometimes, you want to run a function using the global arguments before running the actual command (for instance
initialize a logger based on the `verbose` level).

To do so, you use `set_options_processor` that will receive all the current global options of the CLI.

```python
from piou import Cli

cli = Cli(description='A CLI tool')

cli.add_option('--verbose', help='Increase verbosity')


def processor(verbose: bool):
    print(f'Processing {verbose=}')


cli.set_options_processor(processor)
```

## Custom Formatter

If you want to customize the data displayed when displaying the CLI help or different errors, you can create a custom
one inheriting from `Formatter`.  
See the [Rich formatter](https://github.com/Andarius/piou/blob/master/piou/formatter/rich_formatter.py) for example.

## Complete example

You can try a more complete example by running `python -m piou.test`

