Skip to main content

Proper CLI

Proper CLI is a Python library for creating composable, nestable, and ridiculously good looking command-line-user-interfaces from simple classes.

proper_cli output

Features

  • 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, why make it more complex?

Usage

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

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.

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

python run.py first foo bar

or by name:

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

python run.py first -arg1 -no-arg2

Subgroups

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

from proper_cli import Cli

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

class Manage(Cli):
    # A subgroup
    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:

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

An example

The image at the top was autogenerated by running this example:

# 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 subgroup!
    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().

# green text
echo("<color fg:green-3>foo</color>")

# bold green text
echo("<color fg:green-3 b>foo</color>")

# black text on a cyan background
echo("<color fg:cyan-1 r>foo</color>")

# italic underlined yellow text
echo("<color fg:yellow-2 iu>foo</color>")

The available styles are: bold (b), italic (i), underline (u), strikeout (s), reverse (r), and dim (d)

The closing tag </color> revokes all formatting options established by the last opened tag.

Run proper_cli/colors.py directly to preview the full palette of available colors in your terminal:

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.

API

Everything below is importable directly from proper_cli.

Cli class

Base class for a command group. Subclass it; every method and attribute whose name does not start with an underscore becomes a command (or a subgroup, if it is itself a Cli subclass).

Cli(
    *,
    parent: str = "",
    indent: str = "  ",
    initial_indent: str = " ",
    indent_start: int = 0,
    show_params: bool = True,
    colors: dict[str, str] | None = None,
    **env,
)
  • parent: Prefix shown in the generated usage line. Set automatically from sys.argv[0] when the instance is called.
  • indent, initial_indent, indent_start: Control the indentation of the help page.
  • show_params: When False, command arguments and options are omitted from the help page.
  • colors: A dict mapping the components of the help page to color strings, i.e.: "command": "fg:lime-2".
  • **env: Arbitrary context, stored as the _env dict and inherited by subgroups.

Calling the instance (cli()) parses sys.argv, dispatches to the matching command or subgroup, and prints the help page when no command is given or --help is passed.

FAQ

Why don't just use optparse or argparse?

I find it too verbose.

Why don't just use click?

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

Metadata

Release files for proper-cli 2.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for proper-cli 2.1.0
File Size Uploaded
proper_cli-2.1.0.tar.gz 15.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for proper-cli 2.1.0
File Interpreter ABI Platform
proper_cli-2.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 26.4 kB

Release files / proper_cli-2.1.0.tar.gz

Download URL proper_cli-2.1.0.tar.gz
Size 15.8 kB
Tags Source
SHA-256 checksum
How to use checksums
ad63c0c5695671c1fc36460b1ed8fc940bcbff56b8da430ebe7148a32f6bc21c
BLAKE2b-256 checksum
How to use checksums
6fa351926dd3b479a6f8c764263a4b693ea2b574c50e6c10b7e826ecfbaf7ab5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.22 {"installer":{"name":"uv","version":"0.12.22","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / proper_cli-2.1.0-py3-none-any.whl

Download URL proper_cli-2.1.0-py3-none-any.whl
Size 10.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8a629df27cf2b3daeaec4c8cf6a2576c8ea6701c2b142f7a043bb551480c3a2f
BLAKE2b-256 checksum
How to use checksums
c064a79362c4ade49863d8f54bddd7d295dec13c8a8e86ba0afddb268253c804
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.22 {"installer":{"name":"uv","version":"0.12.22","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

2.1.0 This release

2 release files

2.0.0

2 release files

1.4.1

2 release files

1.4

2 release files

1.3

2 release files

1.2

2 release files

1.1.1

2 release files

1.1

2 release files

1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page