Optik: Basic Usage
==================

While Optik is quite flexible and powerful, you don't have to jump
through hoops or read reams of documentation to get it working in basic
cases.  This document aims to demonstrate some simple usage patterns
that will get you started using Optik in your scripts.

To parse a command line with Optik, you must create an OptionParser
instance and populate it.  Obviously, you'll have to import the
OptionParser classes in any script that uses Optik::

  from optik import OptionParser

Early on in the main program, create a parser::

  parser = OptionParser()

Then you can start populating the parser with options.  Each option is
really a set of synonmous option strings; most commonly, you'll have one
short option string and one long option string -- e.g. "-f" and "--file"::

  parser.add_option("-f", "--file", ...)

The interesting stuff, of course, is what comes after the option
strings.  In this document, we'll only cover four of the things you can
put there: 'action', 'type', 'dest' (destination), and 'help'.


The "store" action
------------------

The action tells Optik what to do when it sees one of the option strings
for this option on the command-line.  For example, the action "store"
means: take the next argument (or the remainder of the current
argument), ensure that it is of the correct type, and store it to your
chosen destination.

For example, let's fill in the "..." of that last option::

  parser.add_option("-f", "--file",
                    action="store", type="string", dest="filename")

Now let's make up a fake command-line and ask Optik to parse it::

  args = ["-f", "foo.txt"]
  (options, args) = parser.parse_args(args)

(Note that if you don't pass an argument list to parse_args(),
it automatically uses sys.argv[1:].)

When Optik sees the "-f", it sucks in the next argument -- "foo.txt" --
and stores it in the 'filename' attribute of a special object.  That
object is the first return value from parse_args(), so ::

  print options.filename

will print "foo.txt".

The other option types supported by Optik are "int" and "float".  Here's
an option that expects an integer argument::

  parser.add_option("-n", type="int", dest="num")

Note that I didn't supply a long option, which is perfectly acceptable.
I also didn't specify the action -- it defaults to "store".
  
Let's parse another fake command-line.  This time, we'll jam the option
argument right up against the option -- "-n42" (one argument) is
equivalent to "-n 42" (two arguments). ::

  (options, args) = parser.parse_args(["-n42"])
  print options.num

will print "42".

Trying out the "float" type is left as an exercise for the reader.

If you don't specify a type, Optik assumes "string".  Combined with the
fact that the default action is "store", that means our first example
can be a lot shorter::

  parser.add_option("-f", "--file", dest="filename")

If you don't supply a destination, Optik figures out a sensible default
from the option strings: if the first long option string is "--foo-bar",
then the default destination is 'foo_bar'.  If there are no long option
strings, Optik looks at the first short option: the default destination
for "-f" is 'f'.

Adding types is fairly easy; see extending.txt for information on
extending Optik.


Other "store_*" actions
-----------------------

Flag options -- set a variable to true or false when a particular option
is seen -- are quite common.  Optik supports them with two separate
actions, "store_true" and "store_false".  For example, you might have a
"verbose" flag that is turned on with "-v" and off with "-q"::

  parser.add_option("-v", action="store_true", dest="verbose")
  parser.add_option("-q", action="store_false", dest="verbose")

Here we have two different options with the same destination, which is
perfectly OK.  (It just means you have to be a bit careful when setting
default values -- see below.)

When Optik sees "-v" on the command line, it sets the 'verbose'
attribute of the special "option values" object to 1; when it sees "-q",
it sets 'verbose' to 0.


Setting default values
----------------------

All of the above examples involve setting some variable (the
"destination") when certain command-line options are seen.  What happens
if those options are never seen?  Since we didn't supply any defaults,
they are all set to None.  Sometimes, this is just fine (which is why
it's the default), but sometimes, you want more control.  To address
that need, Optik lets you supply a default value for each destination,
which is assigned before the command-line is parsed.

First, consider the verbose/quiet example.  If we want Optik to set
'verbose' to 1 unless -q is seen, then we can do this::

  parser.add_option("-v", action="store_true", dest="verbose", default=1)
  parser.add_option("-q", action="store_false", dest="verbose")

Oddly enough, this is exactly equivalent::

  parser.add_option("-v", action="store_true", dest="verbose")
  parser.add_option("-q", action="store_false", dest="verbose", default=1)

Those are equivalent because you're supplying a default value for the
option's *destination*, and these two options happen to have the same
destination (the 'verbose' variable).

Consider this::

  parser.add_option("-v", action="store_true", dest="verbose", default=0)
  parser.add_option("-q", action="store_false", dest="verbose", default=1)

Again, the default value for 'verbose' will be 1: the last default value
supplied for any particular destination attribute is the one that
counts.


Generating help
---------------

The last feature that you will use in every script is Optik's ability to
generate help messages.  All you have to do is supply a 'help' value
when you add an option.  Let's create a new parser and populate it with
user-friendly (documented) options::

  usage = "usage: %prog [options] arg1 arg2"
  parser = OptionParser(usage=usage)
  parser.add_option("-v", "--verbose",
                    action="store_true", dest="verbose", default=1,
                    help="make lots of noise [default]")
  parser.add_option("-q", "--quiet",
                    action="store_false", dest="verbose", 
                    help="be vewwy quiet (I'm hunting wabbits)")
  parser.add_option("-f", "--file", dest="filename",
                    metavar="FILE", help="write output to FILE"),
  parser.add_option("-m", "--mode",
                    default="intermediate",
                    help="interaction mode: one of 'novice', "
                         "'intermediate' [default], 'expert'")

If Optik encounters either "-h" or "--help" on the command-line, or if
you just call "parser.print_help()", it prints the following to stdout::

  usage: <yourscript> [options] arg1 arg2

  options:
    -h, --help           show this help message and exit
    -v, --verbose        make lots of noise [default]
    -q, --quiet          be vewwy quiet (I'm hunting wabbits)
    -fFILE, --file=FILE  write output to FILE
    -mMODE, --mode=MODE  interaction mode: one of 'novice', 'intermediate'
                         [default], 'expert'

There's a lot going on here to help Optik generate the best possible
help message:

* the script defines its own usage message::

     usage = "usage: %prog [options] arg1 arg2"

  Optik expands "%prog" in the usage string to the name of the
  current script, ie. os.path.basename(sys.argv[0]).  The expanded
  string is then printed before the detailed option help.

  If you don't supply a usage string, Optik uses a bland but sensible
  default: "usage: %prog [options]", which is fine if your script
  doesn't take any positional arguments.

* every option defines a help string, and doesn't worry about line-
  wrapping -- Optik takes care of wrapping lines and making the
  help output look good.

* options that take a value indicate this fact in their
  automatically-generated help message, e.g. for the "mode" option::

    -mMODE, --mode=MODE

  Here, "MODE" is called the meta-variable: it stands for the argument
  that the user is expected to supply to -m/--mode.  By default, Optik
  converts the destination variable name to uppercase and uses that
  for the meta-variable.  Sometimes, that's not what you want -- for
  example, the "filename" option explicitly sets metavar="FILE",
  resulting in this automatically-generated option description::

    -fFILE, --file=FILE

  This is important for more than just saving space, though: the
  manually written help text uses the meta-variable "FILE", to clue
  the user in that there's a connection between the formal syntax
  "-fFILE" and the informal semantic description "write output to
  FILE".  This is a simple but effective way to make your help text a
  lot clearer and more useful for end users.


Print a version number
----------------------

Similar to the brief usage string, Optik can also print a version string
for your program.  You have to supply the string, as the 'version'
argument to OptionParser::

  parser = OptionParser(usage="%prog [-f] [-q]", version="%prog 1.0")

Note that "%prog" is expanded just like it is in 'usage'.  Apart from
that, 'version' can contain anything you like.  When you supply it,
Optik automatically adds a "--version" option to your parser.  If it
encounters this option on the command line, it expands your 'version'
string (by replacing "%prog"), prints it to stdout, and exits.

For example, if your script is called /usr/bin/foo, a user might do::

  $ /usr/bin/foo --version
  foo 1.0


Error-handling
--------------

The one thing you need to know for basic usage is how Optik behaves when
it encounters an error on the command-line -- e.g. "-n4x" where "-n" is
an integer-valued option.  Optik prints your usage message to stderr,
followed by a useful and human-readable error message.  Then it
terminates (calls sys.exit()) with a non-zero exit status.

If you don't like this, subclass OptionParser and override the error()
method.  See extending.txt.


Putting it all together
-----------------------

Here's what my Optik-based scripts usually look like::

  from optik import OptionParser

  [...]

  def main ():
      usage = "usage: %prog [options] arg"
      parser = OptionParser(usage)
      parser.add_option("-f", "--file", type="string", dest="filename",
                        help="read data from FILENAME")
      parser.add_option("-v", "--verbose",
                        action="store_true", dest="verbose")
      parser.add_option("-q", "--quiet",
                        action="store_false", dest="verbose")
      [... more options ...]

      (options, args) = parser.parse_args()
      if len(args) != 1:
          parser.error("incorrect number of arguments")

      if options.verbose:
          print "reading %s..." % options.filename

      [... go to work ...]


  if __name__ == "__main__":
      main()
