A CLI toolkit
Project description
Piou
A CLI tool to build beautiful command-line interfaces with type validation.
It is as simple as
from piou import Cli, Option
cli = Cli(description='A CLI tool')
@cli.command(cmd='foo', help='Run foo command')
def foo_main(
bar: int = Option(..., help='Bar positional argument (required)'),
baz: str = Option(..., '-b', '--baz', help='Baz keyword argument (required)'),
foo: str = Option(None, '--foo', help='Foo keyword argument'),
):
pass
if __name__ == '__main__':
cli.run()
The output will look like this:
Why ?
I could not find a library that provided:
- the same developer experience than FastAPI
- customization of the interface (to build a CLI similar to the one of Poetry)
- type validation / casting
Typer is the closest alternative in terms of experience but lacks the possibility to format the output is a custom way using external libraries (like Rich).
Piou provides all these possibilities and lets you define your own Formatter.
Install
You can install piou
with either:
pip install piou
conda install piou -c conda-forge
Features
Commands
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:
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.
@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
.
@cli.command(cmd='bar', help='Run foo command')
async def bar_main():
pass
Command Groups / Sub-commands
You can group commands into sub-commands:
from piou import Cli, Option
cli = Cli(description='A CLI tool')
@cli.command(cmd='foo', help='Run foo command')
def foo_main():
pass
sub_cmd = cli.add_sub_parser(cmd='sub', description='A sub command')
sub_cmd.add_option('--test', help='Test mode')
@sub_cmd.command(cmd='bar', help='Run bar command')
def sub_bar_main(**kwargs):
pass
@sub_cmd.command(cmd='foo', help='Run foo command')
def sub_foo_main(
test: bool,
foo1: int = Option(..., help='Foo argument'),
foo2: str = Option(..., '-f', '--foo2', help='Foo2 argument'),
):
pass
if __name__ == '__main__':
cli.run()
So when running python run.py sub -h
it will output the following:
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.
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 for example.
Complete example
You can try a more complete example by running python -m piou.test
Moving from argparse
If you are migrating code from argparse
to piou
here are some differences:
add_argument('--pick', choices=['foo', 'bar'])
is replaced withpick: Literal['foo', 'bar'] = Option(None, '--pick')
add_argument('--verbose', action='store_true')
is replaced withverbose: bool = Option(False, '--verbose')
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.