Metadata-Version: 2.1
Name: proper-cli
Version: 1.1
Summary: A minimal and ridiculously good looking command-line-interface toolkit.
Home-page: https://github.com/jpsca/proper-cli
Author: Juan-Pablo Scaletti
Author-email: juanpablo@jpscaletti.com
License: MIT
Project-URL: Issue tracker, https://github.com/jpsca/proper-cli/issues
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Requires-Python: <4.0,>=3.7
Description-Content-Type: text/markdown
Provides-Extra: test
Provides-Extra: dev
License-File: MIT-LICENSE

# Proper CLI

Proper CLI is a Python package for creating beautiful, composable, and ridiculously good looking command-line-**user**-interfaces without having to write any extra code.

- Made for interfacing with humans.
- Arbitrary nesting and composition of commands.
- Automatic help page generation.
- No need to redeclare paramaters and options with decorators, just write Python methods.
- The help of a command is its docstring.


## Usage

Declare a class that inherits from `proper_cli.Cli`. Every method/attribute that does not starts with an underscore will be a command.

```python
from proper_cli import Cli

class Manage(Cli):
    def first(self, arg1, arg2=3):
        pass

    def second(self):
        pass

    def _not_a_command(self):
        pass
```

Then, instance that class and call it.

```python
# run.py
cli = Manage()

if __name__ == "__main__":
    cli()
```

The class dosctring will be printed at the beginning of the help page.

The arguments can be then passed by position:

```bash
python run.py first foo bar
```

or by name:

```bash
python run.py first -arg1 foo -arg2 bar
```

To pass a `True` use the name without a value, for a `False`, prepend the name of the argument with `no-`:

```bash
python run.py first -arg1 -no-arg2
```


### Subcommands

If an attribute is a subclass of `proper_cli.Cli`, it will be a subcommand:

```python
from proper_cli import Cli

class DBSub(Cli):
    def migrate(self):
        pass

class Manage(Cli):
    # A subcommand
    db = DBSub  # NOT `DBSub()`
```

### Context

You can pass any named argument as context to be used by your commands. This will be stored at the `_env` attribute.

Example:

```python
>>> cli = Manage(lorem="ipsum")
>>> print(cli._env)
{"lorem": "ipsum"}
```


## An example

![proper_cli output](https://raw.githubusercontent.com/jpsca/proper_cli/master/output.png)

This autogenerated help message is the result of running the example below:

```python
# example.py
from proper_cli import Cli


class DBCli(Cli):
    """Database-related commands
    """

    def migrate(self, message):
        """Autogenerate a new revision file.

        This is an alias for "revision --autogenerate".

        Arguments:

        - message: Revision message

        """
        pass

    def branches(self):
        """Show all branches."""
        pass


class MyCli(Cli):
    """Welcome to Proper CLI 3
    """

    def new(self, path, quiet=False):
        """Creates a new Proper application at `path`.

        Arguments:

        - path: Where to create the new application.
        - quiet [False]: Supress all output.
        """
        pass

    def hello(count, name):
        """Simple program that greets NAME for a total of COUNT times."""
        pass

    # A subcommand!
    db = DBCli


cli = MyCli()

if __name__ == "__main__":
    cli()

```


## Coloring the Output

Whenever you output text, you can surround the text with tags to color its output.
This is automatically enabled for the docstrings, but you can also have it by using `proper_cli.echo()`
as a drop-in replacement of `print()`.

```python
# green text
echo("<fg=green>foo</fg=green>")

# black text on a cyan background
echo("<fg=black;bg=cyan>foo</>")

# bold text on a yellow background
echo("<bg=yellow;options=bold>foo</>")
```

Available foreground and background colors are: black, red, green, yellow, blue, magenta, cyan and white.

The available options are: bold, underscore, blink, reverse and conceal.

The closing tag can be replaced by `</>`, which revokes all formatting options established by the last opened tag.


## Custom styles

These four styles are available by default:

```python
# green text
echo("<info>foo</info>")

# yellow text
echo("<comment>foo</comment>")

# black text on a cyan background
echo("<question>foo</question>")

# white text on a red background
echo("<error>foo</error>")
```

It is possible to define your own styles using the `proper_cli.add_style()` method:

```python
add_style("fire", fg="red", bg="yellow", options=["bold", "blink"])
echo("<fire>foo</fire>")
```


## Helpers

Beyond the CLI builder, proper_cli also includes some commonly-used helper functions

### `confirm(question, default=False, yes_choices=YES_CHOICES, no_choices=NO_CHOICES)`

Ask a yes/no question via and return their answer.

### `ask(question, default=None, alternatives=None)`

Ask a question via input() and return their answer.


## FAQ

### Why don't just use optparse or argparse?

I find it too verbose.

### Why don't just use click?

Are you kidding? Because this looks better and is easier to use and understand.

### Why don't just use...?

Because this library fits better my mental model. I hope it matches yours as well.


