Home | History | Annotate | Download | only in pdist
      1 "Framework for command line interfaces like CVS.  See class CmdFrameWork."
      2 
      3 
      4 class CommandFrameWork:
      5 
      6     """Framework class for command line interfaces like CVS.
      7 
      8     The general command line structure is
      9 
     10             command [flags] subcommand [subflags] [argument] ...
     11 
     12     There's a class variable GlobalFlags which specifies the
     13     global flags options.  Subcommands are defined by defining
     14     methods named do_<subcommand>.  Flags for the subcommand are
     15     defined by defining class or instance variables named
     16     flags_<subcommand>.  If there's no command, method default()
     17     is called.  The __doc__ strings for the do_ methods are used
     18     for the usage message, printed after the general usage message
     19     which is the class variable UsageMessage.  The class variable
     20     PostUsageMessage is printed after all the do_ methods' __doc__
     21     strings.  The method's return value can be a suggested exit
     22     status.  [XXX Need to rewrite this to clarify it.]
     23 
     24     Common usage is to derive a class, instantiate it, and then call its
     25     run() method; by default this takes its arguments from sys.argv[1:].
     26     """
     27 
     28     UsageMessage = \
     29       "usage: (name)s [flags] subcommand [subflags] [argument] ..."
     30 
     31     PostUsageMessage = None
     32 
     33     GlobalFlags = ''
     34 
     35     def __init__(self):
     36         """Constructor, present for completeness."""
     37         pass
     38 
     39     def run(self, args = None):
     40         """Process flags, subcommand and options, then run it."""
     41         import getopt, sys
     42         if args is None: args = sys.argv[1:]
     43         try:
     44             opts, args = getopt.getopt(args, self.GlobalFlags)
     45         except getopt.error, msg:
     46             return self.usage(msg)
     47         self.options(opts)
     48         if not args:
     49             self.ready()
     50             return self.default()
     51         else:
     52             cmd = args[0]
     53             mname = 'do_' + cmd
     54             fname = 'flags_' + cmd
     55             try:
     56                 method = getattr(self, mname)
     57             except AttributeError:
     58                 return self.usage("command %r unknown" % (cmd,))
     59             try:
     60                 flags = getattr(self, fname)
     61             except AttributeError:
     62                 flags = ''
     63             try:
     64                 opts, args = getopt.getopt(args[1:], flags)
     65             except getopt.error, msg:
     66                 return self.usage(
     67                         "subcommand %s: " % cmd + str(msg))
     68             self.ready()
     69             return method(opts, args)
     70 
     71     def options(self, opts):
     72         """Process the options retrieved by getopt.
     73         Override this if you have any options."""
     74         if opts:
     75             print "-"*40
     76             print "Options:"
     77             for o, a in opts:
     78                 print 'option', o, 'value', repr(a)
     79             print "-"*40
     80 
     81     def ready(self):
     82         """Called just before calling the subcommand."""
     83         pass
     84 
     85     def usage(self, msg = None):
     86         """Print usage message.  Return suitable exit code (2)."""
     87         if msg: print msg
     88         print self.UsageMessage % {'name': self.__class__.__name__}
     89         docstrings = {}
     90         c = self.__class__
     91         while 1:
     92             for name in dir(c):
     93                 if name[:3] == 'do_':
     94                     if docstrings.has_key(name):
     95                         continue
     96                     try:
     97                         doc = getattr(c, name).__doc__
     98                     except:
     99                         doc = None
    100                     if doc:
    101                         docstrings[name] = doc
    102             if not c.__bases__:
    103                 break
    104             c = c.__bases__[0]
    105         if docstrings:
    106             print "where subcommand can be:"
    107             names = docstrings.keys()
    108             names.sort()
    109             for name in names:
    110                 print docstrings[name]
    111         if self.PostUsageMessage:
    112             print self.PostUsageMessage
    113         return 2
    114 
    115     def default(self):
    116         """Default method, called when no subcommand is given.
    117         You should always override this."""
    118         print "Nobody expects the Spanish Inquisition!"
    119 
    120 
    121 def test():
    122     """Test script -- called when this module is run as a script."""
    123     import sys
    124     class Hello(CommandFrameWork):
    125         def do_hello(self, opts, args):
    126             "hello -- print 'hello world', needs no arguments"
    127             print "Hello, world"
    128     x = Hello()
    129     tests = [
    130             [],
    131             ['hello'],
    132             ['spam'],
    133             ['-x'],
    134             ['hello', '-x'],
    135             None,
    136             ]
    137     for t in tests:
    138         print '-'*10, t, '-'*10
    139         sts = x.run(t)
    140         print "Exit status:", repr(sts)
    141 
    142 
    143 if __name__ == '__main__':
    144     test()
    145