[getopt-sig] mapping cmdline-interface to python function

holger@trillke.net holger@trillke.net
Tue, 9 Apr 2002 14:11:44 +0200


Hello,

Please bear with me as this is my first time i make a proposal to
a python-sig. i am not new at enjoying and programming
python, though :-) 

The basic idea is to map the commandline interface into
a python class resp. a function. This can be done
via code inspection supporting both the programmer
to easily specify (in a pythonic way) options/arguments/documentation
and the user to get nice output of false usage.
Basically you can write a function and let
a special module ('main') do all the laborious
work for looking at sys.args

I start with a sample program that uses the proposed
functionality. it's a program  that takes at least
one filename, requires a "--id" or "-i" option together with
a decimal value. it also accepts more than one filename.

import main                # this contains the code to map 
                           # cmdline-interface to classes
                           # makes heavy intelligent use of
                           # code inspection

exec main.map_command_line_to('program') # classname as string

class program:
    version="0.1"            
    authors="Holger Krekel <holger@trillke.net>"
    download="http://www.trillke.net/~hpk/program/"
    help = """ longish tutorial with examples ...."

    def run(self, 
         quiet =('q',None,0), # quiet operation
         verbose=('v',None,0), # print verbose information
         id=('i','%d',1),      # specficies the id to use 
         filein='%s',          # the file to be read
         *morefiles):

    """ This program demonstrates the use of the main
        commandline interface mapper.
        it allows you to express without redundance 
        the options, documentation and types of options
        and parameters for you function in one place
        and it is a natural place :-)"""

OK. This is a complete specification of parsing and gettings
the cmdline-args. Let me explain the the lines
of the example:

- the names of the variables are longoptions. 

         quiet =('q',None,0), # quiet operation

- if variables have a tuple as a default value, then
  this is a specification with the following meaning:

  optionaliases,argumentspec, required = (...)

  - optionalias is a string or a tuple of strings to specify
    alias option names

  - argumentspec specifies in string notation what is expected
    (could of course contain more than one '%d %s')

  - required indicates if this option is required

  - an optional fourth element could contain a (typed!) default value

- if variables have a string as a default value, then
  this is a (non-option) argument and you can specify
  if it should be a decimal/string ...

         filein='%s',          # the file to be read
         *morefiles):

- additional args are in morefiles

- the documentation behind the variables is the
  documentation for this option! So if you 
  give an id that is not a decimal value, you would
  get a type-error which cites the documentation 
  and urges the user to use a decimal value. This
  happens automatically.

         id=('i','%d',1),      # id to look for in the files

- usage is in the __doc__ string of the function

    """ This program demonstrates the use of the main
        commandline interface mapper...."""

- non '__.*' class attributes are long options for informational use
  by the user. so

  "program --version" prints 0.1
  "program --download" prints http://www.trillke.net/~hpk/program
  "program --authors" prints 
  "program --help" prints a longish help (not just usage which)

this posting is already long enough, so i am not going into
details of my plan for implementation. But please note several
things:

- the program will just get all correct options and args
  or None if it wasn't specified (and not required).
  the main module educates the user about intended usage
  and your program can just work with the preprocessed given objects
  just as it would if have been called directly in python!

  if you say "program --id 17 /etc/passwd /etc/groups"
  your program.run function will have the following vars
  in locals()

         quiet = None
         verbose= None
         id=17
         filein='/etc/passwd'
         morefiles=('/etc/groups')

- documentation for options and program is right where
  the program is.

- we can easily provide hooks in the class to check for
  correct combinations of arguments, special forbidden
  usages and so on. 

- Please note, that we could improve this design so that 
  other python modules could easily use this program without making
  up an intermediate commandline-representation. they would call
  another function of the class "program" which raises a precise
  exception if you give wrong parameters. But it would feel as
  a ordinary python function... This idea really guides the 
  whole design.

Basically it makes writing standard commandline interfaces with *rich* 
functionality for the user a snap.  Maybe it could even use optik or 
other libs as a backend. 

What do you think? I would really like to work with some people
on improving the design before going to code it.

    holger