Navigation

Cleo 0.3 is now out. It introduces decorators, validators, autocompletion and removes the need to declare a configure method when creating a command.

Decorators

You can now declare a command, and its associated arguments and options, with decorators:

from cleo import Application

app = Application()

@app.command('demo:greet', description='Greets someone')
@app.option('yell', value_required=None)
@app.argument('last_name', description='Your last name?', required=False)
@app.argument('name', description='Who do you want to greet?', required=True)
def greet(i, o):
    name = i.get_argument('name')
    if name:
        text = 'Hello %s' % name
    else:
        text = 'Hello'

    if i.get_option('yell'):
        text = text.upper()

    o.writeln(text)

If you do not set the command name via the command() decorator, it will automatically get the name of the decorated function.

Validators

Cleo now supports validators for arguments and options.

@app.option('iterations', value_required=True, default=1,
            validator=Integer())
def greet(i, o):
    # ...

It is important to note that there is no String() validator.

The reason is quite simple: By default, the command line arguments and options are considered strings, so there is no need to specify it.

Boolean

The Boolean() validator accepts the following values: 1, true, yes, y, on and their negatives (0, no, n, off) or native boolean types (True, False).

Integer and Float

These two are self explanatory.

Range

The Range() validator accepts a value that must be comprised inside a specified range.

The range can be of anything that can be compared to the specified value, like integers, floats or string.

The default validator for ranges is Integer but it can be changed

# Not including the boundaries
Range(0, 6, include_min=False, include_max=False)

# Float validator
Range(12.34, 56.78, validator=Float())

# String validator (just pass None as validator value)
Range('c', 'h', validator=None)

Choice/Enum

The Choice() (or its alias Enum()) restricts a possible value to a specified set of choices.

Choice(['orange', 'blue', 'yellow'])

# With validator
Choice([1, 3, 5, 7, 11], validator=Integer())

Named validators

Instead of declaring explicitely the validators it is possible to use their internal names:

  • Boolean: boolean
  • Integer: integer
  • Float: float
  • Choice/Enum: choice or enum
  • Range: range
@app.option('iterations', value_required=True, default=1,
            validator='integer')
def greet(i, o):
    # ...

When using named validators, the corresponding generated validator will have its default options.

Autocompletion

Cleo now supports autocompletion of commands. However it is not completely automatic. First you have to use the bash_completion.sh file:

# In your .bashrc or .zshrc
source /path/to/bash_completion.sh

Now, if your script is named console autocompletion is set. If not add a line like the following:

complete -F _complete_console you-script-name

A new way to declare commands via classes

In previous versions when you used classes to declare commands you needed to override the configure method, like so:

class HelloCommand(Command):

    def configure(self):
        self.set_name('hello')
        self.set_description('Says hello!')
        self.add_argument('name', InputArgument.REQUIRED, 'The name')

    def execute(i, o):
        name = i.get_argument('name')

        o.writeln('Hello <info>%s</info>' % name)

Now, it is no longer necessary since you can set the parameters as class variables:

class HelloCommand(Command):

    name = 'hello'

    description = 'Says hello!'

    arguments = [
        {
            'name': 'name',
            'description': 'The name',
            'required': True
        }
    ]

    def execute(i, o):
        name = i.get_argument('name')

        o.writeln('Hello <info>%s</info>' % name)

There is a lot more you can do with Cleo, you can just give a look at the documentation: http://cleo.readthedocs.org.

Sébastien Eustace

Sébastien Eustace

sebastien.eustace.io

Born & raised in France, and currently living in the beautiful city of Quito, Ecuador, I'm a software engineer, proud pythonista (but knowledgeable in other languages and technologies as well) but overall an open source lover.

View Comments