Basics tutorial

Installation

You can install clize using pip. If in an activated virtualenv, type:

pip install clize

If you wish to do a user-wide install:

pip install --user clize

A minimal application

A minimal command-line application written with clize consists of writing a function and passing it to run():

from clize import run

def hello_world():
    return "Hello world!"

if __name__ == '__main__':
    run(hello_world)

If you save this as helloworld.py and run it, the function will be run:

$ python3 helloworld.py
Hello world!

In this example, run() simply takes our function, runs it and prints the result.

Requiring arguments

You can require arguments the same way as you would in any Python function definition. To illustrate, lets write an echo command.

from clize import run

def echo(word):
    return word

if __name__ == '__main__':
    run(echo)

Save it as echo.py and run it. You will notice the script requires exactly one argument now:

$ python3 ./echo.py
./echo.py: Missing required arguments: word
Usage: ./echo.py [OPTIONS] word
$ python3 ./echo.py ham
ham
$ python3 ./echo.py ham spam
./echo.py: Received extra arguments: spam
Usage: ./echo.py [OPTIONS] word

Enhancing the --help message

If you try to specify --help when running either of the previous examples, you will notice that Clize has in fact also generated a --help feature for you already:

$ python3 ./echo.py --help
Usage: ./echo.py [OPTIONS] word

Positional arguments:
  word

Other actions:
  -h, --help   Show the help

It is fairly unhelpful right now, so we should improve that by giving our function a docstring:

def echo(word):
    """Echoes word back

    word: One word or quoted string to echo back
    """
    return word

As you would expect, it translates to this:

$ python3 ./echo.py --help
Usage: ./echo.py [OPTIONS] word

Echoes word back

Positional arguments:
  word   One word or quoted string to echo back

Other actions:
  -h, --help   Show the help

Accepting options

Clize will treat any regular parameter of your function as a positional parameter of the resulting command. To specify an option to be passed by name, you will need to use keyword-only parameters.

Let’s add a pair of options to specify a prefix and suffix around each line of word:

def echo(word, *, prefix='', suffix=''):
    """Echoes text back

    word: One word or quoted string to echo back

    prefix: Prepend this to each line in word

    suffix: Append this to each line in word
    """
    if prefix or suffix:
        return '\n'.join(prefix + line + suffix
                         for line in word.split('\n'))
    return word

In Python, any parameters after *args or * become keyword-only: When calling a function with such parameters, you can only provide a value for them by name, i.e.:

echo(word, prefix='b', suffix='a') # good
echo(word, 'b', 'a') # invalid

Clize then treats keyword-only parameters as options rather than as positional parameters.

Note

Python 2 does not support this syntax. See Python 2 support for named parameters

The change reflects on the command and its help when run:

$ python3 ./echo.py --prefix x: --suffix :y 'spam
$ ham'
x:spam:y
x:ham:y
$ python3 ./echo.py --help
Usage: ./echo.py [OPTIONS] word

Echoes text back

Positional arguments:
  word   One word or quoted string to echo back

Options:
  --prefix=STR   Prepend this to each line in word(default: )
  --suffix=STR   Append this to each line in word(default: )

Other actions:
  -h, --help   Show the help

Collecting all positional arguments

Just like when defining a regular Python function, you can prefix a parameter with one asterisk and it will collect all remaining positional arguments:

def echo(*text, prefix='', suffix=''):
    ...

However, just like in Python, this makes the parameter optional. To require that it should receive at least one argument, you will have to tell Clize that text is required using an annotation:

from clize import Parameter, run

def echo(*text:Parameter.REQUIRED, prefix='', suffix=''):
    """Echoes text back

    text: The text to echo back

    prefix: Prepend this to each line in word

    suffix: Append this to each line in word
    """
    text = ' '.join(text)
    if prefix or suffix:
        return '\n'.join(prefix + line + suffix
                         for line in text.split('\n'))
    return text

if __name__ == '__main__':
    run(echo)

Note

Python 2 does not support this syntax. See here.

Accepting flags

Parameters which default to False are treated as flags. Let’s add a flag to reverse the input:

def echo(*text:Parameter.REQUIRED, prefix='', suffix='', reverse=False):
    """Echoes text back

    text: The text to echo back

    reverse: Reverse text before processing

    prefix: Prepend this to each line in word

    suffix: Append this to each line in word

    """
    text = ' '.join(text)
    if reverse:
        text = text[::-1]
    if prefix or suffix:
        return '\n'.join(prefix + line + suffix
                         for line in text.split('\n'))
    return text
$ python3 ./echo.py --reverse hello world
dlrow olleh

Converting arguments

Clize automatically tries to convert arguments to the type of the receiving parameter’s default value. So if you specify an inteteger as default value, Clize will always give you an integer:

def echo(*text:Parameter.REQUIRED,
         prefix='', suffix='', reverse=False, repeat=1):
    """Echoes text back

    text: The text to echo back

    reverse: Reverse text before processing

    repeat: Amount of times to repeat text

    prefix: Prepend this to each line in word

    suffix: Append this to each line in word

    """
    text = ' '.join(text)
    if reverse:
        text = text[::-1]
    text = text * repeat
    if prefix or suffix:
        return '\n'.join(prefix + line + suffix
                         for line in text.split('\n'))
    return text
$ python3 ./echo.py --repeat 3 spam
spamspamspam

Aliasing options

Now what we have a bunch of options, it would be helpful to have shorter names for them. You can specify aliases for them by annotating the corresponding parameter:

def echo(*text:Parameter.REQUIRED,
         prefix:'p'='', suffix:'s'='', reverse:'r'=False, repeat:'n'=1):
    ...
$ python3 ./echo.py --help
Usage: ./echo.py [OPTIONS] text...

Echoes text back

Positional arguments:
  text   The text to echo back

Options:
  -r, --reverse      Reverse text before processing
  -n, --repeat=INT   Amount of times to repeat text(default: 1)
  -p, --prefix=STR   Prepend this to each line in word(default: )
  -s, --suffix=STR   Append this to each line in word(default: )

Other actions:
  -h, --help   Show the help

Arbitrary requirements

Let’s say we want to give an error if the word spam is in the text. To do so, one option is to raise an ArgumentError from within your function:

from clize import ArgumentError, Parameter, run

def echo(*text:Parameter.REQUIRED,
         prefix:'p'='', suffix:'s'='', reverse:'r'=False, repeat:'n'=1):
    """Echoes text back

    text: The text to echo back

    reverse: Reverse text before processing

    repeat: Amount of times to repeat text

    prefix: Prepend this to each line in word

    suffix: Append this to each line in word

    """
    text = ' '.join(text)
    if 'spam' in text:
        raise ArgumentError("I don't want any spam!")
    if reverse:
        text = text[::-1]
    text = text * repeat
    if prefix or suffix:
        return '\n'.join(prefix + line + suffix
                         for line in text.split('\n'))
    return text

def version():
    """Show the version"""
    return 'echo version 0.2'

if __name__ == '__main__':
    run(echo, alt=version)
$ ./echo.py spam bacon and eggs
./echo.py: I don't want any spam!
Usage: ./echo.py [OPTIONS] text...

If you would like the usage line not to be printed, raise UserError instead.

Next up, we will look at how you can have Clize dispatch to multiple functions for you.