Optik: Advanced Usage
=====================

This is reference documentation.  If you haven't read the basic
documentation in basic.txt, do so now.


Creating and populating the parser
----------------------------------

There are several ways to populate the parser with options.  One way is
to pass a list of Options to the OptionParser constructor::

  parser = OptionParser(option_list=[
      make_option("-f", "--filename",
                  action="store", type="string", dest="filename"),
      make_option("-q", "--quiet",
                  action="store_false", dest="verbose")])

(As of Optik 1.3, make_option() is an alias for the Option class,
ie. this just calls the Option constructor.  A future version of Optik
will probably split Option into several classes, and make_option() will
become a factory function that picks the right class to instantiate.)

For long option lists, it's often more convenient/readable to create the
list separately::

  option_list = [make_option("-f", "--filename",
                             action="store", type="string", dest="filename"),
                 # ... 17 other options ...
                 make_option("-q", "--quiet",
                             action="store_false", dest="verbose")]
  parser = OptionParser(option_list=option_list)

Or, you can use the 'add_option()' method of OptionParser to add options
one-at-a-time::

  parser = OptionParser()
  parser.add_option("-f", "--filename",
                    action="store", type="string", dest="filename")
  parser.add_option("-q", "--quiet",
                    action="store_false", dest="verbose")

This method makes it easier to track down exceptions raised by the
Option constructor, which are common because of the complicated
interdependencies among the various keyword arguments -- if you get it
wrong, Optik raises OptionError.

add_option() can be called in one of two ways:

* pass it an Option instance  (as returned by make_option())
* pass it any combination of positional and keyword arguments that are
  acceptable to make_option() (ie., to the Option constructor),
  and it will create the Option instance for you (shown above)


Defining options
----------------

Each Option instance represents a set of synonymous command-line
options, ie. options that have the same meaning and effect, but
different spellings.  You can specify any number of short or long option
strings, but you must specify at least one option string.

To define an option with only a short option string::

  make_option("-f", ...)

And to define an option with only a long option string::

  make_option("--foo", ...)

The "..." represents a set of keyword arguments that define attributes
of the Option object.  Just which keyword args you must supply for a
given Option is fairly complicated (see the various '_check_*()' methods
in the Option class if you don't believe me), but you always have to
supply *some*.  If you get it wrong, Optik raises an OptionError
exception explaining your mistake.

The most important attribute of an option is its action, ie. what to do
when we encounter this option on the command-line.  The possible actions
are:

store
    store this option's argument [default]
store_const
    store a constant value
store_true
    store a true value
store_false
    store a false value
append
    append this option's argument to a list
count
    increment a counter by one
callback
    call a specified function
help
    print a usage message including all options and the
    documentation for them

(If you don't supply an action, the default is "store".  For this
action, you may also supply 'type' and 'dest' keywords; see below.)

As you can see, most actions involve storing or updating a value
somewhere.  Optik always creates a particular object (an instance of the
Values class defined in optik.option_parser) specifically for this
purpose.  Option arguments (and various other values) are stored as
attributes of this object, according to the 'dest' (destination)
argument to make_option()/add_option().

For example, when you call ::

  parser.parse_args()

one of the first things Optik does is create a "values" object::

  values = Values()

If one of the options in this parser is defined with ::

  make_option("-f", "--file", action="store", type="string", dest="filename")

and the command-line being parsed includes any of the following::

  -ffoo
  -f foo
  --file=foo
  --file foo

then Optik, on seeing the -f or --file option, will do the equivalent
of this::

  values.filename = "foo"

Clearly, the 'type' and 'dest' arguments are (usually) almost as
important as 'action'.  'action' is the only attribute that is
meaningful for *all* options, though, so it is the most important.


Option actions
--------------

The various option actions all have slightly different requirements and
effects.  Except for the "help" action, you must supply at least one
other keyword argument when creating the Option; the exact requirements
for each action are listed here.

* store [relevant: 'type', 'dest', 'nargs', 'choices']

  The option must be followed by an argument, which is
  converted to a value according to 'type' and stored in
  'dest'.  If nargs > 1, multiple arguments will be consumed
  from the command line; all will be converted according to
  'type' and stored to 'dest' as a tuple.  See the "Option
  types" section below.

  If 'choices' is supplied (a list or tuple of strings), the type
  defaults to "choice".

  If 'type' is not supplied, it defaults to "string".

  If 'dest' is not supplied, Optik derives a destination from the
  first long option strings (e.g., "--foo-bar" -> 'foo_bar').  If there
  are no long option strings, Optik derives a destination from the
  first short option string (e.g., "-f" -> 'f').

  Example::

    make_option("-f")
    make_option("-p", type="float", nargs=3, dest="point")

  Given the following command line::

    -f foo.txt -p 1 -3.5 4 -fbar.txt

  Optik will set ::

    values.f = "bar.txt"
    values.point = (1.0, -3.5, 4.0)

  (Actually, values.filename will be set twice, but only the
  second time is visible in the end.)

* store_const [required: 'const', 'dest']

  The 'const' value supplied to the Option constructor is
  stored in 'dest'.

  Example::

    make_option("-q", "--quiet",
           action="store_const", const=0, dest="verbose"),
    make_option("-v", "--verbose",
           action="store_const", const=1, dest="verbose"),
    make_option(None, "--noisy",
           action="store_const", const=2, dest="verbose"),

  If "--noisy" is seen, Optik will set ::

    values.verbose = 2

* store_true [required: 'dest']

  A special case of "store_const" that stores a true value
  (specifically, the integer 1) to 'dest'.

* store_false [required: 'dest']

  Like store_true, but stores a false value (the integer 0).

  Example::

    make_option(None, "--clobber", action="store_true", dest="clobber")
    make_option(None, "--no-clobber", action="store_false", dest="clobber")

* append [relevant: 'type', 'dest', 'nargs', 'choices']

  The option must be followed by an argument, which is appended to the
  list in 'dest'.  If no default value for 'dest' is supplied (ie. the
  default is None), an empty list is automatically created when Optik
  first encounters this option on the command-line.  If nargs > 1,
  multiple arguments are consumed, and a tuple of length nargs is
  appended to 'dest'.

  The defaults for 'type' and 'dest' are the same as for the "store"
  action.

  Example::

    make_option("-t", "--tracks", action="append", type="int")

  If "-t3" is seen on the command-line, Optik does the equivalent of::

    values.tracks = []
    values.tracks.append(int("3"))

  If, a little later on, "--tracks=4" is seen, it does::

    values.tracks.append(int("4"))

  See the "Error handling" section in basic.txt for information on how
  Optik deals with something like "--tracks=x".

* count [required: 'dest']

  Increment the integer stored at 'dest'.  'dest' is set to zero
  before being incremented the first time (unless you supply a default
  value).

  Example::

    make_option("-v", action="count", dest="verbosity")

  The first time "-v" is seen on the command line, Optik does the
  equivalent of::

    values.verbosity = 0
    values.verbosity += 1

  Every subsequent occurrence of "-v" results in ::

    values.verbosity += 1

* callback [required: 'callback';
  relevant: 'type', 'nargs', 'callback_args', 'callback_kwargs']

  Call the function specified by 'callback'.  The signature of
  this function should be ::

    func(option : Option,
         opt : string,
         value : any,
         parser : OptionParser,
         *args, **kwargs)

  Callback options are covered in detail in callback.txt.

* help [required: none]

  Prints a complete help message for all the options in the
  current option parser.  The help message is constructed from
  the 'usage' string passed to OptionParser's constructor and
  the 'help' string passed to every option.

  If no 'help' string is supplied for an option, it will still be
  listed in the help message.  To omit an option entirely, use
  the special value optik.SUPPRESS_HELP.

  Example::

    from optik import Option, OptionParser, SUPPRESS_HELP

    usage = "usage: %prog [options]"
    parser = OptionParser(usage, option_list=[
      make_option("-h", "--help", action="help"),
      make_option("-v", action="store_true", dest="verbose",
                  help="Be moderately verbose")
      make_option("--file", dest="filename",
                  help="Input file to read data from"),
      make_option("--secret", help=SUPPRESS_HELP)

  If Optik sees either "-h" or "--help" on the command line, it will
  print something like the following help message to stdout (assuming
  sys.argv[0] is "foo.py") ::

    usage: foo.py [options]

    options:
      -h, --help        Show this help message and exit
      -v                Be moderately verbose
      --file=FILENAME   Input file to read data from

  After printing the help message, Optik terminates your process
  with sys.exit(0).

* version [required: none]

  Prints the version number supplied to the OptionParser to stdout
  and exits.  The version number is actually formatted and printed
  by the print_version() method of OptionParser.  Generally only
  relevant if the 'version' argument is supplied to the OptionParser
  constructor.


Option types
------------

Optik supports six option types out of the box: string, int, long,
choice, float and complex.  (Of these, string, int, float, and choice
are the most commonly used -- long and complex are there mainly for
completeness.)  It's easy to add new option types by subclassing the
Option class; see extending.txt.

Arguments to string options are not checked or converted in any way: the
text on the command line is stored in the destination (or passed to the
callback) as-is.

Integer arguments are passed to int() to convert them to Python
integers.  If int() fails, so will Optik, although with a more useful
error message.  Internally, Optik raises OptionValueError in
optik.option.check_builtin(); at a higher level (in OptionParser) this
is caught and Optik terminates your program with a useful error message.

Likewise, float arguments are passed to float() for conversion, long
arguments to long(), and complex arguments to complex().  Apart from
that, they are handled identically to integer arguments.

Choice options are a subtype of string options.  A master list or
tuple of choices (strings) must be passed to the option constructor
(make_option or OptionParser.add_option) as the 'choices' keyword
argument.  Choice option arguments are compared against this master
list in optik.option.check_choice(), and OptionValueError is raised if
an unknown string is given.


Querying and manipulating your option parser
--------------------------------------------

Sometimes, it's useful to poke around your option parser and see what's
there.  OptionParser provides a couple of methods to help you out:

has_option(opt_str : string) -> boolean
    Given an option string such as "-q" or "--verbose", returns true
    if the OptionParser has an option with that option string.

get_option(opt_str : string) -> Option
    Returns the Option instance that implements the option string
    you supplied, or None if no options implement it.

remove_option(opt_str : string)
    If the OptionParser has an option corresponding to 'opt_str',
    that option is removed.  If that option provided any other
    option strings, all of those option strings become invalid.

    If 'opt_str' does not occur in any option belonging to this
    OptionParser, raises ValueError.


Conflicts between options
-------------------------

If you're not careful, it's easy to define conflicting options::

  parser.add_option("-n", "--dry-run", ...)
  [...]
  parser.add_option("-n", "--noisy", ...)

(This is even easier to do if you've defined your own OptionParser
subclass with some standard options.)

On the assumption that this is usually a mistake, Optik 1.2 and later
raise an exception (OptionConflictError) by default when this happens.
Since this is an easily-fixed programming error, you shouldn't try to
catch this exception -- fix your mistake and get on with life.

Sometimes, you want newer options to deliberately replace the option
strings used by older options.  You can achieve this by calling ::

  parser.set_conflict_handler("resolve")

which instructs Optik to resolve option conflicts intelligently.

Here's how it works: every time you add an option, Optik checks for
conflicts with previously-added options.  If it finds any, it invokes
the conflict-handling mechanism you specify either to the OptionParser
constructor::

  parser = OptionParser(..., conflict_handler="resolve")

or via the set_conflict_handler() method.

The default conflict-handling mechanism is "error".  The only other one
is "ignore", which restores the (arguably broken) behaviour of Optik 1.1
and earlier.

Here's an example: first, define an OptionParser set to resolve
conflicts intelligently::

  parser = OptionParser(conflict_handler="resolve")

Now add all of our options::

  parser.add_option("-n", "--dry-run", ..., help="original dry-run option")
  [...]
  parser.add_option("-n", "--noisy", ..., help="be noisy")

At this point, Optik detects that a previously-added option is already
using the "-n" option string.  Since conflict_handler == "resolve", it
resolves the situation by removing "-n" from the earlier option's list
of option strings.  Now, "--dry-run" is the only way for the user to
activate that option.  If the user asks for help, the help message will
reflect that, e.g.::

  options:
    --dry-run     original dry-run option
    [...]
    -n, --noisy   be noisy

Note that it's possible to whittle away the option strings for a
previously-added option until there are none left, and the user has no
way of invoking that option from the command-line.  In that case, Optik
removes that option completely, so it doesn't show up in help text or
anywhere else.  E.g. if we carry on with our existing OptionParser::

  parser.add_option("--dry-run", ..., help="new dry-run option")

At this point, the first -n/--dry-run option is no longer accessible, so
Optik removes it.  If the user asks for help, they'll get something like
this::

  options:
    [...]
    -n, --noisy   be noisy
    --dry-run     new dry-run option
