-
Notifications
You must be signed in to change notification settings - Fork 0
Commands
In general, everything you interact with in ARC is a Command object instance
from arc import CLI, namespace, command
cli = CLI() # this is a Command
ns = namespace("namespace") # this is a Command
@command() # this is a command
def some_command():
...
@cli.command()
def cool_command(): # This is a Command
...
@ns.subcommand()
def cool_subcommand(): # This is a Command
...The differences between these types of Command objects are as follows:
-
CLIacts as a top level namespace for other commands to be installed into, along with providing some additional functionality (autolading, autocompletions, etc...) -
namespacecommands are used to namespace other commands, and are not executable on their own -
@commandcommands are similar tonamespace, but are executable -
@cli.command&@ns.subcommandCommands are where the meat of your tool's code will reside.
Note that all three command types can hold subcommands. Each of these are gone into more depth below.
arc.namespace is a function used to create a new non-executable Command namespace.
from arc import namespace
foo = namespace("foo")Commands can then be installed onto that namespace. Commands are created using Python decorators. A function becomes a command using the foo.subcommand() decorator. You can pass a name into the command decorator. If there isn't an explicit name, the function's name will be used. This will be the name used to execute the command.
@foo.subcommand()
def hello():
print("Hello, World!")To run the command, you can pass it into the run function exported by ARC
run(foo)from arc import namespace, run
foo = namespace('foo')
@foo.subcommand()
def hello():
'''Command that prints Hello World'''
print("Hello, World!")
run(foo)$ python example.py hello
Hello, World!
While the namespace function is a way for the developer to explicitly declare a namespace, the @foo.subcommand() decorator also creates a namespace! This allows for very easy command nesting. Let's take the example from before and add a nested command to the hello namespace.
from arc import namespace, run
foo = namespace('foo')
@foo.subcommand()
def hello():
'''Command that prints Hello World'''
print("Hello, World!")
@hello.subcommand() # use the hello namespace as this command's parent
def night():
'''Command to greet people later
in the day'''
print("Good evening good sir!")
run(foo)$ python example.py hello:night
Good evening good sir!
Each level of nesting from the namespace that was ran needs to be fully qualified. Since the night command is in the namespace of hello which is in turn in the namespace of foo (the top level namespace) then the command is ran with hello:night. If we added another command in the hello namespace called bar it could be called with hello:bar. If we added it in the night namespace, it would be called with hello:night:bar
That's about all there is to it! You could add another command nested in the hello namespace, or another deeply nested command under the night namespace. There really is no hard limit to how far commands can be nested, but I would recommend to stick to 3 or fewer. Namespaces should be used to indicate that commands show some relation to each other, but serve a different function. If you need to modify the way a single command behaves, use Arguments and Flags for that.
Command operates in the same manner as namespace, but it makes the namespace executable on it's own, rather than just it's subcommands
If you've read the README or getting started, you would have seen a CLI object being used to make commands. The CLI object essentially acts as a "top level namespace" for your tool and a convenience wrapper around calling the run function. We could use the CLI to rewrite the previous example as follows:
from arc import CLI
cli = CLI()
@cli.subcommand()
def hello():
'''Command that prints Hello World'''
print("Hello, World!")
@hello.subcommand()
def night():
'''Command to greet people later
in the day'''
print("Good evening good sir!")
cli() # Equivelant to the 'run' function from before$ python example.py hello
Hello, World!
While these two may seem very similar, I would generally recommend to use the CLI approach for a few reasons:
- It doesn't require you to explicitly declare a namespace when you don't need to
- It makes the top-level or entry point of your CLI tool more clear for other people reading it
- All namespace are created via the
@foo.subcommand()decorator - more consistency
Commands can be given multiple names by passing a list in as the name
from arc import CLI
cli = CLI()
@cli.subcommand(["name1", "name2"])
def foo():
...