Tutorial#

This tutorial is intended as an introduction to working with petcmd.

Overview#

petcmd creates a CLI applications with many commands.
In general, the petcmd commands look like this:

$ app-name command-name [OPTIONS]

So to get it petcmd provides a petcmd.Commander.command decorator to mark specific functions as commands.

Basic usage#

Let’s create a simple functon to sum two numbers in app.py:

def sum_two_numbers(a, b):
    print(a + b)

To create a CLI interface with this command let’s mark it as command with petcmd decorator and call the entrypoint:

from petcmd import Commander

commander = Commander()

@commander.command("sum")
def sum_two_numbers(a: int, b: int):
    print(a + b)

if __name__ == "__main__":
    commander.process()

Now we can try it:

$ python app.py sum 1 2
3

The CLI interface creating based on the command’s function signature. It’s allowed to specify arguments both by position and keyword. It’s also supports *arga and *kwargs. Let’s see some examples:

from petcmd import Commander

commander = Commander()

@commander.command("calc")
def calculate(a: int, b: int, operator: str = "+"):
	print(eval(f"{a} {operator} {b}"))

if __name__ == "__main__":
	commander.process()
$ python app.py calc 1 2
3
$ python app.py calc 10 2 /
5.0
$ python app.py calc 10 2 -o /
5.0
$ python app.py calc 10 2 --operator /
5.0
$ python app.py calc -a 10 -b 2 --operator /
5.0

Basically, you can pass values right as in the simple python function. There are some rules and restrictions for specifying command arguments:

Positional arguments#

You can specify arguments values by position of these arguments in the function signature, but it’s allowed only up to the first argument specified by the keyword - no mater positional or keyword by signature. Note that it’s related to arguments position in the function signature, not to the command values position, so you can first specify a keyword arguments and only then a positional:

$ python app.py calc -o / 10 2
5.0
$ python app.py calc 10 -o / 2
5.0
$ python app.py calc 10 2 /
5.0
$ python app.py calc 2 / -a 10

Invalid usage: positional argument follows keyword argument

Usage: calc a b [-operator]

a       int
b       int

Options:
        --operator -o   str     [default: +]

$ python app.py calc 10 / -b 2

Invalid usage: unexpected number of positional arguments

Usage: calc a b [-operator]

a       int
b       int

Options:
        --operator -o   str     [default: +]

It’s forbidden to specify keyword arguments by position in case *args presents in the function signature:

@commander.command("sum")
def calc_the_sum(init: int, *args: int, limit: int = -1):
    result = init + sum(args)
    if limit == -1 or result < limit:
        print(result)
    else:
        print("Limit exceeded")
$ python app.py sum 1 2 3 4 5
15
$ python app.py sum 1 2 3 4 -l 5
Limit exceeded

You also can’t duplicate keyword arguments by specifying them both by position and by keyword:

$ python app.py calc 10 2 / -o +

Invalid usage: keyword argument operator have been specified as positional already

Usage: calc a b [-operator]

a       int
b       int

Options:
        --operator -o   str     [default: +]