Skip to content

Using the CLI

@arc.command() can be used to create applications with multiple parameters like echo or grep.

arc.CLI() is used to create a cli-tool with multiple commands where each can have their own parameters, like git

Example

cli_demo.py
import arc

cli = arc.CLI()


@cli.command()
def c1():
    """First command"""
    print("the first command")


@cli.command()
def c2():
    """Second command"""
    print("The second command")


cli()
We can check out the usage for the cli with --help. Then execute the commands by referencing them by name.
$ python cli_demo.py --help
USAGE
    cli_demo.py [OPTIONS] <command> [ARGUMENTS ...]

OPTIONS
    --help (-h)  Shows help documentation

SUBCOMMANDS
    c1           First command
    c2           Second command

$ python cli_demo.py c1
the first command

$ python cli_demo.py c2
The second command

Command Specific Help Documentation

Not only does the entire application have a --help flag, each command has a specialized --help

$ python cli_demo.py c1 --help
USAGE
    cli_demo.py c1 [--help]

DESCRIPTION
    First command

OPTIONS
    --help (-h)  Shows help documentation

$ python cli_demo.py c2 --help
USAGE
    cli_demo.py c2 [--help]

DESCRIPTION
    Second command

OPTIONS
    --help (-h)  Shows help documentation

Subcommands

arc.CLI() also enables arbitrary nesting of commands. When you create a command with @cli.command(), subcommands can be added to it with @command.subcommand()

cli_subcommands.py
from arc import CLI

cli = CLI()


@cli.command()
def command():
    print("command")


@command.subcommand()
def sub():
    print("command:sub")


@sub.subcommand()
def sub2():
    print("command:sub:sub2")


cli()
Subcommands are executed by prefixing the command name with all of it's parent's names, all the way up to the root, with each name seperated by colons (grandparent:parent:command)
$ python cli_subcommands.py command
command

$ python cli_subcommands.py command:sub
command:sub

$ python cli_subcommands.py command:sub:sub2
command:sub:sub2

Some notes about subcommands:

  • Subcommands inherit their parent's state.
  • Subcommand will inherit their parent's callbacks.

Multiple Files

arc.CLI() makes it easy to break you application out into multiple files.

There are two ways to create a stand-alone command:

@arc.command()

We've been using @arc.command() to create single-command applications, but any command created with @arc.command() may also be used with arc.CLI().

arc.namespace()

arc.namespace() is a special function that is used to create a namespace for subcommands. When executing the namespace directly, it will simply print out it's own help information.

The following is an example of how to (mostly) emulate what namespace does.

namespace_example.py
import arc

cli = arc.CLI()


@arc.command()
def n1(ctx: arc.Context):
    print(ctx.command.get_help(ctx))
    ctx.exit(1)


@n1.subcommand()
def sub():
    print("n1:sub")


n2 = arc.namespace("n2")


@n2.subcommand()
def sub2():
    print("n2:sub2")


cli.install_commands(n1, n2)

cli()
$ python namespace_example.py n1
USAGE
    namespace_example.py n1 [--help]

OPTIONS
    --help (-h)  Shows help documentation

SUBCOMMANDS
    sub

$ python namespace_example.py n2
USAGE
    namespace_example.py n2:<subcommand> [ARGUMENTS ...]

OPTIONS
    --help (-h)  Shows help documentation

SUBCOMMANDS
    sub2

Putting it Together

from arc import CLI
from cli_command import command
from cli_namespace import ns

cli = CLI()
cli.install_commands(command, ns)

# Could also place any commands you wanted in here :)


cli()
import arc


@arc.command()
def command():
    print("CLI Command Example")


# Do not execute command() in here
# like we would with a single-command
import arc

ns = arc.namespace("ns")


@ns.subcommand()
def sub():
    print("Namespace Example")
$ python cli_main.py --help
USAGE
    cli_main.py [OPTIONS] <command> [ARGUMENTS ...]

OPTIONS
    --help (-h)  Shows help documentation

SUBCOMMANDS
    command
    ns

$ python cli_main.py command
CLI Command Example

$ python cli_main.py ns:sub
Namespace Example

Global Options

When using arc.CLI(), each command handles it's own parameters. As such, they don't allow us to add parameters to the application as a whole.

We can solve this problem with @cli.options. This is similar to how @cli.command() works, but for the entire app, rather than an individual command. Additionally, arguments aren't allowed, only options or flags

cli_options.py
import arc

cli = arc.CLI()


@cli.command()
def command(state: arc.State):
    if state.verbose:
        print("--------- Verbose Mode ---------------")

    print("hi there")


@cli.options
def options(*, verbose: bool, ctx: arc.Context):
    ctx.state.verbose = verbose


cli()
$ python cli_options.py command
hi there

$ python cli_options.py --verbose command
--------- Verbose Mode ---------------
hi there