Parseļƒ

This is intended to be a gentle introduction to Parser, a command-line parsing class based on argparse.

Optional arguments can be added to commands using the quo.parse.Parser.

Optional arguments in Quo are profoundly configurable and ought not to be mistaken for positional arguments.

Parameters
  • filename (str) - The name of the file to open (or '-' for stdin/stdout).

  • prog - The name of the program (default:os.path.basename(sys.argv[0]))

  • color (bool) - Print a colorful help output

  • usage - A usage message (default: auto-generated from arguments)

  • description - A description of what the program does

  • epilog - Text following the argument descriptions

  • argument_default - The default value for all arguments

  • add_help (bool) - Add a -h/-help option

  • allow_abbrev (bool)- Allow long options to be abbreviated unambiguously

  • exit_on_error (bool) - Determines whether or not ArgumentParser exits with error info when an error occurs

How to name Optional Argumentsļƒ

For the purpose of uniformity, a name is chosen in the following order

  1. In the event that the name is not prefixed with ā€“ or -, it will be considered a positional argument.

  2. If there is more than one name prefixed with ā€“ or -, the first one given is used as the name.

The basicsļƒ

Let us start with a very simple example which does (almost) nothing:

from quo.parse import Parser
arg = Parser()
arg.parse()

Following is a result of running the code:

python example1.py --help
_images/example1a.png
python example1.py --verbose
_images/example1b.png

Running the script without any options results in nothing displayed to stdout. Not so useful. The second one starts to display the usefulness of Parser. We have done almost nothing, but already we get a nice help message.

The ā€“help option, which can also be shortened to -h, is the only option we get for free (i.e. no need to specify it). Specifying anything else results in an error. But even then, we do get a useful usage message.

from quo.parse import Parser

optional = Parser()
optional.argument("--verbosity", help="Increase the verbosity")
arg = optional.parse()
if arg.verbosity:
       print("Verbosity turned on")
python example2.py --verbosity 1
_images/example2a.png
python example2.py --help
_images/example2b.png
python example2.py --verbosity

The program above is written so as to display something when ā€“verbosity is specified and display nothing when not specified. To show that the option is actually optional, there is no error when running the program without it.

Note

By default, if an optional argument isnā€™t used, the relevant variable, in this case argsverbosity, is given None as its value.

When using the optional argument in this case ā€“verbosity option, one must also specify some value, any value.

The above example accepts arbitrary integer values for ā€“verbosity, but for our simple program, only two values are actually useful, True or False. Letā€™s modify the code accordingly:

from quo.parse import Parser

optional = Parser()
optional.argument("--verbose", help="Increase the verbosity", action="store_true")
arg = optional.parse()
if arg.verbose:
       print("Verbosity turned on")

And the output:

python example2.py --verbose
python example2.py --verbose 1
python example2.py --help

Here is what is happening:

The option is now more of a flag than something that requires a value. We even changed the name of the option to match that idea. Note that we now specify a new keyword, action, and give it the value ā€œstore_trueā€. This means that, if the option is specified, assign the value True to arg.verbose. Not specifying it implies False.

It complains when you specify a value, in true spirit of what flags actually are.

Notice the different help text.

Short optionsļƒ

If you are familiar with command line usage, you will notice that I havenā€™t yet touched on the topic of short versions of the options. Itā€™s quite simple:

from quo.parse import Parser

optional = Parser()
optional.argument("-v", "--verbose", help="Increase the verbosity", action="store_true")
arg = optional.parse()
if arg.verbose:
       print("Verbosity turned on")

And here goes:

python example3.py -v

/image/

python example3.py --help

/image/

Note that the new ability is also reflected in the help text.

import argparse The help message is a bit different.

Positional Argumentsļƒ

Introducing Positional arguments An example:

from quo.parse import Parser

positional = Parser()

positional.argument("echo")
arg = positional.parse()
print(arg.echo)

Note however that, although the help display looks nice and all, it currently is not as helpful as it can be. For example we see that we got echo as a positional argument, but we donā€™t know what it does, other than by guessing or by reading the source code. So, letā€™s make it a bit more useful:

 from quo.parse import Parser

 positional = Parser()

 positional.argument("echo", help="echo the string you use here")
 arg = positional.parse()
 print(arg.echo)


-h, --help  show this help message and exit

Now, how about doing something even more useful:

from quo.parse import Parser

positional = Parser()

positional.argument("square", help="display a square of a given number")
arg = positional.parse()
print(arg.square**2)

Following is a result of running the code:

python prog.py 4
_images/example2c.png

That didnā€™t go so well. Thatā€™s because Parser treats the options we give it as strings, unless we tell it otherwise. So, letā€™s tell it to treat that input as an integer:

from quo.parse import Parser

positional = Parser()

positional.argument("square", help="display a square of a given number", type=int)
arg = positional.parse()
print(arg.square**2)

Following is a result of running the code:

python prog.py 4
_images/example2c.png

16

how about thisā€¦

python prog.py four
_images/example2c.png

That went well. The program now even helpfully quit on illegal input before proceeding.

Combining Positional and Optional argumentsļƒ

Our program keeps growing in complexity

from quo.parse import Parser

parser = Parser()

parser.argument("square", type=int, help="display a square of a given number")
parser.argument("-v", "--verbose", action="store_true", help="increase output verbosity")
arg = parser.parse()

answer = arg.square**2

if args.verbose:
     print(f"the square of {arg.square} equals {answer}")

else:
  print(answer)

And now the output:

python prog.py
_images/example2c.png
python3 prog.py 4 --verbose
_images/example2c.png

Note that the order does not matter. The above program can be written like so:

python3 prog.py --verbose 4

How about we give this program of ours back the ability to have multiple verbosity values, and actually get to use them:

from quo.parse import Parser

parser = Parser()

parser.argument("square", type=int, help="display a square of a given number")
parser.argument("-v", "--verbosity", type=int, help="increase output verbosity")
arg = parser.parse()

answer = arg.square**2

if arg.verbosity == 2:
      print(f"the square of {arg.square} equals {answer}")
elif arg.verbosity == 1:
    print(f"{arg.square}^2 == {answer}")
else:
    print(answer)

And the output:

python prog.py  4

$ python3 prog.py 4 16

python prog.py 4 -v

$ python3 prog.py 4 -v usage: prog.py [-h] [-v VERBOSITY] square prog.py: error: argument -v/ā€“verbosity: expected one argument

python prog.py 4  -v 1
python prog.py 4 -v 2
python prog.py 4 -v 3

These all look good except the last one, which exposes a bug in our program. Letā€™s fix it by restricting the values the --verbosity option can accept:

from quo.parse import Parser

parser = Parser()

parser.argument("square", type=int, help="display a square of a given number")
parser.argument("-v", "--verbosity", type=int, choices=[0, 1, 2], help="increase output verbosity")
arg = parser.parse()

answer = arg.square**2

if arg.verbosity == 2:
    print(f"the square of {arg.square} equals {answer}")

elif arg.verbosity == 1:
    print(f"{arg.square}^2 == {answer}")

else:
 print(answer)

And the output:

python prog.py 4 -v 3

Note that the change also reflects both in the error message as well as the help string. .. code:: console

python prog.py 4 -h

Now, letā€™s use a different approach of playing with verbosity, which is pretty common. It also matches the way the CPython executable handles its own verbosity argument (check the output of python ā€“help):

from quo.parse import Parser

parser = Parser()

parser.argument("square", type=int, help="display a square of a given number")
parser.argument("-v", "--verbosity", action="count", help="increase output verbosity")
arg = parser.parse()

answer = arg.square**2
if arg.verbosity == 2:
    print(f"the square of {arg.square} equals {answer}")

elif arg.verbosity == 1:
    print(f"{arg.square}^2 == {answer}")

else:
    print(answer)

We have introduced another action, count, to count the number of occurrences of specific options.

python prog.py 4
python prog.py 4 -v
python prog.py 4 -vv
python prog.py 4 -v 1

Grouping conflicting optional argumentsļƒ

group() allows us to specify options that conflict with each other. Letā€™s also change the rest of the program so that the new functionality makes more sense: weā€™ll introduce the --quiet option, which will be the opposite of the --verbose one.

from quo.parse import Parser

parser = Parser()

group = parser.group()
group.argument("-v", "--verbose", action="store_true")
group.argument("-q", "--quiet", action="store_true")

parser.argument("x", type=int, help="the base")
parser.argument("y", type=int, help="the exponent")

arg = parser.parse()

answer = arg.x**arg.y

if arg.quiet:
    print(answer)
elif arg.verbose:
    print(f"{arg.x} to the power {arg.y} equals {answer}")
else:
    print(f"{arg.x}^{arg.y} == {answer}")

Our program is now simpler, and weā€™ve lost some functionality for the sake of demonstration. Anyways, hereā€™s the output

python prog.py 4 2

Ouput:

4^2 == 16
$ python prog.py 4 2 -q

Output:

16
$ python3 prog.py 4 2 -v

Ouput:

4 to the power 2 equals 16
$ python prog.py 4 2 -vq

That should be easy to follow. Iā€™ve added that last output so you can see the sort of flexibility you get, i.e. mixing long form options with short form ones.

Before we conclude, you probably want to tell your users the main purpose of your program, just in case they donā€™t know

from quo.parse import Parser

parser = Parser(description="calculate X to the power of Y")

group = parser.group()

group.argument("-v", "--verbose", action="store_true")
group.argument("-q", "--quiet", action="store_true")
parser.argument("x", type=int, help="the base")
parser.argument("y", type=int, help="the exponent")

arg = parser.parse()
answer = arg.x**arg.y

if arg.quiet:
    print(answer)
elif arg.verbose:
    print("{} to the power {} equals {}".format(arg.x, arg.y, answer))
else:
    print("{}^{} == {}".format(arg.x, arg.y, answer))

Note that slight difference in the usage text. Note the [-v | -q], which tells us that we can either use -v or -q, but not both at the same time

python prog.py --help