Command Line Parser

The Naked framework provides a command line parser that is intended to make the transition from a user command string to a Python object seamless and to make the generated Command object easy to use in your application logic.

How it Works

The command string is parsed into a series of positional and command line syntax specific arguments that are easily accessible through Command object attribute lookups or instance methods. If that statement was as clear as mud, here is an example that walks you through access to the commands, options, and their arguments.

Say you are developing a command suite application that expects a user to control the application with a command syntax like the following:

<executable> <primary command> [secondary command] [short option(s)] [long option(s)] [argument to option]

Let’s take a look at how you retrieve the information from the user input.

How to Import the Command Line Parser

The parser is available in the Naked.commandline module and can be imported into your app.py file (app.py file information in Naked Project Structure) with:

import sys
import Naked.commandline

If you created your project with the naked make command, this import is added to the generated app.py file for you.

How to Instantiate a Command Object

Create an instance of the command line parser with:

c = Naked.commandline.Command(sys.argv[0], sys.argv[1:])

The class is instantiated with two arguments. The name of your executable and the remainder of the command line string. The Python sys module takes care of both of these arguments for you.

Note

Import the Python sys module in your app.py file in order to pass the entire command line string to the Naked Command constructor (as shown above)

The Primary and Secondary Commands

The parser creates a new attribute from the first positional argument to the executable that is named cmd (for the primary command) and an attribute for the second positional argument that is named cmd2 (for the secondary or sub-command). Assuming that you call your Command object instance, ‘c’, as I demonstrated above, these commands are accessible with the following attribute lookups:

primary_command = c.cmd
secondary_command = c.cmd2

And you can test for the presence of a specific command in the same fashion:

if c.cmd == "command1":
        # do something
elif c.cmd == "command2":
        # do something else
elif c.cmd == "command3":
        # do yet another thing

The secondary command can be inserted into the application logic for each primary command like so:

if c.cmd == "command1":
        if c.cmd2 == "sub_command1":
                # do command1 branch 1
        elif c.cmd2 == "sub_command2":
                # do command1 branch 2
        elif c.cmd2 == "sub_command3":
                # do command1 branch 3

Options

For the purposes of this discussion, I am going to call an option that looks like this -s a short option, one that looks like this --long a long option, and one that has the following appearance --flag=argument a flag.

The parser identifies options by the presence of the first ‘-‘ symbol in the string. You can test for the presence of these option forms with a Command object method.

For exclusive options:

if c.option('-s') or c.option('--something'):
        # the user indicated this option, handle it
elif c.option('-e') or c.option('--else'):
        # the user indicated this option, handle it

For non-exclusive, independent options:

if c.option('-s') or c.option('--something'):
        # the user indicated this option, handle it
if c.option('-e') or c.option('--else'):
        # the user indicated this option, handle it

For non-exclusive, dependent options:

if c.option('-s') or c.option('--something'):
        if c.option('-e') or c.option('--else'):
                # the user indicated both options, handle them

The presence of a flag (as you’ll recall, an option that looks like this --flag=argument) is tested for with the flag() method:

if c.flag('--flag'):
        argument = c.flag_arg('--flag') # more information below on arguments!

Test for the Existence of Short and Long Options

To determine whether there were one or more options in the command that the user submitted, use either of the following tests that return a boolean:

Method Approach

if c.option_exists():
        # there is at least one short option, long option, or flag in command
else:
        # there are no options

Attribute Approach

if c.options:
        # there is at least one short option, long option, or flag in command
else:
        # there are no options

Test for the Existence of Flags

Flags are a subset of options. The above option tests will always return True if this test is True.

Method Approach

if c.flags_exists():
        # at least one flag was present in the command
else:
        # no flags were present in the command

Attribute Approach

if c.flags:
        # at least one flag was present in the command
else:
        # no flags were present in the command

Arguments to Options

Arguments to the options are retrieved with the arg() method for short and long options, and with the flag_arg() method for flags. These methods return a string that contains the n+1 positional argument relative to the option name that you enter as the method argument, or the string that immediately follows the ‘=’ character for a flag. Here are examples:

For a short option:

# user enters '-l python' in the command
arg_value = c.arg('-l')
print(arg_value)  # prints 'python'

For a long option:

# user enters '--language python' in the command:
arg_value = c.arg('--language')
print(arg_value)  #prints 'python'

For a flag:

# user enters '--language=python' in the command:
arg_value = c.flag_arg('--language')
print(arg_value)  #prints python

Other Available Command Attributes

There is overlap in the naming of the Command object attributes in order to provide a flexible scheme that (hopefully) addresses most command line application needs. For instance, if you are developing an application that does not require primary or secondary commands, and instead takes up to one option after the executable:

<executable> [option]

then you could use an approach like the following:

# Example: <executable> --test
if c.options:
    if c.arg0 == '--test':
                # do something
else:
        # there are no options

or alternatively,

# Example: <executable> --test
if c.options:
        if c.first == '--test':
                # do something
else:
        # there are no options

Here is the list of all available Command object attributes

Attribute Definition
obj.app executable path
obj.argv list of command arguments (excluding the executable)
obj.argc number of command line arguments (excluding the executable)
obj.arg0 the first positional argument (excluding the executable)
obj.arg1 the second positional argument (excluding the executable)
obj.arg2 the third positional argument (excluding the executable)
obj.arg3 the fourth positional argument (excluding the executable)
obj.arg4 the fifth positional argument (excluding the executable)
obj.first the first positional argument
obj.second the second positional argument
obj.third the third positional argument
obj.fourth the fourth positional argument
obj.fifth the fifth positional argument
obj.arglp the last positional argument
obj.last the last positional argument
obj.arg_to_exec the first argument to the executable = obj.arg0
obj.arg_to_cmd the first argument to a primary command = obj.arg1
obj.cmd the primary command = first positional argument
obj.cmd2 the secondary command = second positional argument
obj.options boolean for presence of one or more options
obj.flags boolean for presence of one or more flags

The naked Executable Args Command

The naked executable args command will help you design your command syntax logic with the Naked parser. Just pass a complete command example as an argument and the args command will display every parsed attribute, the truth testing for options and flags, and the result of argument assignments to options and flags.

Here is an example of how it is used:

naked args 'testapp save something --unicode -s --name=file.txt'

and the output looks like this:

Application
-----------
c.app = testapp

Argument List Length
--------------------
c.argc = 5

Argument List Items
-------------------
c.argobj = ['save', 'something', '--unicode', '-s', '--name=file.txt']

Arguments by Zero Indexed Start Position
----------------------------------------
c.arg0 = save
c.arg1 = something
c.arg2 = --unicode
c.arg3 = -s
c.arg4 = --name=file.txt

Arguments by Named Position
---------------------------
c.first = save
c.second = something
c.third = --unicode
c.fourth = -s
c.fifth = --name=file.txt

Last Positional Argument
------------------------
c.arglp = --name=file.txt
c.last = --name=file.txt

Primary & Secondary Commands
----------------------------
c.cmd = save
c.cmd2 = something

Option Exists Tests
------------------
c.option_exists() = True
c.options = True

Option Argument Assignment
--------------------------
c.arg("--unicode") = -s
c.arg("-s") = --name=file.txt

Flag Exists Tests
----------------
c.flag_exists() = True
c.flags = True

Flag Argument Assignment
------------------------
c.flag_arg("--name") = file.txt

Syntax Validation

Two types of command syntax validation are available.

Validation of at Least One Argument

You can confirm that there is at least one argument (including options) passed to the executable with the following:

import sys
from Naked.commandline import app_validates_args

if not c.app_validates_args():
        # handle invalid syntax (e.g. print usage)
        sys.exit(1) # exit application with non-zero exit status

This test confirms that the argument list length is > 0 (i.e. obj.argc > 0) and returns a boolean value.

Validation of a Primary Command

You can also confirm that there is a primary command that is passed to the executable for command suite style applications. Use a test like this:

import sys
from Naked.commandline import command_suite_validates

if not c.command_suite_validates():
        # handle invalid syntax (e.g. print usage)
        sys.exit(1) # exit application with non-zero exit status

A primary command is defined as any non-option string (i.e. a string that does not begin with a ‘-‘ character). The method returns a boolean value for this test.