Package: boeah.cleo
Create beautiful and testable command-line interfaces.Cleo is mostly a higher level wrapper forCliKit, so
a lot of the components and utilities comes from it. Refer to its documentation for more
information.ResourcesDocumentationIssue TrackerUsageTo make a command that greets you from the command line,
creategreet_command.pyand add the following to it:fromcleoimportCommandclassGreetCommand(Command):"""
    Greets someone

    greet
        {name? : Who do you want to greet?}
        {--y|yell : If set, the task will yell in uppercase letters}
    """defhandle(self):name=self.argument('name')ifname:text='Hello{}'.format(name)else:text='Hello'ifself.option('yell'):text=text.upper()self.line(text)You also need to create the file to run at the command line which creates
anApplicationand adds commands to it:#!/usr/bin/env pythonfromgreet_commandimportGreetCommandfromcleoimportApplicationapplication=Application()application.add(GreetCommand())if__name__=='__main__':application.run()Test the new command by running the following$pythonapplication.pygreetJohnThis will print the following to the command line:Hello JohnYou can also use the--yelloption to make everything uppercase:$pythonapplication.pygreetJohn--yellThis prints:HELLO JOHNAs you may have already seen, Cleo uses the command docstring to determine
the command definition.
The docstring must be in the following form :"""
Command description

Command signature
"""The signature being in the following form:"""
command:name {argument : Argument description} {--option : Option description}
"""The signature can span multiple lines."""
command:name
    {argument : Argument description}
    {--option : Option description}
"""Coloring the OutputWhenever you output text, you can surround the text with tags to color its
output. For example:# green textself.line('<info>foo</info>')# yellow textself.line('<comment>foo</comment>')# black text on a cyan backgroundself.line('<question>foo</question>')# white text on a red backgroundself.line('<error>foo</error>')The closing tag can be replaced by</>, which revokes all formatting options established by the last opened tag.It is possible to define your own styles using theadd_style()method:self.add_style('fire',fg='red',bg='yellow',options=['bold','blink'])self.line('<fire>foo</fire>')Available foreground and background colors are:black,red,green,yellow,blue,magenta,cyanandwhite.And available options are:bold,underscore,blink,reverseandconceal.You can also set these colors and options inside the tag name:# green textself.line('<fg=green>foo</>')# black text on a cyan backgroundself.line('<fg=black;bg=cyan>foo</>')# bold text on a yellow backgroundself.line('<bg=yellow;options=bold>foo</>')Verbosity LevelsCleo has four verbosity levels. These are defined in theOutputclass:ModeMeaningConsole optionNADo not output any messages-qor--quietclikit.VERBOSITY_NORMALThe default verbosity level(none)clikit.VERBOSITY_VERBOSEIncreased verbosity of messages-vclikit.VERBOSITY_VERY_VERBOSEInformative non essential messages-vvclikit.VERBOSITY_DEBUGDebug messages-vvvIt is possible to print a message in a command for only a specific verbosity
level. For example:ifclikit.VERBOSITY_VERBOSE<=self.io.verbosity:self.line(...)There are also more semantic methods you can use to test for each of the
verbosity levels:ifself.output.is_quiet():# ...ifself.output.is_verbose():# ...You can also pass the verbosity flag directly toline().self.line("",verbosity=clikit.VERBOSITY_VERBOSE)When the quiet level is used, all output is suppressed.Using ArgumentsThe most interesting part of the commands are the arguments and options that
you can make available. Arguments are the strings - separated by spaces - that
come after the command name itself. They are ordered, and can be optional
or required. For example, add an optionallast_nameargument to the command
and make thenameargument required:classGreetCommand(Command):"""
    Greets someone

    greet
        {name : Who do you want to greet?}
        {last_name? : Your last name?}
        {--y|yell : If set, the task will yell in uppercase letters}
    """You now have access to alast_nameargument in your command:last_name=self.argument('last_name')iflast_name:text+='{}'.format(last_name)The command can now be used in either of the following ways:$pythonapplication.pygreetJohn$pythonapplication.pygreetJohnDoeIt is also possible to let an argument take a list of values (imagine you want
to greet all your friends). For this it must be specified at the end of the
argument list:classGreetCommand(Command):"""
    Greets someone

    greet
        {names* : Who do you want to greet?}
        {--y|yell : If set, the task will yell in uppercase letters}
    """To use this, just specify as many names as you want:$pythonapplication.pydemo:greetJohnJaneYou can access thenamesargument as a list:names=self.argument('names')ifnames:text+='{}'.format(', '.join(names))There are 3 argument variants you can use:ModeNotationValueclikit.ARGUMENT_REQUIREDnone (just write the argument name)The argument is requiredclikit.ARGUMENT_OPTIONALargument?The argument is optional and therefore can be omittedclikit.ARGUMENT_MULTI_VALUEDargument*The argument can contain an indefinite number of arguments and must be used at the end of the argument listYou can combine them like this:classGreetCommand(Command):"""
    Greets someone

    greet
        {names?* : Who do you want to greet?}
        {--y|yell : If set, the task will yell in uppercase letters}
    """If you want to set a default value, you can it like so:argument=defaultThe argument will then be considered optional.Using OptionsUnlike arguments, options are not ordered (meaning you can specify them in any
order) and are specified with two dashes (e.g.--yell- you can also
declare a one-letter shortcut that you can call with a single dash like-y). Options arealwaysoptional, and can be setup to accept a value
(e.g.--dir=src) or simply as a boolean flag without a value (e.g.--yell).TipIt is also possible to make an optionoptionallyaccept a value (so that--yellor--yell=loudwork). Options can also be configured to
accept a list of values.For example, add a new option to the command that can be used to specify
how many times in a row the message should be printed:classGreetCommand(Command):"""
    Greets someone

    greet
        {name? : Who do you want to greet?}
        {--y|yell : If set, the task will yell in uppercase letters}
        {--iterations=1 : How many times should the message be printed?}
    """Next, use this in the command to print the message multiple times:for_inrange(0,self.option('iterations')):self.line(text)Now, when you run the task, you can optionally specify a--iterationsflag:$pythonapplication.pydemo:greetJohn$pythonapplication.pydemo:greetJohn--iterations=5The first example will only print once, sinceiterationsis empty and
defaults to1. The second example will print five times.Recall that options don’t care about their order. So, either of the following
will work:$pythonapplication.pydemo:greetJohn--iterations=5--yell$pythonapplication.pydemo:greetJohn--yell--iterations=5There are 4 option variants you can use:OptionNotationValueclikit.OPTION_MULTI_VALUED--option=*This option accepts multiple values (e.g.--dir=/foo--dir=/bar)clikit.OPTION_NO_VALUE--optionDo not accept input for this option (e.g.--yell)clikit.OPTION_REQUIRED_VALUE--option=This value is required (e.g.--iterations=5), the option itself is still optionalclikit.OPTION_OPTIONAL_VALUE--option=?This option may or may not have a value (e.g.--yellor--yell=loud)You can combine them like this:classGreetCommand(Command):"""
    Greets someone

    greet
        {name? : Who do you want to greet?}
        {--y|yell : If set, the task will yell in uppercase letters}
        {--iterations=?*1 : How many times should the message be printed?}
    """Testing CommandsCleo provides several tools to help you test your commands. The most
useful one is theCommandTesterclass.
It uses a special IO class to ease testing without a real
console:importpytestfromcleoimportApplicationfromcleoimportCommandTesterdeftest_execute(self):application=Application()application.add(GreetCommand())command=application.find('demo:greet')command_tester=CommandTester(command)command_tester.execute()assert"..."==tester.io.fetch_output()TheCommandTester.io.fetch_output()method returns what would have been displayed
during a normal call from the console.CommandTester.io.fetch_error()is also available
to get what you have been written to the stderr.You can test sending arguments and options to the command by passing them
as a string to theCommandTester.execute()method:importpytestfromcleoimportApplicationfromcleoimportCommandTesterdeftest_execute(self):application=Application()application.add(GreetCommand())command=application.find('demo:greet')command_tester=CommandTester(command)command_tester.execute("John")assert"John"intester.io.fetch_output()You can also test a whole console application by using theApplicationTesterclass.Calling an existing CommandIf a command depends on another one being run before it, instead of asking the
user to remember the order of execution, you can call it directly yourself.
This is also useful if you want to create a “meta” command that just runs a
bunch of other commands.Calling a command from another one is straightforward:defhandle(self):return_code=self.call('demo:greet',"John --yell")# ...If you want to suppress the output of the executed command,
you can use thecall_silent()method instead.AutocompletionCleo supports automatic (tab) completion inbash,zshandfish.To activate support for autocompletion, pass acompletekeyword when initializing
your application:application=Application('My Application','0.1',complete=True)Now, register completion for your application by running one of the following in a terminal,
replacing[program]with the command you use to run your application:# BASH - Ubuntu / Debian[program]completionsbash|sudotee/etc/bash_completion.d/[program].bash-completion# BASH - Mac OSX (with Homebrew "bash-completion")[program]completionsbash>$(brew--prefix)/etc/bash_completion.d/[program].bash-completion# ZSH - Config filemkdir~/.zfuncecho"fpath+=~/.zfunc">>~/.zshrc[program]completionszsh>~/.zfunc/_test# FISH[program]completionsfish>~/.config/fish/completions/[program].fish