Subcommands
In all of the examples so far, we've had a single command, with a single set of parameters. This works fine for smaller applications, but once a tool starts to grow in scope, it is useful to start breaking up it's functionality into descrete units using subcommands.
What is a Subcommand?¶
A subcommand is an arc command object that lives underneath another command object. Each subcommand will have it's own callback function and it's own set of parameters. When ran from the commandline it will look a little something like this
Example¶
There are a couple of ways to define subcommands, in this doc we'll focus on the most common
import arc
@arc.command
def command():
...
@command.subcommand
def sub1():
arc.print("This is sub 1")
@command.subcommand
def sub2():
arc.print("This is sub 2")
command()
We can then execute each subcommand by referring to them by name.
Documentation¶
Subcommands also get their own --help
$ python subcommand.py --help
USAGE
subcommand.py [-h]
subcommand.py <subcommand> [ARGUMENTS ...]
OPTIONS
--help (-h) Displays this help message
SUBCOMMANDS
sub1
sub2
$ python subcommand.py sub1 --help
USAGE
subcommand.py sub1 [-h]
OPTIONS
--help (-h) Displays this help message
$ python subcommand.py sub2 --help
USAGE
subcommand.py sub2 [-h]
OPTIONS
--help (-h) Displays this help message
Nesting Subcommands¶
Subcommands can be arbitrarly nested
import arc
@arc.command
def command():
...
@command.subcommand
def sub1():
arc.print("This is sub 1")
@sub1.subcommand
def nested1():
arc.print("This is nested 1")
@command.subcommand
def sub2():
arc.print("This is sub 2")
@sub2.subcommand
def nested2():
arc.print("This is nested 2")
command()
$ python nested_subcommands.py sub1 nested1
This is nested 1
$ python nested_subcommands.py sub2 nested2
This is nested 2
Root Command¶
It is important to note that when using subcommands, the root command is still executable. One must take care that subcommand names may come into collision with arguments for the root or command (or any parent command, really). If you do not want a parent command to be executable, you may use this pattern:
import arc
ns = arc.namespace("ns")
@ns.subcommand
def sub():
arc.print("Namespace Example")
ns()
$ python namespace.py
USAGE
ns [-h]
ns <subcommand> [ARGUMENTS ...]
ns --help for more information
$ python namespace.py sub
Namespace Example
Namespace commands do not take any arguments (besides --help
), and when invoked, will print out the usage for the command, and exit with an error code.
Naming Subcommands¶
By default, commands are the kebab-case version of the function they decorate. You can provide an explicit name for the command:
import arc
@arc.command
def command():
...
@command.subcommand("some-other-name")
def sub():
...
command()
or provide multiple names, for a command:
import arc
@arc.command
def command():
...
@command.subcommand("some-other-name", "another-name", "a-third-name")
def sub():
...
command()
Note that when you provide multiple names, the first in the list of names will be considered the "canonical" name while the others will be considered aliases
Subcommands in other Files¶
Breaking up your CLI interface into multiple files in arc is a very straightforward process.