Package: clidesc
clidescclidescis a CLI interface framework that can be used to build simple,
yet functional, command line interfaces with minimal client code.The goal is to create a framework to build the command line interface only
using configuration files (YAML or JSON format), and minimizing the need to
write code for it.UsageTo create a simple "Greeting" application, the CLI definition file, should
look like:---program:greetingdescription:A greeting application.version:1.0handler:greeting.helloarguments:-name:someonedescription:Someone to greet.type:stringrequired:trueAnd the application code would be:# Contents of greeting.pyfromclidescimportCLIDescdefhello(someone):print(f"Hello,{someone}!")if__name__=="__main__":cli=CLIDesc.from_file("greeting.yml")cli.run()With this configuration, the application will have options to display its
version (--version), help instructions (-h or --help), and a required
positional argument. If run with--help, the output is:usage: greeting [-h] [--version] someone

A greeting application.

positional arguments:
  someone     Someone to greet.

optional arguments:
  -h, --help  show this help message and exit
  --version   display program versionIf the application does not receive the required argument, an error is
displayed. For example, the output for runninggreetingis:usage: greeting [-h] [--version] someone
greeting: error: the following arguments are required: someoneWhen running an application with one parameter,greeting World, the output
would be:Hello, World!You may also useclidescto automatically format the output returned by
the handler methods. Use theoutputattribute along with the handler
method to configure the output format.The next example configures the output format, with a formatting string,
that follows Python's formatting rules.---program:outputdescription:Auto-formatting output.version:1.0handler:output.hellooutput:format:"Hello,{someone}"arguments:-name:someonedescription:Someone to greet.required:trueAnd the code for this application would be:# contents of output.py.fromclidescimportCLIDescdefhello(someone):"""Greet someone."""return{"someone":someone}if__name__=="__main__":cli=CLIDesc.from_file('output.yaml'))cli.run()Applications with multiple commands and command groups (likegit) are
supported throughsub_commands. Eachcommandinsub_commandcan have
its ownsub_command, creating a command hierarchy (deep hierarchies are
not recommended).The configuration for such application would be:---program:multidescription:A multi-command application.version:1.0sub_commands:title:Commandsdescription:Application sub-commandsgroup_name:Sub commandscommands:-name:abcdescription:First command.handler:multi.abcarguments:-name:some_argdescription:Some argument.-name:xyzdescription:Second command.handler:multi.xyzarguments:-name:another_argdescription:Another argument.And the client code:# contents of multi.pyfromclidescimportCLIDescdefabc(some_arg):"""Greet someone."""print(f"ABC:{some_arg}")defxyz(another_arg):"""Greet someone."""print(f"XYZ:{another_arg}")if__name__=="__main__":cli=CLIDesc.from_file("multi.yml")cli.run()Output FormattingNote: The output formatting is still a "preview" and might change in the
near future. Documentation and testing is far from complete. Check
[features/output_display.feature] for tested usage examples.clidesc allows automatic formatting of the command handlers result. To display
the returned values,outputmust be set toyes, or provide the format
and/or format options.The default output formatting will depend on the data type that is returned
by the command handler. Strings are written as returned, numbers (int, float
and complex) follow standard Python output conventions.Lists, tuples and sets are displayed one item per line, with a "dash" before
the item:- First item
- Second item
- Third itemDictionaries are displayed askey: valuepairs, but the value will be
formatted according to its type, and padded:a_string: Some text.
a_list:
    - an item
    - another item
a_dict:
    a_key: a value
    another_key:
        - an inner listTo modify the default display behavior,outputmust be configured. When
configuring the output formatting,clidescuses Python'sFormat String Syntax.For example, if the result of a handler is the dictionary{someone: John},
and the output is set tooutput: Hello, {someone}., the output will be"Hello, John.". The complete configuration for such an application might be
(seeexamples/output.py):---
program: output
description: Auto-formatting output.
version: 1.0
handler: output.hello
output: "Hello, {someone}."
arguments:
  - name: someone
    description: Someone to greet.
    required: trueLists can be set to be displayed with the item numbers by settingenumeratetoyes(by default, it is1):output:
   enumerate: yesWhich would result in something like:users:
    1. Amy
    2. Peter
    3. JimTo change the base number of the list, set enumerate to the desired value,
for exampleenumerate: 0would result in:users:
    0. Amy
    1. Peter
    2. JimThe list items can also have its format customized, with a format string. To
mimic theenumerate: yesconfiguration, the format sting can be defined as:output:
  users: "{_pad}{_index}. {_item}"This will result in:users:
    1. Amy
    2. Peter
    3. JimIt is also possible to hide thekeyusing theno_keysetting:output:
  users:
    no_key: yes
    format: "{_pad}{_index}. {_item}"Resulting in the output:1. Amy
2. Peter
3. JimThe attributes available to configure the output are:NameDescriptionDefaultoutputSet to anything than No or False, will force output. If set to a string, will act as the format string.Nofield nameThe name of the field to control output formatting. If set to a string, will act as the format string.NoneformatThe formatting string, can be applied tooutputor to afieldVaries for data type.no_keyHide the display ofkeysin dictionaries, if set toyes.NoenumerateIf set toyesdisplay numbered lists (starting on1), if set to anint, set the value for the first element of the list.NopaddingThe amount ofspacesto be used for padding for each level of data. Set to 0 or False to disable padding. This attribute must be used with the globaloutput, not with afield.4The format string follows the same rules as the Python'sFormat String Syntax, and some special attributes are available to aid in formatting output:NameDescription_padThe current padding for the data to be displayed._keyThe key of the current item._valueThe value of the current item._pathThe path to the current item (all of its keys)._indexThe index of the current list item._itemThe value of the current list item.Note: These attributes are available to lists, they might not be available to other data types.ExceptionsExceptions might occur during execution of a command handle, andclidescdoes not change the default Python behavior, but allows exceptions to be
handled as error messages to the end user.For example, the following description would print an error message and set
the program exit code if the specific exception occurs:---
program: calculator
description: A simple calculator
version: 1.0
handler: calculator.compute
exceptions:
  - class: ValueError
    exit_code: 5
    message: An error occured with message "{exception}".
arguments:
  - name: lhs
    description: Left hand symbol.
  - name: rhs
    description: Right hand symbol.Exception handlers are set globally, in a handler base.The following attributes can be used when configuring exception handling:NameDescriptionDefaultRequiredclassThe name of the exception class to handle.-yesactionThe kind of action to take: raise, abort, traceback.raisenoexit_codeAn integer that will be the program exit code. If explicitly set to a non-zero, forceactionto beabort.1nomessageA format string to be displayed. It will be formatted with anexceptionobject of the raised exception.-noUsing attributes forversionOften, the program version is available as a module attribute, and
maintaining this value in more than one place adds a duplication that
can make the different locations showing different versions. To avoid
this,clidescsupports setting theversionattribute using an
attribute of a module. The value will still be bound when theCLIDescis created, that is, if the attribute value changes while the program is
running, it will not be reflected on the CLI.For example, to define the program version as an attribute, use:---program:greetingdescription:A greeting application.version:attribute:greeting.__version__And the code forgreeting.py(orgreeting/__init__.py) should include
the attribute definition, for example:__version__ = "1.2.0"Project configurationWhen usingclidescin your project, if usingyamllintto verify the CLI
description file structure, you might want to add the following configuration
to.yamllint:truthy:
  allowed-values: [yes, no, true, false, True, False]AuthorsRafael Guterres Jeffmanrafasgj@gmail.com